From a805a36161a54cab4ca3ff1b8e52e4c849718e08 Mon Sep 17 00:00:00 2001 From: Kai Prince <34746763+KaiPrince@users.noreply.github.com> Date: Sat, 2 Aug 2025 21:37:35 -0700 Subject: [PATCH 01/16] chore: refactor async callbacks --- packages/cli/src/commands/build.js | 8 +- packages/cli/src/lifecycles/bundle.js | 128 ++++++++---------- packages/cli/src/lifecycles/config.js | 32 +++-- packages/cli/src/lifecycles/copy.js | 6 +- packages/cli/src/lifecycles/prerender.js | 34 ++--- packages/cli/src/lifecycles/serve.js | 67 ++++----- .../src/puppeteer-handler.js | 22 +-- test/smoke-test.js | 2 +- 8 files changed, 135 insertions(+), 164 deletions(-) diff --git a/packages/cli/src/commands/build.js b/packages/cli/src/commands/build.js index 9579c8240..4c8a211d0 100644 --- a/packages/cli/src/commands/build.js +++ b/packages/cli/src/commands/build.js @@ -38,13 +38,7 @@ const runProductionBuild = async (compilation) => { }); } - await Promise.all( - servers.map(async (server) => { - await server.start(); - - return Promise.resolve(server); - }), - ); + await Promise.all(servers.map((server) => server.start())); if (prerenderPlugin.executeModuleUrl) { await preRenderCompilationWorker(compilation, prerenderPlugin); diff --git a/packages/cli/src/lifecycles/bundle.js b/packages/cli/src/lifecycles/bundle.js index e72146e62..56e9529a4 100644 --- a/packages/cli/src/lifecycles/bundle.js +++ b/packages/cli/src/lifecycles/bundle.js @@ -123,36 +123,40 @@ async function optimizeStaticPages(compilation, plugins) { (page.isSSR && page.prerender) || (page.isSSR && compilation.config.prerender), ) - .map(async (page) => { - const { route, outputHref } = page; - const outputDirUrl = new URL(outputHref.replace("index.html", "").replace("404.html", "")); - const url = new URL(`http://localhost:${compilation.config.port}${route}`); - const contents = await fs.readFile( - new URL(`./${outputHref.replace(outputDir.href, "")}`, scratchDir), - "utf-8", - ); - const headers = new Headers({ "Content-Type": "text/html" }); - let response = new Response(contents, { headers }); - - if (!(await checkResourceExists(outputDirUrl))) { - await fs.mkdir(outputDirUrl, { - recursive: true, - }); - } + .map((page) => + (async () => { + const { route, outputHref } = page; + const outputDirUrl = new URL( + outputHref.replace("index.html", "").replace("404.html", ""), + ); + const url = new URL(`http://localhost:${compilation.config.port}${route}`); + const contents = await fs.readFile( + new URL(`./${outputHref.replace(outputDir.href, "")}`, scratchDir), + "utf-8", + ); + const headers = new Headers({ "Content-Type": "text/html" }); + let response = new Response(contents, { headers }); + + if (!(await checkResourceExists(outputDirUrl))) { + await fs.mkdir(outputDirUrl, { + recursive: true, + }); + } - for (const plugin of plugins) { - if (plugin.shouldOptimize && (await plugin.shouldOptimize(url, response.clone()))) { - const currentResponse = await plugin.optimize(url, response.clone()); + for (const plugin of plugins) { + if (plugin.shouldOptimize && (await plugin.shouldOptimize(url, response.clone()))) { + const currentResponse = await plugin.optimize(url, response.clone()); - response = mergeResponse(response.clone(), currentResponse.clone()); + response = mergeResponse(response.clone(), currentResponse.clone()); + } } - } - // clean up optimization markers - const body = (await response.text()).replace(/data-gwd-opt=".*?[a-z]"/g, ""); + // clean up optimization markers + const body = (await response.text()).replace(/data-gwd-opt=".*?[a-z]"/g, ""); - await fs.writeFile(new URL(outputHref), body); - }), + await fs.writeFile(new URL(outputHref), body); + })(), + ), ); } @@ -202,84 +206,58 @@ async function bundleStyleResources(compilation, resourcePlugins) { const request = new Request(url, { headers }); const initResponse = new Response(contents, { headers }); - let response = await resourcePlugins.reduce(async (responsePromise, plugin) => { - const intermediateResponse = await responsePromise; + let response = initResponse; + + for (const plugin of resourcePlugins) { const shouldServe = plugin.shouldServe && (await plugin.shouldServe(url, request)); if (shouldServe) { const currentResponse = await plugin.serve(url, request); - const mergedResponse = mergeResponse( - intermediateResponse.clone(), - currentResponse.clone(), - ); + const mergedResponse = mergeResponse(response.clone(), currentResponse.clone()); if (mergedResponse.headers.get("Content-Type").indexOf(contentType) >= 0) { - return Promise.resolve(mergedResponse.clone()); + response = mergedResponse.clone(); } } + } - return Promise.resolve(responsePromise); - }, Promise.resolve(initResponse)); - - response = await resourcePlugins.reduce(async (responsePromise, plugin) => { - const intermediateResponse = await responsePromise; + for (const plugin of resourcePlugins) { const shouldPreIntercept = plugin.shouldPreIntercept && - (await plugin.shouldPreIntercept(url, request, intermediateResponse.clone())); + (await plugin.shouldPreIntercept(url, request, response.clone())); if (shouldPreIntercept) { - const currentResponse = await plugin.preIntercept( - url, - request, - intermediateResponse.clone(), - ); - const mergedResponse = mergeResponse( - intermediateResponse.clone(), - currentResponse.clone(), - ); + const currentResponse = await plugin.preIntercept(url, request, response.clone()); + const mergedResponse = mergeResponse(response.clone(), currentResponse.clone()); if (mergedResponse.headers.get("Content-Type").indexOf(contentType) >= 0) { - return Promise.resolve(mergedResponse.clone()); + response = mergedResponse.clone(); } } + } - return Promise.resolve(responsePromise); - }, Promise.resolve(response.clone())); - - response = await resourcePlugins.reduce(async (responsePromise, plugin) => { - const intermediateResponse = await responsePromise; + for (const plugin of resourcePlugins) { const shouldIntercept = - plugin.shouldIntercept && - (await plugin.shouldIntercept(url, request, intermediateResponse.clone())); + plugin.shouldIntercept && (await plugin.shouldIntercept(url, request, response.clone())); if (shouldIntercept) { - const currentResponse = await plugin.intercept( - url, - request, - intermediateResponse.clone(), - ); - const mergedResponse = mergeResponse( - intermediateResponse.clone(), - currentResponse.clone(), - ); + const currentResponse = await plugin.intercept(url, request, response.clone()); + const mergedResponse = mergeResponse(response.clone(), currentResponse.clone()); if (mergedResponse.headers.get("Content-Type").indexOf(contentType) >= 0) { - return Promise.resolve(mergedResponse.clone()); + response = mergedResponse.clone(); } } + } - return Promise.resolve(responsePromise); - }, Promise.resolve(response.clone())); - - response = await resourcePlugins.reduce(async (responsePromise, plugin) => { - const intermediateResponse = await responsePromise; + for (const plugin of resourcePlugins) { const shouldOptimize = - plugin.shouldOptimize && (await plugin.shouldOptimize(url, intermediateResponse.clone())); + plugin.shouldOptimize && (await plugin.shouldOptimize(url, response.clone())); - return shouldOptimize - ? Promise.resolve(await plugin.optimize(url, intermediateResponse.clone())) - : Promise.resolve(responsePromise); - }, Promise.resolve(response.clone())); + if (shouldOptimize) { + response = await plugin.optimize(url, response.clone()); + } + } optimizedFileContents = await response.text(); diff --git a/packages/cli/src/lifecycles/config.js b/packages/cli/src/lifecycles/config.js index f3ab21ede..570ce513c 100644 --- a/packages/cli/src/lifecycles/config.js +++ b/packages/cli/src/lifecycles/config.js @@ -15,20 +15,24 @@ const greenwoodPlugins = ( new URL("./renderer/", greenwoodPluginsDirectoryUrl), new URL("./resource/", greenwoodPluginsDirectoryUrl), new URL("./server/", greenwoodPluginsDirectoryUrl), - ].map(async (pluginDirectoryUrl) => { - const files = await fs.readdir(pluginDirectoryUrl); - - return await Promise.all( - files.map(async (file) => { - const importUrl = new URL(`./${file}`, pluginDirectoryUrl); - // @ts-expect-error see https://github.com/microsoft/TypeScript/issues/42866 - const pluginImport = await import(importUrl); - const plugin = pluginImport[Object.keys(pluginImport)[0]]; - - return Array.isArray(plugin) ? plugin : [plugin]; - }), - ); - }), + ].map((pluginDirectoryUrl) => + (async () => { + const files = await fs.readdir(pluginDirectoryUrl); + + return await Promise.all( + files.map((file) => + (async () => { + const importUrl = new URL(`./${file}`, pluginDirectoryUrl); + // @ts-expect-error see https://github.com/microsoft/TypeScript/issues/42866 + const pluginImport = await import(importUrl); + const plugin = pluginImport[Object.keys(pluginImport)[0]]; + + return Array.isArray(plugin) ? plugin : [plugin]; + })(), + ), + ); + })(), + ), ) ) .flat(PLUGINS_FLATTENED_DEPTH) diff --git a/packages/cli/src/lifecycles/copy.js b/packages/cli/src/lifecycles/copy.js index 83672d9c8..525e0e19f 100644 --- a/packages/cli/src/lifecycles/copy.js +++ b/packages/cli/src/lifecycles/copy.js @@ -7,10 +7,10 @@ async function rreaddir(dir, allFiles = []) { allFiles.push(...files); await Promise.all( - files.map( - async (f) => + files.map((f) => + (async () => (await fs.stat(f)).isDirectory() && - (await rreaddir(new URL(`file://${f.pathname}/`), allFiles)), + (await rreaddir(new URL(`file://${f.pathname}/`), allFiles)))(), ), ); diff --git a/packages/cli/src/lifecycles/prerender.js b/packages/cli/src/lifecycles/prerender.js index d34f0385f..88b75e482 100644 --- a/packages/cli/src/lifecycles/prerender.js +++ b/packages/cli/src/lifecycles/prerender.js @@ -187,23 +187,23 @@ async function staticRenderCompilation(compilation) { console.info("pages to generate", `\n ${pages.map((page) => page.route).join("\n ")}`); await Promise.all( - pages.map(async (page) => { - const { route, outputHref } = page; - const scratchUrl = toScratchUrl(outputHref, context); - const url = new URL(`http://localhost:${config.port}${route}`); - const request = new Request(url); - - let body = await (await servePage(url, request, plugins)).text(); - body = await (await interceptPage(url, request, plugins, body)).text(); - - await trackResourcesForRoute(body, compilation, route); - await createOutputDirectory(route, new URL(scratchUrl.href.replace("index.html", ""))); - await fs.writeFile(scratchUrl, body); - - console.info("generated page...", route); - - return Promise.resolve(); - }), + pages.map((page) => + (async () => { + const { route, outputHref } = page; + const scratchUrl = toScratchUrl(outputHref, context); + const url = new URL(`http://localhost:${config.port}${route}`); + const request = new Request(url); + + let body = await (await servePage(url, request, plugins)).text(); + body = await (await interceptPage(url, request, plugins, body)).text(); + + await trackResourcesForRoute(body, compilation, route); + await createOutputDirectory(route, new URL(scratchUrl.href.replace("index.html", ""))); + await fs.writeFile(scratchUrl, body); + + console.info("generated page...", route); + })(), + ), ); } diff --git a/packages/cli/src/lifecycles/serve.js b/packages/cli/src/lifecycles/serve.js index 375bd6c62..49c8d0a5d 100644 --- a/packages/cli/src/lifecycles/serve.js +++ b/packages/cli/src/lifecycles/serve.js @@ -40,13 +40,13 @@ async function getDevServer(compilation) { try { const url = new URL(`http://localhost:${compilation.config.port}${ctx.url}`); const initRequest = transformKoaRequestIntoStandardRequest(url, ctx.request); - const request = await resourcePlugins.reduce(async (requestPromise, plugin) => { - const intermediateRequest = await requestPromise; - return plugin.shouldResolve && - (await plugin.shouldResolve(url, intermediateRequest.clone())) - ? Promise.resolve(await plugin.resolve(url, intermediateRequest.clone())) - : Promise.resolve(await requestPromise); - }, Promise.resolve(initRequest)); + + let request = initRequest; + for (const plugin of resourcePlugins) { + if (plugin.shouldResolve && (await plugin.shouldResolve(url, request.clone()))) { + request = await plugin.resolve(url, request.clone()); + } + } ctx.url = request.url; } catch (e) { @@ -69,9 +69,8 @@ async function getDevServer(compilation) { for (const plugin of resourcePlugins) { if (plugin.shouldServe && (await plugin.shouldServe(url, request))) { const current = await plugin.serve(url, request); - const merged = mergeResponse(response.clone(), current.clone()); - response = merged.clone(); + response = mergeResponse(response.clone(), current.clone()); } } @@ -100,24 +99,18 @@ async function getDevServer(compilation) { status, headers: new Headers(header), }); - const response = await resourcePlugins.reduce(async (responsePromise, plugin) => { - const intermediateResponse = await responsePromise; + + let response = initResponse; + for (const plugin of resourcePlugins) { if ( plugin.shouldPreIntercept && - (await plugin.shouldPreIntercept(url, request, intermediateResponse.clone())) + (await plugin.shouldPreIntercept(url, request, response.clone())) ) { - const current = await plugin.preIntercept( - url, - request, - await intermediateResponse.clone(), - ); - const merged = mergeResponse(intermediateResponse.clone(), current); - - return Promise.resolve(merged); - } else { - return Promise.resolve(await responsePromise); + const current = await plugin.preIntercept(url, request, response.clone()); + + response = mergeResponse(response.clone(), current); } - }, Promise.resolve(initResponse.clone())); + } ctx.body = response.body ? Readable.from(response.body) : ""; ctx.message = response.statusText; @@ -143,20 +136,18 @@ async function getDevServer(compilation) { status, headers: new Headers(header), }); - const response = await resourcePlugins.reduce(async (responsePromise, plugin) => { - const intermediateResponse = await responsePromise; + + let response = initResponse; + for (const plugin of resourcePlugins) { if ( plugin.shouldIntercept && - (await plugin.shouldIntercept(url, request, intermediateResponse.clone())) + (await plugin.shouldIntercept(url, request, response.clone())) ) { - const current = await plugin.intercept(url, request, await intermediateResponse.clone()); - const merged = mergeResponse(intermediateResponse.clone(), current); + const current = await plugin.intercept(url, request, response.clone()); - return Promise.resolve(merged); - } else { - return Promise.resolve(await responsePromise); + response = mergeResponse(response.clone(), current); } - }, Promise.resolve(initResponse.clone())); + } ctx.body = response.body ? Readable.from(response.body) : ""; ctx.message = response.statusText; @@ -302,11 +293,13 @@ async function getStaticServer(compilation, composable) { status: ctx.response.status, headers: new Headers(ctx.response.header), }); - const response = await resourcePlugins.reduce(async (responsePromise, plugin) => { - return plugin.shouldServe && (await plugin.shouldServe(url, request)) - ? Promise.resolve(await plugin.serve(url, request)) - : responsePromise; - }, Promise.resolve(initResponse)); + + let response = initResponse; + for (const plugin of resourcePlugins) { + if (plugin.shouldServe && (await plugin.shouldServe(url, request))) { + response = await plugin.serve(url, request); + } + } if (response.ok) { ctx.body = Readable.from(response.body); diff --git a/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js b/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js index fcc729b44..fff9c3753 100644 --- a/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js +++ b/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js @@ -5,19 +5,21 @@ export default async function (compilation, callback) { const runBrowser = async (serverUrl, pages) => { try { return Promise.all( - pages.map(async (page) => { - const { route } = page; - console.info("prerendering page...", route); + pages.map((page) => + (async () => { + const { route } = page; + console.info("prerendering page...", route); - return await browserRunner.serialize(`${serverUrl}${route}`).then(async (html) => { - console.info(`prerendering complete for page ${route}.`); + return await browserRunner.serialize(`${serverUrl}${route}`).then(async (html) => { + console.info(`prerendering complete for page ${route}.`); - // clean this up here to avoid sending webcomponents-bundle to rollup - html = html.replace(/