From 40e7f217bdf654132558c54729068147d8321b65 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 18 Jan 2020 16:01:19 +1100 Subject: [PATCH 01/39] allow custom cloudfront config --- packages/serverless-nextjs-component/serverless.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/serverless-nextjs-component/serverless.js b/packages/serverless-nextjs-component/serverless.js index 0e51c2755a..b3258828f6 100644 --- a/packages/serverless-nextjs-component/serverless.js +++ b/packages/serverless-nextjs-component/serverless.js @@ -282,6 +282,7 @@ class NextjsComponent extends Component { : process.cwd(); const nextStaticPath = inputs.nextStaticDir || ""; const staticPath = nextStaticPath || nextConfigPath; + const cloudFrontConfigs = inputs.cloudfront || {}; const [defaultBuildManifest, apiBuildManifest] = await Promise.all([ this.readDefaultBuildManifest(nextConfigPath), @@ -372,7 +373,8 @@ class NextjsComponent extends Component { }, "static/*": { ttl: 86400 - } + }, + ...cloudFrontConfigs } } ]; From 5680f90f8f744f49a061e30d3953570d5b31d123 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Mon, 20 Jan 2020 21:20:45 +1100 Subject: [PATCH 02/39] move cloudfront config generation to after lambda arn is known --- packages/serverless-nextjs-component/serverless.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/serverless-nextjs-component/serverless.js b/packages/serverless-nextjs-component/serverless.js index b3258828f6..9b632008ce 100644 --- a/packages/serverless-nextjs-component/serverless.js +++ b/packages/serverless-nextjs-component/serverless.js @@ -373,8 +373,7 @@ class NextjsComponent extends Component { }, "static/*": { ttl: 86400 - }, - ...cloudFrontConfigs + } } } ]; @@ -451,6 +450,16 @@ class NextjsComponent extends Component { const defaultEdgeLambdaPublishOutputs = await defaultEdgeLambda.publishVersion(); + // Add any custom cloudfront configuration + Object.entries(cloudFrontConfigs).map(([path, config]) => { + cloudFrontOrigins[0].pathPatterns[path] = { + "lambda@edge": { + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` + }, + ...config + }; + }); + const cloudFrontOutputs = await cloudFront({ defaults: { ttl: 0, From 483d04a7cb64a38b4280e301b0116259c749a01b Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Mon, 20 Jan 2020 23:03:04 +1100 Subject: [PATCH 03/39] add tests and update readme --- package-lock.json | 40 ++++++++++++ package.json | 4 ++ .../serverless-nextjs-component/README.md | 22 +++++++ .../__tests__/custom-inputs.test.js | 62 +++++++++++++++++++ 4 files changed, 128 insertions(+) diff --git a/package-lock.json b/package-lock.json index 692a76983e..bcc1a43bf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11167,6 +11167,46 @@ "jest-util": "^24.9.0" } }, + "jest-extended": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.2.tgz", + "integrity": "sha512-gwNMXrAPN0IY5L7VXWfSlC2aGo0KHIsGGcW+lTHYpedt5SJksEvBgMxs29iNikiNOz+cqAZY1s/+kYK0jlj4Jw==", + "dev": true, + "requires": { + "expect": "^24.1.0", + "jest-get-type": "^22.4.3", + "jest-matcher-utils": "^22.0.0" + }, + "dependencies": { + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "jest-matcher-utils": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", + "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-get-type": "^22.4.3", + "pretty-format": "^22.4.3" + } + }, + "pretty-format": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", + "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } + } + }, "jest-get-type": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", diff --git a/package.json b/package.json index 1b088f2717..a8842ed1a1 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "eslint-config-prettier": "^4.2.0", "eslint-plugin-prettier": "^3.0.1", "jest": "^24.9.0", + "jest-extended": "^0.11.2", "jest-when": "^2.7.0", "lerna": "^3.16.4", "next": "^9.1.5", @@ -71,6 +72,9 @@ ], "setupFiles": [ "/jest.setup.js" + ], + "setupFilesAfterEnv": [ + "jest-extended" ] }, "dependencies": { diff --git a/packages/serverless-nextjs-component/README.md b/packages/serverless-nextjs-component/README.md index 6bd9d04f5f..1a62db6dff 100644 --- a/packages/serverless-nextjs-component/README.md +++ b/packages/serverless-nextjs-component/README.md @@ -18,6 +18,7 @@ A zero configuration Nextjs 9.0 [serverless component](https://github.com/server - [Getting started](#getting-started) - [Lambda@Edge configuration](#lambda-at-edge-configuration) - [Custom domain name](#custom-domain-name) +- [Custom CloudFront configuration](#custom-cloudfront-configuration) - [AWS Permissions](#aws-permissions) - [Architecture](#architecture) - [Inputs](#inputs) @@ -119,6 +120,26 @@ myNextApplication: domain: ["sub", "example.com"] # [ sub-domain, domain ] ``` +### Custom CloudFront configuration + +To specify your own CloudFront inputs, just add any [aws-cloudfront inputs](https://github.com/serverless-components/aws-cloudfront#3-configure) under `cloudfront`: + +```yml +# serverless.yml + +myNextApplication: + component: serverless-next.js + inputs: + cloudfront: + my-page/*: + ttl: 0 + forward: + cookies: "all" + queryString: false + my-other-page: + viewerProtocolPolicy: redirect-to-https +``` + ### AWS Permissions By default the Lambda@Edge functions run using AWSLambdaBasicExecutionRole which only allows uploading logs to CloudWatch. If you need permissions beyond this, like for example access to DynamoDB or any other AWS resource you will need your own custom policy arn: @@ -211,6 +232,7 @@ The fourth cache behaviour handles next API requests `api/*`. | build.cwd | `string` | `./` | Override the current working directory | | build.enabled | `boolean` | `true` | Same as passing `build:false` but from within the config | | build.env | `object` | `{}` | Add additional environment variables to the script | +| cloudfront | `object` | `{}` | Inputs to be passed to [aws-cloudfront](https://github.com/serverless-components/aws-cloudfront) | Custom inputs can be configured like this: diff --git a/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js b/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js index 3e4ccd4757..8bd3e0f5ec 100644 --- a/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-nextjs-component/__tests__/custom-inputs.test.js @@ -13,6 +13,21 @@ const { jest.mock("execa"); +expect.extend({ + objectContainingCloudFrontInput(received, expected) { + const receivedPathPatterns = received.origins[0].pathPatterns; + expect(receivedPathPatterns).toContainKeys(Object.keys(expected)); + for (pathPattern in expected) { + expect(receivedPathPatterns[pathPattern]).toContainEntries( + Object.entries(expected[pathPattern]) + ); + } + return { + pass: true + }; + } +}); + describe("Custom inputs", () => { describe.each([ [["dev", "example.com"], "https://dev.example.com"], @@ -92,6 +107,53 @@ describe("Custom inputs", () => { }); }); + describe.each([ + { test1: { a: "1", b: "2" } }, + { "test*123": { some: "other", 3: "here" }, second: { entry: "here" } } + ])("Custom CloudFront input", inputCloudFront => { + let tmpCwd; + let componentOutputs; + + const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + + beforeEach(async () => { + execa.mockResolvedValueOnce(); + + tmpCwd = process.cwd(); + process.chdir(fixturePath); + + mockS3.mockResolvedValue({ + name: "bucket-xyz" + }); + mockLambda.mockResolvedValue({ + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" + }); + mockLambdaPublish.mockResolvedValue({ + version: "v1" + }); + mockCloudFront.mockResolvedValueOnce({ + url: "https://cloudfrontdistrib.amazonaws.com" + }); + + const component = new NextjsComponent(); + componentOutputs = await component.default({ + policy: "arn:aws:iam::aws:policy/CustomRole", + memory: 512, + cloudfront: inputCloudFront + }); + }); + + afterEach(() => { + process.chdir(tmpCwd); + }); + + it("passes custom cloudfront input to cloudfront component", () => { + expect(mockCloudFront).toBeCalledWith( + expect.objectContainingCloudFrontInput(inputCloudFront) + ); + }); + }); + describe.each([ [undefined, { defaultMemory: 512, apiMemory: 512 }], [{}, { defaultMemory: 512, apiMemory: 512 }], From 7c2c9b3c91cc9b6430e2b3922d7214ba2d80c78e Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Mon, 20 Jan 2020 23:26:11 +1100 Subject: [PATCH 04/39] update main readme --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 6bd9d04f5f..1a62db6dff 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ A zero configuration Nextjs 9.0 [serverless component](https://github.com/server - [Getting started](#getting-started) - [Lambda@Edge configuration](#lambda-at-edge-configuration) - [Custom domain name](#custom-domain-name) +- [Custom CloudFront configuration](#custom-cloudfront-configuration) - [AWS Permissions](#aws-permissions) - [Architecture](#architecture) - [Inputs](#inputs) @@ -119,6 +120,26 @@ myNextApplication: domain: ["sub", "example.com"] # [ sub-domain, domain ] ``` +### Custom CloudFront configuration + +To specify your own CloudFront inputs, just add any [aws-cloudfront inputs](https://github.com/serverless-components/aws-cloudfront#3-configure) under `cloudfront`: + +```yml +# serverless.yml + +myNextApplication: + component: serverless-next.js + inputs: + cloudfront: + my-page/*: + ttl: 0 + forward: + cookies: "all" + queryString: false + my-other-page: + viewerProtocolPolicy: redirect-to-https +``` + ### AWS Permissions By default the Lambda@Edge functions run using AWSLambdaBasicExecutionRole which only allows uploading logs to CloudWatch. If you need permissions beyond this, like for example access to DynamoDB or any other AWS resource you will need your own custom policy arn: @@ -211,6 +232,7 @@ The fourth cache behaviour handles next API requests `api/*`. | build.cwd | `string` | `./` | Override the current working directory | | build.enabled | `boolean` | `true` | Same as passing `build:false` but from within the config | | build.env | `object` | `{}` | Add additional environment variables to the script | +| cloudfront | `object` | `{}` | Inputs to be passed to [aws-cloudfront](https://github.com/serverless-components/aws-cloudfront) | Custom inputs can be configured like this: From da1706fc1492edcbe80e6a422c6ff0d775806a32 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Mon, 20 Jan 2020 23:53:33 +1100 Subject: [PATCH 05/39] commit test md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1a62db6dff..16cd612642 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ A zero configuration Nextjs 9.0 [serverless component](https://github.com/server - [Architecture](#architecture) - [Inputs](#inputs) - [FAQ](#faq) +- [TEST](#test) ### Motivation From a1521c5b679759b75ffc5536832c55bc998df6ec Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Mon, 20 Jan 2020 23:59:43 +1100 Subject: [PATCH 06/39] remove test list item --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 16cd612642..1a62db6dff 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ A zero configuration Nextjs 9.0 [serverless component](https://github.com/server - [Architecture](#architecture) - [Inputs](#inputs) - [FAQ](#faq) -- [TEST](#test) ### Motivation From 64bc744ca7a78baadb7e006dbaa06f9a56940894 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 2 May 2020 10:00:58 +1000 Subject: [PATCH 07/39] WIP --- package-lock.json | 32 ++++++++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 34 insertions(+) diff --git a/package-lock.json b/package-lock.json index bcc1a43bf5..3718daa6b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3278,6 +3278,23 @@ "write-file-atomic": "^2.3.0" } }, + "@mapbox/eslint-config-mapbox": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@mapbox/eslint-config-mapbox/-/eslint-config-mapbox-1.2.1.tgz", + "integrity": "sha512-hhE2sN7L/RO+MhdHIhmpL0KLhA/sWxV1WWH998fmGimI40XDbBgG1Z4d8nTlfuf9jaRDJ88/uklSBvFRhvMeDA==", + "dev": true + }, + "@mapbox/s3urls": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@mapbox/s3urls/-/s3urls-1.5.3.tgz", + "integrity": "sha1-hXj1GUGvK6g2Yj9JQadhUVzpL/E=", + "dev": true, + "requires": { + "@mapbox/eslint-config-mapbox": "^1.0.0", + "minimist": "^1.1.0", + "s3signed": "^0.1.0" + } + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -12121,6 +12138,12 @@ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -15666,6 +15689,15 @@ "tslib": "^1.9.0" } }, + "s3signed": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/s3signed/-/s3signed-0.1.0.tgz", + "integrity": "sha1-rgO4YllBMhXtQ+mShcjDR1eXNfs=", + "dev": true, + "requires": { + "aws-sdk": "^2.0.4" + } + }, "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", diff --git a/package.json b/package.json index a8842ed1a1..55711ed5e1 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ }, "homepage": "https://github.com/danielcondemarin/serverless-nextjs-plugin#readme", "devDependencies": { + "@mapbox/s3urls": "^1.5.3", "adm-zip": "^0.4.13", "coveralls": "^3.0.6", "eslint": "^5.16.0", @@ -42,6 +43,7 @@ "jest-extended": "^0.11.2", "jest-when": "^2.7.0", "lerna": "^3.16.4", + "lodash.merge": "^4.6.2", "next": "^9.1.5", "prettier": "^1.17.1", "react": "^16.9.0", From 8cac047d51b1c0f8cddc9b557a3051e126cf2ed0 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 2 May 2020 12:54:14 +1000 Subject: [PATCH 08/39] Revert "WIP" This reverts commit 64bc744ca7a78baadb7e006dbaa06f9a56940894. --- package-lock.json | 32 -------------------------------- package.json | 2 -- 2 files changed, 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3718daa6b4..bcc1a43bf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3278,23 +3278,6 @@ "write-file-atomic": "^2.3.0" } }, - "@mapbox/eslint-config-mapbox": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@mapbox/eslint-config-mapbox/-/eslint-config-mapbox-1.2.1.tgz", - "integrity": "sha512-hhE2sN7L/RO+MhdHIhmpL0KLhA/sWxV1WWH998fmGimI40XDbBgG1Z4d8nTlfuf9jaRDJ88/uklSBvFRhvMeDA==", - "dev": true - }, - "@mapbox/s3urls": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@mapbox/s3urls/-/s3urls-1.5.3.tgz", - "integrity": "sha1-hXj1GUGvK6g2Yj9JQadhUVzpL/E=", - "dev": true, - "requires": { - "@mapbox/eslint-config-mapbox": "^1.0.0", - "minimist": "^1.1.0", - "s3signed": "^0.1.0" - } - }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -12138,12 +12121,6 @@ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", "dev": true }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -15689,15 +15666,6 @@ "tslib": "^1.9.0" } }, - "s3signed": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/s3signed/-/s3signed-0.1.0.tgz", - "integrity": "sha1-rgO4YllBMhXtQ+mShcjDR1eXNfs=", - "dev": true, - "requires": { - "aws-sdk": "^2.0.4" - } - }, "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", diff --git a/package.json b/package.json index 55711ed5e1..a8842ed1a1 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ }, "homepage": "https://github.com/danielcondemarin/serverless-nextjs-plugin#readme", "devDependencies": { - "@mapbox/s3urls": "^1.5.3", "adm-zip": "^0.4.13", "coveralls": "^3.0.6", "eslint": "^5.16.0", @@ -43,7 +42,6 @@ "jest-extended": "^0.11.2", "jest-when": "^2.7.0", "lerna": "^3.16.4", - "lodash.merge": "^4.6.2", "next": "^9.1.5", "prettier": "^1.17.1", "react": "^16.9.0", From 3dc818068d210df20a678101c92596b4a2061d6b Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 2 May 2020 14:13:55 +1000 Subject: [PATCH 09/39] pull upstream and handle merge conflicts --- package-lock.json | 387 +++++++++++++++--- package.json | 2 +- .../lambda-at-edge-compat/package-lock.json | 2 +- packages/lambda-at-edge/package.json | 2 +- .../__tests__/custom-inputs.test.js | 147 +++---- .../serverless-component/package-lock.json | 190 ++++++++- packages/serverless-component/serverless.js | 94 ++--- 7 files changed, 623 insertions(+), 201 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a047d8b47..5870c91069 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11255,65 +11255,6 @@ "resolve-cwd": "^3.0.0" } }, - "jest-environment-node": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", - "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", - "dev": true, - "requires": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0" - } - }, - "jest-extended": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.2.tgz", - "integrity": "sha512-gwNMXrAPN0IY5L7VXWfSlC2aGo0KHIsGGcW+lTHYpedt5SJksEvBgMxs29iNikiNOz+cqAZY1s/+kYK0jlj4Jw==", - "dev": true, - "requires": { - "expect": "^24.1.0", - "jest-get-type": "^22.4.3", - "jest-matcher-utils": "^22.0.0" - }, - "dependencies": { - "jest-get-type": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", - "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", - "dev": true - }, - "jest-matcher-utils": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", - "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" - } - }, - "pretty-format": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", - "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - } - } - } - }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -12481,6 +12422,334 @@ "semver": "^6.3.0" } }, + "jest-extended": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.2.tgz", + "integrity": "sha512-gwNMXrAPN0IY5L7VXWfSlC2aGo0KHIsGGcW+lTHYpedt5SJksEvBgMxs29iNikiNOz+cqAZY1s/+kYK0jlj4Jw==", + "dev": true, + "requires": { + "expect": "^24.1.0", + "jest-get-type": "^22.4.3", + "jest-matcher-utils": "^22.0.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", + "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, + "dependencies": { + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + } + } + }, + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "jest-matcher-utils": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", + "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-get-type": "^22.4.3", + "pretty-format": "^22.4.3" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "pretty-format": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", + "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, "jest-get-type": { "version": "25.2.6", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", diff --git a/package.json b/package.json index a01aeb6eba..e5adbd47db 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "homepage": "https://github.com/danielcondemarin/serverless-next.js#readme", "devDependencies": { "@babel/preset-typescript": "^7.9.0", + "@sls-next/lambda-at-edge": "file:packages/lambda-at-edge", "@types/jest": "^25.2.1", "@typescript-eslint/eslint-plugin": "^2.28.0", "@typescript-eslint/parser": "^2.28.0", @@ -50,7 +51,6 @@ "lerna": "^3.16.4", "next": "^9.1.5", "next-aws-cloudfront": "file:packages/lambda-at-edge-compat", - "@sls-next/lambda-at-edge": "file:packages/lambda-at-edge", "prettier": "^1.17.1", "react": "^16.9.0", "react-dom": "^16.9.0", diff --git a/packages/lambda-at-edge-compat/package-lock.json b/packages/lambda-at-edge-compat/package-lock.json index 620b8c05c6..c7fd5c6e65 100644 --- a/packages/lambda-at-edge-compat/package-lock.json +++ b/packages/lambda-at-edge-compat/package-lock.json @@ -6,7 +6,7 @@ "dependencies": { "@types/aws-lambda": { "version": "8.10.50", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.50.tgz", + "resolved": false, "integrity": "sha512-RDzmQ5mO1f0BViKiuOudENZmoCACEa461nTRVtxhsAiEqGCgwdhCYN0aFgk42X5+ELAiqJKbv2mK0LkopYRYQg==", "dev": true } diff --git a/packages/lambda-at-edge/package.json b/packages/lambda-at-edge/package.json index 0a47c580be..3200c3ba13 100644 --- a/packages/lambda-at-edge/package.json +++ b/packages/lambda-at-edge/package.json @@ -39,7 +39,7 @@ "ts-loader": "^7.0.0" }, "peerDependencies": { - "next-aws-cloudfront": "file:../cloudfront-lambda@edge-compat" + "next-aws-cloudfront": "file:../lambda-at-edge-compat" }, "dependencies": { "execa": "^4.0.0", diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 88c2abd11a..0279003d17 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -7,29 +7,24 @@ const NextjsComponent = require("../serverless"); const obtainDomains = require("../lib/obtainDomains"); const { DEFAULT_LAMBDA_CODE_DIR, - API_LAMBDA_CODE_DIR + API_LAMBDA_CODE_DIR, } = require("../constants"); -<<<<<<< HEAD:packages/serverless-nextjs-component/__tests__/custom-inputs.test.js -jest.mock("execa"); - expect.extend({ objectContainingCloudFrontInput(received, expected) { const receivedPathPatterns = received.origins[0].pathPatterns; expect(receivedPathPatterns).toContainKeys(Object.keys(expected)); - for (pathPattern in expected) { + for (pathPattern in expected.origins) { expect(receivedPathPatterns[pathPattern]).toContainEntries( Object.entries(expected[pathPattern]) ); } return { - pass: true + pass: true, }; - } + }, }); -======= ->>>>>>> 269537d01796fe29c638e9c4ce7f241a8cc663e1:packages/serverless-component/__tests__/custom-inputs.test.js describe("Custom inputs", () => { let tmpCwd; let componentOutputs; @@ -39,7 +34,7 @@ describe("Custom inputs", () => { [["www", "example.com"], "https://www.example.com"], [[undefined, "example.com"], "https://www.example.com"], [["example.com"], "https://www.example.com"], - ["example.com", "https://www.example.com"] + ["example.com", "https://www.example.com"], ])("Custom domain", (inputDomains, expectedDomain, memory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -48,26 +43,26 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockS3.mockResolvedValue({ - name: "bucket-xyz" + name: "bucket-xyz", }); mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", }); mockLambdaPublish.mockResolvedValue({ - version: "v1" + version: "v1", }); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); mockDomain.mockResolvedValueOnce({ - domains: [expectedDomain] + domains: [expectedDomain], }); const component = new NextjsComponent(); componentOutputs = await component.default({ policy: "arn:aws:iam::aws:policy/CustomRole", domain: inputDomains, - memory: 512 + memory: 512, }); }); @@ -83,9 +78,9 @@ describe("Custom inputs", () => { domain, subdomains: { [subdomain]: { - url: "https://cloudfrontdistrib.amazonaws.com" - } - } + url: "https://cloudfrontdistrib.amazonaws.com", + }, + }, }); }); @@ -95,9 +90,9 @@ describe("Custom inputs", () => { description: expect.stringContaining("Default Lambda@Edge"), role: expect.objectContaining({ policy: { - arn: "arn:aws:iam::aws:policy/CustomRole" - } - }) + arn: "arn:aws:iam::aws:policy/CustomRole", + }, + }), }) ); }); @@ -109,37 +104,35 @@ describe("Custom inputs", () => { describe.each([ { test1: { a: "1", b: "2" } }, - { "test*123": { some: "other", 3: "here" }, second: { entry: "here" } } - ])("Custom CloudFront input", inputCloudFront => { + { "test*123": { some: "other", 3: "here" }, second: { entry: "here" } }, + ])("Custom CloudFront input", (inputCloudFront) => { let tmpCwd; let componentOutputs; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); beforeEach(async () => { - execa.mockResolvedValueOnce(); - tmpCwd = process.cwd(); process.chdir(fixturePath); mockS3.mockResolvedValue({ - name: "bucket-xyz" + name: "bucket-xyz", }); mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", }); mockLambdaPublish.mockResolvedValue({ - version: "v1" + version: "v1", }); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ policy: "arn:aws:iam::aws:policy/CustomRole", memory: 512, - cloudfront: inputCloudFront + cloudfront: inputCloudFront, }); }); @@ -162,8 +155,8 @@ describe("Custom inputs", () => { [{ apiLambda: 2048 }, { defaultMemory: 512, apiMemory: 2048 }], [ { defaultLambda: 128, apiLambda: 2048 }, - { defaultMemory: 128, apiMemory: 2048 } - ] + { defaultMemory: 128, apiMemory: 2048 }, + ], ])("Lambda memory input", (inputMemory, expectedMemory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -171,12 +164,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - memory: inputMemory + memory: inputMemory, }); }); it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { @@ -186,7 +179,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - memory: defaultMemory + memory: defaultMemory, }) ); @@ -194,7 +187,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - memory: apiMemory + memory: apiMemory, }) ); }); @@ -208,8 +201,8 @@ describe("Custom inputs", () => { [{ apiLambda: 20 }, { defaultTimeout: 10, apiTimeout: 20 }], [ { defaultLambda: 15, apiLambda: 20 }, - { defaultTimeout: 15, apiTimeout: 20 } - ] + { defaultTimeout: 15, apiTimeout: 20 }, + ], ])("Lambda timeout input", (inputTimeout, expectedTimeout) => { let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -219,12 +212,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - timeout: inputTimeout + timeout: inputTimeout, }); }); @@ -239,7 +232,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - timeout: defaultTimeout + timeout: defaultTimeout, }) ); @@ -247,7 +240,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - timeout: apiTimeout + timeout: apiTimeout, }) ); }); @@ -259,16 +252,16 @@ describe("Custom inputs", () => { ["fooFunction", { defaultName: "fooFunction", apiName: "fooFunction" }], [ { defaultLambda: "fooFunction" }, - { defaultName: "fooFunction", apiName: undefined } + { defaultName: "fooFunction", apiName: undefined }, ], [ { apiLambda: "fooFunction" }, - { defaultName: undefined, apiName: "fooFunction" } + { defaultName: undefined, apiName: "fooFunction" }, ], [ { defaultLambda: "fooFunction", apiLambda: "barFunction" }, - { defaultName: "fooFunction", apiName: "barFunction" } - ] + { defaultName: "fooFunction", apiName: "barFunction" }, + ], ])("Lambda name input", (inputName, expectedName) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -276,12 +269,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - name: inputName + name: inputName, }); }); it(`sets default lambda name to ${expectedName.defaultName} and api lambda name to ${expectedName.apiName}`, () => { @@ -289,7 +282,7 @@ describe("Custom inputs", () => { // Default Lambda const expectedDefaultObject = { - code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR) + code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), }; if (defaultName) expectedDefaultObject.name = defaultName; @@ -299,7 +292,7 @@ describe("Custom inputs", () => { // Api Lambda const expectedApiObject = { - code: path.join(fixturePath, API_LAMBDA_CODE_DIR) + code: path.join(fixturePath, API_LAMBDA_CODE_DIR), }; if (apiName) expectedApiObject.name = apiName; @@ -314,56 +307,34 @@ describe("Custom inputs", () => { [{}, {}], // empty input [ { defaults: { ttl: 500, "lambda@edge": "ignored value" } }, - { defaults: { ttl: 500 } } // expecting lambda@edge value to be ignored + { defaults: { ttl: 500 } }, // expecting lambda@edge value to be ignored ], [ { defaults: { forward: { headers: "X" } } }, - { defaults: { forward: { headers: "X" } } } + { defaults: { forward: { headers: "X" } } }, ], [ { "api/*": { ttl: 500, "lambda@edge": "ignored value" } }, - { "api/*": { ttl: 500 } } // expecting lambda@edge value to be ignored + { "api/*": { ttl: 500 } }, // expecting lambda@edge value to be ignored ], - [ - { - origins: [ - "http://some-origin", - "/relative", - { url: "http://diff-origin" }, - { url: "/diff-relative" } - ] - }, - { - origins: [ - "http://some-origin", - "http://bucket-xyz.s3.amazonaws.com/relative", - { url: "http://diff-origin" }, - { url: "http://bucket-xyz.s3.amazonaws.com/diff-relative" } - ] - } - ] ])("Custom cloudfront inputs", (inputCloudfrontConfig, expectedInConfig) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); const defaultCloudfrontInputs = expectedInConfig.defaults || {}; -<<<<<<< HEAD:packages/serverless-nextjs-component/__tests__/custom-inputs.test.js const apiCloudfrontInputs = expectedInConfig["api/*"] || {}; -======= - const apiCloudfrontInputs = expectedInConfig.api || {}; const originCloudfrontInputs = expectedInConfig.origins || []; ->>>>>>> 269537d01796fe29c638e9c4ce7f241a8cc663e1:packages/serverless-component/__tests__/custom-inputs.test.js const cloudfrontConfig = { defaults: { ttl: 0, allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true + queryString: true, }, ...defaultCloudfrontInputs, "lambda@edge": { "origin-request": - "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1" - } + "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", + }, }, origins: [ { @@ -377,34 +348,34 @@ describe("Custom inputs", () => { "GET", "OPTIONS", "PUT", - "PATCH" + "PATCH", ], ttl: 0, "lambda@edge": { "origin-request": - "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1" + "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", }, - ...apiCloudfrontInputs + ...apiCloudfrontInputs, }, - "static/*": { ttl: 86400 } + "static/*": { ttl: 86400 }, }, private: true, - url: "http://bucket-xyz.s3.amazonaws.com" + url: "http://bucket-xyz.s3.amazonaws.com", }, - ...originCloudfrontInputs - ] + ...originCloudfrontInputs, + ], }; beforeEach(async () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - cloudfront: inputCloudfrontConfig + cloudfront: inputCloudfrontConfig, }); }); diff --git a/packages/serverless-component/package-lock.json b/packages/serverless-component/package-lock.json index 291b4ef638..7c97c8af5a 100644 --- a/packages/serverless-component/package-lock.json +++ b/packages/serverless-component/package-lock.json @@ -188,7 +188,195 @@ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" }, "@sls-next/lambda-at-edge": { - "version": "file:../lambda-at-edge" + "version": "file:../lambda-at-edge", + "requires": { + "execa": "^4.0.0", + "fs-extra": "^9.0.0", + "path-to-regexp": "^6.1.0" + }, + "dependencies": { + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "execa": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.0.tgz", + "integrity": "sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA==", + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "fs-extra": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", + "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-to-regexp": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.1.0.tgz", + "integrity": "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } }, "@szmarczak/http-timer": { "version": "1.1.2", diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 99743a1712..6ab0cfc1e0 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -48,7 +48,7 @@ class NextjsComponent extends Component { cwd: inputs.build && inputs.build.cwd ? path.resolve(inputs.build.cwd) - : nextConfigPath + : nextConfigPath, }; if (buildConfig.enabled) { @@ -59,7 +59,7 @@ class NextjsComponent extends Component { cmd: buildConfig.cmd, cwd: buildConfig.cwd, env: buildConfig.env, - args: buildConfig.args + args: buildConfig.args, } ); @@ -77,24 +77,24 @@ class NextjsComponent extends Component { const [defaultBuildManifest, apiBuildManifest] = await Promise.all([ this.readDefaultBuildManifest(nextConfigPath), - this.readApiBuildManifest(nextConfigPath) + this.readApiBuildManifest(nextConfigPath), ]); const [ bucket, cloudFront, defaultEdgeLambda, - apiEdgeLambda + apiEdgeLambda, ] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), this.load("@serverless/aws-lambda", "defaultEdgeLambda"), - this.load("@serverless/aws-lambda", "apiEdgeLambda") + this.load("@serverless/aws-lambda", "apiEdgeLambda"), ]); const bucketOutputs = await bucket({ accelerated: true, - name: inputs.bucketName + name: inputs.bucketName, }); const nonDynamicHtmlPages = Object.values( @@ -103,13 +103,13 @@ class NextjsComponent extends Component { const dynamicHtmlPages = Object.values( defaultBuildManifest.pages.html.dynamic - ).map(x => x.file); + ).map((x) => x.file); const uploadHtmlPages = [...nonDynamicHtmlPages, ...dynamicHtmlPages].map( - page => + (page) => bucket.upload({ file: join(nextConfigPath, ".next/serverless", page), - key: `static-pages/${page.replace("pages/", "")}` + key: `static-pages/${page.replace("pages/", "")}`, }) ); @@ -117,21 +117,21 @@ class NextjsComponent extends Component { bucket.upload({ dir: join(nextConfigPath, ".next/static"), keyPrefix: "_next/static", - cacheControl: "public, max-age=31536000, immutable" + cacheControl: "public, max-age=31536000, immutable", }), - ...uploadHtmlPages + ...uploadHtmlPages, ]; const [publicDirExists, staticDirExists] = await Promise.all([ fse.exists(join(staticPath, "public")), - fse.exists(join(staticPath, "static")) + fse.exists(join(staticPath, "static")), ]); if (publicDirExists) { assetsUpload.push( bucket.upload({ dir: join(staticPath, "public"), - keyPrefix: "public" + keyPrefix: "public", }) ); } @@ -140,7 +140,7 @@ class NextjsComponent extends Component { assetsUpload.push( bucket.upload({ dir: join(staticPath, "static"), - keyPrefix: "static" + keyPrefix: "static", }) ); } @@ -149,15 +149,15 @@ class NextjsComponent extends Component { defaultBuildManifest.cloudFrontOrigins = { staticOrigin: { - domainName: `${bucketOutputs.name}.s3.amazonaws.com` - } + domainName: `${bucketOutputs.name}.s3.amazonaws.com`, + }, }; const bucketUrl = `http://${bucketOutputs.name}.s3.amazonaws.com`; // If origin is relative path then prepend the bucketUrl // e.g. /path => http://bucket.s3.aws.com/path - const expandRelativeUrls = origin => { + const expandRelativeUrls = (origin) => { const originUrl = typeof origin === "string" ? origin : origin.url; const fullOriginUrl = originUrl.charAt(0) === "/" ? `${bucketUrl}${originUrl}` : originUrl; @@ -167,15 +167,10 @@ class NextjsComponent extends Component { } else { return { ...origin, - url: fullOriginUrl + url: fullOriginUrl, }; } }; - // Parse origins from inputs - const inputOrigins = ( - (inputs.cloudfront && inputs.cloudfront.origins) || - [] - ).map(expandRelativeUrls); const cloudFrontOrigins = [ { @@ -183,14 +178,13 @@ class NextjsComponent extends Component { private: true, pathPatterns: { "_next/*": { - ttl: 86400 + ttl: 86400, }, "static/*": { - ttl: 86400 - } - } + ttl: 86400, + }, + }, }, - ...inputOrigins ]; let apiEdgeLambdaOutputs; @@ -201,17 +195,17 @@ class NextjsComponent extends Component { (Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 || Object.keys(apiBuildManifest.apis.dynamic).length > 0); - const getLambdaMemory = lambdaType => + const getLambdaMemory = (lambdaType) => typeof inputs.memory === "number" ? inputs.memory : (inputs.memory && inputs.memory[lambdaType]) || 512; - const getLambdaTimeout = lambdaType => + const getLambdaTimeout = (lambdaType) => typeof inputs.timeout === "number" ? inputs.timeout : (inputs.timeout && inputs.timeout[lambdaType]) || 10; - const getLambdaName = lambdaType => + const getLambdaName = (lambdaType) => typeof inputs.name === "string" ? inputs.name : inputs.name && inputs.name[lambdaType]; @@ -226,11 +220,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, }, memory: getLambdaMemory("apiLambda"), - timeout: getLambdaTimeout("apiLambda") + timeout: getLambdaTimeout("apiLambda"), }; const apiLambdaName = getLambdaName("apiLambda"); if (apiLambdaName) apiEdgeLambdaInput.name = apiLambdaName; @@ -248,12 +242,12 @@ class NextjsComponent extends Component { "GET", "OPTIONS", "PUT", - "PATCH" + "PATCH", ], // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}` - } + "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}`, + }, }; } @@ -266,11 +260,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, }, memory: getLambdaMemory("defaultLambda"), - timeout: getLambdaTimeout("defaultLambda") + timeout: getLambdaTimeout("defaultLambda"), }; const defaultLambdaName = getLambdaName("defaultLambda"); if (defaultLambdaName) defaultEdgeLambdaInput.name = defaultLambdaName; @@ -298,8 +292,8 @@ class NextjsComponent extends Component { ...config, // set lambda@edge last so that it can't be overriden "lambda@edge": { - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` - } + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, + }, }; }); @@ -309,15 +303,15 @@ class NextjsComponent extends Component { allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true + queryString: true, }, ...defaultCloudfrontInputs, // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` - } + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, + }, }, - origins: cloudFrontOrigins + origins: cloudFrontOrigins, }); let appUrl = cloudFrontOutputs.url; @@ -330,15 +324,15 @@ class NextjsComponent extends Component { privateZone: false, domain, subdomains: { - [subdomain]: cloudFrontOutputs - } + [subdomain]: cloudFrontOutputs, + }, }); appUrl = domainOutputs.domains[0]; } return { appUrl, - bucketName: bucketOutputs.name + bucketName: bucketOutputs.name, }; } @@ -346,7 +340,7 @@ class NextjsComponent extends Component { const [bucket, cloudfront, domain] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), - this.load("@serverless/domain") + this.load("@serverless/domain"), ]); await Promise.all([bucket.remove(), cloudfront.remove(), domain.remove()]); From 097800469cc5828865dfd58e40fdf938001a0325 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 2 May 2020 17:15:56 +1000 Subject: [PATCH 10/39] dont append lambda@origin to _next and static --- packages/serverless-component/serverless.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 6ab0cfc1e0..d44ce83339 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -290,8 +290,9 @@ class NextjsComponent extends Component { ...cloudFrontOrigins[0].pathPatterns[path], // spread the supplied overrides ...config, - // set lambda@edge last so that it can't be overriden - "lambda@edge": { + // set lambda@edge last so that it can't be overriden. dont set if + // the path is static or _next + "lambda@edge": !["static/*", "_next/*"].includes(path) && { "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, }, }; From fc533b6dfd85c40ff3002cafeeee1744f5bdf213 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sun, 3 May 2020 09:32:03 +1000 Subject: [PATCH 11/39] fix lintin --- .../__tests__/custom-inputs.test.js | 118 +++++++++--------- packages/serverless-component/serverless.js | 90 ++++++------- 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 0279003d17..90cf9bea64 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -7,7 +7,7 @@ const NextjsComponent = require("../serverless"); const obtainDomains = require("../lib/obtainDomains"); const { DEFAULT_LAMBDA_CODE_DIR, - API_LAMBDA_CODE_DIR, + API_LAMBDA_CODE_DIR } = require("../constants"); expect.extend({ @@ -20,9 +20,9 @@ expect.extend({ ); } return { - pass: true, + pass: true }; - }, + } }); describe("Custom inputs", () => { @@ -34,7 +34,7 @@ describe("Custom inputs", () => { [["www", "example.com"], "https://www.example.com"], [[undefined, "example.com"], "https://www.example.com"], [["example.com"], "https://www.example.com"], - ["example.com", "https://www.example.com"], + ["example.com", "https://www.example.com"] ])("Custom domain", (inputDomains, expectedDomain, memory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -43,26 +43,26 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockS3.mockResolvedValue({ - name: "bucket-xyz", + name: "bucket-xyz" }); mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" }); mockLambdaPublish.mockResolvedValue({ - version: "v1", + version: "v1" }); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); mockDomain.mockResolvedValueOnce({ - domains: [expectedDomain], + domains: [expectedDomain] }); const component = new NextjsComponent(); componentOutputs = await component.default({ policy: "arn:aws:iam::aws:policy/CustomRole", domain: inputDomains, - memory: 512, + memory: 512 }); }); @@ -78,9 +78,9 @@ describe("Custom inputs", () => { domain, subdomains: { [subdomain]: { - url: "https://cloudfrontdistrib.amazonaws.com", - }, - }, + url: "https://cloudfrontdistrib.amazonaws.com" + } + } }); }); @@ -90,9 +90,9 @@ describe("Custom inputs", () => { description: expect.stringContaining("Default Lambda@Edge"), role: expect.objectContaining({ policy: { - arn: "arn:aws:iam::aws:policy/CustomRole", - }, - }), + arn: "arn:aws:iam::aws:policy/CustomRole" + } + }) }) ); }); @@ -104,8 +104,8 @@ describe("Custom inputs", () => { describe.each([ { test1: { a: "1", b: "2" } }, - { "test*123": { some: "other", 3: "here" }, second: { entry: "here" } }, - ])("Custom CloudFront input", (inputCloudFront) => { + { "test*123": { some: "other", 3: "here" }, second: { entry: "here" } } + ])("Custom CloudFront input", inputCloudFront => { let tmpCwd; let componentOutputs; @@ -116,23 +116,23 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockS3.mockResolvedValue({ - name: "bucket-xyz", + name: "bucket-xyz" }); mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" }); mockLambdaPublish.mockResolvedValue({ - version: "v1", + version: "v1" }); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ policy: "arn:aws:iam::aws:policy/CustomRole", memory: 512, - cloudfront: inputCloudFront, + cloudfront: inputCloudFront }); }); @@ -155,8 +155,8 @@ describe("Custom inputs", () => { [{ apiLambda: 2048 }, { defaultMemory: 512, apiMemory: 2048 }], [ { defaultLambda: 128, apiLambda: 2048 }, - { defaultMemory: 128, apiMemory: 2048 }, - ], + { defaultMemory: 128, apiMemory: 2048 } + ] ])("Lambda memory input", (inputMemory, expectedMemory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -164,12 +164,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - memory: inputMemory, + memory: inputMemory }); }); it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { @@ -179,7 +179,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - memory: defaultMemory, + memory: defaultMemory }) ); @@ -187,7 +187,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - memory: apiMemory, + memory: apiMemory }) ); }); @@ -201,8 +201,8 @@ describe("Custom inputs", () => { [{ apiLambda: 20 }, { defaultTimeout: 10, apiTimeout: 20 }], [ { defaultLambda: 15, apiLambda: 20 }, - { defaultTimeout: 15, apiTimeout: 20 }, - ], + { defaultTimeout: 15, apiTimeout: 20 } + ] ])("Lambda timeout input", (inputTimeout, expectedTimeout) => { let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -212,12 +212,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - timeout: inputTimeout, + timeout: inputTimeout }); }); @@ -232,7 +232,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - timeout: defaultTimeout, + timeout: defaultTimeout }) ); @@ -240,7 +240,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - timeout: apiTimeout, + timeout: apiTimeout }) ); }); @@ -252,16 +252,16 @@ describe("Custom inputs", () => { ["fooFunction", { defaultName: "fooFunction", apiName: "fooFunction" }], [ { defaultLambda: "fooFunction" }, - { defaultName: "fooFunction", apiName: undefined }, + { defaultName: "fooFunction", apiName: undefined } ], [ { apiLambda: "fooFunction" }, - { defaultName: undefined, apiName: "fooFunction" }, + { defaultName: undefined, apiName: "fooFunction" } ], [ { defaultLambda: "fooFunction", apiLambda: "barFunction" }, - { defaultName: "fooFunction", apiName: "barFunction" }, - ], + { defaultName: "fooFunction", apiName: "barFunction" } + ] ])("Lambda name input", (inputName, expectedName) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -269,12 +269,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - name: inputName, + name: inputName }); }); it(`sets default lambda name to ${expectedName.defaultName} and api lambda name to ${expectedName.apiName}`, () => { @@ -282,7 +282,7 @@ describe("Custom inputs", () => { // Default Lambda const expectedDefaultObject = { - code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), + code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR) }; if (defaultName) expectedDefaultObject.name = defaultName; @@ -292,7 +292,7 @@ describe("Custom inputs", () => { // Api Lambda const expectedApiObject = { - code: path.join(fixturePath, API_LAMBDA_CODE_DIR), + code: path.join(fixturePath, API_LAMBDA_CODE_DIR) }; if (apiName) expectedApiObject.name = apiName; @@ -307,16 +307,16 @@ describe("Custom inputs", () => { [{}, {}], // empty input [ { defaults: { ttl: 500, "lambda@edge": "ignored value" } }, - { defaults: { ttl: 500 } }, // expecting lambda@edge value to be ignored + { defaults: { ttl: 500 } } // expecting lambda@edge value to be ignored ], [ { defaults: { forward: { headers: "X" } } }, - { defaults: { forward: { headers: "X" } } }, + { defaults: { forward: { headers: "X" } } } ], [ { "api/*": { ttl: 500, "lambda@edge": "ignored value" } }, - { "api/*": { ttl: 500 } }, // expecting lambda@edge value to be ignored - ], + { "api/*": { ttl: 500 } } // expecting lambda@edge value to be ignored + ] ])("Custom cloudfront inputs", (inputCloudfrontConfig, expectedInConfig) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); const defaultCloudfrontInputs = expectedInConfig.defaults || {}; @@ -328,13 +328,13 @@ describe("Custom inputs", () => { allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true, + queryString: true }, ...defaultCloudfrontInputs, "lambda@edge": { "origin-request": - "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - }, + "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1" + } }, origins: [ { @@ -348,34 +348,34 @@ describe("Custom inputs", () => { "GET", "OPTIONS", "PUT", - "PATCH", + "PATCH" ], ttl: 0, "lambda@edge": { "origin-request": - "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", + "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1" }, - ...apiCloudfrontInputs, + ...apiCloudfrontInputs }, - "static/*": { ttl: 86400 }, + "static/*": { ttl: 86400 } }, private: true, - url: "http://bucket-xyz.s3.amazonaws.com", + url: "http://bucket-xyz.s3.amazonaws.com" }, - ...originCloudfrontInputs, - ], + ...originCloudfrontInputs + ] }; beforeEach(async () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - cloudfront: inputCloudfrontConfig, + cloudfront: inputCloudfrontConfig }); }); diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index d44ce83339..7b1c6f6387 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -48,7 +48,7 @@ class NextjsComponent extends Component { cwd: inputs.build && inputs.build.cwd ? path.resolve(inputs.build.cwd) - : nextConfigPath, + : nextConfigPath }; if (buildConfig.enabled) { @@ -59,7 +59,7 @@ class NextjsComponent extends Component { cmd: buildConfig.cmd, cwd: buildConfig.cwd, env: buildConfig.env, - args: buildConfig.args, + args: buildConfig.args } ); @@ -77,24 +77,24 @@ class NextjsComponent extends Component { const [defaultBuildManifest, apiBuildManifest] = await Promise.all([ this.readDefaultBuildManifest(nextConfigPath), - this.readApiBuildManifest(nextConfigPath), + this.readApiBuildManifest(nextConfigPath) ]); const [ bucket, cloudFront, defaultEdgeLambda, - apiEdgeLambda, + apiEdgeLambda ] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), this.load("@serverless/aws-lambda", "defaultEdgeLambda"), - this.load("@serverless/aws-lambda", "apiEdgeLambda"), + this.load("@serverless/aws-lambda", "apiEdgeLambda") ]); const bucketOutputs = await bucket({ accelerated: true, - name: inputs.bucketName, + name: inputs.bucketName }); const nonDynamicHtmlPages = Object.values( @@ -103,13 +103,13 @@ class NextjsComponent extends Component { const dynamicHtmlPages = Object.values( defaultBuildManifest.pages.html.dynamic - ).map((x) => x.file); + ).map(x => x.file); const uploadHtmlPages = [...nonDynamicHtmlPages, ...dynamicHtmlPages].map( - (page) => + page => bucket.upload({ file: join(nextConfigPath, ".next/serverless", page), - key: `static-pages/${page.replace("pages/", "")}`, + key: `static-pages/${page.replace("pages/", "")}` }) ); @@ -117,21 +117,21 @@ class NextjsComponent extends Component { bucket.upload({ dir: join(nextConfigPath, ".next/static"), keyPrefix: "_next/static", - cacheControl: "public, max-age=31536000, immutable", + cacheControl: "public, max-age=31536000, immutable" }), - ...uploadHtmlPages, + ...uploadHtmlPages ]; const [publicDirExists, staticDirExists] = await Promise.all([ fse.exists(join(staticPath, "public")), - fse.exists(join(staticPath, "static")), + fse.exists(join(staticPath, "static")) ]); if (publicDirExists) { assetsUpload.push( bucket.upload({ dir: join(staticPath, "public"), - keyPrefix: "public", + keyPrefix: "public" }) ); } @@ -140,7 +140,7 @@ class NextjsComponent extends Component { assetsUpload.push( bucket.upload({ dir: join(staticPath, "static"), - keyPrefix: "static", + keyPrefix: "static" }) ); } @@ -149,15 +149,15 @@ class NextjsComponent extends Component { defaultBuildManifest.cloudFrontOrigins = { staticOrigin: { - domainName: `${bucketOutputs.name}.s3.amazonaws.com`, - }, + domainName: `${bucketOutputs.name}.s3.amazonaws.com` + } }; const bucketUrl = `http://${bucketOutputs.name}.s3.amazonaws.com`; // If origin is relative path then prepend the bucketUrl // e.g. /path => http://bucket.s3.aws.com/path - const expandRelativeUrls = (origin) => { + const expandRelativeUrls = origin => { const originUrl = typeof origin === "string" ? origin : origin.url; const fullOriginUrl = originUrl.charAt(0) === "/" ? `${bucketUrl}${originUrl}` : originUrl; @@ -167,7 +167,7 @@ class NextjsComponent extends Component { } else { return { ...origin, - url: fullOriginUrl, + url: fullOriginUrl }; } }; @@ -178,13 +178,13 @@ class NextjsComponent extends Component { private: true, pathPatterns: { "_next/*": { - ttl: 86400, + ttl: 86400 }, "static/*": { - ttl: 86400, - }, - }, - }, + ttl: 86400 + } + } + } ]; let apiEdgeLambdaOutputs; @@ -195,17 +195,17 @@ class NextjsComponent extends Component { (Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 || Object.keys(apiBuildManifest.apis.dynamic).length > 0); - const getLambdaMemory = (lambdaType) => + const getLambdaMemory = lambdaType => typeof inputs.memory === "number" ? inputs.memory : (inputs.memory && inputs.memory[lambdaType]) || 512; - const getLambdaTimeout = (lambdaType) => + const getLambdaTimeout = lambdaType => typeof inputs.timeout === "number" ? inputs.timeout : (inputs.timeout && inputs.timeout[lambdaType]) || 10; - const getLambdaName = (lambdaType) => + const getLambdaName = lambdaType => typeof inputs.name === "string" ? inputs.name : inputs.name && inputs.name[lambdaType]; @@ -220,11 +220,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - }, + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } }, memory: getLambdaMemory("apiLambda"), - timeout: getLambdaTimeout("apiLambda"), + timeout: getLambdaTimeout("apiLambda") }; const apiLambdaName = getLambdaName("apiLambda"); if (apiLambdaName) apiEdgeLambdaInput.name = apiLambdaName; @@ -242,12 +242,12 @@ class NextjsComponent extends Component { "GET", "OPTIONS", "PUT", - "PATCH", + "PATCH" ], // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}`, - }, + "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}` + } }; } @@ -260,11 +260,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - }, + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } }, memory: getLambdaMemory("defaultLambda"), - timeout: getLambdaTimeout("defaultLambda"), + timeout: getLambdaTimeout("defaultLambda") }; const defaultLambdaName = getLambdaName("defaultLambda"); if (defaultLambdaName) defaultEdgeLambdaInput.name = defaultLambdaName; @@ -293,8 +293,8 @@ class NextjsComponent extends Component { // set lambda@edge last so that it can't be overriden. dont set if // the path is static or _next "lambda@edge": !["static/*", "_next/*"].includes(path) && { - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, - }, + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` + } }; }); @@ -304,15 +304,15 @@ class NextjsComponent extends Component { allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true, + queryString: true }, ...defaultCloudfrontInputs, // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, - }, + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` + } }, - origins: cloudFrontOrigins, + origins: cloudFrontOrigins }); let appUrl = cloudFrontOutputs.url; @@ -325,15 +325,15 @@ class NextjsComponent extends Component { privateZone: false, domain, subdomains: { - [subdomain]: cloudFrontOutputs, - }, + [subdomain]: cloudFrontOutputs + } }); appUrl = domainOutputs.domains[0]; } return { appUrl, - bucketName: bucketOutputs.name, + bucketName: bucketOutputs.name }; } @@ -341,7 +341,7 @@ class NextjsComponent extends Component { const [bucket, cloudfront, domain] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), - this.load("@serverless/domain"), + this.load("@serverless/domain") ]); await Promise.all([bucket.remove(), cloudfront.remove(), domain.remove()]); From f9e96d09e76cd2c913d05f73dd7d03b9ebec8982 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sun, 3 May 2020 09:34:28 +1000 Subject: [PATCH 12/39] revert changes to lambda-at-edge/package.json --- packages/lambda-at-edge/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lambda-at-edge/package.json b/packages/lambda-at-edge/package.json index 3200c3ba13..0a47c580be 100644 --- a/packages/lambda-at-edge/package.json +++ b/packages/lambda-at-edge/package.json @@ -39,7 +39,7 @@ "ts-loader": "^7.0.0" }, "peerDependencies": { - "next-aws-cloudfront": "file:../lambda-at-edge-compat" + "next-aws-cloudfront": "file:../cloudfront-lambda@edge-compat" }, "dependencies": { "execa": "^4.0.0", From 5bceeaed5c476b3a83d61f9a8d8903f5d5e5caf8 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sun, 3 May 2020 09:37:33 +1000 Subject: [PATCH 13/39] revert changes to package files --- .../lambda-at-edge-compat/package-lock.json | 2 +- .../serverless-component/package-lock.json | 190 +----------------- 2 files changed, 2 insertions(+), 190 deletions(-) diff --git a/packages/lambda-at-edge-compat/package-lock.json b/packages/lambda-at-edge-compat/package-lock.json index c7fd5c6e65..620b8c05c6 100644 --- a/packages/lambda-at-edge-compat/package-lock.json +++ b/packages/lambda-at-edge-compat/package-lock.json @@ -6,7 +6,7 @@ "dependencies": { "@types/aws-lambda": { "version": "8.10.50", - "resolved": false, + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.50.tgz", "integrity": "sha512-RDzmQ5mO1f0BViKiuOudENZmoCACEa461nTRVtxhsAiEqGCgwdhCYN0aFgk42X5+ELAiqJKbv2mK0LkopYRYQg==", "dev": true } diff --git a/packages/serverless-component/package-lock.json b/packages/serverless-component/package-lock.json index 7c97c8af5a..291b4ef638 100644 --- a/packages/serverless-component/package-lock.json +++ b/packages/serverless-component/package-lock.json @@ -188,195 +188,7 @@ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" }, "@sls-next/lambda-at-edge": { - "version": "file:../lambda-at-edge", - "requires": { - "execa": "^4.0.0", - "fs-extra": "^9.0.0", - "path-to-regexp": "^6.1.0" - }, - "dependencies": { - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "execa": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.0.tgz", - "integrity": "sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA==", - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "fs-extra": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", - "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-to-regexp": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.1.0.tgz", - "integrity": "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } + "version": "file:../lambda-at-edge" }, "@szmarczak/http-timer": { "version": "1.1.2", From 4a3880de76d00ed0d8d36cab95e957f8fb57874c Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sun, 3 May 2020 09:38:43 +1000 Subject: [PATCH 14/39] update jest extended to latest --- package-lock.json | 68 +++++++++++++++++++---------------------------- package.json | 2 +- 2 files changed, 29 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5870c91069..f2e4a25791 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12423,9 +12423,9 @@ } }, "jest-extended": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.2.tgz", - "integrity": "sha512-gwNMXrAPN0IY5L7VXWfSlC2aGo0KHIsGGcW+lTHYpedt5SJksEvBgMxs29iNikiNOz+cqAZY1s/+kYK0jlj4Jw==", + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.5.tgz", + "integrity": "sha512-3RsdFpLWKScpsLD6hJuyr/tV5iFOrw7v6YjA3tPdda9sJwoHwcMROws5gwiIZfcwhHlJRwFJB2OUvGmF3evV/Q==", "dev": true, "requires": { "expect": "^24.1.0", @@ -12558,18 +12558,6 @@ "jest-get-type": "^24.9.0", "pretty-format": "^24.9.0" } - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } } } }, @@ -12633,18 +12621,6 @@ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", "dev": true - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } } } }, @@ -12663,6 +12639,24 @@ "chalk": "^2.0.1", "jest-get-type": "^22.4.3", "pretty-format": "^22.4.3" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "pretty-format": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", + "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + } } }, "jest-message-util": { @@ -12709,21 +12703,15 @@ } }, "pretty-format": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", - "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" } }, "slash": { diff --git a/package.json b/package.json index e5adbd47db..fb265a3e87 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "eslint-config-prettier": "^6.10.1", "eslint-plugin-prettier": "^3.0.1", "jest": "^25.3.0", - "jest-extended": "^0.11.2", + "jest-extended": "^0.11.5", "jest-when": "^2.7.0", "lerna": "^3.16.4", "next": "^9.1.5", From 343e97958195f465dcba2324b39de4476ffa2860 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Tue, 5 May 2020 19:16:06 +1000 Subject: [PATCH 15/39] implement gehan's changes. Fix tests. move my own tests into existing test structure so jest-extended can be removed as a dep --- .../__tests__/custom-inputs.test.js | 353 +++++++++++------- packages/serverless-component/serverless.js | 111 +++--- 2 files changed, 288 insertions(+), 176 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 90cf9bea64..a0687c1bc9 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -7,23 +7,23 @@ const NextjsComponent = require("../serverless"); const obtainDomains = require("../lib/obtainDomains"); const { DEFAULT_LAMBDA_CODE_DIR, - API_LAMBDA_CODE_DIR + API_LAMBDA_CODE_DIR, } = require("../constants"); -expect.extend({ - objectContainingCloudFrontInput(received, expected) { - const receivedPathPatterns = received.origins[0].pathPatterns; - expect(receivedPathPatterns).toContainKeys(Object.keys(expected)); - for (pathPattern in expected.origins) { - expect(receivedPathPatterns[pathPattern]).toContainEntries( - Object.entries(expected[pathPattern]) - ); - } - return { - pass: true - }; - } -}); +// expect.extend({ +// objectContainingCloudFrontInput(received, expected) { +// const receivedPathPatterns = received.origins[0].pathPatterns; +// expect(receivedPathPatterns).toContainKeys(Object.keys(expected)); +// for (pathPattern in expected.origins) { +// expect(receivedPathPatterns[pathPattern]).toContainEntries( +// Object.entries(expected[pathPattern]) +// ); +// } +// return { +// pass: true, +// }; +// }, +// }); describe("Custom inputs", () => { let tmpCwd; @@ -34,7 +34,7 @@ describe("Custom inputs", () => { [["www", "example.com"], "https://www.example.com"], [[undefined, "example.com"], "https://www.example.com"], [["example.com"], "https://www.example.com"], - ["example.com", "https://www.example.com"] + ["example.com", "https://www.example.com"], ])("Custom domain", (inputDomains, expectedDomain, memory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -43,26 +43,26 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockS3.mockResolvedValue({ - name: "bucket-xyz" + name: "bucket-xyz", }); mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", }); mockLambdaPublish.mockResolvedValue({ - version: "v1" + version: "v1", }); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); mockDomain.mockResolvedValueOnce({ - domains: [expectedDomain] + domains: [expectedDomain], }); const component = new NextjsComponent(); componentOutputs = await component.default({ policy: "arn:aws:iam::aws:policy/CustomRole", domain: inputDomains, - memory: 512 + memory: 512, }); }); @@ -78,9 +78,9 @@ describe("Custom inputs", () => { domain, subdomains: { [subdomain]: { - url: "https://cloudfrontdistrib.amazonaws.com" - } - } + url: "https://cloudfrontdistrib.amazonaws.com", + }, + }, }); }); @@ -90,9 +90,9 @@ describe("Custom inputs", () => { description: expect.stringContaining("Default Lambda@Edge"), role: expect.objectContaining({ policy: { - arn: "arn:aws:iam::aws:policy/CustomRole" - } - }) + arn: "arn:aws:iam::aws:policy/CustomRole", + }, + }), }) ); }); @@ -102,50 +102,50 @@ describe("Custom inputs", () => { }); }); - describe.each([ - { test1: { a: "1", b: "2" } }, - { "test*123": { some: "other", 3: "here" }, second: { entry: "here" } } - ])("Custom CloudFront input", inputCloudFront => { - let tmpCwd; - let componentOutputs; - - const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); - - beforeEach(async () => { - tmpCwd = process.cwd(); - process.chdir(fixturePath); - - mockS3.mockResolvedValue({ - name: "bucket-xyz" - }); - mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" - }); - mockLambdaPublish.mockResolvedValue({ - version: "v1" - }); - mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" - }); - - const component = new NextjsComponent(); - componentOutputs = await component.default({ - policy: "arn:aws:iam::aws:policy/CustomRole", - memory: 512, - cloudfront: inputCloudFront - }); - }); - - afterEach(() => { - process.chdir(tmpCwd); - }); - - it("passes custom cloudfront input to cloudfront component", () => { - expect(mockCloudFront).toBeCalledWith( - expect.objectContainingCloudFrontInput(inputCloudFront) - ); - }); - }); + // describe.each([ + // { test1: { a: "1", b: "2" } }, + // { "test*123": { some: "other", 3: "here" }, second: { entry: "here" } }, + // ])("Custom CloudFront input", (inputCloudFront) => { + // let tmpCwd; + // let componentOutputs; + + // const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + + // beforeEach(async () => { + // tmpCwd = process.cwd(); + // process.chdir(fixturePath); + + // mockS3.mockResolvedValue({ + // name: "bucket-xyz", + // }); + // mockLambda.mockResolvedValue({ + // arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", + // }); + // mockLambdaPublish.mockResolvedValue({ + // version: "v1", + // }); + // mockCloudFront.mockResolvedValueOnce({ + // url: "https://cloudfrontdistrib.amazonaws.com", + // }); + + // const component = new NextjsComponent(); + // componentOutputs = await component.default({ + // policy: "arn:aws:iam::aws:policy/CustomRole", + // memory: 512, + // cloudfront: inputCloudFront, + // }); + // }); + + // afterEach(() => { + // process.chdir(tmpCwd); + // }); + + // it("passes custom cloudfront input to cloudfront component", () => { + // expect(mockCloudFront).toBeCalledWith( + // expect.objectContainingCloudFrontInput(inputCloudFront) + // ); + // }); + // }); describe.each([ [undefined, { defaultMemory: 512, apiMemory: 512 }], @@ -155,8 +155,8 @@ describe("Custom inputs", () => { [{ apiLambda: 2048 }, { defaultMemory: 512, apiMemory: 2048 }], [ { defaultLambda: 128, apiLambda: 2048 }, - { defaultMemory: 128, apiMemory: 2048 } - ] + { defaultMemory: 128, apiMemory: 2048 }, + ], ])("Lambda memory input", (inputMemory, expectedMemory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -164,12 +164,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - memory: inputMemory + memory: inputMemory, }); }); it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { @@ -179,7 +179,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - memory: defaultMemory + memory: defaultMemory, }) ); @@ -187,7 +187,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - memory: apiMemory + memory: apiMemory, }) ); }); @@ -201,8 +201,8 @@ describe("Custom inputs", () => { [{ apiLambda: 20 }, { defaultTimeout: 10, apiTimeout: 20 }], [ { defaultLambda: 15, apiLambda: 20 }, - { defaultTimeout: 15, apiTimeout: 20 } - ] + { defaultTimeout: 15, apiTimeout: 20 }, + ], ])("Lambda timeout input", (inputTimeout, expectedTimeout) => { let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -212,12 +212,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - timeout: inputTimeout + timeout: inputTimeout, }); }); @@ -232,7 +232,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - timeout: defaultTimeout + timeout: defaultTimeout, }) ); @@ -240,7 +240,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - timeout: apiTimeout + timeout: apiTimeout, }) ); }); @@ -252,16 +252,16 @@ describe("Custom inputs", () => { ["fooFunction", { defaultName: "fooFunction", apiName: "fooFunction" }], [ { defaultLambda: "fooFunction" }, - { defaultName: "fooFunction", apiName: undefined } + { defaultName: "fooFunction", apiName: undefined }, ], [ { apiLambda: "fooFunction" }, - { defaultName: undefined, apiName: "fooFunction" } + { defaultName: undefined, apiName: "fooFunction" }, ], [ { defaultLambda: "fooFunction", apiLambda: "barFunction" }, - { defaultName: "fooFunction", apiName: "barFunction" } - ] + { defaultName: "fooFunction", apiName: "barFunction" }, + ], ])("Lambda name input", (inputName, expectedName) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -269,12 +269,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - name: inputName + name: inputName, }); }); it(`sets default lambda name to ${expectedName.defaultName} and api lambda name to ${expectedName.apiName}`, () => { @@ -282,7 +282,7 @@ describe("Custom inputs", () => { // Default Lambda const expectedDefaultObject = { - code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR) + code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), }; if (defaultName) expectedDefaultObject.name = defaultName; @@ -292,7 +292,7 @@ describe("Custom inputs", () => { // Api Lambda const expectedApiObject = { - code: path.join(fixturePath, API_LAMBDA_CODE_DIR) + code: path.join(fixturePath, API_LAMBDA_CODE_DIR), }; if (apiName) expectedApiObject.name = apiName; @@ -305,77 +305,176 @@ describe("Custom inputs", () => { describe.each([ [undefined, {}], // no input [{}, {}], // empty input + // Ignore origin-requests [ - { defaults: { ttl: 500, "lambda@edge": "ignored value" } }, - { defaults: { ttl: 500 } } // expecting lambda@edge value to be ignored + { + defaults: { + ttl: 500, + "lambda@edge": { "origin-request": "ignored value" }, + }, + }, + { defaults: { ttl: 500 } }, // expecting lambda@edge origin-request to be ignored ], + // Allow other lamdba@edge types [ + { + defaults: { + ttl: 500, + "lambda@edge": { "origin-response": "used value" }, + }, + }, + { + defaults: { + ttl: 500, + "lambda@edge": { "origin-response": "used value" }, + }, + }, + ], + [ + { defaults: { forward: { headers: "X" } } }, { defaults: { forward: { headers: "X" } } }, - { defaults: { forward: { headers: "X" } } } ], [ - { "api/*": { ttl: 500, "lambda@edge": "ignored value" } }, - { "api/*": { ttl: 500 } } // expecting lambda@edge value to be ignored - ] + { + "api/*": { + ttl: 500, + "lambda@edge": { "origin-request": "ignored value" }, + }, + }, + { "api/*": { ttl: 500 } }, // expecting lambda@edge origin-request to be ignored + ], + [ + { + "api/*": { + ttl: 500, + "lambda@edge": { "origin-response": "used value" }, + }, + }, + { + "api/*": { + ttl: 500, + "lambda@edge": { "origin-response": "used value" }, + }, + }, + ], + [ + { + origins: [ + "http://some-origin", + "/relative", + { url: "http://diff-origin" }, + { url: "/diff-relative" }, + ], + }, + { + origins: [ + "http://some-origin", + "http://bucket-xyz.s3.amazonaws.com/relative", + { url: "http://diff-origin" }, + { url: "http://bucket-xyz.s3.amazonaws.com/diff-relative" }, + ], + }, + ], + [ + { + "some-other-path/*": { + ttl: 5500, + "misc-param": "misc-value", + "lambda@edge": { "origin-request": "ignored value" }, + }, + }, + { + "some-other-path/*": { + ttl: 5500, + "misc-param": "misc-value", + }, + }, + ], ])("Custom cloudfront inputs", (inputCloudfrontConfig, expectedInConfig) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); - const defaultCloudfrontInputs = expectedInConfig.defaults || {}; - const apiCloudfrontInputs = expectedInConfig["api/*"] || {}; - const originCloudfrontInputs = expectedInConfig.origins || []; + const { origins = [], defaults = {}, ...other } = expectedInConfig; + const defaultCloudfrontInputs = { + ...defaults, + "lambda@edge": { + "origin-request": + "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", + ...defaults["lambda@edge"], + }, + }; + const apiCloudfrontInputs = { + ...other["api/*"], + allowedHttpMethods: [ + "HEAD", + "DELETE", + "POST", + "GET", + "OPTIONS", + "PUT", + "PATCH", + ], + "lambda@edge": { + "origin-request": + "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", + ...(other["api/*"] && other["api/*"]["lambda@edge"]), + }, + }; + + let otherCloudfrontInputs = {}; + Object.entries(other).forEach(([path, config]) => { + otherCloudfrontInputs[path] = { + ...config, + "lambda@edge": { + "origin-request": + "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", + ...(config && config["lambda@edge"]), + }, + }; + }); + const cloudfrontConfig = { defaults: { ttl: 0, allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true + queryString: true, }, ...defaultCloudfrontInputs, - "lambda@edge": { - "origin-request": - "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1" - } }, origins: [ { pathPatterns: { - "_next/*": { ttl: 86400 }, + ...otherCloudfrontInputs, + "_next/*": { + ...otherCloudfrontInputs["_next/*"], + ttl: 86400, + }, "api/*": { - allowedHttpMethods: [ - "HEAD", - "DELETE", - "POST", - "GET", - "OPTIONS", - "PUT", - "PATCH" - ], ttl: 0, - "lambda@edge": { - "origin-request": - "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1" - }, - ...apiCloudfrontInputs + ...apiCloudfrontInputs, + }, + "static/*": { + ...otherCloudfrontInputs["static/*"], + ttl: 86400, }, - "static/*": { ttl: 86400 } }, private: true, - url: "http://bucket-xyz.s3.amazonaws.com" + url: "http://bucket-xyz.s3.amazonaws.com", }, - ...originCloudfrontInputs - ] + ...origins, + ], }; beforeEach(async () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = new NextjsComponent(); componentOutputs = await component.default({ - cloudfront: inputCloudfrontConfig + cloudfront: inputCloudfrontConfig, }); }); diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 7b1c6f6387..54b8d5ec00 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -48,7 +48,7 @@ class NextjsComponent extends Component { cwd: inputs.build && inputs.build.cwd ? path.resolve(inputs.build.cwd) - : nextConfigPath + : nextConfigPath, }; if (buildConfig.enabled) { @@ -59,7 +59,7 @@ class NextjsComponent extends Component { cmd: buildConfig.cmd, cwd: buildConfig.cwd, env: buildConfig.env, - args: buildConfig.args + args: buildConfig.args, } ); @@ -77,24 +77,24 @@ class NextjsComponent extends Component { const [defaultBuildManifest, apiBuildManifest] = await Promise.all([ this.readDefaultBuildManifest(nextConfigPath), - this.readApiBuildManifest(nextConfigPath) + this.readApiBuildManifest(nextConfigPath), ]); const [ bucket, cloudFront, defaultEdgeLambda, - apiEdgeLambda + apiEdgeLambda, ] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), this.load("@serverless/aws-lambda", "defaultEdgeLambda"), - this.load("@serverless/aws-lambda", "apiEdgeLambda") + this.load("@serverless/aws-lambda", "apiEdgeLambda"), ]); const bucketOutputs = await bucket({ accelerated: true, - name: inputs.bucketName + name: inputs.bucketName, }); const nonDynamicHtmlPages = Object.values( @@ -103,13 +103,13 @@ class NextjsComponent extends Component { const dynamicHtmlPages = Object.values( defaultBuildManifest.pages.html.dynamic - ).map(x => x.file); + ).map((x) => x.file); const uploadHtmlPages = [...nonDynamicHtmlPages, ...dynamicHtmlPages].map( - page => + (page) => bucket.upload({ file: join(nextConfigPath, ".next/serverless", page), - key: `static-pages/${page.replace("pages/", "")}` + key: `static-pages/${page.replace("pages/", "")}`, }) ); @@ -117,21 +117,21 @@ class NextjsComponent extends Component { bucket.upload({ dir: join(nextConfigPath, ".next/static"), keyPrefix: "_next/static", - cacheControl: "public, max-age=31536000, immutable" + cacheControl: "public, max-age=31536000, immutable", }), - ...uploadHtmlPages + ...uploadHtmlPages, ]; const [publicDirExists, staticDirExists] = await Promise.all([ fse.exists(join(staticPath, "public")), - fse.exists(join(staticPath, "static")) + fse.exists(join(staticPath, "static")), ]); if (publicDirExists) { assetsUpload.push( bucket.upload({ dir: join(staticPath, "public"), - keyPrefix: "public" + keyPrefix: "public", }) ); } @@ -140,7 +140,7 @@ class NextjsComponent extends Component { assetsUpload.push( bucket.upload({ dir: join(staticPath, "static"), - keyPrefix: "static" + keyPrefix: "static", }) ); } @@ -149,15 +149,15 @@ class NextjsComponent extends Component { defaultBuildManifest.cloudFrontOrigins = { staticOrigin: { - domainName: `${bucketOutputs.name}.s3.amazonaws.com` - } + domainName: `${bucketOutputs.name}.s3.amazonaws.com`, + }, }; const bucketUrl = `http://${bucketOutputs.name}.s3.amazonaws.com`; // If origin is relative path then prepend the bucketUrl // e.g. /path => http://bucket.s3.aws.com/path - const expandRelativeUrls = origin => { + const expandRelativeUrls = (origin) => { const originUrl = typeof origin === "string" ? origin : origin.url; const fullOriginUrl = originUrl.charAt(0) === "/" ? `${bucketUrl}${originUrl}` : originUrl; @@ -167,24 +167,32 @@ class NextjsComponent extends Component { } else { return { ...origin, - url: fullOriginUrl + url: fullOriginUrl, }; } }; + // Parse origins from inputs + let inputOrigins = []; + if (inputs.cloudfront && inputs.cloudfront.origins) { + inputOrigins = inputs.cloudfront.origins.map(expandRelativeUrls); + delete inputs.cloudfront.origins; + } + const cloudFrontOrigins = [ { url: bucketUrl, private: true, pathPatterns: { "_next/*": { - ttl: 86400 + ttl: 86400, }, "static/*": { - ttl: 86400 - } - } - } + ttl: 86400, + }, + }, + }, + ...inputOrigins, ]; let apiEdgeLambdaOutputs; @@ -195,17 +203,17 @@ class NextjsComponent extends Component { (Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 || Object.keys(apiBuildManifest.apis.dynamic).length > 0); - const getLambdaMemory = lambdaType => + const getLambdaMemory = (lambdaType) => typeof inputs.memory === "number" ? inputs.memory : (inputs.memory && inputs.memory[lambdaType]) || 512; - const getLambdaTimeout = lambdaType => + const getLambdaTimeout = (lambdaType) => typeof inputs.timeout === "number" ? inputs.timeout : (inputs.timeout && inputs.timeout[lambdaType]) || 10; - const getLambdaName = lambdaType => + const getLambdaName = (lambdaType) => typeof inputs.name === "string" ? inputs.name : inputs.name && inputs.name[lambdaType]; @@ -220,11 +228,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, }, memory: getLambdaMemory("apiLambda"), - timeout: getLambdaTimeout("apiLambda") + timeout: getLambdaTimeout("apiLambda"), }; const apiLambdaName = getLambdaName("apiLambda"); if (apiLambdaName) apiEdgeLambdaInput.name = apiLambdaName; @@ -242,12 +250,12 @@ class NextjsComponent extends Component { "GET", "OPTIONS", "PUT", - "PATCH" + "PATCH", ], // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}` - } + "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}`, + }, }; } @@ -260,11 +268,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, }, memory: getLambdaMemory("defaultLambda"), - timeout: getLambdaTimeout("defaultLambda") + timeout: getLambdaTimeout("defaultLambda"), }; const defaultLambdaName = getLambdaName("defaultLambda"); if (defaultLambdaName) defaultEdgeLambdaInput.name = defaultLambdaName; @@ -284,17 +292,21 @@ class NextjsComponent extends Component { } // Add any custom cloudfront configuration + // this includes overrides for _next, static and api Object.entries(cloudFrontConfigs).map(([path, config]) => { cloudFrontOrigins[0].pathPatterns[path] = { // spread the existing value if there is one ...cloudFrontOrigins[0].pathPatterns[path], - // spread the supplied overrides + // spread custom config ...config, - // set lambda@edge last so that it can't be overriden. dont set if - // the path is static or _next - "lambda@edge": !["static/*", "_next/*"].includes(path) && { - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` - } + "lambda@edge": { + ...(config["lambda@edge"] || {}), + "origin-request": + // dont set if the path is static or _next + // spread the supplied overrides + !["static/*", "_next/*"].includes(path) && + `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, + }, }; }); @@ -304,15 +316,16 @@ class NextjsComponent extends Component { allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true + queryString: true, }, ...defaultCloudfrontInputs, // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` - } + ...(defaultCloudfrontInputs["lambda@edge"] || {}), + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, + }, }, - origins: cloudFrontOrigins + origins: cloudFrontOrigins, }); let appUrl = cloudFrontOutputs.url; @@ -325,15 +338,15 @@ class NextjsComponent extends Component { privateZone: false, domain, subdomains: { - [subdomain]: cloudFrontOutputs - } + [subdomain]: cloudFrontOutputs, + }, }); appUrl = domainOutputs.domains[0]; } return { appUrl, - bucketName: bucketOutputs.name + bucketName: bucketOutputs.name, }; } @@ -341,7 +354,7 @@ class NextjsComponent extends Component { const [bucket, cloudfront, domain] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), - this.load("@serverless/domain") + this.load("@serverless/domain"), ]); await Promise.all([bucket.remove(), cloudfront.remove(), domain.remove()]); From 5a415c0e4b6bfe771716c524d800f6c219964818 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Tue, 5 May 2020 19:17:06 +1000 Subject: [PATCH 16/39] fix linting --- .../__tests__/custom-inputs.test.js | 160 +++++++++--------- packages/serverless-component/serverless.js | 90 +++++----- 2 files changed, 125 insertions(+), 125 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index a0687c1bc9..af31da4bcd 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -7,7 +7,7 @@ const NextjsComponent = require("../serverless"); const obtainDomains = require("../lib/obtainDomains"); const { DEFAULT_LAMBDA_CODE_DIR, - API_LAMBDA_CODE_DIR, + API_LAMBDA_CODE_DIR } = require("../constants"); // expect.extend({ @@ -34,7 +34,7 @@ describe("Custom inputs", () => { [["www", "example.com"], "https://www.example.com"], [[undefined, "example.com"], "https://www.example.com"], [["example.com"], "https://www.example.com"], - ["example.com", "https://www.example.com"], + ["example.com", "https://www.example.com"] ])("Custom domain", (inputDomains, expectedDomain, memory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -43,26 +43,26 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockS3.mockResolvedValue({ - name: "bucket-xyz", + name: "bucket-xyz" }); mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" }); mockLambdaPublish.mockResolvedValue({ - version: "v1", + version: "v1" }); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); mockDomain.mockResolvedValueOnce({ - domains: [expectedDomain], + domains: [expectedDomain] }); const component = new NextjsComponent(); componentOutputs = await component.default({ policy: "arn:aws:iam::aws:policy/CustomRole", domain: inputDomains, - memory: 512, + memory: 512 }); }); @@ -78,9 +78,9 @@ describe("Custom inputs", () => { domain, subdomains: { [subdomain]: { - url: "https://cloudfrontdistrib.amazonaws.com", - }, - }, + url: "https://cloudfrontdistrib.amazonaws.com" + } + } }); }); @@ -90,9 +90,9 @@ describe("Custom inputs", () => { description: expect.stringContaining("Default Lambda@Edge"), role: expect.objectContaining({ policy: { - arn: "arn:aws:iam::aws:policy/CustomRole", - }, - }), + arn: "arn:aws:iam::aws:policy/CustomRole" + } + }) }) ); }); @@ -155,8 +155,8 @@ describe("Custom inputs", () => { [{ apiLambda: 2048 }, { defaultMemory: 512, apiMemory: 2048 }], [ { defaultLambda: 128, apiLambda: 2048 }, - { defaultMemory: 128, apiMemory: 2048 }, - ], + { defaultMemory: 128, apiMemory: 2048 } + ] ])("Lambda memory input", (inputMemory, expectedMemory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -164,12 +164,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - memory: inputMemory, + memory: inputMemory }); }); it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { @@ -179,7 +179,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - memory: defaultMemory, + memory: defaultMemory }) ); @@ -187,7 +187,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - memory: apiMemory, + memory: apiMemory }) ); }); @@ -201,8 +201,8 @@ describe("Custom inputs", () => { [{ apiLambda: 20 }, { defaultTimeout: 10, apiTimeout: 20 }], [ { defaultLambda: 15, apiLambda: 20 }, - { defaultTimeout: 15, apiTimeout: 20 }, - ], + { defaultTimeout: 15, apiTimeout: 20 } + ] ])("Lambda timeout input", (inputTimeout, expectedTimeout) => { let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -212,12 +212,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - timeout: inputTimeout, + timeout: inputTimeout }); }); @@ -232,7 +232,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - timeout: defaultTimeout, + timeout: defaultTimeout }) ); @@ -240,7 +240,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - timeout: apiTimeout, + timeout: apiTimeout }) ); }); @@ -252,16 +252,16 @@ describe("Custom inputs", () => { ["fooFunction", { defaultName: "fooFunction", apiName: "fooFunction" }], [ { defaultLambda: "fooFunction" }, - { defaultName: "fooFunction", apiName: undefined }, + { defaultName: "fooFunction", apiName: undefined } ], [ { apiLambda: "fooFunction" }, - { defaultName: undefined, apiName: "fooFunction" }, + { defaultName: undefined, apiName: "fooFunction" } ], [ { defaultLambda: "fooFunction", apiLambda: "barFunction" }, - { defaultName: "fooFunction", apiName: "barFunction" }, - ], + { defaultName: "fooFunction", apiName: "barFunction" } + ] ])("Lambda name input", (inputName, expectedName) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -269,12 +269,12 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - name: inputName, + name: inputName }); }); it(`sets default lambda name to ${expectedName.defaultName} and api lambda name to ${expectedName.apiName}`, () => { @@ -282,7 +282,7 @@ describe("Custom inputs", () => { // Default Lambda const expectedDefaultObject = { - code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), + code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR) }; if (defaultName) expectedDefaultObject.name = defaultName; @@ -292,7 +292,7 @@ describe("Custom inputs", () => { // Api Lambda const expectedApiObject = { - code: path.join(fixturePath, API_LAMBDA_CODE_DIR), + code: path.join(fixturePath, API_LAMBDA_CODE_DIR) }; if (apiName) expectedApiObject.name = apiName; @@ -310,52 +310,52 @@ describe("Custom inputs", () => { { defaults: { ttl: 500, - "lambda@edge": { "origin-request": "ignored value" }, - }, + "lambda@edge": { "origin-request": "ignored value" } + } }, - { defaults: { ttl: 500 } }, // expecting lambda@edge origin-request to be ignored + { defaults: { ttl: 500 } } // expecting lambda@edge origin-request to be ignored ], // Allow other lamdba@edge types [ { defaults: { ttl: 500, - "lambda@edge": { "origin-response": "used value" }, - }, + "lambda@edge": { "origin-response": "used value" } + } }, { defaults: { ttl: 500, - "lambda@edge": { "origin-response": "used value" }, - }, - }, + "lambda@edge": { "origin-response": "used value" } + } + } ], [ { defaults: { forward: { headers: "X" } } }, - { defaults: { forward: { headers: "X" } } }, + { defaults: { forward: { headers: "X" } } } ], [ { "api/*": { ttl: 500, - "lambda@edge": { "origin-request": "ignored value" }, - }, + "lambda@edge": { "origin-request": "ignored value" } + } }, - { "api/*": { ttl: 500 } }, // expecting lambda@edge origin-request to be ignored + { "api/*": { ttl: 500 } } // expecting lambda@edge origin-request to be ignored ], [ { "api/*": { ttl: 500, - "lambda@edge": { "origin-response": "used value" }, - }, + "lambda@edge": { "origin-response": "used value" } + } }, { "api/*": { ttl: 500, - "lambda@edge": { "origin-response": "used value" }, - }, - }, + "lambda@edge": { "origin-response": "used value" } + } + } ], [ { @@ -363,33 +363,33 @@ describe("Custom inputs", () => { "http://some-origin", "/relative", { url: "http://diff-origin" }, - { url: "/diff-relative" }, - ], + { url: "/diff-relative" } + ] }, { origins: [ "http://some-origin", "http://bucket-xyz.s3.amazonaws.com/relative", { url: "http://diff-origin" }, - { url: "http://bucket-xyz.s3.amazonaws.com/diff-relative" }, - ], - }, + { url: "http://bucket-xyz.s3.amazonaws.com/diff-relative" } + ] + } ], [ { "some-other-path/*": { ttl: 5500, "misc-param": "misc-value", - "lambda@edge": { "origin-request": "ignored value" }, - }, + "lambda@edge": { "origin-request": "ignored value" } + } }, { "some-other-path/*": { ttl: 5500, - "misc-param": "misc-value", - }, - }, - ], + "misc-param": "misc-value" + } + } + ] ])("Custom cloudfront inputs", (inputCloudfrontConfig, expectedInConfig) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); const { origins = [], defaults = {}, ...other } = expectedInConfig; @@ -398,8 +398,8 @@ describe("Custom inputs", () => { "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...defaults["lambda@edge"], - }, + ...defaults["lambda@edge"] + } }; const apiCloudfrontInputs = { ...other["api/*"], @@ -410,13 +410,13 @@ describe("Custom inputs", () => { "GET", "OPTIONS", "PUT", - "PATCH", + "PATCH" ], "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...(other["api/*"] && other["api/*"]["lambda@edge"]), - }, + ...(other["api/*"] && other["api/*"]["lambda@edge"]) + } }; let otherCloudfrontInputs = {}; @@ -426,8 +426,8 @@ describe("Custom inputs", () => { "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...(config && config["lambda@edge"]), - }, + ...(config && config["lambda@edge"]) + } }; }); @@ -437,9 +437,9 @@ describe("Custom inputs", () => { allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true, + queryString: true }, - ...defaultCloudfrontInputs, + ...defaultCloudfrontInputs }, origins: [ { @@ -447,34 +447,34 @@ describe("Custom inputs", () => { ...otherCloudfrontInputs, "_next/*": { ...otherCloudfrontInputs["_next/*"], - ttl: 86400, + ttl: 86400 }, "api/*": { ttl: 0, - ...apiCloudfrontInputs, + ...apiCloudfrontInputs }, "static/*": { ...otherCloudfrontInputs["static/*"], - ttl: 86400, - }, + ttl: 86400 + } }, private: true, - url: "http://bucket-xyz.s3.amazonaws.com", + url: "http://bucket-xyz.s3.amazonaws.com" }, - ...origins, - ], + ...origins + ] }; beforeEach(async () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com", + url: "https://cloudfrontdistrib.amazonaws.com" }); const component = new NextjsComponent(); componentOutputs = await component.default({ - cloudfront: inputCloudfrontConfig, + cloudfront: inputCloudfrontConfig }); }); diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 54b8d5ec00..72b1d65caf 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -48,7 +48,7 @@ class NextjsComponent extends Component { cwd: inputs.build && inputs.build.cwd ? path.resolve(inputs.build.cwd) - : nextConfigPath, + : nextConfigPath }; if (buildConfig.enabled) { @@ -59,7 +59,7 @@ class NextjsComponent extends Component { cmd: buildConfig.cmd, cwd: buildConfig.cwd, env: buildConfig.env, - args: buildConfig.args, + args: buildConfig.args } ); @@ -77,24 +77,24 @@ class NextjsComponent extends Component { const [defaultBuildManifest, apiBuildManifest] = await Promise.all([ this.readDefaultBuildManifest(nextConfigPath), - this.readApiBuildManifest(nextConfigPath), + this.readApiBuildManifest(nextConfigPath) ]); const [ bucket, cloudFront, defaultEdgeLambda, - apiEdgeLambda, + apiEdgeLambda ] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), this.load("@serverless/aws-lambda", "defaultEdgeLambda"), - this.load("@serverless/aws-lambda", "apiEdgeLambda"), + this.load("@serverless/aws-lambda", "apiEdgeLambda") ]); const bucketOutputs = await bucket({ accelerated: true, - name: inputs.bucketName, + name: inputs.bucketName }); const nonDynamicHtmlPages = Object.values( @@ -103,13 +103,13 @@ class NextjsComponent extends Component { const dynamicHtmlPages = Object.values( defaultBuildManifest.pages.html.dynamic - ).map((x) => x.file); + ).map(x => x.file); const uploadHtmlPages = [...nonDynamicHtmlPages, ...dynamicHtmlPages].map( - (page) => + page => bucket.upload({ file: join(nextConfigPath, ".next/serverless", page), - key: `static-pages/${page.replace("pages/", "")}`, + key: `static-pages/${page.replace("pages/", "")}` }) ); @@ -117,21 +117,21 @@ class NextjsComponent extends Component { bucket.upload({ dir: join(nextConfigPath, ".next/static"), keyPrefix: "_next/static", - cacheControl: "public, max-age=31536000, immutable", + cacheControl: "public, max-age=31536000, immutable" }), - ...uploadHtmlPages, + ...uploadHtmlPages ]; const [publicDirExists, staticDirExists] = await Promise.all([ fse.exists(join(staticPath, "public")), - fse.exists(join(staticPath, "static")), + fse.exists(join(staticPath, "static")) ]); if (publicDirExists) { assetsUpload.push( bucket.upload({ dir: join(staticPath, "public"), - keyPrefix: "public", + keyPrefix: "public" }) ); } @@ -140,7 +140,7 @@ class NextjsComponent extends Component { assetsUpload.push( bucket.upload({ dir: join(staticPath, "static"), - keyPrefix: "static", + keyPrefix: "static" }) ); } @@ -149,15 +149,15 @@ class NextjsComponent extends Component { defaultBuildManifest.cloudFrontOrigins = { staticOrigin: { - domainName: `${bucketOutputs.name}.s3.amazonaws.com`, - }, + domainName: `${bucketOutputs.name}.s3.amazonaws.com` + } }; const bucketUrl = `http://${bucketOutputs.name}.s3.amazonaws.com`; // If origin is relative path then prepend the bucketUrl // e.g. /path => http://bucket.s3.aws.com/path - const expandRelativeUrls = (origin) => { + const expandRelativeUrls = origin => { const originUrl = typeof origin === "string" ? origin : origin.url; const fullOriginUrl = originUrl.charAt(0) === "/" ? `${bucketUrl}${originUrl}` : originUrl; @@ -167,7 +167,7 @@ class NextjsComponent extends Component { } else { return { ...origin, - url: fullOriginUrl, + url: fullOriginUrl }; } }; @@ -185,14 +185,14 @@ class NextjsComponent extends Component { private: true, pathPatterns: { "_next/*": { - ttl: 86400, + ttl: 86400 }, "static/*": { - ttl: 86400, - }, - }, + ttl: 86400 + } + } }, - ...inputOrigins, + ...inputOrigins ]; let apiEdgeLambdaOutputs; @@ -203,17 +203,17 @@ class NextjsComponent extends Component { (Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 || Object.keys(apiBuildManifest.apis.dynamic).length > 0); - const getLambdaMemory = (lambdaType) => + const getLambdaMemory = lambdaType => typeof inputs.memory === "number" ? inputs.memory : (inputs.memory && inputs.memory[lambdaType]) || 512; - const getLambdaTimeout = (lambdaType) => + const getLambdaTimeout = lambdaType => typeof inputs.timeout === "number" ? inputs.timeout : (inputs.timeout && inputs.timeout[lambdaType]) || 10; - const getLambdaName = (lambdaType) => + const getLambdaName = lambdaType => typeof inputs.name === "string" ? inputs.name : inputs.name && inputs.name[lambdaType]; @@ -228,11 +228,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - }, + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } }, memory: getLambdaMemory("apiLambda"), - timeout: getLambdaTimeout("apiLambda"), + timeout: getLambdaTimeout("apiLambda") }; const apiLambdaName = getLambdaName("apiLambda"); if (apiLambdaName) apiEdgeLambdaInput.name = apiLambdaName; @@ -250,12 +250,12 @@ class NextjsComponent extends Component { "GET", "OPTIONS", "PUT", - "PATCH", + "PATCH" ], // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}`, - }, + "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}` + } }; } @@ -268,11 +268,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - }, + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } }, memory: getLambdaMemory("defaultLambda"), - timeout: getLambdaTimeout("defaultLambda"), + timeout: getLambdaTimeout("defaultLambda") }; const defaultLambdaName = getLambdaName("defaultLambda"); if (defaultLambdaName) defaultEdgeLambdaInput.name = defaultLambdaName; @@ -305,8 +305,8 @@ class NextjsComponent extends Component { // dont set if the path is static or _next // spread the supplied overrides !["static/*", "_next/*"].includes(path) && - `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, - }, + `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` + } }; }); @@ -316,16 +316,16 @@ class NextjsComponent extends Component { allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true, + queryString: true }, ...defaultCloudfrontInputs, // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { ...(defaultCloudfrontInputs["lambda@edge"] || {}), - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, - }, + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` + } }, - origins: cloudFrontOrigins, + origins: cloudFrontOrigins }); let appUrl = cloudFrontOutputs.url; @@ -338,15 +338,15 @@ class NextjsComponent extends Component { privateZone: false, domain, subdomains: { - [subdomain]: cloudFrontOutputs, - }, + [subdomain]: cloudFrontOutputs + } }); appUrl = domainOutputs.domains[0]; } return { appUrl, - bucketName: bucketOutputs.name, + bucketName: bucketOutputs.name }; } @@ -354,7 +354,7 @@ class NextjsComponent extends Component { const [bucket, cloudfront, domain] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), - this.load("@serverless/domain"), + this.load("@serverless/domain") ]); await Promise.all([bucket.remove(), cloudfront.remove(), domain.remove()]); From 8ba5413b84b908c491d4b21255ee5f5b8e0d0400 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Tue, 5 May 2020 19:17:53 +1000 Subject: [PATCH 17/39] remove jest extended as a dep --- package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index fb265a3e87..42d904306e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "packages-build": "lerna run build", "test:watch": "npm run test -- --watch --collect-coverage=false", "publish": "lerna publish --conventional-commits", - "lint": "eslint .", + "lint": "eslint . --fix", "coveralls": "jest --runInBand --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", "integration": "jest --config jest.integration.config.json --setupTestFrameworkScriptFile=./jest.integration.setup.js", "postinstall": "opencollective-postinstall || true" @@ -46,7 +46,6 @@ "eslint-config-prettier": "^6.10.1", "eslint-plugin-prettier": "^3.0.1", "jest": "^25.3.0", - "jest-extended": "^0.11.5", "jest-when": "^2.7.0", "lerna": "^3.16.4", "next": "^9.1.5", @@ -82,9 +81,6 @@ ], "setupFiles": [ "/jest.setup.js" - ], - "setupFilesAfterEnv": [ - "jest-extended" ] }, "dependencies": { From cc015ad54791b08d4eb7bcdf1287c32da808f2d6 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Tue, 5 May 2020 19:18:39 +1000 Subject: [PATCH 18/39] commit changes to package lock --- package-lock.json | 316 ---------------------------------------------- 1 file changed, 316 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2e4a25791..7ce1302875 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12422,322 +12422,6 @@ "semver": "^6.3.0" } }, - "jest-extended": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.5.tgz", - "integrity": "sha512-3RsdFpLWKScpsLD6hJuyr/tV5iFOrw7v6YjA3tPdda9sJwoHwcMROws5gwiIZfcwhHlJRwFJB2OUvGmF3evV/Q==", - "dev": true, - "requires": { - "expect": "^24.1.0", - "jest-get-type": "^22.4.3", - "jest-matcher-utils": "^22.0.0" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", - "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - } - }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", - "dev": true, - "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" - } - }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.8", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", - "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", - "dev": true - }, - "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" - }, - "dependencies": { - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "dependencies": { - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - } - } - }, - "jest-get-type": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", - "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", - "dev": true - }, - "jest-matcher-utils": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", - "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "pretty-format": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", - "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - } - } - } - }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, "jest-get-type": { "version": "25.2.6", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", From 8db855651e829196a9ba62fd71e075a4f7037cda Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Tue, 5 May 2020 19:35:28 +1000 Subject: [PATCH 19/39] install @sls-next/s3-static-assets as it's required to run the tests --- package-lock.json | 57 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 58 insertions(+) diff --git a/package-lock.json b/package-lock.json index a2e6d9acb8..9141268438 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3962,6 +3962,57 @@ } } }, + "@sls-next/s3-static-assets": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@sls-next/s3-static-assets/-/s3-static-assets-1.0.1.tgz", + "integrity": "sha512-LjGb/p9ZhWeUdv7V3nzlxgcErE5lpajjw/l9R90hr7SJgLdUF9OZyiNs6TlxQQ2tJwGMclf0YoXTRHE0l1MyNg==", + "dev": true, + "requires": { + "aws-sdk": "^2.664.0", + "fs-extra": "^9.0.0", + "klaw": "^3.0.0", + "mime-types": "^2.1.27" + }, + "dependencies": { + "fs-extra": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", + "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, "@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -4983,6 +5034,12 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", diff --git a/package.json b/package.json index 1075bc1616..de49d82202 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "devDependencies": { "@babel/preset-typescript": "^7.9.0", "@sls-next/lambda-at-edge": "file:packages/lambda-at-edge", + "@sls-next/s3-static-assets": "^1.0.1", "@types/jest": "^25.2.1", "@typescript-eslint/eslint-plugin": "^2.28.0", "@typescript-eslint/parser": "^2.28.0", From ae71b2a7059fa0873faa806e4ecc920f56b74b5c Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Tue, 5 May 2020 19:48:43 +1000 Subject: [PATCH 20/39] fix linting --- packages/serverless-component/serverless.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 640605995f..8ff8926ea3 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -127,7 +127,7 @@ class NextjsComponent extends Component { }; } }; - + // Parse origins from inputs let inputOrigins = []; if (inputs.cloudfront && inputs.cloudfront.origins) { @@ -273,7 +273,7 @@ class NextjsComponent extends Component { forward: { cookies: "all", queryString: true, - queryString: true, + queryString: true }, ...defaultCloudfrontInputs, // lambda@edge key is last and therefore cannot be overridden From 529d384a0c2ea603399f960ec092d7932fd51492 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Thu, 7 May 2020 19:39:51 +1000 Subject: [PATCH 21/39] add validation step and update tests --- .../__tests__/custom-inputs.test.js | 212 +++++++++++------- packages/serverless-component/serverless.js | 168 ++++++++++---- 2 files changed, 252 insertions(+), 128 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index c9279f597e..ef664fc6ce 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -8,16 +8,16 @@ const NextjsComponent = require("../serverless"); const obtainDomains = require("../lib/obtainDomains"); const { DEFAULT_LAMBDA_CODE_DIR, - API_LAMBDA_CODE_DIR + API_LAMBDA_CODE_DIR, } = require("../constants"); -const createNextComponent = inputs => { +const createNextComponent = (inputs) => { const component = new NextjsComponent(inputs); component.context.credentials = { aws: { accessKeyId: "123", - secretAccessKey: "456" - } + secretAccessKey: "456", + }, }; return component; }; @@ -43,7 +43,7 @@ describe("Custom inputs", () => { [["www", "example.com"], "https://www.example.com"], [[undefined, "example.com"], "https://www.example.com"], [["example.com"], "https://www.example.com"], - ["example.com", "https://www.example.com"] + ["example.com", "https://www.example.com"], ])("Custom domain", (inputDomains, expectedDomain, memory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -52,19 +52,19 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockS3.mockResolvedValue({ - name: "bucket-xyz" + name: "bucket-xyz", }); mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func", }); mockLambdaPublish.mockResolvedValue({ - version: "v1" + version: "v1", }); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); mockDomain.mockResolvedValueOnce({ - domains: [expectedDomain] + domains: [expectedDomain], }); const component = createNextComponent(); @@ -72,7 +72,7 @@ describe("Custom inputs", () => { componentOutputs = await component.default({ policy: "arn:aws:iam::aws:policy/CustomRole", domain: inputDomains, - memory: 512 + memory: 512, }); }); @@ -88,9 +88,9 @@ describe("Custom inputs", () => { domain, subdomains: { [subdomain]: { - url: "https://cloudfrontdistrib.amazonaws.com" - } - } + url: "https://cloudfrontdistrib.amazonaws.com", + }, + }, }); }); @@ -100,9 +100,9 @@ describe("Custom inputs", () => { description: expect.stringContaining("Default Lambda@Edge"), role: expect.objectContaining({ policy: { - arn: "arn:aws:iam::aws:policy/CustomRole" - } - }) + arn: "arn:aws:iam::aws:policy/CustomRole", + }, + }), }) ); }); @@ -120,8 +120,8 @@ describe("Custom inputs", () => { [{ apiLambda: 2048 }, { defaultMemory: 512, apiMemory: 2048 }], [ { defaultLambda: 128, apiLambda: 2048 }, - { defaultMemory: 128, apiMemory: 2048 } - ] + { defaultMemory: 128, apiMemory: 2048 }, + ], ])("Lambda memory input", (inputMemory, expectedMemory) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -129,15 +129,15 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = createNextComponent({ - memory: inputMemory + memory: inputMemory, }); componentOutputs = await component.default({ - memory: inputMemory + memory: inputMemory, }); }); it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { @@ -147,7 +147,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - memory: defaultMemory + memory: defaultMemory, }) ); @@ -155,7 +155,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - memory: apiMemory + memory: apiMemory, }) ); }); @@ -169,8 +169,8 @@ describe("Custom inputs", () => { [{ apiLambda: 20 }, { defaultTimeout: 10, apiTimeout: 20 }], [ { defaultLambda: 15, apiLambda: 20 }, - { defaultTimeout: 15, apiTimeout: 20 } - ] + { defaultTimeout: 15, apiTimeout: 20 }, + ], ])("Lambda timeout input", (inputTimeout, expectedTimeout) => { let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -180,13 +180,13 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = createNextComponent(); componentOutputs = await component.default({ - timeout: inputTimeout + timeout: inputTimeout, }); }); @@ -201,7 +201,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), - timeout: defaultTimeout + timeout: defaultTimeout, }) ); @@ -209,7 +209,7 @@ describe("Custom inputs", () => { expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), - timeout: apiTimeout + timeout: apiTimeout, }) ); }); @@ -221,16 +221,16 @@ describe("Custom inputs", () => { ["fooFunction", { defaultName: "fooFunction", apiName: "fooFunction" }], [ { defaultLambda: "fooFunction" }, - { defaultName: "fooFunction", apiName: undefined } + { defaultName: "fooFunction", apiName: undefined }, ], [ { apiLambda: "fooFunction" }, - { defaultName: undefined, apiName: "fooFunction" } + { defaultName: undefined, apiName: "fooFunction" }, ], [ { defaultLambda: "fooFunction", apiLambda: "barFunction" }, - { defaultName: "fooFunction", apiName: "barFunction" } - ] + { defaultName: "fooFunction", apiName: "barFunction" }, + ], ])("Lambda name input", (inputName, expectedName) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -238,13 +238,13 @@ describe("Custom inputs", () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = createNextComponent(); componentOutputs = await component.default({ - name: inputName + name: inputName, }); }); it(`sets default lambda name to ${expectedName.defaultName} and api lambda name to ${expectedName.apiName}`, () => { @@ -252,7 +252,7 @@ describe("Custom inputs", () => { // Default Lambda const expectedDefaultObject = { - code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR) + code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), }; if (defaultName) expectedDefaultObject.name = defaultName; @@ -262,7 +262,7 @@ describe("Custom inputs", () => { // Api Lambda const expectedApiObject = { - code: path.join(fixturePath, API_LAMBDA_CODE_DIR) + code: path.join(fixturePath, API_LAMBDA_CODE_DIR), }; if (apiName) expectedApiObject.name = apiName; @@ -280,52 +280,52 @@ describe("Custom inputs", () => { { defaults: { ttl: 500, - "lambda@edge": { "origin-request": "ignored value" } - } + "lambda@edge": { "origin-request": "ignored value" }, + }, }, - { defaults: { ttl: 500 } } // expecting lambda@edge origin-request to be ignored + { defaults: { ttl: 500 } }, // expecting lambda@edge origin-request to be ignored ], // Allow other lamdba@edge types [ { defaults: { ttl: 500, - "lambda@edge": { "origin-response": "used value" } - } + "lambda@edge": { "origin-response": "used value" }, + }, }, { defaults: { ttl: 500, - "lambda@edge": { "origin-response": "used value" } - } - } + "lambda@edge": { "origin-response": "used value" }, + }, + }, ], [ { defaults: { forward: { headers: "X" } } }, - { defaults: { forward: { headers: "X" } } } + { defaults: { forward: { headers: "X" } } }, ], [ { "api/*": { ttl: 500, - "lambda@edge": { "origin-request": "ignored value" } - } + "lambda@edge": { "origin-request": "ignored value" }, + }, }, - { "api/*": { ttl: 500 } } // expecting lambda@edge origin-request to be ignored + { "api/*": { ttl: 500 } }, // expecting lambda@edge origin-request to be ignored ], [ { "api/*": { ttl: 500, - "lambda@edge": { "origin-response": "used value" } - } + "lambda@edge": { "origin-response": "used value" }, + }, }, { "api/*": { ttl: 500, - "lambda@edge": { "origin-response": "used value" } - } - } + "lambda@edge": { "origin-response": "used value" }, + }, + }, ], [ { @@ -333,33 +333,45 @@ describe("Custom inputs", () => { "http://some-origin", "/relative", { url: "http://diff-origin" }, - { url: "/diff-relative" } - ] + { url: "/diff-relative" }, + ], }, { origins: [ "http://some-origin", "http://bucket-xyz.s3.amazonaws.com/relative", { url: "http://diff-origin" }, - { url: "http://bucket-xyz.s3.amazonaws.com/diff-relative" } - ] - } + { url: "http://bucket-xyz.s3.amazonaws.com/diff-relative" }, + ], + }, ], [ { - "some-other-path/*": { + "/terms": { + ttl: 5500, + "misc-param": "misc-value", + "lambda@edge": { "origin-request": "ignored value" }, + }, + }, + { + "/terms": { ttl: 5500, "misc-param": "misc-value", - "lambda@edge": { "origin-request": "ignored value" } - } + }, + }, + ], + [ + { + "/customers/stan-sack": { + ttl: 5500, + }, }, { - "some-other-path/*": { + "/customers/stan-sack": { ttl: 5500, - "misc-param": "misc-value" - } - } - ] + }, + }, + ], ])("Custom cloudfront inputs", (inputCloudfrontConfig, expectedInConfig) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); const { origins = [], defaults = {}, ...other } = expectedInConfig; @@ -368,8 +380,8 @@ describe("Custom inputs", () => { "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...defaults["lambda@edge"] - } + ...defaults["lambda@edge"], + }, }; const apiCloudfrontInputs = { ...other["api/*"], @@ -380,13 +392,13 @@ describe("Custom inputs", () => { "GET", "OPTIONS", "PUT", - "PATCH" + "PATCH", ], "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...(other["api/*"] && other["api/*"]["lambda@edge"]) - } + ...(other["api/*"] && other["api/*"]["lambda@edge"]), + }, }; let otherCloudfrontInputs = {}; @@ -396,8 +408,8 @@ describe("Custom inputs", () => { "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...(config && config["lambda@edge"]) - } + ...(config && config["lambda@edge"]), + }, }; }); @@ -407,9 +419,9 @@ describe("Custom inputs", () => { allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", - queryString: true + queryString: true, }, - ...defaultCloudfrontInputs + ...defaultCloudfrontInputs, }, origins: [ { @@ -417,35 +429,35 @@ describe("Custom inputs", () => { ...otherCloudfrontInputs, "_next/*": { ...otherCloudfrontInputs["_next/*"], - ttl: 86400 + ttl: 86400, }, "api/*": { ttl: 0, - ...apiCloudfrontInputs + ...apiCloudfrontInputs, }, "static/*": { ...otherCloudfrontInputs["static/*"], - ttl: 86400 - } + ttl: 86400, + }, }, private: true, - url: "http://bucket-xyz.s3.amazonaws.com" + url: "http://bucket-xyz.s3.amazonaws.com", }, - ...origins - ] + ...origins, + ], }; beforeEach(async () => { process.chdir(fixturePath); mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" + url: "https://cloudfrontdistrib.amazonaws.com", }); const component = createNextComponent(); componentOutputs = await component.default({ - cloudfront: inputCloudfrontConfig + cloudfront: inputCloudfrontConfig, }); }); @@ -455,4 +467,32 @@ describe("Custom inputs", () => { ); }); }); + + describe.each([ + { + "some-invalid-path": { ttl: 100 }, + }, + { + "/api": { ttl: 100 }, + }, + { api: { ttl: 100 } }, + { "api/test": { ttl: 100 } }, + ])("Invalid cloudfront inputs", (inputCloudfrontConfig) => { + const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + + beforeEach(async () => { + process.chdir(fixturePath); + }); + + it("throws an error", () => { + const component = createNextComponent(); + expect( + // only deploy because the fixture will be cleaned up if + // build throws an error + component.deploy({ + cloudfront: inputCloudfrontConfig, + }) + ).rejects.toThrow(); + }); + }); }); diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 8ff8926ea3..ba2e5e4ca0 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -23,6 +23,85 @@ class NextjsComponent extends Component { ); } + validatePathPatterns(pathPatterns, buildManifest) { + let stillToMatch = new Set(pathPatterns); + if (stillToMatch.size !== pathPatterns.length) { + throw Error("duplicate path path declared in cloudfront configuration"); + } + + // there wont be a page path for this so we can remove it + stillToMatch.delete("api/*"); + // check for other api like paths + for (const path of stillToMatch) { + if (/^(\/?api\/.*|\/?api)$/.test(path)) { + throw Error( + `No custom CloudFront configuration is permitted for path ${path} . It violates api/* behaivour` + ); + } + } + + // theres a lot of n^2 code running below but n is small and it only runs once so we can + // accept it + + // setup containers for the paths we're going to be matching against + + // for dynamic routes + let manifestRegex = []; + // for static routes + let manifestPaths = new Set(); + + // extract paths to validate against from build manifest + const ssrDynamic = buildManifest.pages.ssr.dynamic || {}; + const ssrNonDynamic = buildManifest.pages.ssr.nonDynamic || {}; + const htmlDynamic = buildManifest.pages.html.dynamic || {}; + const htmlNonDynamic = buildManifest.pages.html.nonDynamic || {}; + + // dynamic paths to check. We use their regex to match against our input yaml + Object.entries({ + ...ssrDynamic, + ...htmlDynamic, + }).map(([path, { file, regex }]) => { + manifestRegex.push(new RegExp(regex)); + }); + + // static paths to check + Object.entries({ + ...ssrNonDynamic, + ...htmlNonDynamic, + }).map(([path]) => { + manifestPaths.add(path); + }); + + // first we check if the path patterns match any of the dynamic page regex. + // paths with stars (*) shouldnt cause any issues because the regex will treat these + // as characters. Its n^2 but n is small + manifestRegex.forEach((re) => { + for (const path of stillToMatch) { + if (re.test(path)) { + stillToMatch.delete(path); + } + } + }); + + // now we check the remaining unmatched paths against the non dynamic paths + // and use the path as regex so that we are testing * + for (const pathToMatch of stillToMatch) { + for (const path of manifestPaths) { + if (new RegExp(pathToMatch).test(path)) { + stillToMatch.delete(pathToMatch); + } + } + } + + if (stillToMatch.size > 0) { + throw Error( + `Custom CloudFront input failed validation. Failed to find Next.js paths for ${[ + ...stillToMatch, + ]}` + ); + } + } + async readApiBuildManifest(nextConfigPath) { const path = join( nextConfigPath, @@ -48,7 +127,7 @@ class NextjsComponent extends Component { cwd: inputs.build && inputs.build.cwd ? path.resolve(inputs.build.cwd) - : nextConfigPath + : nextConfigPath, }; if (buildConfig.enabled) { @@ -59,7 +138,7 @@ class NextjsComponent extends Component { cmd: buildConfig.cmd, cwd: buildConfig.cwd, env: buildConfig.env, - args: buildConfig.args + args: buildConfig.args, } ); @@ -73,47 +152,47 @@ class NextjsComponent extends Component { : process.cwd(); const nextStaticPath = inputs.nextStaticDir || ""; const staticPath = nextStaticPath || nextConfigPath; - const cloudFrontConfigs = inputs.cloudfront || {}; + const customCloudFrontConfig = inputs.cloudfront || {}; const [defaultBuildManifest, apiBuildManifest] = await Promise.all([ this.readDefaultBuildManifest(nextConfigPath), - this.readApiBuildManifest(nextConfigPath) + this.readApiBuildManifest(nextConfigPath), ]); const [ bucket, cloudFront, defaultEdgeLambda, - apiEdgeLambda + apiEdgeLambda, ] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), this.load("@serverless/aws-lambda", "defaultEdgeLambda"), - this.load("@serverless/aws-lambda", "apiEdgeLambda") + this.load("@serverless/aws-lambda", "apiEdgeLambda"), ]); const bucketOutputs = await bucket({ accelerated: true, - name: inputs.bucketName + name: inputs.bucketName, }); await uploadAssetsToS3.default({ bucketName: bucketOutputs.name, nextConfigDir: nextConfigPath, - credentials: this.context.credentials.aws + credentials: this.context.credentials.aws, }); defaultBuildManifest.cloudFrontOrigins = { staticOrigin: { - domainName: `${bucketOutputs.name}.s3.amazonaws.com` - } + domainName: `${bucketOutputs.name}.s3.amazonaws.com`, + }, }; const bucketUrl = `http://${bucketOutputs.name}.s3.amazonaws.com`; // If origin is relative path then prepend the bucketUrl // e.g. /path => http://bucket.s3.aws.com/path - const expandRelativeUrls = origin => { + const expandRelativeUrls = (origin) => { const originUrl = typeof origin === "string" ? origin : origin.url; const fullOriginUrl = originUrl.charAt(0) === "/" ? `${bucketUrl}${originUrl}` : originUrl; @@ -123,7 +202,7 @@ class NextjsComponent extends Component { } else { return { ...origin, - url: fullOriginUrl + url: fullOriginUrl, }; } }; @@ -141,14 +220,14 @@ class NextjsComponent extends Component { private: true, pathPatterns: { "_next/*": { - ttl: 86400 + ttl: 86400, }, "static/*": { - ttl: 86400 - } - } + ttl: 86400, + }, + }, }, - ...inputOrigins + ...inputOrigins, ]; let apiEdgeLambdaOutputs; @@ -159,17 +238,17 @@ class NextjsComponent extends Component { (Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 || Object.keys(apiBuildManifest.apis.dynamic).length > 0); - const getLambdaMemory = lambdaType => + const getLambdaMemory = (lambdaType) => typeof inputs.memory === "number" ? inputs.memory : (inputs.memory && inputs.memory[lambdaType]) || 512; - const getLambdaTimeout = lambdaType => + const getLambdaTimeout = (lambdaType) => typeof inputs.timeout === "number" ? inputs.timeout : (inputs.timeout && inputs.timeout[lambdaType]) || 10; - const getLambdaName = lambdaType => + const getLambdaName = (lambdaType) => typeof inputs.name === "string" ? inputs.name : inputs.name && inputs.name[lambdaType]; @@ -184,11 +263,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, }, memory: getLambdaMemory("apiLambda"), - timeout: getLambdaTimeout("apiLambda") + timeout: getLambdaTimeout("apiLambda"), }; const apiLambdaName = getLambdaName("apiLambda"); if (apiLambdaName) apiEdgeLambdaInput.name = apiLambdaName; @@ -206,12 +285,12 @@ class NextjsComponent extends Component { "GET", "OPTIONS", "PUT", - "PATCH" + "PATCH", ], // lambda@edge key is last and therefore cannot be overridden "lambda@edge": { - "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}` - } + "origin-request": `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}`, + }, }; } @@ -224,11 +303,11 @@ class NextjsComponent extends Component { policy: { arn: inputs.policy || - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, }, memory: getLambdaMemory("defaultLambda"), - timeout: getLambdaTimeout("defaultLambda") + timeout: getLambdaTimeout("defaultLambda"), }; const defaultLambdaName = getLambdaName("defaultLambda"); if (defaultLambdaName) defaultEdgeLambdaInput.name = defaultLambdaName; @@ -247,9 +326,15 @@ class NextjsComponent extends Component { defaultCloudfrontInputs = {}; } + // validate that the custom config paths match generated paths in the manifest + this.validatePathPatterns( + Object.keys(customCloudFrontConfig), + defaultBuildManifest + ); + // Add any custom cloudfront configuration // this includes overrides for _next, static and api - Object.entries(cloudFrontConfigs).map(([path, config]) => { + Object.entries(customCloudFrontConfig).map(([path, config]) => { cloudFrontOrigins[0].pathPatterns[path] = { // spread the existing value if there is one ...cloudFrontOrigins[0].pathPatterns[path], @@ -261,28 +346,27 @@ class NextjsComponent extends Component { // dont set if the path is static or _next // spread the supplied overrides !["static/*", "_next/*"].includes(path) && - `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` - } + `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, + }, }; }); const cloudFrontOutputs = await cloudFront({ defaults: { ttl: 0, - allowedHttpMethods: ["HEAD", "GET"], forward: { cookies: "all", queryString: true, - queryString: true }, ...defaultCloudfrontInputs, - // lambda@edge key is last and therefore cannot be overridden + // everything after here cant be overriden + allowedHttpMethods: ["HEAD", "GET"], "lambda@edge": { ...(defaultCloudfrontInputs["lambda@edge"] || {}), - "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` - } + "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`, + }, }, - origins: cloudFrontOrigins + origins: cloudFrontOrigins, }); let appUrl = cloudFrontOutputs.url; @@ -295,15 +379,15 @@ class NextjsComponent extends Component { privateZone: false, domain, subdomains: { - [subdomain]: cloudFrontOutputs - } + [subdomain]: cloudFrontOutputs, + }, }); appUrl = domainOutputs.domains[0]; } return { appUrl, - bucketName: bucketOutputs.name + bucketName: bucketOutputs.name, }; } @@ -311,7 +395,7 @@ class NextjsComponent extends Component { const [bucket, cloudfront, domain] = await Promise.all([ this.load("@serverless/aws-s3"), this.load("@serverless/aws-cloudfront"), - this.load("@serverless/domain") + this.load("@serverless/domain"), ]); await Promise.all([bucket.remove(), cloudfront.remove(), domain.remove()]); From f6a866cfaaa49571d303ba22c20da7deeb3b1313 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Thu, 7 May 2020 19:48:49 +1000 Subject: [PATCH 22/39] remove old path vars --- packages/serverless-component/serverless.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 1b291c1d12..6ed3161067 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -150,8 +150,7 @@ class NextjsComponent extends Component { const nextConfigPath = inputs.nextConfigDir ? path.resolve(inputs.nextConfigDir) : process.cwd(); - const nextStaticPath = inputs.nextStaticDir || ""; - const staticPath = nextStaticPath || nextConfigPath; + const customCloudFrontConfig = inputs.cloudfront || {}; const [defaultBuildManifest, apiBuildManifest] = await Promise.all([ From 2c3a8e4f437ad1891977c036cec05102b40af4c8 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Fri, 8 May 2020 07:28:13 +1000 Subject: [PATCH 23/39] remove s3-static-assets from package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 9849ca966c..bedb1e3a3c 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "devDependencies": { "@babel/preset-typescript": "^7.9.0", "@sls-next/lambda-at-edge": "file:packages/lambda-at-edge", - "@sls-next/s3-static-assets": "^1.0.1", "@types/jest": "^25.2.1", "@typescript-eslint/eslint-plugin": "^2.28.0", "@typescript-eslint/parser": "^2.28.0", From a711e1f04a333f5939dcd038ad97fa2aa2665400 Mon Sep 17 00:00:00 2001 From: danielconde Date: Fri, 8 May 2020 08:57:51 +0100 Subject: [PATCH 24/39] tidy up custom domain input tests --- .../__tests__/custom-inputs.test.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index acf661bedc..88e3870878 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -38,13 +38,14 @@ describe("Custom inputs", () => { consoleWarnSpy.mockRestore(); }); - describe.each([ - [["dev", "example.com"], "https://dev.example.com"], - [["www", "example.com"], "https://www.example.com"], - [[undefined, "example.com"], "https://www.example.com"], - [["example.com"], "https://www.example.com"], - ["example.com", "https://www.example.com"] - ])("Custom domain", (inputDomains, expectedDomain, memory) => { + describe.each` + inputDomains | expectedDomain + ${["dev", "example.com"]} | ${"https://dev.example.com"} + ${["www", "example.com"]} | ${"https://www.example.com"} + ${"example.com"} | ${"https://www.example.com"} + ${[undefined, "example.com"]} | ${"https://www.example.com"} + ${"example.com"} | ${"https://www.example.com"} + `("Custom domain", ({ inputDomains, expectedDomain }) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); beforeEach(async () => { From 6b718b6e2593e8a49e5ec918b0c399cb2dfd7941 Mon Sep 17 00:00:00 2001 From: danielconde Date: Fri, 8 May 2020 10:42:14 +0100 Subject: [PATCH 25/39] tidy up custom lambda name tests --- .../__tests__/custom-inputs.test.js | 68 ++++++++----------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 88e3870878..a543f84929 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -28,6 +28,7 @@ describe("Custom inputs", () => { let consoleWarnSpy; beforeEach(() => { + // mock out remove to prevent fixture files from being wiped out jest.spyOn(fse, "remove").mockImplementation(() => { return; }); @@ -114,17 +115,16 @@ describe("Custom inputs", () => { }); }); - describe.each([ - [undefined, { defaultMemory: 512, apiMemory: 512 }], - [{}, { defaultMemory: 512, apiMemory: 512 }], - [1024, { defaultMemory: 1024, apiMemory: 1024 }], - [{ defaultLambda: 1024 }, { defaultMemory: 1024, apiMemory: 512 }], - [{ apiLambda: 2048 }, { defaultMemory: 512, apiMemory: 2048 }], - [ - { defaultLambda: 128, apiLambda: 2048 }, - { defaultMemory: 128, apiMemory: 2048 } - ] - ])("Lambda memory input", (inputMemory, expectedMemory) => { + describe.each` + inputMemory | expectedMemory + ${undefined} | ${{ defaultMemory: 512, apiMemory: 512 }} + ${1024} | ${{ defaultMemory: 1024, apiMemory: 1024 }} + ${{ defaultLambda: 1024 }} | ${{ defaultMemory: 1024, apiMemory: 512 }} + ${{}} | ${{ defaultMemory: 512, apiMemory: 512 }} + ${{ apiLambda: 2048 }} | ${{ defaultMemory: 512, apiMemory: 2048 }} + ${{ defaultLambda: 128, apiLambda: 2048 }} | ${{ defaultMemory: 128, apiMemory: 2048 }} + ${{ defaultLambda: 1024 }} | ${{ defaultMemory: 1024, apiMemory: 512 }} + `("Lambda memory input", ({ inputMemory, expectedMemory }) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); beforeEach(async () => { @@ -163,17 +163,15 @@ describe("Custom inputs", () => { }); }); - describe.each([ - [undefined, { defaultTimeout: 10, apiTimeout: 10 }], - [{}, { defaultTimeout: 10, apiTimeout: 10 }], - [40, { defaultTimeout: 40, apiTimeout: 40 }], - [{ defaultLambda: 20 }, { defaultTimeout: 20, apiTimeout: 10 }], - [{ apiLambda: 20 }, { defaultTimeout: 10, apiTimeout: 20 }], - [ - { defaultLambda: 15, apiLambda: 20 }, - { defaultTimeout: 15, apiTimeout: 20 } - ] - ])("Lambda timeout input", (inputTimeout, expectedTimeout) => { + describe.each` + inputTimeout | expectedTimeout + ${undefined} | ${{ defaultTimeout: 10, apiTimeout: 10 }} + ${{}} | ${{ defaultTimeout: 10, apiTimeout: 10 }} + ${40} | ${{ defaultTimeout: 40, apiTimeout: 40 }} + ${{ defaultLambda: 20 }} | ${{ defaultTimeout: 20, apiTimeout: 10 }} + ${{ apiLambda: 20 }} | ${{ defaultTimeout: 10, apiTimeout: 20 }} + ${{ defaultLambda: 15, apiLambda: 20 }} | ${{ defaultTimeout: 15, apiTimeout: 20 }} + `("Input timeout options", ({ inputTimeout, expectedTimeout }) => { let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); @@ -217,23 +215,15 @@ describe("Custom inputs", () => { }); }); - describe.each([ - [undefined, { defaultName: undefined, apiName: undefined }], - [{}, { defaultName: undefined, apiName: undefined }], - ["fooFunction", { defaultName: "fooFunction", apiName: "fooFunction" }], - [ - { defaultLambda: "fooFunction" }, - { defaultName: "fooFunction", apiName: undefined } - ], - [ - { apiLambda: "fooFunction" }, - { defaultName: undefined, apiName: "fooFunction" } - ], - [ - { defaultLambda: "fooFunction", apiLambda: "barFunction" }, - { defaultName: "fooFunction", apiName: "barFunction" } - ] - ])("Lambda name input", (inputName, expectedName) => { + describe.each` + inputName | expectedName + ${undefined} | ${{ defaultName: undefined, apiName: undefined }} + ${{}} | ${{ defaultName: undefined, apiName: undefined }} + ${"fooFunction"} | ${{ defaultName: "fooFunction", apiName: "fooFunction" }} + ${{ defaultLambda: "fooFunction" }} | ${{ defaultName: "fooFunction", apiName: undefined }} + ${{ apiLambda: "fooFunction" }} | ${{ defaultName: undefined, apiName: "fooFunction" }} + ${{ defaultLambda: "fooFunction", apiLambda: "barFunction" }} | ${{ defaultName: "fooFunction", apiName: "barFunction" }} + `("Lambda name input", ({ inputName, expectedName }) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); beforeEach(async () => { From addc1f50a0ebbfde4a9d4b37516c7d0e73f0567c Mon Sep 17 00:00:00 2001 From: danielconde Date: Fri, 8 May 2020 16:05:15 +0100 Subject: [PATCH 26/39] better comments for each cloudfront custom input test case --- .../__tests__/custom-inputs.test.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index a543f84929..30b6639e22 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -265,9 +265,11 @@ describe("Custom inputs", () => { }); describe.each([ - [undefined, {}], // no input - [{}, {}], // empty input - // Ignore origin-requests + // no input + [undefined, {}], + // empty input + [{}, {}], + // ignores custom lambda@edge origin-request trigger set on the default cache behaviour [ { defaults: { @@ -275,9 +277,9 @@ describe("Custom inputs", () => { "lambda@edge": { "origin-request": "ignored value" } } }, - { defaults: { ttl: 500 } } // expecting lambda@edge origin-request to be ignored + { defaults: { ttl: 500 } } ], - // Allow other lamdba@edge types + // allow lamdba@edge triggers other than origin-request [ { defaults: { @@ -296,6 +298,7 @@ describe("Custom inputs", () => { { defaults: { forward: { headers: "X" } } }, { defaults: { forward: { headers: "X" } } } ], + // ignore custom lambda@edge origin-request trigger set on the api cache behaviour [ { "api/*": { @@ -303,8 +306,9 @@ describe("Custom inputs", () => { "lambda@edge": { "origin-request": "ignored value" } } }, - { "api/*": { ttl: 500 } } // expecting lambda@edge origin-request to be ignored + { "api/*": { ttl: 500 } } ], + // allow other lambda@edge triggers on the api cache behaviour [ { "api/*": { @@ -319,6 +323,7 @@ describe("Custom inputs", () => { } } ], + // custom origins and expanding relative URLs to full S3 origin [ { origins: [ @@ -337,6 +342,7 @@ describe("Custom inputs", () => { ] } ], + // custom page cache behaviours [ { "/terms": { From 92bbfa05269408f600515845950a096dcc06ad8c Mon Sep 17 00:00:00 2001 From: danielconde Date: Fri, 8 May 2020 18:16:49 +0100 Subject: [PATCH 27/39] better var names --- .../__tests__/custom-inputs.test.js | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 30b6639e22..0814cfd2ee 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -373,7 +373,8 @@ describe("Custom inputs", () => { ])("Custom cloudfront inputs", (inputCloudfrontConfig, expectedInConfig) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); const { origins = [], defaults = {}, ...other } = expectedInConfig; - const defaultCloudfrontInputs = { + + const expectedDefaultCacheBehaviour = { ...defaults, "lambda@edge": { "origin-request": @@ -381,8 +382,9 @@ describe("Custom inputs", () => { ...defaults["lambda@edge"] } }; - const apiCloudfrontInputs = { - ...other["api/*"], + + const expectedApiCacheBehaviour = { + ...expectedInConfig["api/*"], allowedHttpMethods: [ "HEAD", "DELETE", @@ -395,18 +397,19 @@ describe("Custom inputs", () => { "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...(other["api/*"] && other["api/*"]["lambda@edge"]) + ...(expectedInConfig["api/*"] && + expectedInConfig["api/*"]["lambda@edge"]) } }; - let otherCloudfrontInputs = {}; - Object.entries(other).forEach(([path, config]) => { - otherCloudfrontInputs[path] = { - ...config, + let customPageCacheBehaviours = {}; + Object.entries(other).forEach(([path, cacheBehaviour]) => { + customPageCacheBehaviours[path] = { + ...cacheBehaviour, "lambda@edge": { "origin-request": "arn:aws:lambda:us-east-1:123456789012:function:my-func:v1", - ...(config && config["lambda@edge"]) + ...(cacheBehaviour && cacheBehaviour["lambda@edge"]) } }; }); @@ -419,22 +422,22 @@ describe("Custom inputs", () => { cookies: "all", queryString: true }, - ...defaultCloudfrontInputs + ...expectedDefaultCacheBehaviour }, origins: [ { pathPatterns: { - ...otherCloudfrontInputs, + ...customPageCacheBehaviours, "_next/*": { - ...otherCloudfrontInputs["_next/*"], + ...customPageCacheBehaviours["_next/*"], ttl: 86400 }, "api/*": { ttl: 0, - ...apiCloudfrontInputs + ...expectedApiCacheBehaviour }, "static/*": { - ...otherCloudfrontInputs["static/*"], + ...customPageCacheBehaviours["static/*"], ttl: 86400 } }, From 812d14bede119619ec8d85b0b15746047c5e87f2 Mon Sep 17 00:00:00 2001 From: danielconde Date: Fri, 8 May 2020 18:19:02 +0100 Subject: [PATCH 28/39] better description --- .../serverless-component/__tests__/custom-inputs.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 0814cfd2ee..f7ebeeeb42 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -469,9 +469,10 @@ describe("Custom inputs", () => { }); }); - describe.each([ + describe.each` + `([ { - "some-invalid-path": { ttl: 100 } + "some-invalid-page-route": { ttl: 100 } }, { "/api": { ttl: 100 } From ed1735cbe599f0700916d0c2b362ca68354ce35e Mon Sep 17 00:00:00 2001 From: Daniel Conde Date: Fri, 8 May 2020 22:52:28 +0100 Subject: [PATCH 29/39] fix typo --- packages/serverless-component/__tests__/custom-inputs.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index f7ebeeeb42..1f75043668 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -469,8 +469,7 @@ describe("Custom inputs", () => { }); }); - describe.each` - `([ + describe.each([ { "some-invalid-page-route": { ttl: 100 } }, From 32d9bd003b4014984d693bdeed6fc6099ff1761b Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 9 May 2020 12:56:22 +1000 Subject: [PATCH 30/39] dont allow overriding on origin-response --- .../__tests__/custom-inputs.test.js | 17 ++++++++++++----- packages/serverless-component/serverless.js | 10 +++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 617d2322ab..fbc223e944 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -337,23 +337,28 @@ describe("Custom inputs", () => { { defaults: { ttl: 500, - "lambda@edge": { "origin-request": "ignored value" } + "lambda@edge": { + "origin-request": "ignored value", + "origin-response": "other ignored value" + } } }, { defaults: { ttl: 500 } } ], - // allow lamdba@edge triggers other than origin-request + // allow lamdba@edge triggers other than origin-request and origin-response [ { defaults: { ttl: 500, - "lambda@edge": { "origin-response": "used value" } + "lambda@edge": { + "other-field": "used value" + } } }, { defaults: { ttl: 500, - "lambda@edge": { "origin-response": "used value" } + "lambda@edge": { "other-field": "used value" } } } ], @@ -411,7 +416,9 @@ describe("Custom inputs", () => { "/terms": { ttl: 5500, "misc-param": "misc-value", - "lambda@edge": { "origin-request": "ignored value" } + "lambda@edge": { + "origin-request": "ignored value" + } } }, { diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index bde4bdea7c..4c30a43b23 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -358,6 +358,14 @@ class NextjsComponent extends Component { }; }); + // make sure that origin-response is not set. + // if you inject "origin-response": undefined + // the key will still exist and break our tests + let defaultCloudfrontEdgeInputs = { + ...(defaultCloudfrontInputs["lambda@edge"] || {}) + }; + delete defaultCloudfrontEdgeInputs["origin-response"]; + const cloudFrontOutputs = await cloudFront({ defaults: { ttl: 0, @@ -369,7 +377,7 @@ class NextjsComponent extends Component { // everything after here cant be overriden allowedHttpMethods: ["HEAD", "GET"], "lambda@edge": { - ...(defaultCloudfrontInputs["lambda@edge"] || {}), + ...defaultCloudfrontEdgeInputs, "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` } }, From 8d892e75c13475bba002b6007dde708316b1c2bb Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 9 May 2020 13:50:39 +1000 Subject: [PATCH 31/39] WIP test --- .../__tests__/custom-inputs.test.js | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index fbc223e944..19207613cb 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -539,31 +539,36 @@ describe("Custom inputs", () => { }); }); - describe.each([ - { - "some-invalid-page-route": { ttl: 100 } - }, - { - "/api": { ttl: 100 } - }, - { api: { ttl: 100 } }, - { "api/test": { ttl: 100 } } - ])("Invalid cloudfront inputs", inputCloudfrontConfig => { - const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); - - beforeEach(async () => { - process.chdir(fixturePath); - }); - - it("throws an error", () => { - const component = createNextComponent(); - expect( - // only deploy because the fixture will be cleaned up if - // build throws an error - component.deploy({ - cloudfront: inputCloudfrontConfig - }) - ).rejects.toThrow(); - }); - }); + // describe.each([ + // [ + // { + // "some-invalid-page-route": { ttl: 100 } + // }, + // Error( + // "Custom CloudFront input failed validation. Failed to find Next.js paths for some-invalid-page-route" + // ) + // ], + // [ + // { + // "/api": { ttl: 100 } + // }, + // Error("test") + // ], + // [{ api: { ttl: 100 } }, Error("test")], + // [{ "api/test": { ttl: 100 } }, Error("test")] + // ])("Invalid cloudfront inputs", (inputCloudfrontConfig, expectedError) => { + // const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + + // beforeEach(async () => { + // process.chdir(fixturePath); + // }); + + // it("throws the correct error", () => { + // expect(() => + // createNextComponent().default({ + // cloudfront: inputCloudfrontConfig + // }) + // ).toEqual(expectedError); + // }); + // }); }); From 122d6730a4da7265b68b323c022df8b696e47ae2 Mon Sep 17 00:00:00 2001 From: "Rattus P. Rattus" Date: Sat, 9 May 2020 14:05:58 +1000 Subject: [PATCH 32/39] fix tests --- .../__tests__/custom-inputs.test.js | 78 +++++++++++-------- packages/serverless-component/serverless.js | 2 +- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 19207613cb..6f75282f09 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -539,36 +539,50 @@ describe("Custom inputs", () => { }); }); - // describe.each([ - // [ - // { - // "some-invalid-page-route": { ttl: 100 } - // }, - // Error( - // "Custom CloudFront input failed validation. Failed to find Next.js paths for some-invalid-page-route" - // ) - // ], - // [ - // { - // "/api": { ttl: 100 } - // }, - // Error("test") - // ], - // [{ api: { ttl: 100 } }, Error("test")], - // [{ "api/test": { ttl: 100 } }, Error("test")] - // ])("Invalid cloudfront inputs", (inputCloudfrontConfig, expectedError) => { - // const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); - - // beforeEach(async () => { - // process.chdir(fixturePath); - // }); - - // it("throws the correct error", () => { - // expect(() => - // createNextComponent().default({ - // cloudfront: inputCloudfrontConfig - // }) - // ).toEqual(expectedError); - // }); - // }); + describe.each([ + [ + { + "some-invalid-page-route": { ttl: 100 } + }, + Error( + "Custom CloudFront input failed validation. Failed to find Next.js paths for some-invalid-page-route" + ) + ], + [ + { + "/api": { ttl: 100 } + }, + Error( + "No custom CloudFront configuration is permitted for path /api. It violates api/* behaivour" + ) + ], + [ + { api: { ttl: 100 } }, + Error( + "No custom CloudFront configuration is permitted for path api. It violates api/* behaivour" + ) + ], + [ + { "api/test": { ttl: 100 } }, + Error( + "No custom CloudFront configuration is permitted for path api/test. It violates api/* behaivour" + ) + ] + ])("Invalid cloudfront inputs", (inputCloudfrontConfig, expectedError) => { + const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + + beforeEach(async () => { + process.chdir(fixturePath); + }); + + it("throws the correct error", async () => { + // jest doesnt handle exceptions in async code very well, + // its being documented here: https://github.com/facebook/jest/issues/1700 + await expect( + createNextComponent().deploy({ + cloudfront: inputCloudfrontConfig + }) + ).rejects.toThrow(expectedError); + }); + }); }); diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index 4c30a43b23..d1808eb0f8 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -35,7 +35,7 @@ class NextjsComponent extends Component { for (const path of stillToMatch) { if (/^(\/?api\/.*|\/?api)$/.test(path)) { throw Error( - `No custom CloudFront configuration is permitted for path ${path} . It violates api/* behaivour` + `No custom CloudFront configuration is permitted for path ${path}. It violates api/* behaivour` ); } } From 555ba9d8453463f89da79f93ab21a9cb560dbbe7 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 9 May 2020 08:15:18 +0100 Subject: [PATCH 33/39] use jest.describe.eachi consistent with other tests --- .../__tests__/custom-inputs.test.js | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 6f75282f09..acfc727050 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -539,36 +539,13 @@ describe("Custom inputs", () => { }); }); - describe.each([ - [ - { - "some-invalid-page-route": { ttl: 100 } - }, - Error( - "Custom CloudFront input failed validation. Failed to find Next.js paths for some-invalid-page-route" - ) - ], - [ - { - "/api": { ttl: 100 } - }, - Error( - "No custom CloudFront configuration is permitted for path /api. It violates api/* behaivour" - ) - ], - [ - { api: { ttl: 100 } }, - Error( - "No custom CloudFront configuration is permitted for path api. It violates api/* behaivour" - ) - ], - [ - { "api/test": { ttl: 100 } }, - Error( - "No custom CloudFront configuration is permitted for path api/test. It violates api/* behaivour" - ) - ] - ])("Invalid cloudfront inputs", (inputCloudfrontConfig, expectedError) => { + describe.each` + cloudFrontInput | expectedErrorMessage + ${{ "some-invalid-page-route": { ttl: 100 } }} | ${"Custom CloudFront input failed validation. Failed to find Next.js paths for some-invalid-page-route"} + ${{ "/api": { ttl: 100 } }} | ${"No custom CloudFront configuration is permitted for path /api. It violates api/* behaivour"} + ${{ api: { ttl: 100 } }} | ${"No custom CloudFront configuration is permitted for path api. It violates api/* behaivour"} + ${{ "api/test": { ttl: 100 } }} | ${"No custom CloudFront configuration is permitted for path api/test. It violates api/* behaivour"} + `("Invalid cloudfront inputs", (inputCloudfrontConfig, expectedError) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); beforeEach(async () => { From ea1a8e12a2289d17fd754a8fee0bbba522fc5fe8 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 9 May 2020 08:44:39 +0100 Subject: [PATCH 34/39] small tweaks to tests and error messaging --- .../__tests__/custom-inputs.test.js | 59 +++++++++++-------- packages/serverless-component/serverless.js | 12 ++-- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index acfc727050..8391a6a596 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -33,10 +33,16 @@ describe("Custom inputs", () => { jest.spyOn(fse, "remove").mockImplementation(() => { return; }); + jest.spyOn(fse, "emptyDir").mockImplementation(() => { + return; + }); + consoleWarnSpy = jest.spyOn(console, "warn").mockReturnValue(); }); afterEach(() => { + fse.remove.mockRestore(); + fse.emptyDir.mockRestore(); consoleWarnSpy.mockRestore(); }); @@ -332,14 +338,14 @@ describe("Custom inputs", () => { [undefined, {}], // empty input [{}, {}], - // ignores custom lambda@edge origin-request trigger set on the default cache behaviour + // ignores origin-request and origin-response triggers as they're reserved by serverless-next.js [ { defaults: { ttl: 500, "lambda@edge": { - "origin-request": "ignored value", - "origin-response": "other ignored value" + "origin-request": "ignored", + "origin-response": "also ignored" } } }, @@ -351,14 +357,14 @@ describe("Custom inputs", () => { defaults: { ttl: 500, "lambda@edge": { - "other-field": "used value" + "viewer-request": "used value" } } }, { defaults: { ttl: 500, - "lambda@edge": { "other-field": "used value" } + "lambda@edge": { "viewer-request": "used value" } } } ], @@ -541,25 +547,30 @@ describe("Custom inputs", () => { describe.each` cloudFrontInput | expectedErrorMessage - ${{ "some-invalid-page-route": { ttl: 100 } }} | ${"Custom CloudFront input failed validation. Failed to find Next.js paths for some-invalid-page-route"} - ${{ "/api": { ttl: 100 } }} | ${"No custom CloudFront configuration is permitted for path /api. It violates api/* behaivour"} - ${{ api: { ttl: 100 } }} | ${"No custom CloudFront configuration is permitted for path api. It violates api/* behaivour"} - ${{ "api/test": { ttl: 100 } }} | ${"No custom CloudFront configuration is permitted for path api/test. It violates api/* behaivour"} - `("Invalid cloudfront inputs", (inputCloudfrontConfig, expectedError) => { - const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + ${{ "some-invalid-page-route": { ttl: 100 } }} | ${'Could not find next.js pages for "some-invalid-page-route"'} + ${{ "/api": { ttl: 100 } }} | ${'route "/api" is not supported'} + ${{ api: { ttl: 100 } }} | ${'route "api" is not supported'} + ${{ "api/test": { ttl: 100 } }} | ${'route "api/test" is not supported'} + `( + "Invalid cloudfront inputs", + ({ cloudFrontInput, expectedErrorMessage }) => { + const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); - beforeEach(async () => { - process.chdir(fixturePath); - }); + beforeEach(async () => { + process.chdir(fixturePath); + }); - it("throws the correct error", async () => { - // jest doesnt handle exceptions in async code very well, - // its being documented here: https://github.com/facebook/jest/issues/1700 - await expect( - createNextComponent().deploy({ - cloudfront: inputCloudfrontConfig - }) - ).rejects.toThrow(expectedError); - }); - }); + it("throws the correct error", async () => { + expect.assertions(1); + + try { + await createNextComponent().default({ + cloudfront: cloudFrontInput + }); + } catch (err) { + expect(err.message).toContain(expectedErrorMessage); + } + }); + } + ); }); diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index d1808eb0f8..b95d788122 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -26,7 +26,7 @@ class NextjsComponent extends Component { validatePathPatterns(pathPatterns, buildManifest) { let stillToMatch = new Set(pathPatterns); if (stillToMatch.size !== pathPatterns.length) { - throw Error("duplicate path path declared in cloudfront configuration"); + throw Error("Duplicate path declared in cloudfront configuration"); } // there wont be a page path for this so we can remove it @@ -35,7 +35,7 @@ class NextjsComponent extends Component { for (const path of stillToMatch) { if (/^(\/?api\/.*|\/?api)$/.test(path)) { throw Error( - `No custom CloudFront configuration is permitted for path ${path}. It violates api/* behaivour` + `Setting custom cache behaviour for api/ route "${path}" is not supported` ); } } @@ -60,7 +60,7 @@ class NextjsComponent extends Component { Object.entries({ ...ssrDynamic, ...htmlDynamic - }).map(([path, { file, regex }]) => { + }).map(([, { regex }]) => { manifestRegex.push(new RegExp(regex)); }); @@ -74,7 +74,7 @@ class NextjsComponent extends Component { // first we check if the path patterns match any of the dynamic page regex. // paths with stars (*) shouldnt cause any issues because the regex will treat these - // as characters. Its n^2 but n is small + // as characters. manifestRegex.forEach(re => { for (const path of stillToMatch) { if (re.test(path)) { @@ -95,9 +95,9 @@ class NextjsComponent extends Component { if (stillToMatch.size > 0) { throw Error( - `Custom CloudFront input failed validation. Failed to find Next.js paths for ${[ + `CloudFront input failed validation. Could not find next.js pages for "${[ ...stillToMatch - ]}` + ]}"` ); } } From 28e72e769ec9c7acbfb8670a723b63b4718dd8e0 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 9 May 2020 08:56:21 +0100 Subject: [PATCH 35/39] consistent comments casing --- packages/serverless-component/serverless.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/serverless-component/serverless.js b/packages/serverless-component/serverless.js index b95d788122..bfb610459c 100644 --- a/packages/serverless-component/serverless.js +++ b/packages/serverless-component/serverless.js @@ -210,7 +210,7 @@ class NextjsComponent extends Component { } }; - // Parse origins from inputs + // parse origins from inputs let inputOrigins = []; if (inputs.cloudfront && inputs.cloudfront.origins) { inputOrigins = inputs.cloudfront.origins.map(expandRelativeUrls); @@ -339,7 +339,7 @@ class NextjsComponent extends Component { defaultBuildManifest ); - // Add any custom cloudfront configuration + // add any custom cloudfront configuration // this includes overrides for _next, static and api Object.entries(customCloudFrontConfig).map(([path, config]) => { cloudFrontOrigins[0].pathPatterns[path] = { @@ -359,12 +359,11 @@ class NextjsComponent extends Component { }); // make sure that origin-response is not set. - // if you inject "origin-response": undefined - // the key will still exist and break our tests - let defaultCloudfrontEdgeInputs = { + // this is reserved for serverless-next.js usage + let defaultLambdaAtEdgeConfig = { ...(defaultCloudfrontInputs["lambda@edge"] || {}) }; - delete defaultCloudfrontEdgeInputs["origin-response"]; + delete defaultLambdaAtEdgeConfig["origin-response"]; const cloudFrontOutputs = await cloudFront({ defaults: { @@ -377,7 +376,7 @@ class NextjsComponent extends Component { // everything after here cant be overriden allowedHttpMethods: ["HEAD", "GET"], "lambda@edge": { - ...defaultCloudfrontEdgeInputs, + ...defaultLambdaAtEdgeConfig, "origin-request": `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}` } }, @@ -386,7 +385,7 @@ class NextjsComponent extends Component { let appUrl = cloudFrontOutputs.url; - // Create domain + // create domain const { domain, subdomain } = obtainDomains(inputs.domain); if (domain) { const domainComponent = await this.load("@serverless/domain"); From 4a0ab4a5d600fde0f3d8c96613370820fc205f52 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 9 May 2020 10:44:02 +0100 Subject: [PATCH 36/39] create common fn for reusing sls component deps --- .../__tests__/custom-inputs.test.js | 89 +++++++++---------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index 8391a6a596..e7afaa2442 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -23,6 +23,28 @@ const createNextComponent = inputs => { return component; }; +const mockServerlessComponentDependencies = ({ expectedDomain }) => { + mockS3.mockResolvedValue({ + name: "bucket-xyz" + }); + + mockLambda.mockResolvedValue({ + arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" + }); + + mockLambdaPublish.mockResolvedValue({ + version: "v1" + }); + + mockCloudFront.mockResolvedValueOnce({ + url: "https://cloudfrontdistrib.amazonaws.com" + }); + + mockDomain.mockResolvedValueOnce({ + domains: [expectedDomain] + }); +}; + describe("Custom inputs", () => { let tmpCwd; let componentOutputs; @@ -60,24 +82,8 @@ describe("Custom inputs", () => { tmpCwd = process.cwd(); process.chdir(fixturePath); - mockS3.mockResolvedValue({ - name: "bucket-xyz" - }); - - mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" - }); - - mockLambdaPublish.mockResolvedValue({ - version: "v1" - }); - - mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" - }); - - mockDomain.mockResolvedValueOnce({ - domains: [expectedDomain] + mockServerlessComponentDependencies({ + expectedDomain }); const component = createNextComponent(); @@ -135,20 +141,8 @@ describe("Custom inputs", () => { beforeEach(async () => { process.chdir(fixturePath); - mockS3.mockResolvedValue({ - name: "bucket-xyz" - }); - - mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" - }); + mockServerlessComponentDependencies({}); - mockLambda.mockResolvedValue({ - arn: "arn:aws:lambda:us-east-1:123456789012:function:my-func" - }); - mockLambdaPublish.mockResolvedValue({ - version: "v1" - }); const component = createNextComponent(); componentOutputs = await component.default({ @@ -199,9 +193,7 @@ describe("Custom inputs", () => { beforeEach(async () => { process.chdir(fixturePath); - mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" - }); + mockServerlessComponentDependencies({}); const component = createNextComponent({ memory: inputMemory @@ -211,10 +203,11 @@ describe("Custom inputs", () => { memory: inputMemory }); }); + it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { const { defaultMemory, apiMemory } = expectedMemory; - // Default Lambda + // default Lambda expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), @@ -222,7 +215,7 @@ describe("Custom inputs", () => { }) ); - // Api Lambda + // api Lambda expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), @@ -248,9 +241,7 @@ describe("Custom inputs", () => { tmpCwd = process.cwd(); process.chdir(fixturePath); - mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" - }); + mockServerlessComponentDependencies({}); const component = createNextComponent(); @@ -266,7 +257,6 @@ describe("Custom inputs", () => { it(`sets default lambda timeout to ${expectedTimeout.defaultTimeout} and api lambda timeout to ${expectedTimeout.apiTimeout}`, () => { const { defaultTimeout, apiTimeout } = expectedTimeout; - // Default Lambda expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR), @@ -274,7 +264,6 @@ describe("Custom inputs", () => { }) ); - // Api Lambda expect(mockLambda).toBeCalledWith( expect.objectContaining({ code: path.join(fixturePath, API_LAMBDA_CODE_DIR), @@ -298,9 +287,7 @@ describe("Custom inputs", () => { beforeEach(async () => { process.chdir(fixturePath); - mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" - }); + mockServerlessComponentDependencies({}); const component = createNextComponent(); @@ -311,7 +298,6 @@ describe("Custom inputs", () => { it(`sets default lambda name to ${expectedName.defaultName} and api lambda name to ${expectedName.apiName}`, () => { const { defaultName, apiName } = expectedName; - // Default Lambda const expectedDefaultObject = { code: path.join(fixturePath, DEFAULT_LAMBDA_CODE_DIR) }; @@ -321,7 +307,6 @@ describe("Custom inputs", () => { expect.objectContaining(expectedDefaultObject) ); - // Api Lambda const expectedApiObject = { code: path.join(fixturePath, API_LAMBDA_CODE_DIR) }; @@ -527,9 +512,7 @@ describe("Custom inputs", () => { beforeEach(async () => { process.chdir(fixturePath); - mockCloudFront.mockResolvedValueOnce({ - url: "https://cloudfrontdistrib.amazonaws.com" - }); + mockServerlessComponentDependencies({}); const component = createNextComponent(); @@ -555,9 +538,17 @@ describe("Custom inputs", () => { "Invalid cloudfront inputs", ({ cloudFrontInput, expectedErrorMessage }) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + let tmpCwd; beforeEach(async () => { + tmpCwd = process.cwd(); process.chdir(fixturePath); + + mockServerlessComponentDependencies({}); + }); + + afterEach(() => { + process.chdir(tmpCwd); }); it("throws the correct error", async () => { From ec7cc5be56511958089c1a4ec598a35e1db107b7 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 9 May 2020 15:10:11 +0100 Subject: [PATCH 37/39] remove superfluous mock and tidy up fixture usage --- .../__tests__/custom-inputs.test.js | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/packages/serverless-component/__tests__/custom-inputs.test.js b/packages/serverless-component/__tests__/custom-inputs.test.js index e7afaa2442..a4c2a75828 100644 --- a/packages/serverless-component/__tests__/custom-inputs.test.js +++ b/packages/serverless-component/__tests__/custom-inputs.test.js @@ -11,6 +11,7 @@ const { DEFAULT_LAMBDA_CODE_DIR, API_LAMBDA_CODE_DIR } = require("../constants"); +const { cleanupFixtureDirectory } = require("../lib/test-utils"); const createNextComponent = inputs => { const component = new NextjsComponent(inputs); @@ -46,7 +47,6 @@ const mockServerlessComponentDependencies = ({ expectedDomain }) => { }; describe("Custom inputs", () => { - let tmpCwd; let componentOutputs; let consoleWarnSpy; @@ -55,16 +55,12 @@ describe("Custom inputs", () => { jest.spyOn(fse, "remove").mockImplementation(() => { return; }); - jest.spyOn(fse, "emptyDir").mockImplementation(() => { - return; - }); consoleWarnSpy = jest.spyOn(console, "warn").mockReturnValue(); }); afterEach(() => { fse.remove.mockRestore(); - fse.emptyDir.mockRestore(); consoleWarnSpy.mockRestore(); }); @@ -77,6 +73,7 @@ describe("Custom inputs", () => { ${"example.com"} | ${"https://www.example.com"} `("Custom domain", ({ inputDomains, expectedDomain }) => { const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); + let tmpCwd; beforeEach(async () => { tmpCwd = process.cwd(); @@ -98,9 +95,10 @@ describe("Custom inputs", () => { afterEach(() => { process.chdir(tmpCwd); + return cleanupFixtureDirectory(fixturePath); }); - it("uses @serverless/domain to provision custom domain", async () => { + it("uses @serverless/domain to provision custom domain", () => { const { domain, subdomain } = obtainDomains(inputDomains); expect(mockDomain).toBeCalledWith({ @@ -127,7 +125,7 @@ describe("Custom inputs", () => { ); }); - it("outputs custom domain url", async () => { + it("outputs custom domain url", () => { expect(componentOutputs.appUrl).toEqual(expectedDomain); }); }); @@ -138,7 +136,10 @@ describe("Custom inputs", () => { `( "nextConfigDir=$nextConfigDir, nextStaticDir=$nextStaticDir", ({ fixturePath, ...inputs }) => { + let tmpCwd; + beforeEach(async () => { + tmpCwd = process.cwd(); process.chdir(fixturePath); mockServerlessComponentDependencies({}); @@ -151,6 +152,11 @@ describe("Custom inputs", () => { }); }); + afterEach(() => { + process.chdir(tmpCwd); + return cleanupFixtureDirectory(fixturePath); + }); + it("uploads static assets to S3 correctly", () => { expect(mockUpload).toBeCalledWith( expect.objectContaining({ @@ -188,9 +194,11 @@ describe("Custom inputs", () => { ${{ defaultLambda: 128, apiLambda: 2048 }} | ${{ defaultMemory: 128, apiMemory: 2048 }} ${{ defaultLambda: 1024 }} | ${{ defaultMemory: 1024, apiMemory: 512 }} `("Lambda memory input", ({ inputMemory, expectedMemory }) => { + let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); beforeEach(async () => { + tmpCwd = process.cwd(); process.chdir(fixturePath); mockServerlessComponentDependencies({}); @@ -204,6 +212,11 @@ describe("Custom inputs", () => { }); }); + afterEach(() => { + process.chdir(tmpCwd); + return cleanupFixtureDirectory(fixturePath); + }); + it(`sets default lambda memory to ${expectedMemory.defaultMemory} and api lambda memory to ${expectedMemory.apiMemory}`, () => { const { defaultMemory, apiMemory } = expectedMemory; @@ -252,6 +265,7 @@ describe("Custom inputs", () => { afterEach(() => { process.chdir(tmpCwd); + return cleanupFixtureDirectory(fixturePath); }); it(`sets default lambda timeout to ${expectedTimeout.defaultTimeout} and api lambda timeout to ${expectedTimeout.apiTimeout}`, () => { @@ -282,9 +296,11 @@ describe("Custom inputs", () => { ${{ apiLambda: "fooFunction" }} | ${{ defaultName: undefined, apiName: "fooFunction" }} ${{ defaultLambda: "fooFunction", apiLambda: "barFunction" }} | ${{ defaultName: "fooFunction", apiName: "barFunction" }} `("Lambda name input", ({ inputName, expectedName }) => { + let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); beforeEach(async () => { + tmpCwd = process.cwd(); process.chdir(fixturePath); mockServerlessComponentDependencies({}); @@ -295,6 +311,12 @@ describe("Custom inputs", () => { name: inputName }); }); + + afterEach(() => { + process.chdir(tmpCwd); + return cleanupFixtureDirectory(fixturePath); + }); + it(`sets default lambda name to ${expectedName.defaultName} and api lambda name to ${expectedName.apiName}`, () => { const { defaultName, apiName } = expectedName; @@ -432,6 +454,7 @@ describe("Custom inputs", () => { } ] ])("Custom cloudfront inputs", (inputCloudfrontConfig, expectedInConfig) => { + let tmpCwd; const fixturePath = path.join(__dirname, "./fixtures/generic-fixture"); const { origins = [], defaults = {}, ...other } = expectedInConfig; @@ -510,6 +533,7 @@ describe("Custom inputs", () => { }; beforeEach(async () => { + tmpCwd = process.cwd(); process.chdir(fixturePath); mockServerlessComponentDependencies({}); @@ -521,6 +545,11 @@ describe("Custom inputs", () => { }); }); + afterEach(() => { + process.chdir(tmpCwd); + return cleanupFixtureDirectory(fixturePath); + }); + it("Sets cloudfront options if present", () => { expect(mockCloudFront).toBeCalledWith( expect.objectContaining(cloudfrontConfig) @@ -549,6 +578,7 @@ describe("Custom inputs", () => { afterEach(() => { process.chdir(tmpCwd); + return cleanupFixtureDirectory(fixturePath); }); it("throws the correct error", async () => { From db955ac6ce12479f6bf45ec7b4a39f1e86b9ff31 Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 9 May 2020 15:59:19 +0100 Subject: [PATCH 38/39] add example application with cache settings --- .gitignore | 1 + .../next.config.js | 3 ++ .../package.json | 32 +++++++++++++++++++ .../pages/about.js | 3 ++ .../pages/post/[slug].js | 8 +++++ .../serverless.yml | 11 +++++++ 6 files changed, 58 insertions(+) create mode 100644 packages/serverless-component/examples/app-with-custom-caching-config/next.config.js create mode 100644 packages/serverless-component/examples/app-with-custom-caching-config/package.json create mode 100644 packages/serverless-component/examples/app-with-custom-caching-config/pages/about.js create mode 100644 packages/serverless-component/examples/app-with-custom-caching-config/pages/post/[slug].js create mode 100644 packages/serverless-component/examples/app-with-custom-caching-config/serverless.yml diff --git a/.gitignore b/.gitignore index 3015fb6399..1d50bb5c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ sls-next-build yarn.lock dist .vscode +.env diff --git a/packages/serverless-component/examples/app-with-custom-caching-config/next.config.js b/packages/serverless-component/examples/app-with-custom-caching-config/next.config.js new file mode 100644 index 0000000000..aacc3fcd6a --- /dev/null +++ b/packages/serverless-component/examples/app-with-custom-caching-config/next.config.js @@ -0,0 +1,3 @@ +module.exports = { + target: "serverless" +} diff --git a/packages/serverless-component/examples/app-with-custom-caching-config/package.json b/packages/serverless-component/examples/app-with-custom-caching-config/package.json new file mode 100644 index 0000000000..1b779812e7 --- /dev/null +++ b/packages/serverless-component/examples/app-with-custom-caching-config/package.json @@ -0,0 +1,32 @@ +{ + "name": "app-with-custom-caching-config", + "version": "1.0.0", + "description": "Example app to show different cache configurations you can use with serverless-next.js", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/danielcondemarin/serverless-next.js.git" + }, + "keywords": [ + "Cache", + "Serverless", + "Next.js" + ], + "author": "Daniel Conde ", + "license": "ISC", + "bugs": { + "url": "https://github.com/danielcondemarin/serverless-next.js/issues" + }, + "homepage": "https://github.com/danielcondemarin/serverless-next.js#readme", + "dependencies": { + "next": "^9.3.6", + "react": "^16.13.1", + "react-dom": "^16.13.1" + }, + "devDependencies": { + "serverless": "^1.70.0" + } +} diff --git a/packages/serverless-component/examples/app-with-custom-caching-config/pages/about.js b/packages/serverless-component/examples/app-with-custom-caching-config/pages/about.js new file mode 100644 index 0000000000..662df5c7b7 --- /dev/null +++ b/packages/serverless-component/examples/app-with-custom-caching-config/pages/about.js @@ -0,0 +1,3 @@ +export default function About() { + return
About page updated
; +} diff --git a/packages/serverless-component/examples/app-with-custom-caching-config/pages/post/[slug].js b/packages/serverless-component/examples/app-with-custom-caching-config/pages/post/[slug].js new file mode 100644 index 0000000000..46c88520bb --- /dev/null +++ b/packages/serverless-component/examples/app-with-custom-caching-config/pages/post/[slug].js @@ -0,0 +1,8 @@ +import { useRouter } from "next/router"; + +export default () => { + const router = useRouter(); + const { slug } = router.query; + + return
Blog post updated: {slug}
; +}; diff --git a/packages/serverless-component/examples/app-with-custom-caching-config/serverless.yml b/packages/serverless-component/examples/app-with-custom-caching-config/serverless.yml new file mode 100644 index 0000000000..2ae4b1a243 --- /dev/null +++ b/packages/serverless-component/examples/app-with-custom-caching-config/serverless.yml @@ -0,0 +1,11 @@ +nextApp: + component: ../../ + inputs: + cloudfront: + # the cache settings below will be applied to the CloudFront distribution + # the pages will be cached at CloudFront's edge locations + /post/*: + ttl: 86400 # 1 day cache + /about: + ttl: 31536000 # 1 year + From bcb0bd5fdf6c65a5c36ce41363d4d495516cd76e Mon Sep 17 00:00:00 2001 From: danielconde Date: Sat, 9 May 2020 16:07:02 +0100 Subject: [PATCH 39/39] update docs --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f8a5b1d156..0c0b9c0ab3 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,9 @@ In most cases you wouldn't want to use CloudFront's distribution domain to acces You can use any domain name but you must be using AWS Route53 for your DNS hosting. To migrate DNS records from an existing domain follow the instructions [here](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/MigratingDNS.html). The requirements to use a custom domain name: - * Route53 must include a _hosted zone_ for your domain (e.g. `mydomain.com`) with a set of nameservers. - * You must update the nameservers listed with your domain name registrar (e.g. namecheap, godaddy, etc.) with those provided for your new _hosted zone_. + +- Route53 must include a _hosted zone_ for your domain (e.g. `mydomain.com`) with a set of nameservers. +- You must update the nameservers listed with your domain name registrar (e.g. namecheap, godaddy, etc.) with those provided for your new _hosted zone_. The serverless next.js component will automatically generate an SSL certificate and create a new record to point to your CloudFront distribution. @@ -144,6 +145,8 @@ myNextApplication: viewerProtocolPolicy: redirect-to-https ``` +This is particularly useful for caching any of your next.js pages at CloudFront's edge locations. See [this](/https://github.com/danielcondemarin/serverless-next.js/tree/master/packages/serverless-component/examples/app-with-custom-caching-config) for an example application with custom cache configuration. + ### AWS Permissions By default the Lambda@Edge functions run using AWSLambdaBasicExecutionRole which only allows uploading logs to CloudWatch. If you need permissions beyond this, like for example access to DynamoDB or any other AWS resource you will need your own custom policy arn: