diff --git a/packages/astro/src/content/vite-plugin-content-assets.ts b/packages/astro/src/content/vite-plugin-content-assets.ts index 54b84380d..8862d95f8 100644 --- a/packages/astro/src/content/vite-plugin-content-assets.ts +++ b/packages/astro/src/content/vite-plugin-content-assets.ts @@ -75,19 +75,37 @@ export function astroContentProdBundlePlugin({ internals }: { internals: BuildIn name: 'astro:content-prod-bundle', async generateBundle(_options, bundle) { for (const [_, chunk] of Object.entries(bundle)) { - if (chunk.type === 'chunk' && chunk.code.includes(LINKS_PLACEHOLDER)) { + if ( + chunk.type === 'chunk' && + (chunk.code.includes(LINKS_PLACEHOLDER) || chunk.code.includes(SCRIPTS_PLACEHOLDER)) + ) { for (const id of Object.keys(chunk.modules)) { for (const [pageInfo, depth, order] of walkParentInfos(id, this)) { if (moduleIsTopLevelPage(pageInfo)) { const pageViteID = pageInfo.id; const pageData = getPageDataByViteID(internals, pageViteID); if (!pageData) continue; + console.log({ pageData }); const entryCss = pageData.contentCollectionCss?.get(id); - if (!entryCss) continue; - chunk.code = chunk.code.replace( - JSON.stringify(LINKS_PLACEHOLDER), - JSON.stringify([...entryCss]) - ); + const entryScripts = pageData.propagatedScripts?.get(id); + if (entryCss) { + chunk.code = chunk.code.replace( + JSON.stringify(LINKS_PLACEHOLDER), + JSON.stringify([...entryCss]) + ); + } + if (entryScripts) { + console.log({ entryScripts }); + chunk.code = chunk.code.replace( + JSON.stringify(SCRIPTS_PLACEHOLDER), + JSON.stringify( + [...entryScripts].map((src) => ({ + props: { src, type: 'module' }, + children: '', + })) + ) + ); + } } } } diff --git a/packages/astro/src/core/build/graph.ts b/packages/astro/src/core/build/graph.ts index 5edb07a71..29a0429b3 100644 --- a/packages/astro/src/core/build/graph.ts +++ b/packages/astro/src/core/build/graph.ts @@ -40,9 +40,10 @@ export function moduleIsTopLevelPage(info: ModuleInfo): boolean { // This could be a .astro page, a .markdown or a .md (or really any file extension for markdown files) page. export function* getTopLevelPages( id: string, - ctx: { getModuleInfo: GetModuleInfo } + ctx: { getModuleInfo: GetModuleInfo }, + walkUntil?: (importer: string) => boolean ): Generator<[ModuleInfo, number, number], void, unknown> { - for (const res of walkParentInfos(id, ctx)) { + for (const res of walkParentInfos(id, ctx, walkUntil)) { if (moduleIsTopLevelPage(res[0])) { yield res; } diff --git a/packages/astro/src/core/build/page-data.ts b/packages/astro/src/core/build/page-data.ts index 1a03b934b..1504234f5 100644 --- a/packages/astro/src/core/build/page-data.ts +++ b/packages/astro/src/core/build/page-data.ts @@ -55,6 +55,7 @@ export async function collectPagesData( moduleSpecifier: '', css: new Map(), contentCollectionCss: new Map(), + propagatedScripts: new Map(), hoistedScript: undefined, }; @@ -77,6 +78,7 @@ export async function collectPagesData( moduleSpecifier: '', css: new Map(), contentCollectionCss: new Map(), + propagatedScripts: new Map(), hoistedScript: undefined, }; } diff --git a/packages/astro/src/core/build/types.ts b/packages/astro/src/core/build/types.ts index 0c4d9a069..ad4fe7a94 100644 --- a/packages/astro/src/core/build/types.ts +++ b/packages/astro/src/core/build/types.ts @@ -22,6 +22,7 @@ export interface PageBuildData { moduleSpecifier: string; css: Map; contentCollectionCss: Map>; + propagatedScripts: Map>; hoistedScript: { type: 'inline' | 'external'; value: string } | undefined; } export type AllPagesData = Record; diff --git a/packages/astro/src/core/build/vite-plugin-analyzer.ts b/packages/astro/src/core/build/vite-plugin-analyzer.ts index 0e6a991bd..1aff465c1 100644 --- a/packages/astro/src/core/build/vite-plugin-analyzer.ts +++ b/packages/astro/src/core/build/vite-plugin-analyzer.ts @@ -4,13 +4,24 @@ import type { BuildInternals } from '../../core/build/internal.js'; import type { PluginMetadata as AstroPluginMetadata } from '../../vite-plugin-astro/types'; import { prependForwardSlash } from '../../core/path.js'; -import { getTopLevelPages } from './graph.js'; +import { getTopLevelPages, moduleIsTopLevelPage, walkParentInfos } from './graph.js'; import { getPageDataByViteID, trackClientOnlyPageDatas } from './internal.js'; +import { PROPAGATED_ASSET_FLAG } from '../../content/consts.js'; + +function isPropagatedAsset(id: string) { + return new URL('file://' + id).searchParams.has(PROPAGATED_ASSET_FLAG); +} export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin { function hoistedScriptScanner() { const uniqueHoistedIds = new Map(); - const pageScripts = new Map>(); + const pageScripts = new Map< + string, + { + hoistedSet: Set; + propagatedMapByImporter: Map>; + } + >(); return { scan(this: PluginContext, scripts: AstroPluginMetadata['astro']['scripts'], from: string) { @@ -21,13 +32,36 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin { } if (hoistedScripts.size) { - for (const [pageInfo] of getTopLevelPages(from, this)) { - const pageId = pageInfo.id; - for (const hid of hoistedScripts) { - if (pageScripts.has(pageId)) { - pageScripts.get(pageId)?.add(hid); - } else { - pageScripts.set(pageId, new Set([hid])); + for (const [parentInfo] of walkParentInfos(from, this, function until(importer) { + return isPropagatedAsset(importer); + })) { + if (isPropagatedAsset(parentInfo.id)) { + for (const [nestedParentInfo] of walkParentInfos(from, this)) { + if (moduleIsTopLevelPage(nestedParentInfo)) { + for (const hid of hoistedScripts) { + if (!pageScripts.has(nestedParentInfo.id)) { + pageScripts.set(nestedParentInfo.id, { + hoistedSet: new Set(), + propagatedMapByImporter: new Map(), + }); + } + const entry = pageScripts.get(nestedParentInfo.id)!; + if (!entry.propagatedMapByImporter.has(parentInfo.id)) { + entry.propagatedMapByImporter.set(parentInfo.id, new Set()); + } + entry.propagatedMapByImporter.get(parentInfo.id)!.add(hid); + } + } + } + } else if (moduleIsTopLevelPage(parentInfo)) { + for (const hid of hoistedScripts) { + if (!pageScripts.has(parentInfo.id)) { + pageScripts.set(parentInfo.id, { + hoistedSet: new Set(), + propagatedMapByImporter: new Map(), + }); + } + pageScripts.get(parentInfo.id)?.hoistedSet.add(hid); } } } @@ -35,14 +69,14 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin { }, finalize() { - for (const [pageId, hoistedScripts] of pageScripts) { + for (const [pageId, { hoistedSet, propagatedMapByImporter }] of pageScripts) { const pageData = getPageDataByViteID(internals, pageId); if (!pageData) continue; const { component } = pageData; const astroModuleId = prependForwardSlash(component); - const uniqueHoistedId = JSON.stringify(Array.from(hoistedScripts).sort()); + const uniqueHoistedId = JSON.stringify(Array.from(hoistedSet).sort()); let moduleId: string; // If we're already tracking this set of hoisted scripts, get the unique id @@ -55,13 +89,35 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin { } internals.discoveredScripts.add(moduleId); + pageData.propagatedScripts = propagatedMapByImporter; + + // Add propagated scripts to client build, + // but DON'T add to pages -> hoisted script map. + // const flattenedPropagatedScripts = Array.from(propagatedMapByImporter.values()) + // .map((s) => Array.from(s)) + // .flat(); + // const uniquePropagatedId = JSON.stringify(flattenedPropagatedScripts.sort()); + // if (uniqueHoistedIds.has(uniquePropagatedId)) { + // moduleId = uniqueHoistedIds.get(uniquePropagatedId)!; + // } else { + // // Otherwise, create a unique id for this set of hoisted scripts + // moduleId = `/astro/hoisted.js?q=${uniqueHoistedIds.size}`; + // console.log('uniquePropagatedId', uniquePropagatedId, moduleId); + // uniqueHoistedIds.set(uniquePropagatedId, moduleId); + // } + for (const propagatedScripts of propagatedMapByImporter.values()) { + for (const propagatedScript of propagatedScripts) { + internals.discoveredScripts.add(propagatedScript); + } + } + // 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); + internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedSet); } } },