Implement getting hoisted scripts for propagation

This commit is contained in:
Matthew Phillips 2023-02-03 12:18:07 -05:00
parent 0ae1c91b8d
commit 0d57cafd04
6 changed files with 91 additions and 58 deletions

View file

@ -2,7 +2,6 @@ export { createContentTypesGenerator } from './types-generator.js';
export { contentObservable, getContentPaths, getDotAstroTypeReference } from './utils.js'; export { contentObservable, getContentPaths, getDotAstroTypeReference } from './utils.js';
export { export {
astroContentAssetPropagationPlugin, astroContentAssetPropagationPlugin,
astroContentProdBundlePlugin,
} from './vite-plugin-content-assets.js'; } from './vite-plugin-content-assets.js';
export { astroContentServerPlugin } from './vite-plugin-content-server.js'; export { astroContentServerPlugin } from './vite-plugin-content-server.js';
export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js'; export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js';

View file

@ -1,4 +1,5 @@
import { pathToFileURL } from 'url'; import { pathToFileURL } from 'url';
import npath from 'node:path';
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import { moduleIsTopLevelPage, walkParentInfos } from '../core/build/graph.js'; import { moduleIsTopLevelPage, walkParentInfos } from '../core/build/graph.js';
import { BuildInternals, getPageDataByViteID } from '../core/build/internal.js'; import { BuildInternals, getPageDataByViteID } from '../core/build/internal.js';
@ -14,6 +15,8 @@ import {
SCRIPTS_PLACEHOLDER, SCRIPTS_PLACEHOLDER,
STYLES_PLACEHOLDER, STYLES_PLACEHOLDER,
} from './consts.js'; } from './consts.js';
import type { RollupOutput, OutputChunk, StaticBuildOptions } from '../core/build/types';
import { prependForwardSlash } from '../core/path.js';
function isPropagatedAsset(viteId: string): boolean { function isPropagatedAsset(viteId: string): boolean {
const url = new URL(viteId, 'file://'); const url = new URL(viteId, 'file://');
@ -73,67 +76,95 @@ export function astroContentAssetPropagationPlugin({ mode }: { mode: string }):
}; };
} }
export function astroContentProdBundlePlugin({ internals }: { internals: BuildInternals }): Plugin { export function astroConfigBuildPlugin(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin {
let ssrPluginContext: any = undefined;
return { return {
name: 'astro:content-prod-bundle', build: 'ssr',
async generateBundle(_options, bundle) { hooks: {
for (const [_, chunk] of Object.entries(bundle)) { 'build:before': ({ build }) => {
if ( return {
chunk.type === 'chunk' && vitePlugin: {
(chunk.code.includes(LINKS_PLACEHOLDER) || chunk.code.includes(SCRIPTS_PLACEHOLDER)) name: 'astro:content-build-plugin',
) { generateBundle() {
for (const id of Object.keys(chunk.modules)) { if(build === 'ssr') {
for (const [pageInfo, depth, order] of walkParentInfos(id, this)) { ssrPluginContext = this;
if (moduleIsTopLevelPage(pageInfo)) { }
const pageViteID = pageInfo.id; }
const pageData = getPageDataByViteID(internals, pageViteID); },
if (!pageData) continue; };
<<<<<<< HEAD },
'build:post': ({ ssrOutputs, clientOutputs, mutate }) => {
const outputs = ssrOutputs.flatMap(o => o.output);
for (const chunk of outputs) {
if (
chunk.type === 'chunk' &&
(chunk.code.includes(LINKS_PLACEHOLDER) || chunk.code.includes(SCRIPTS_PLACEHOLDER))
) {
let entryCSS = new Set<string>();
let entryScripts = new Set<string>();
======= for (const id of Object.keys(chunk.modules)) {
console.log({ pageData }); for (const [pageInfo] of walkParentInfos(id, ssrPluginContext)) {
>>>>>>> bdc0a6498 (updated) if (moduleIsTopLevelPage(pageInfo)) {
const entryCss = pageData.contentCollectionCss?.get(id); const pageViteID = pageInfo.id;
const entryScripts = pageData.propagatedScripts?.get(id); const pageData = getPageDataByViteID(internals, pageViteID);
if (entryCss) { if (!pageData) continue;
chunk.code = chunk.code.replace(
JSON.stringify(LINKS_PLACEHOLDER), const _entryCss = pageData.contentCollectionCss?.get(id);
JSON.stringify([...entryCss]) const _entryScripts = pageData.propagatedScripts?.get(id);
); if(_entryCss) {
} for(const value of _entryCss) {
if (entryScripts) { entryCSS.add(value);
<<<<<<< HEAD }
======= }
console.log({ entryScripts }); if(_entryScripts) {
>>>>>>> bdc0a6498 (updated) for(const value of _entryScripts) {
chunk.code = chunk.code.replace( entryScripts.add(value);
JSON.stringify(SCRIPTS_PLACEHOLDER), }
JSON.stringify( }
[...entryScripts].map((src) => ({
props: { src, type: 'module' },
children: '',
}))
)
);
} }
} }
} }
let newCode = chunk.code;
if (entryCSS.size) {
newCode = newCode.replace(
JSON.stringify(LINKS_PLACEHOLDER),
JSON.stringify([...entryCSS])
);
}
if (entryScripts.size) {
const entryFileNames = new Set<string>();
for(const output of clientOutputs) {
for(const clientChunk of output.output) {
if(clientChunk.type !== 'chunk') continue;
for(const [id] of Object.entries(clientChunk.modules)) {
if(entryScripts.has(id)) {
entryFileNames.add(clientChunk.fileName);
}
}
}
}
newCode = newCode.replace(
JSON.stringify(SCRIPTS_PLACEHOLDER),
JSON.stringify(
[...entryFileNames].map((src) => ({
props: {
src: prependForwardSlash(npath.posix.join(
options.settings.config.base,
src
)),
type: 'module'
},
children: '',
}))
)
);
}
mutate(chunk, 'server', newCode);
} }
} }
} }
}, },
}; };
} }
export function astroConfigBuildPlugin(internals: BuildInternals): AstroBuildPlugin {
return {
build: 'ssr',
hooks: {
'build:before': () => {
return {
vitePlugin: astroContentProdBundlePlugin({ internals }),
};
},
},
};
}

View file

@ -16,7 +16,7 @@ export function registerAllPlugins({ internals, options, register }: AstroBuildP
register(pluginPages(options, internals)); register(pluginPages(options, internals));
register(pluginCSS(options, internals)); register(pluginCSS(options, internals));
register(pluginPrerender(options, internals)); register(pluginPrerender(options, internals));
register(astroConfigBuildPlugin(internals)); register(astroConfigBuildPlugin(options, internals));
register(pluginHoistedScripts(options, internals)); register(pluginHoistedScripts(options, internals));
register(pluginSSR(options, internals)); register(pluginSSR(options, internals));
} }

View file

@ -179,7 +179,7 @@ export function pluginAnalyzer(internals: BuildInternals): AstroBuildPlugin {
return { return {
vitePlugin: vitePluginAnalyzer(internals), vitePlugin: vitePluginAnalyzer(internals),
}; };
}, }
}, },
}; };
} }

View file

@ -245,9 +245,12 @@ async function runPostBuildHooks(
clientReturn: Awaited<ReturnType<typeof clientBuild>> clientReturn: Awaited<ReturnType<typeof clientBuild>>
) { ) {
const mutations = await container.runPostHook(ssrReturn, clientReturn); const mutations = await container.runPostHook(ssrReturn, clientReturn);
const config = container.options.settings.config;
const buildConfig = container.options.settings.config.build; const buildConfig = container.options.settings.config.build;
for (const [fileName, mutation] of mutations) { for (const [fileName, mutation] of mutations) {
const root = mutation.build === 'server' ? buildConfig.server : buildConfig.client; const root = config.output === 'server' ?
mutation.build === 'server' ? buildConfig.server : buildConfig.client :
config.outDir;
const fileURL = new URL(fileName, root); const fileURL = new URL(fileName, root);
await fs.promises.mkdir(new URL('./', fileURL), { recursive: true }); await fs.promises.mkdir(new URL('./', fileURL), { recursive: true });
await fs.promises.writeFile(fileURL, mutation.code, 'utf-8'); await fs.promises.writeFile(fileURL, mutation.code, 'utf-8');

View file

@ -46,7 +46,7 @@ describe('Content Collections - render()', () => {
// Includes hoisted script // Includes hoisted script
expect( expect(
[...allScripts].find((script) => [...allScripts].find((script) =>
$(script).text().includes('document.querySelector("#update-me")') $(script).attr('src')?.includes('WithScripts')
), ),
'`WithScripts.astro` hoisted script missing from head.' '`WithScripts.astro` hoisted script missing from head.'
).to.not.be.undefined; ).to.not.be.undefined;