diff options
Diffstat (limited to 'packages/integrations/cloudflare/src/index.ts')
-rw-r--r-- | packages/integrations/cloudflare/src/index.ts | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index deed35655..d7a68c3e4 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -1,7 +1,7 @@ import type { AstroConfig, AstroIntegration, RouteData } from 'astro'; import { createReadStream } from 'node:fs'; -import { appendFile, rename, stat } from 'node:fs/promises'; +import { appendFile, rename, stat, unlink } from 'node:fs/promises'; import { createInterface } from 'node:readline/promises'; import { appendForwardSlash, @@ -13,6 +13,7 @@ import { AstroError } from 'astro/errors'; import { getPlatformProxy } from 'wrangler'; import { createRoutesFile, getParts } from './utils/generate-routes-json.js'; import { setImageConfig } from './utils/image-config.js'; +import { NonServerChunkDetector } from './utils/non-server-chunk-detector.js'; import { wasmModuleLoader } from './utils/wasm-module-loader.js'; export type { Runtime } from './entrypoints/server.js'; @@ -64,6 +65,13 @@ export type Options = { export default function createIntegration(args?: Options): AstroIntegration { let _config: AstroConfig; + // Initialize the unused chunk analyzer as a shared state between hooks. + // The analyzer is used on earlier hooks to collect information about used hooks on a Vite plugin + // and then later after the full build to clean up unused chunks, so it has to be shared between them. + const chunkAnalyzer = new NonServerChunkDetector(); + + const prerenderImports: string[][] = []; + return { name: '@astrojs/cloudflare', hooks: { @@ -84,6 +92,63 @@ export default function createIntegration(args?: Options): AstroIntegration { wasmModuleLoader({ disabled: !args?.wasmModuleImports, }), + chunkAnalyzer.getPlugin(), + { + name: 'dynamic-imports-analyzer', + enforce: 'post', + generateBundle(_, bundle) { + // Find all pages (ignore the ssr entrypoint) which are prerendered based on the dynamic imports of the prerender chunk + for (const chunk of Object.values(bundle)) { + if (chunk.type !== 'chunk') continue; + + const isPrerendered = chunk.dynamicImports.some( + (entry) => + entry.includes('prerender') && chunk.name !== '_@astrojs-ssr-virtual-entry' + ); + if (isPrerendered) { + prerenderImports.push([chunk.facadeModuleId ?? '', chunk.fileName]); + } + } + + const entryChunk = bundle['index.js']; + if ( + entryChunk && + entryChunk.type === 'chunk' && + entryChunk.name === '_@astrojs-ssr-virtual-entry' + ) { + // Update dynamicImports information, so that there are no imports listed which we remove later + entryChunk.dynamicImports = entryChunk.dynamicImports.filter( + (entry) => !prerenderImports.map((e) => e[1]).includes(entry) + ); + + // Clean the ssr entry file from prerendered imporst, since Astro adds them, which it shouldn't. But this is a current limitation in core, because the prerender meta info gets added later in the chain + for (const page of prerenderImports) { + // Find the dynamic import inside of the ssr entry file, which get generated by Astro: https://github.com/withastro/astro/blob/08cdd0919d3249a762822e4bba9e0c5d3966916c/packages/astro/src/core/build/plugins/plugin-ssr.ts#L56 + const importRegex = new RegExp( + `^const (_page\\d) = \\(\\) => import\\('.\\/${page[1]}'\\);$\\n`, + 'gm' + ); + + let pageId: string | undefined; + const matches = entryChunk.code.matchAll(importRegex); + for (const match of matches) { + if (match[1]) { + pageId = match[1]; + } + } + const pageSource = page[0].split(':')[1].replace('@_@', '.'); + entryChunk.code = entryChunk.code.replace(importRegex, ''); + if (pageId) { + // Find the page in the pageMap of the ssr entry file, which get generated by Astro: https://github.com/withastro/astro/blob/08cdd0919d3249a762822e4bba9e0c5d3966916c/packages/astro/src/core/build/plugins/plugin-ssr.ts#L65 + const arrayRegex = new RegExp(`\\["${pageSource}", ?${pageId}\\],?`, 'gm'); + entryChunk.code = entryChunk.code.replace(arrayRegex, ''); + } + } + } else { + // We don't want to handle this case, since it will always occur for the client build. + } + }, + }, ], }, image: setImageConfig(args?.imageService ?? 'DEFAULT', config.image, command, logger), @@ -303,6 +368,14 @@ export default function createIntegration(args?: Options): AstroIntegration { logger.error('Failed to write _redirects file'); } } + + // Get chunks from the bundle that are not needed on the server and delete them + // Those modules are build only for prerendering routes. + const chunksToDelete = chunkAnalyzer.getNonServerChunks(); + for (const chunk of chunksToDelete) { + // Chunks are located on `./_worker.js` directory inside of the output directory + await unlink(new URL(`./_worker.js/${chunk}`, _config.outDir)); + } }, }, }; |