From 4ca60e9344f882ee1a216ec66fb409c88c740641 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 24 May 2022 14:39:23 -0500 Subject: [PATCH] Ensure hoisted scripts are deduplicated in build (#3433) * fix(build): ensure hoisted scripts are deduplicated in build * chore: add changeset --- .changeset/silly-ears-unite.md | 5 ++++ packages/astro/src/core/build/internal.ts | 8 ++++++- packages/astro/src/core/build/static-build.ts | 23 +++++++++++++++++-- .../core/build/vite-plugin-hoisted-scripts.ts | 15 ++++++------ 4 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 .changeset/silly-ears-unite.md diff --git a/.changeset/silly-ears-unite.md b/.changeset/silly-ears-unite.md new file mode 100644 index 000000000000..b4e23917b4bf --- /dev/null +++ b/.changeset/silly-ears-unite.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix build issue where hoisted scripts would be duplicated per page diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index c6f98a4fa5db..e796804fbb21 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -8,8 +8,10 @@ export interface BuildInternals { // Pure CSS chunks are chunks that only contain CSS. pureCSSChunks: Set; - // TODO document what this is + // A mapping of hoisted script ids back to the exact hoisted scripts it references hoistedScriptIdToHoistedMap: Map>; + // A mapping of hoisted script ids back to the pages which reference it + hoistedScriptIdToPagesMap: Map>; // A mapping of specifiers like astro/client/idle.js to the hashed bundled name. // Used to render pages with the correct specifiers. @@ -50,9 +52,13 @@ export function createBuildInternals(): BuildInternals { // These are for tracking hoisted script bundling const hoistedScriptIdToHoistedMap = new Map>(); + // This tracks hoistedScriptId => page components + const hoistedScriptIdToPagesMap = new Map>(); + return { pureCSSChunks, hoistedScriptIdToHoistedMap, + hoistedScriptIdToPagesMap, entrySpecifierToBundleMap: new Map(), pagesByComponent: new Map(), diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index ae08c54dc7d6..f101f6a3dc3f 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -38,6 +38,7 @@ export async function staticBuild(opts: StaticBuildOptions) { // Build internals needed by the CSS plugin const internals = createBuildInternals(); + const uniqueHoistedIds = new Map(); const timer: Record = {}; @@ -76,9 +77,27 @@ export async function staticBuild(opts: StaticBuildOptions) { // Add hoisted scripts const hoistedScripts = new Set(metadata.hoistedScriptPaths()); if (hoistedScripts.size) { - const moduleId = npath.posix.join(astroModuleId, 'hoisted.js'); - internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedScripts); + const uniqueHoistedId = JSON.stringify(Array.from(hoistedScripts).sort()); + let moduleId: string; + + // If we're already tracking this set of hoisted scripts, get the unique id + if (uniqueHoistedIds.has(uniqueHoistedId)) { + moduleId = uniqueHoistedIds.get(uniqueHoistedId)!; + } else { + // Otherwise, create a unique id for this set of hoisted scripts + moduleId = `/astro/hoisted.js?q=${uniqueHoistedIds.size}`; + uniqueHoistedIds.set(uniqueHoistedId, moduleId); + } topLevelImports.add(moduleId); + + // Make sure to track that this page uses this set of hoisted scripts + if (internals.hoistedScriptIdToPagesMap.has(moduleId)) { + const pages = internals.hoistedScriptIdToPagesMap.get(moduleId); + pages!.add(astroModuleId); + } else { + internals.hoistedScriptIdToPagesMap.set(moduleId, new Set([astroModuleId])) + internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedScripts); + } } for (const specifier of topLevelImports) { diff --git a/packages/astro/src/core/build/vite-plugin-hoisted-scripts.ts b/packages/astro/src/core/build/vite-plugin-hoisted-scripts.ts index 8e4872e0c7f1..4641b5f137ec 100644 --- a/packages/astro/src/core/build/vite-plugin-hoisted-scripts.ts +++ b/packages/astro/src/core/build/vite-plugin-hoisted-scripts.ts @@ -5,7 +5,7 @@ import { viteID } from '../util.js'; import { getPageDataByViteID } from './internal.js'; function virtualHoistedEntry(id: string) { - return id.endsWith('.astro/hoisted.js') || id.endsWith('.md/hoisted.js'); + return id.startsWith('/astro/hoisted.js?q='); } export function vitePluginHoistedScripts( @@ -49,12 +49,13 @@ export function vitePluginHoistedScripts( virtualHoistedEntry(output.facadeModuleId) ) { const facadeId = output.facadeModuleId!; - const pathname = facadeId.slice(0, facadeId.length - '/hoisted.js'.length); - - const vid = viteID(new URL('.' + pathname, astroConfig.root)); - const pageInfo = getPageDataByViteID(internals, vid); - if (pageInfo) { - pageInfo.hoistedScript = id; + const pages = internals.hoistedScriptIdToPagesMap.get(facadeId)!; + for (const pathname of pages) { + const vid = viteID(new URL('.' + pathname, astroConfig.root)); + const pageInfo = getPageDataByViteID(internals, vid); + if (pageInfo) { + pageInfo.hoistedScript = id; + } } } }