diff --git a/CHANGELOG.md b/CHANGELOG.md index b02cc628da..21bc44fcd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.8.0-alpha.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0...v3.8.0-alpha.0) (2022-04-20) + +### Bug Fixes + +- change defaultNextLambda removalPolicy from DESTROY to RETAIN ([#2420](https://github.com/serverless-nextjs/serverless-next.js/issues/2420)) ([1baa444](https://github.com/serverless-nextjs/serverless-next.js/commit/1baa4447dea1e8e2f24c7caaa26c0d150176cb7c)) +- logging error ([#2426](https://github.com/serverless-nextjs/serverless-next.js/issues/2426)) ([acfe76e](https://github.com/serverless-nextjs/serverless-next.js/commit/acfe76ed36ed5b1cd7b5c6f40323c355bdce4c04)) + +### Features + +- allow to set custom handler ([#2409](https://github.com/serverless-nextjs/serverless-next.js/issues/2409)) ([f5c33ac](https://github.com/serverless-nextjs/serverless-next.js/commit/f5c33ac7a3089a7d998b6f17bc0f742c570109f8)) + # [3.7.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0-alpha.12...v3.7.0) (2022-03-31) **Note:** Version bump only for package serverless-nextjs-monorepo diff --git a/lerna.json b/lerna.json index f82d96c1ad..529caaf868 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "3.7.0", + "version": "3.8.0-alpha.0", "npmClient": "yarn", "useWorkspaces": true, "command": { diff --git a/packages/libs/aws-common/CHANGELOG.md b/packages/libs/aws-common/CHANGELOG.md index 86404eb8d3..d179d68de3 100644 --- a/packages/libs/aws-common/CHANGELOG.md +++ b/packages/libs/aws-common/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.8.0-alpha.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0...v3.8.0-alpha.0) (2022-04-20) + +### Bug Fixes + +- logging error ([#2426](https://github.com/serverless-nextjs/serverless-next.js/issues/2426)) ([acfe76e](https://github.com/serverless-nextjs/serverless-next.js/commit/acfe76ed36ed5b1cd7b5c6f40323c355bdce4c04)) + # [3.7.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0-alpha.12...v3.7.0) (2022-03-31) **Note:** Version bump only for package @sls-next/aws-common diff --git a/packages/libs/aws-common/package.json b/packages/libs/aws-common/package.json index 95029ec4e7..2fe303e8aa 100644 --- a/packages/libs/aws-common/package.json +++ b/packages/libs/aws-common/package.json @@ -1,6 +1,6 @@ { "name": "@sls-next/aws-common", - "version": "3.7.0", + "version": "3.8.0-alpha.0", "description": "Common AWS code that is used in Lambda, Lambda@Edge and other AWS platforms.", "publishConfig": { "access": "public" diff --git a/packages/libs/aws-common/src/awsPlatformClient.ts b/packages/libs/aws-common/src/awsPlatformClient.ts index ee5e8ed14d..e64b6dbe42 100644 --- a/packages/libs/aws-common/src/awsPlatformClient.ts +++ b/packages/libs/aws-common/src/awsPlatformClient.ts @@ -62,7 +62,7 @@ export class AwsPlatformClient implements PlatformClient { console.info( "Got error response from S3. Will default to returning empty response. Error: " + - JSON.stringify(e) + e ); return { body: undefined, diff --git a/packages/libs/core/CHANGELOG.md b/packages/libs/core/CHANGELOG.md index 054b82e0f5..9d9514b3b7 100644 --- a/packages/libs/core/CHANGELOG.md +++ b/packages/libs/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.8.0-alpha.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0...v3.8.0-alpha.0) (2022-04-20) + +**Note:** Version bump only for package @sls-next/core + # [3.7.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0-alpha.12...v3.7.0) (2022-03-31) **Note:** Version bump only for package @sls-next/core diff --git a/packages/libs/core/package.json b/packages/libs/core/package.json index ad9c9377cf..62e451e5b2 100644 --- a/packages/libs/core/package.json +++ b/packages/libs/core/package.json @@ -1,6 +1,6 @@ { "name": "@sls-next/core", - "version": "3.7.0", + "version": "3.8.0-alpha.0", "description": "Handles Next.js routing independent of provider", "publishConfig": { "access": "public" diff --git a/packages/libs/core/src/images/imageOptimizer.ts b/packages/libs/core/src/images/imageOptimizer.ts index dcfd19bdd5..feb9d3e0ed 100644 --- a/packages/libs/core/src/images/imageOptimizer.ts +++ b/packages/libs/core/src/images/imageOptimizer.ts @@ -212,7 +212,9 @@ export async function imageOptimizer( const hash = getHash([CACHE_VERSION, href, width, quality, mimeType]); const imagesDir = join("/tmp", "cache", "images"); // Use Lambda tmp directory + const imagesMetaDir = join("/tmp", "cache", "imageMeta"); const hashDir = join(imagesDir, hash); + const metaDir = join(imagesMetaDir, hash); const now = Date.now(); if (fs.existsSync(hashDir)) { @@ -223,8 +225,15 @@ export async function imageOptimizer( const contentType = getContentType(extension); const fsPath = join(hashDir, file); if (now < expireAt) { + const meta = JSON.parse( + (await promises.readFile(join(metaDir, `${file}.json`))).toString() + ); if (!res.getHeader("Cache-Control")) { - res.setHeader("Cache-Control", "public, max-age=60"); + if (meta.headers["Cache-Control"]) { + res.setHeader("Cache-Control", meta.headers["Cache-Control"]); + } else { + res.setHeader("Cache-Control", "public, max-age=60"); + } } if (sendEtagResponse(req, res, etag)) { return { finished: true }; @@ -243,6 +252,7 @@ export async function imageOptimizer( let upstreamBuffer: Buffer | undefined; let upstreamType: string | undefined; let maxAge: number; + let cacheControl: string | undefined | null; if (isAbsolute) { const upstreamRes = await fetch(href); @@ -256,12 +266,10 @@ export async function imageOptimizer( res.statusCode = upstreamRes.status; upstreamBuffer = Buffer.from(await upstreamRes.arrayBuffer()); upstreamType = upstreamRes.headers.get("Content-Type") ?? undefined; - maxAge = getMaxAge(upstreamRes.headers.get("Cache-Control") ?? undefined); - if (upstreamRes.headers.get("Cache-Control")) { - res.setHeader( - "Cache-Control", - upstreamRes.headers.get("Cache-Control") as string - ); + cacheControl = upstreamRes.headers.get("Cache-Control"); + maxAge = getMaxAge(cacheControl ?? undefined); + if (cacheControl) { + res.setHeader("Cache-Control", cacheControl as string); } } else { let objectKey; @@ -284,6 +292,7 @@ export async function imageOptimizer( upstreamBuffer = response.body ?? Buffer.of(); upstreamType = response.contentType ?? undefined; + cacheControl = response.cacheControl; maxAge = getMaxAge(response.cacheControl); // If object response provides cache control header, use that @@ -357,11 +366,22 @@ export async function imageOptimizer( } const optimizedBuffer = await transformer.toBuffer(); - await promises.mkdir(hashDir, { recursive: true }); + await Promise.all([ + promises.mkdir(hashDir, { recursive: true }), + promises.mkdir(metaDir, { recursive: true }) + ]); const extension = getExtension(contentType); const etag = getHash([optimizedBuffer]); - const filename = join(hashDir, `${expireAt}.${etag}.${extension}`); - await promises.writeFile(filename, optimizedBuffer); + const fileName = `${expireAt}.${etag}.${extension}`; + const filePath = join(hashDir, fileName); + const metaFilename = join(metaDir, `${fileName}.json`); + await Promise.all([ + promises.writeFile(filePath, optimizedBuffer), + promises.writeFile( + metaFilename, + JSON.stringify({ headers: { "Cache-Control": cacheControl } }) + ) + ]); sendResponse(req, res, contentType, optimizedBuffer); } catch (error: any) { console.error( diff --git a/packages/libs/core/tests/images/imageOptimizer.test.ts b/packages/libs/core/tests/images/imageOptimizer.test.ts index 6f6c9beae6..ff5834d14f 100644 --- a/packages/libs/core/tests/images/imageOptimizer.test.ts +++ b/packages/libs/core/tests/images/imageOptimizer.test.ts @@ -1,11 +1,11 @@ import sharp from "sharp"; -import { ImagesManifest } from "../../src"; +import { ImagesManifest, PlatformClient } from "../../src"; import { imageOptimizer } from "../../src/images/imageOptimizer"; import imagesManifest from "./image-images-manifest.json"; +import fs from "fs"; import url from "url"; import http from "http"; import Stream from "stream"; -import { PlatformClient } from "../../src"; import { jest } from "@jest/globals"; jest.mock("node-fetch", () => require("fetch-mock-jest").sandbox()); @@ -103,7 +103,7 @@ describe("Image optimizer", () => { ); }; - beforeEach(async () => { + const setupPlatformClientResponse = async (cacheControlHeader?: string) => { const imageBuffer: Buffer = await sharp({ create: { width: 100, @@ -122,9 +122,14 @@ describe("Image optimizer", () => { expires: undefined, eTag: "etag", statusCode: 200, - cacheControl: undefined, + cacheControl: cacheControlHeader, contentType: "image/png" }); + }; + + beforeEach(() => { + fs.rmdirSync("/tmp/cache/images", { recursive: true }); + fs.rmdirSync("/tmp/cache/imageMeta", { recursive: true }); }); describe("Routes", () => { @@ -137,6 +142,7 @@ describe("Image optimizer", () => { `( "serves image request", async ({ imagePath, accept, expectedObjectKey }) => { + await setupPlatformClientResponse(); const { parsedUrl, req, res } = createEventByImagePath(imagePath, { accept: accept }); @@ -162,47 +168,61 @@ describe("Image optimizer", () => { ); it.each` - imagePath - ${"/test-image-cached.png"} - `("serves cached image on second request", async ({ imagePath }) => { - const { - parsedUrl: parsedUrl1, - req: req1, - res: res1 - } = createEventByImagePath(imagePath); - const { - parsedUrl: parsedUrl2, - req: req2, - res: res2 - } = createEventByImagePath(imagePath); + imagePath | cacheControlHeader + ${"/test-image-cached.png"} | ${undefined} + ${"/test-image-cached.png"} | ${"public,max-age=31536000,immutable"} + `( + "serves cached image on second request with $cacheControlHeader cache header", + async ({ imagePath, cacheControlHeader }) => { + await setupPlatformClientResponse(cacheControlHeader); + const { + parsedUrl: parsedUrl1, + req: req1, + res: res1 + } = createEventByImagePath(imagePath); + const { + parsedUrl: parsedUrl2, + req: req2, + res: res2 + } = createEventByImagePath(imagePath); - await imageOptimizer( - "", - imagesManifest as ImagesManifest, - req1, - res1, - parsedUrl1, - mockPlatformClient as PlatformClient - ); - await imageOptimizer( - "", - imagesManifest as ImagesManifest, - req2, - res2, - parsedUrl2, - mockPlatformClient as PlatformClient - ); + await imageOptimizer( + "", + imagesManifest as ImagesManifest, + req1, + res1, + parsedUrl1, + mockPlatformClient as PlatformClient + ); + await imageOptimizer( + "", + imagesManifest as ImagesManifest, + req2, + res2, + parsedUrl2, + mockPlatformClient as PlatformClient + ); - expect(res1.statusCode).toEqual(200); - expect(res2.statusCode).toEqual(200); + expect(res1.statusCode).toEqual(200); + expect(res2.statusCode).toEqual(200); - expect(mockPlatformClient.getObject).toBeCalledTimes(1); - }); + let defaultCacheHeader = "public, max-age=60"; + expect(res1.headers["cache-control"]).toEqual( + cacheControlHeader ?? defaultCacheHeader + ); + expect(res2.headers["cache-control"]).toEqual( + cacheControlHeader ?? defaultCacheHeader + ); + + expect(mockPlatformClient.getObject).toBeCalledTimes(1); + } + ); it.each` imagePath ${"/test-image-etag.png"} `("serves 304 when etag matches", async ({ imagePath }) => { + await setupPlatformClientResponse(); const { parsedUrl: parsedUrl1, req: req1, diff --git a/packages/libs/lambda-at-edge/CHANGELOG.md b/packages/libs/lambda-at-edge/CHANGELOG.md index 645c2b00b8..3ad37957a6 100644 --- a/packages/libs/lambda-at-edge/CHANGELOG.md +++ b/packages/libs/lambda-at-edge/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.8.0-alpha.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0...v3.8.0-alpha.0) (2022-04-20) + +**Note:** Version bump only for package @sls-next/lambda-at-edge + # [3.7.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0-alpha.12...v3.7.0) (2022-03-31) **Note:** Version bump only for package @sls-next/lambda-at-edge diff --git a/packages/libs/lambda-at-edge/package.json b/packages/libs/lambda-at-edge/package.json index 9d808a4c96..5249e70bc2 100644 --- a/packages/libs/lambda-at-edge/package.json +++ b/packages/libs/lambda-at-edge/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "3.7.0", + "version": "3.8.0-alpha.0", "description": "Provides handlers that can be used in CloudFront Lambda@Edge to deploy next.js applications to the edge", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/libs/lambda/CHANGELOG.md b/packages/libs/lambda/CHANGELOG.md index 2cc2d3999b..aef4fd0881 100644 --- a/packages/libs/lambda/CHANGELOG.md +++ b/packages/libs/lambda/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.8.0-alpha.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0...v3.8.0-alpha.0) (2022-04-20) + +**Note:** Version bump only for package @sls-next/lambda + # [3.7.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0-alpha.12...v3.7.0) (2022-03-31) **Note:** Version bump only for package @sls-next/lambda diff --git a/packages/libs/lambda/package.json b/packages/libs/lambda/package.json index 44b1f32166..31e93666ca 100644 --- a/packages/libs/lambda/package.json +++ b/packages/libs/lambda/package.json @@ -1,6 +1,6 @@ { "name": "@sls-next/lambda", - "version": "3.7.0", + "version": "3.8.0-alpha.0", "description": "Code to build and deploy for Lambda + API Gateway", "publishConfig": { "access": "public" diff --git a/packages/serverless-components/nextjs-cdk-construct/CHANGELOG.md b/packages/serverless-components/nextjs-cdk-construct/CHANGELOG.md index 9e3f5e349a..0dca3caa46 100644 --- a/packages/serverless-components/nextjs-cdk-construct/CHANGELOG.md +++ b/packages/serverless-components/nextjs-cdk-construct/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.8.0-alpha.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0...v3.8.0-alpha.0) (2022-04-20) + +### Bug Fixes + +- change defaultNextLambda removalPolicy from DESTROY to RETAIN ([#2420](https://github.com/serverless-nextjs/serverless-next.js/issues/2420)) ([1baa444](https://github.com/serverless-nextjs/serverless-next.js/commit/1baa4447dea1e8e2f24c7caaa26c0d150176cb7c)) + +### Features + +- allow to set custom handler ([#2409](https://github.com/serverless-nextjs/serverless-next.js/issues/2409)) ([f5c33ac](https://github.com/serverless-nextjs/serverless-next.js/commit/f5c33ac7a3089a7d998b6f17bc0f742c570109f8)) + # [3.7.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0-alpha.12...v3.7.0) (2022-03-31) **Note:** Version bump only for package @sls-next/cdk-construct diff --git a/packages/serverless-components/nextjs-cdk-construct/__tests__/__snapshots__/snapshots.test.ts.snap b/packages/serverless-components/nextjs-cdk-construct/__tests__/__snapshots__/snapshots.test.ts.snap index d50e1a1a31..c58269170c 100644 --- a/packages/serverless-components/nextjs-cdk-construct/__tests__/__snapshots__/snapshots.test.ts.snap +++ b/packages/serverless-components/nextjs-cdk-construct/__tests__/__snapshots__/snapshots.test.ts.snap @@ -1250,14 +1250,14 @@ Object { "Type": "AWS::CloudFront::CachePolicy", }, "StackNextLambdaCurrentVersion21F01F879970bafa5c9141f6152d4057c3ab8184": Object { - "DeletionPolicy": "Delete", + "DeletionPolicy": "Retain", "Properties": Object { "FunctionName": Object { "Ref": "StackNextLambdaF64DCE99", }, }, "Type": "AWS::Lambda::Version", - "UpdateReplacePolicy": "Delete", + "UpdateReplacePolicy": "Retain", }, "StackNextLambdaCurrentVersionAliasliveB07D2AA0": Object { "Properties": Object { @@ -2999,14 +2999,14 @@ Object { "Type": "AWS::CloudFront::CachePolicy", }, "StackNextLambdaCurrentVersion21F01F87b5875487743b0e6b4f7058e417862406": Object { - "DeletionPolicy": "Delete", + "DeletionPolicy": "Retain", "Properties": Object { "FunctionName": Object { "Ref": "StackNextLambdaF64DCE99", }, }, "Type": "AWS::Lambda::Version", - "UpdateReplacePolicy": "Delete", + "UpdateReplacePolicy": "Retain", }, "StackNextLambdaCurrentVersionAliasliveB07D2AA0": Object { "Properties": Object { diff --git a/packages/serverless-components/nextjs-cdk-construct/package.json b/packages/serverless-components/nextjs-cdk-construct/package.json index 7e4a9fa67e..ba463b8749 100644 --- a/packages/serverless-components/nextjs-cdk-construct/package.json +++ b/packages/serverless-components/nextjs-cdk-construct/package.json @@ -4,7 +4,7 @@ "access": "public" }, "description": "Serverless Next.js powered by AWS CDK", - "version": "3.7.0", + "version": "3.8.0-alpha.0", "main": "dist/index.js", "types": "dist/index.d.ts", "author": "Henry Kirkness ", diff --git a/packages/serverless-components/nextjs-cdk-construct/src/index.ts b/packages/serverless-components/nextjs-cdk-construct/src/index.ts index 5c901adf72..8ecaade56b 100644 --- a/packages/serverless-components/nextjs-cdk-construct/src/index.ts +++ b/packages/serverless-components/nextjs-cdk-construct/src/index.ts @@ -151,9 +151,9 @@ export class NextJSLambdaEdge extends Construct { this.defaultNextLambda = new lambda.Function(this, "NextLambda", { functionName: toLambdaOption("defaultLambda", props.name), description: `Default Lambda@Edge for Next CloudFront distribution`, - handler: "index.handler", + handler: props.handler || "index.handler", currentVersionOptions: { - removalPolicy: RemovalPolicy.DESTROY // destroy old versions + removalPolicy: RemovalPolicy.RETAIN // retain old versions to prevent premature removal, cleanup via trigger later on }, logRetention: logs.RetentionDays.THREE_DAYS, code: lambda.Code.fromAsset( diff --git a/packages/serverless-components/nextjs-cdk-construct/src/props.ts b/packages/serverless-components/nextjs-cdk-construct/src/props.ts index cfcc8fb59a..88f8107f0a 100644 --- a/packages/serverless-components/nextjs-cdk-construct/src/props.ts +++ b/packages/serverless-components/nextjs-cdk-construct/src/props.ts @@ -63,6 +63,11 @@ export interface Props extends StackProps { imageCache?: string; lambdaCache?: string; }; + /** + * If you use a custom handler with `.build()`, you can set the handler here. + */ + handler?: string; + /** * Enable logging on the cloudfront distribution */ diff --git a/packages/serverless-components/nextjs-component/CHANGELOG.md b/packages/serverless-components/nextjs-component/CHANGELOG.md index 1fe0848e26..6d331deea3 100644 --- a/packages/serverless-components/nextjs-component/CHANGELOG.md +++ b/packages/serverless-components/nextjs-component/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.8.0-alpha.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0...v3.8.0-alpha.0) (2022-04-20) + +**Note:** Version bump only for package @sls-next/serverless-component + # [3.7.0](https://github.com/serverless-nextjs/serverless-next.js/compare/v3.7.0-alpha.12...v3.7.0) (2022-03-31) **Note:** Version bump only for package @sls-next/serverless-component diff --git a/packages/serverless-components/nextjs-component/package.json b/packages/serverless-components/nextjs-component/package.json index 00c140644d..c55d81de35 100644 --- a/packages/serverless-components/nextjs-component/package.json +++ b/packages/serverless-components/nextjs-component/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "3.7.0", + "version": "3.8.0-alpha.0", "description": "Serverless Next.js powered by Serverless Components", "main": "./serverless.js", "types": "dist/component.d.ts",