Ensure hoisted scripts are deduplicated in build (#3433)
* fix(build): ensure hoisted scripts are deduplicated in build * chore: add changeset
This commit is contained in:
parent
62036eefc8
commit
4ca60e9344
4 changed files with 41 additions and 10 deletions
5
.changeset/silly-ears-unite.md
Normal file
5
.changeset/silly-ears-unite.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix build issue where hoisted scripts would be duplicated per page
|
|
@ -8,8 +8,10 @@ export interface BuildInternals {
|
||||||
// Pure CSS chunks are chunks that only contain CSS.
|
// Pure CSS chunks are chunks that only contain CSS.
|
||||||
pureCSSChunks: Set<RenderedChunk>;
|
pureCSSChunks: Set<RenderedChunk>;
|
||||||
|
|
||||||
// TODO document what this is
|
// A mapping of hoisted script ids back to the exact hoisted scripts it references
|
||||||
hoistedScriptIdToHoistedMap: Map<string, Set<string>>;
|
hoistedScriptIdToHoistedMap: Map<string, Set<string>>;
|
||||||
|
// A mapping of hoisted script ids back to the pages which reference it
|
||||||
|
hoistedScriptIdToPagesMap: Map<string, Set<string>>;
|
||||||
|
|
||||||
// A mapping of specifiers like astro/client/idle.js to the hashed bundled name.
|
// A mapping of specifiers like astro/client/idle.js to the hashed bundled name.
|
||||||
// Used to render pages with the correct specifiers.
|
// Used to render pages with the correct specifiers.
|
||||||
|
@ -50,9 +52,13 @@ export function createBuildInternals(): BuildInternals {
|
||||||
// These are for tracking hoisted script bundling
|
// These are for tracking hoisted script bundling
|
||||||
const hoistedScriptIdToHoistedMap = new Map<string, Set<string>>();
|
const hoistedScriptIdToHoistedMap = new Map<string, Set<string>>();
|
||||||
|
|
||||||
|
// This tracks hoistedScriptId => page components
|
||||||
|
const hoistedScriptIdToPagesMap = new Map<string, Set<string>>();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pureCSSChunks,
|
pureCSSChunks,
|
||||||
hoistedScriptIdToHoistedMap,
|
hoistedScriptIdToHoistedMap,
|
||||||
|
hoistedScriptIdToPagesMap,
|
||||||
entrySpecifierToBundleMap: new Map<string, string>(),
|
entrySpecifierToBundleMap: new Map<string, string>(),
|
||||||
|
|
||||||
pagesByComponent: new Map(),
|
pagesByComponent: new Map(),
|
||||||
|
|
|
@ -38,6 +38,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
|
||||||
|
|
||||||
// Build internals needed by the CSS plugin
|
// Build internals needed by the CSS plugin
|
||||||
const internals = createBuildInternals();
|
const internals = createBuildInternals();
|
||||||
|
const uniqueHoistedIds = new Map<string, string>();
|
||||||
|
|
||||||
const timer: Record<string, number> = {};
|
const timer: Record<string, number> = {};
|
||||||
|
|
||||||
|
@ -76,9 +77,27 @@ export async function staticBuild(opts: StaticBuildOptions) {
|
||||||
// Add hoisted scripts
|
// Add hoisted scripts
|
||||||
const hoistedScripts = new Set(metadata.hoistedScriptPaths());
|
const hoistedScripts = new Set(metadata.hoistedScriptPaths());
|
||||||
if (hoistedScripts.size) {
|
if (hoistedScripts.size) {
|
||||||
const moduleId = npath.posix.join(astroModuleId, 'hoisted.js');
|
const uniqueHoistedId = JSON.stringify(Array.from(hoistedScripts).sort());
|
||||||
internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedScripts);
|
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);
|
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) {
|
for (const specifier of topLevelImports) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { viteID } from '../util.js';
|
||||||
import { getPageDataByViteID } from './internal.js';
|
import { getPageDataByViteID } from './internal.js';
|
||||||
|
|
||||||
function virtualHoistedEntry(id: string) {
|
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(
|
export function vitePluginHoistedScripts(
|
||||||
|
@ -49,12 +49,13 @@ export function vitePluginHoistedScripts(
|
||||||
virtualHoistedEntry(output.facadeModuleId)
|
virtualHoistedEntry(output.facadeModuleId)
|
||||||
) {
|
) {
|
||||||
const facadeId = output.facadeModuleId!;
|
const facadeId = output.facadeModuleId!;
|
||||||
const pathname = facadeId.slice(0, facadeId.length - '/hoisted.js'.length);
|
const pages = internals.hoistedScriptIdToPagesMap.get(facadeId)!;
|
||||||
|
for (const pathname of pages) {
|
||||||
const vid = viteID(new URL('.' + pathname, astroConfig.root));
|
const vid = viteID(new URL('.' + pathname, astroConfig.root));
|
||||||
const pageInfo = getPageDataByViteID(internals, vid);
|
const pageInfo = getPageDataByViteID(internals, vid);
|
||||||
if (pageInfo) {
|
if (pageInfo) {
|
||||||
pageInfo.hoistedScript = id;
|
pageInfo.hoistedScript = id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue