From 059db6800d9c879a05195e316ead434bce8d80ce Mon Sep 17 00:00:00 2001 From: Dorseuil Nicolas Date: Sat, 23 Mar 2024 14:59:17 +0100 Subject: [PATCH] fix resolve in image optimization also fix image opt not using streaming --- .../open-next/src/adapters/edge-adapter.ts | 4 +- .../adapters/image-optimization-adapter.ts | 48 ++++++++++++--- packages/open-next/src/build.ts | 59 ++++++++++++------- .../open-next/src/build/createServerBundle.ts | 10 +--- .../open-next/src/core/routing/middleware.ts | 2 +- packages/open-next/src/plugins/resolve.ts | 26 ++++---- 6 files changed, 100 insertions(+), 49 deletions(-) diff --git a/packages/open-next/src/adapters/edge-adapter.ts b/packages/open-next/src/adapters/edge-adapter.ts index 57b3ab02..09470a8a 100644 --- a/packages/open-next/src/adapters/edge-adapter.ts +++ b/packages/open-next/src/adapters/edge-adapter.ts @@ -49,9 +49,9 @@ const defaultHandler = async ( responseHeaders[key] = value; } }); - console.log("responseHeaders", responseHeaders); + // console.log("responseHeaders", responseHeaders); const body = buffer.toString(); - console.log("body", body); + // console.log("body", body); return { type: "core", diff --git a/packages/open-next/src/adapters/image-optimization-adapter.ts b/packages/open-next/src/adapters/image-optimization-adapter.ts index f8a0b143..a4d8aa64 100644 --- a/packages/open-next/src/adapters/image-optimization-adapter.ts +++ b/packages/open-next/src/adapters/image-optimization-adapter.ts @@ -9,6 +9,7 @@ import { Writable } from "node:stream"; import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { loadConfig } from "config/util.js"; +import { OpenNextNodeResponse, StreamCreator } from "http/openNextResponse.js"; // @ts-ignore import { defaultConfig } from "next/dist/server/config-shared"; import { @@ -27,8 +28,6 @@ import { setNodeEnv } from "./util.js"; // Expected environment variables const { BUCKET_NAME, BUCKET_KEY_PREFIX } = process.env; -const s3Client = new S3Client({ logger: awsLogger }); - setNodeEnv(); const nextDir = path.join(__dirname, ".next"); const config = loadConfig(nextDir); @@ -56,6 +55,7 @@ export const handler = await createGenericHandler({ export async function defaultHandler( event: InternalEvent, + streamCreator?: StreamCreator, ): Promise { // Images are handled via header and query param information. debug("handler event", event); @@ -63,7 +63,7 @@ export async function defaultHandler( try { // const headers = normalizeHeaderKeysToLowercase(rawHeaders); - ensureBucketExists(); + const imageParams = validateImageParams( headers, queryString === null ? undefined : queryString, @@ -75,9 +75,9 @@ export async function defaultHandler( downloadHandler, ); - return buildSuccessResponse(result); + return buildSuccessResponse(result, streamCreator); } catch (e: any) { - return buildFailureResponse(e); + return buildFailureResponse(e, streamCreator); } } @@ -119,7 +119,23 @@ function validateImageParams( return imageParams; } -function buildSuccessResponse(result: any): InternalResult { +function buildSuccessResponse( + result: any, + streamCreator?: StreamCreator, +): InternalResult { + if (streamCreator) { + const response = new OpenNextNodeResponse( + () => void 0, + async () => void 0, + streamCreator, + ); + response.writeHead(200, { + Vary: "Accept", + "Cache-Control": `public,max-age=${result.maxAge},immutable`, + "Content-Type": result.contentType, + }); + response.end(result.buffer); + } return { type: "core", statusCode: 200, @@ -133,8 +149,24 @@ function buildSuccessResponse(result: any): InternalResult { }; } -function buildFailureResponse(e: any): InternalResult { +function buildFailureResponse( + e: any, + streamCreator?: StreamCreator, +): InternalResult { debug(e); + if (streamCreator) { + const response = new OpenNextNodeResponse( + () => void 0, + async () => void 0, + streamCreator, + ); + response.writeHead(500, { + Vary: "Accept", + "Cache-Control": `public,max-age=60,immutable`, + "Content-Type": "application/json", + }); + response.end(e?.message || e?.toString() || e); + } return { type: "core", isBase64Encoded: false, @@ -154,10 +186,12 @@ const resolveLoader = () => { if (typeof openNextParams?.loader === "function") { return openNextParams.loader(); } else { + const s3Client = new S3Client({ logger: awsLogger }); return Promise.resolve({ name: "s3", // @ts-ignore load: async (key: string) => { + ensureBucketExists(); const keyPrefix = BUCKET_KEY_PREFIX?.replace(/^\/|\/$/g, ""); const response = await s3Client.send( new GetObjectCommand({ diff --git a/packages/open-next/src/build.ts b/packages/open-next/src/build.ts index 5b820e1f..22da479f 100755 --- a/packages/open-next/src/build.ts +++ b/packages/open-next/src/build.ts @@ -80,9 +80,9 @@ export async function build(openNextConfigPath?: string) { await createCacheAssets(monorepoRoot); await createServerBundle(config, options); - await createRevalidationBundle(); + await createRevalidationBundle(config); await createImageOptimizationBundle(config); - await createWarmerBundle(); + await createWarmerBundle(config); await generateOutput(options.appPath, options.appBuildOutputPath, config); logger.info("OpenNext build complete."); } @@ -244,7 +244,7 @@ function initOutputDir() { fs.writeFileSync(path.join(tempDir, "open-next.config.mjs"), openNextConfig); } -async function createWarmerBundle() { +async function createWarmerBundle(config: OpenNextConfig) { logger.info(`Bundling warmer function...`); const { outputDir } = options; @@ -268,8 +268,10 @@ async function createWarmerBundle() { plugins: [ openNextResolvePlugin({ overrides: { - converter: "dummy", + converter: config.warmer?.override?.converter ?? "dummy", + wrapper: config.warmer?.override?.wrapper, }, + fnName: "warmer", }), ], banner: { @@ -285,7 +287,7 @@ async function createWarmerBundle() { ); } -async function createRevalidationBundle() { +async function createRevalidationBundle(config: OpenNextConfig) { logger.info(`Bundling revalidation function...`); const { appBuildOutputPath, outputDir } = options; @@ -305,8 +307,11 @@ async function createRevalidationBundle() { outfile: path.join(outputPath, "index.mjs"), plugins: [ openNextResolvePlugin({ + fnName: "revalidate", overrides: { - converter: "sqs-revalidate", + converter: + config.revalidate?.override?.converter ?? "sqs-revalidate", + wrapper: config.revalidate?.override?.wrapper, }, }), ], @@ -333,20 +338,29 @@ async function createImageOptimizationBundle(config: OpenNextConfig) { // Copy open-next.config.mjs into the bundle copyOpenNextConfig(options.tempDir, outputPath); - const plugins = - compareSemver(options.nextVersion, "14.1.1") >= 0 - ? [ - openNextReplacementPlugin({ - name: "opennext-14.1.1-image-optimization", - target: /plugins\/image-optimization\/image-optimization\.js/g, - replacements: [ - require.resolve( - "./adapters/plugins/image-optimization/image-optimization.replacement.js", - ), - ], - }), - ] - : undefined; + const plugins = [ + openNextResolvePlugin({ + fnName: "imageOptimization", + overrides: { + converter: config.imageOptimization?.override?.converter, + wrapper: config.imageOptimization?.override?.wrapper, + }, + }), + ]; + + if (compareSemver(options.nextVersion, "14.1.1") >= 0) { + plugins.push( + openNextReplacementPlugin({ + name: "opennext-14.1.1-image-optimization", + target: /plugins\/image-optimization\/image-optimization\.js/g, + replacements: [ + require.resolve( + "./adapters/plugins/image-optimization/image-optimization.replacement.js", + ), + ], + }), + ); + } // Build Lambda code (1st pass) // note: bundle in OpenNext package b/c the adapter relies on the @@ -639,8 +653,11 @@ async function createCacheAssets(monorepoRoot: string) { target: ["node18"], plugins: [ openNextResolvePlugin({ + fnName: "initializationFunction", overrides: { - converter: "dummy", + converter: + config.initializationFunction?.override?.converter ?? "dummy", + wrapper: config.initializationFunction?.override?.wrapper, }, }), ], diff --git a/packages/open-next/src/build/createServerBundle.ts b/packages/open-next/src/build/createServerBundle.ts index c96e1ace..ed2233dc 100644 --- a/packages/open-next/src/build/createServerBundle.ts +++ b/packages/open-next/src/build/createServerBundle.ts @@ -213,14 +213,8 @@ async function generateBundle( openNextResolvePlugin({ fnName: name, overrides: { - converter: - typeof overrides.converter === "function" - ? "dummy" - : overrides.converter, - wrapper: - typeof overrides.wrapper === "function" - ? "aws-lambda" - : overrides.wrapper, + converter: overrides.converter, + wrapper: overrides.wrapper, }, }), ]; diff --git a/packages/open-next/src/core/routing/middleware.ts b/packages/open-next/src/core/routing/middleware.ts index 2de20cfc..d52c4a4a 100644 --- a/packages/open-next/src/core/routing/middleware.ts +++ b/packages/open-next/src/core/routing/middleware.ts @@ -68,7 +68,7 @@ export async function handleMiddleware( const initialUrl = new URL(normalizedPath, host); initialUrl.search = convertToQueryString(query); const url = initialUrl.toString(); - console.log("url", url, normalizedPath); + // console.log("url", url, normalizedPath); // @ts-expect-error - This is bundled const middleware = await import("./middleware.mjs"); diff --git a/packages/open-next/src/plugins/resolve.ts b/packages/open-next/src/plugins/resolve.ts index eafa0487..1767d309 100644 --- a/packages/open-next/src/plugins/resolve.ts +++ b/packages/open-next/src/plugins/resolve.ts @@ -2,19 +2,18 @@ import { readFileSync } from "node:fs"; import { Plugin } from "esbuild"; import type { - IncludedConverter, + DefaultOverrideOptions, IncludedIncrementalCache, IncludedQueue, IncludedTagCache, - IncludedWrapper, } from "types/open-next"; import logger from "../logger.js"; export interface IPluginSettings { - overrides: { - wrapper?: IncludedWrapper; - converter?: IncludedConverter; + overrides?: { + wrapper?: DefaultOverrideOptions["wrapper"]; + converter?: DefaultOverrideOptions["converter"]; // Right now theses do nothing since there is only one implementation tag?: IncludedTagCache; queue?: IncludedQueue; @@ -37,17 +36,24 @@ export function openNextResolvePlugin({ logger.debug(`OpenNext Resolve plugin for ${fnName}`); build.onLoad({ filter: /core\/resolve.js/g }, async (args) => { let contents = readFileSync(args.path, "utf-8"); - if (overrides?.wrapper) { + if (overrides?.wrapper && typeof overrides.wrapper === "string") { contents = contents.replace( "../wrappers/aws-lambda.js", `../wrappers/${overrides.wrapper}.js`, ); } if (overrides?.converter) { - contents = contents.replace( - "../converters/aws-apigw-v2.js", - `../converters/${overrides.converter}.js`, - ); + if (typeof overrides.converter === "function") { + contents = contents.replace( + "../converters/aws-apigw-v2.js", + `../converters/dummy.js`, + ); + } else { + contents = contents.replace( + "../converters/aws-apigw-v2.js", + `../converters/${overrides.converter}.js`, + ); + } } return { contents,