summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/astro/src/content/vite-plugin-content-assets.ts14
-rw-r--r--packages/astro/src/core/build/plugin.ts116
-rw-r--r--packages/astro/src/core/build/plugins/index.ts23
-rw-r--r--packages/astro/src/core/build/plugins/plugin-alias-resolve.ts (renamed from packages/astro/src/core/build/vite-plugin-alias-resolve.ts)16
-rw-r--r--packages/astro/src/core/build/plugins/plugin-analyzer.ts (renamed from packages/astro/src/core/build/vite-plugin-analyzer.ts)24
-rw-r--r--packages/astro/src/core/build/plugins/plugin-css.ts (renamed from packages/astro/src/core/build/vite-plugin-css.ts)75
-rw-r--r--packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts (renamed from packages/astro/src/core/build/vite-plugin-hoisted-scripts.ts)23
-rw-r--r--packages/astro/src/core/build/plugins/plugin-internals.ts (renamed from packages/astro/src/core/build/vite-plugin-internals.ts)16
-rw-r--r--packages/astro/src/core/build/plugins/plugin-pages.ts (renamed from packages/astro/src/core/build/vite-plugin-pages.ts)23
-rw-r--r--packages/astro/src/core/build/plugins/plugin-prerender.ts48
-rw-r--r--packages/astro/src/core/build/plugins/plugin-ssr.ts (renamed from packages/astro/src/core/build/vite-plugin-ssr.ts)66
-rw-r--r--packages/astro/src/core/build/plugins/util.ts40
-rw-r--r--packages/astro/src/core/build/static-build.ts71
-rw-r--r--packages/astro/src/core/build/types.ts5
-rw-r--r--packages/astro/src/core/build/vite-plugin-prerender.ts43
15 files changed, 461 insertions, 142 deletions
diff --git a/packages/astro/src/content/vite-plugin-content-assets.ts b/packages/astro/src/content/vite-plugin-content-assets.ts
index fd73caf47..a5b205151 100644
--- a/packages/astro/src/content/vite-plugin-content-assets.ts
+++ b/packages/astro/src/content/vite-plugin-content-assets.ts
@@ -2,6 +2,7 @@ import { pathToFileURL } from 'url';
import type { Plugin } from 'vite';
import { moduleIsTopLevelPage, walkParentInfos } from '../core/build/graph.js';
import { BuildInternals, getPageDataByViteID } from '../core/build/internal.js';
+import { AstroBuildPlugin } from '../core/build/plugin.js';
import type { ModuleLoader } from '../core/module-loader/loader.js';
import { createViteLoader } from '../core/module-loader/vite.js';
import { getStylesForURL } from '../core/render/dev/css.js';
@@ -98,3 +99,16 @@ export function astroContentProdBundlePlugin({ internals }: { internals: BuildIn
},
};
}
+
+export function astroConfigBuildPlugin(internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'ssr',
+ hooks: {
+ 'build:before': () => {
+ return {
+ vitePlugin: astroContentProdBundlePlugin({ internals })
+ };
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/plugin.ts b/packages/astro/src/core/build/plugin.ts
new file mode 100644
index 000000000..47c87e334
--- /dev/null
+++ b/packages/astro/src/core/build/plugin.ts
@@ -0,0 +1,116 @@
+import type { Plugin as VitePlugin } from 'vite';
+import type { BuildInternals } from './internal';
+import type { StaticBuildOptions, ViteBuildReturn } from './types';
+
+type RollupOutputArray = Extract<ViteBuildReturn, Array<any>>;
+type OutputChunkorAsset = RollupOutputArray[number]['output'][number]
+type OutputChunk = Extract<OutputChunkorAsset, { type: 'chunk' }>;
+
+type MutateChunk = (chunk: OutputChunk, build: 'server' | 'client', newCode: string) => void;
+
+export type AstroBuildPlugin = {
+ build: 'ssr' | 'client' | 'both';
+ hooks?: {
+ 'build:before'?: (opts: {
+ build: 'ssr' | 'client';
+ input: Set<string>;
+ }) => {
+ enforce?: 'after-user-plugins';
+ vitePlugin: VitePlugin | VitePlugin[] | undefined
+ };
+ 'build:post'?: (opts: {ssrOutputs: RollupOutputArray; clientOutputs: RollupOutputArray; mutate: MutateChunk}) => void | Promise<void>;
+ }
+};
+
+export function createPluginContainer(options: StaticBuildOptions, internals: BuildInternals) {
+ const clientPlugins: AstroBuildPlugin[] = [];
+ const ssrPlugins: AstroBuildPlugin[] = [];
+ const allPlugins = new Set<AstroBuildPlugin>();
+
+ return {
+ options,
+ internals,
+ register(plugin: AstroBuildPlugin) {
+ allPlugins.add(plugin);
+ switch(plugin.build) {
+ case 'client': {
+ clientPlugins.push(plugin);
+ break;
+ }
+ case 'ssr': {
+ ssrPlugins.push(plugin);
+ break;
+ }
+ case 'both': {
+ clientPlugins.push(plugin);
+ ssrPlugins.push(plugin);
+ break;
+ }
+ }
+ },
+
+ // Hooks
+ runBeforeHook(build: 'ssr' | 'client', input: Set<string>) {
+ let plugins = build === 'ssr' ? ssrPlugins : clientPlugins;
+ let vitePlugins: Array<VitePlugin | VitePlugin[]> = [];
+ let lastVitePlugins: Array<VitePlugin | VitePlugin[]> = [];
+ for(const plugin of plugins) {
+ if(plugin.hooks?.['build:before']) {
+ let result = plugin.hooks['build:before']({ build, input });
+ if(result.vitePlugin) {
+ vitePlugins.push(result.vitePlugin);
+ }
+ }
+ }
+
+ return {
+ vitePlugins,
+ lastVitePlugins
+ };
+ },
+
+ async runPostHook(ssrReturn: ViteBuildReturn, clientReturn: ViteBuildReturn | null) {
+ const mutations = new Map<string, {
+ build: 'server' | 'client';
+ code: string;
+ }>();
+ const ssrOutputs: RollupOutputArray = [];
+ const clientOutputs: RollupOutputArray = [];
+
+ if(Array.isArray(ssrReturn)) {
+ ssrOutputs.push(...ssrReturn);
+ } else if('output' in ssrReturn) {
+ ssrOutputs.push(ssrReturn);
+ }
+
+ if(Array.isArray(clientReturn)) {
+ clientOutputs.push(...clientReturn);
+ } else if(clientReturn && 'output' in clientReturn) {
+ clientOutputs.push(clientReturn);
+ }
+
+ const mutate: MutateChunk = (chunk, build, newCode) => {
+ chunk.code = newCode;
+ mutations.set(chunk.fileName, {
+ build,
+ code: newCode,
+ });
+ };
+
+ for(const plugin of allPlugins) {
+ const postHook = plugin.hooks?.['build:post'];
+ if(postHook) {
+ await postHook({
+ ssrOutputs,
+ clientOutputs,
+ mutate
+ });
+ }
+ }
+
+ return mutations;
+ }
+ };
+}
+
+export type AstroBuildPluginContainer = ReturnType<typeof createPluginContainer>;
diff --git a/packages/astro/src/core/build/plugins/index.ts b/packages/astro/src/core/build/plugins/index.ts
new file mode 100644
index 000000000..f253fe289
--- /dev/null
+++ b/packages/astro/src/core/build/plugins/index.ts
@@ -0,0 +1,23 @@
+import type { AstroBuildPluginContainer, AstroBuildPlugin } from '../plugin';
+import type { PageBuildData, StaticBuildOptions } from '../types';
+import { pluginAnalyzer } from './plugin-analyzer.js';
+import { pluginInternals } from './plugin-internals.js';
+import { pluginPages } from './plugin-pages.js';
+import { pluginCSS } from './plugin-css.js';
+import { pluginPrerender } from './plugin-prerender.js';
+import { astroConfigBuildPlugin } from '../../../content/vite-plugin-content-assets.js';
+import { pluginSSR } from './plugin-ssr.js';
+import { pluginAliasResolve } from './plugin-alias-resolve.js';
+import { pluginHoistedScripts } from './plugin-hoisted-scripts.js';
+
+export function registerAllPlugins({ internals, options, register }: AstroBuildPluginContainer) {
+ register(pluginAliasResolve(internals));
+ register(pluginAnalyzer(internals));
+ register(pluginInternals(internals));
+ register(pluginPages(options, internals));
+ register(pluginCSS(options, internals));
+ register(pluginPrerender(options, internals));
+ register(astroConfigBuildPlugin(internals));
+ register(pluginHoistedScripts(options, internals));
+ register(pluginSSR(options, internals));
+}
diff --git a/packages/astro/src/core/build/vite-plugin-alias-resolve.ts b/packages/astro/src/core/build/plugins/plugin-alias-resolve.ts
index ac37e66cd..1487ca2b9 100644
--- a/packages/astro/src/core/build/vite-plugin-alias-resolve.ts
+++ b/packages/astro/src/core/build/plugins/plugin-alias-resolve.ts
@@ -1,5 +1,6 @@
import type { Alias, Plugin as VitePlugin } from 'vite';
-import type { BuildInternals } from '../../core/build/internal.js';
+import type { BuildInternals } from '../internal.js';
+import { AstroBuildPlugin } from '../plugin.js';
/**
* `@rollup/plugin-alias` doesn't resolve aliases in Rollup input by default. This plugin fixes it
@@ -48,3 +49,16 @@ function matches(pattern: string | RegExp, importee: string) {
}
return importee.startsWith(pattern + '/');
}
+
+export function pluginAliasResolve(internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'client',
+ hooks: {
+ 'build:before': () => {
+ return {
+ vitePlugin: vitePluginAliasResolve(internals)
+ };
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/vite-plugin-analyzer.ts b/packages/astro/src/core/build/plugins/plugin-analyzer.ts
index 0e6a991bd..4214cde8e 100644
--- a/packages/astro/src/core/build/vite-plugin-analyzer.ts
+++ b/packages/astro/src/core/build/plugins/plugin-analyzer.ts
@@ -1,11 +1,12 @@
import type { PluginContext } from 'rollup';
import type { Plugin as VitePlugin } from 'vite';
-import type { BuildInternals } from '../../core/build/internal.js';
-import type { PluginMetadata as AstroPluginMetadata } from '../../vite-plugin-astro/types';
+import type { BuildInternals } from '../internal.js';
+import type { PluginMetadata as AstroPluginMetadata } from '../../../vite-plugin-astro/types';
+import type { AstroBuildPlugin } from '../plugin.js';
-import { prependForwardSlash } from '../../core/path.js';
-import { getTopLevelPages } from './graph.js';
-import { getPageDataByViteID, trackClientOnlyPageDatas } from './internal.js';
+import { prependForwardSlash } from '../../path.js';
+import { getTopLevelPages } from '../graph.js';
+import { getPageDataByViteID, trackClientOnlyPageDatas } from '../internal.js';
export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
function hoistedScriptScanner() {
@@ -122,3 +123,16 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
},
};
}
+
+export function pluginAnalyzer(internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'ssr',
+ hooks: {
+ 'build:before': () => {
+ return {
+ vitePlugin: vitePluginAnalyzer(internals)
+ };
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/vite-plugin-css.ts b/packages/astro/src/core/build/plugins/plugin-css.ts
index 2a9b5b739..b52b5c0fc 100644
--- a/packages/astro/src/core/build/vite-plugin-css.ts
+++ b/packages/astro/src/core/build/plugins/plugin-css.ts
@@ -2,20 +2,22 @@ import * as crypto from 'node:crypto';
import * as npath from 'node:path';
import type { GetModuleInfo } from 'rollup';
import { Plugin as VitePlugin, ResolvedConfig, transformWithEsbuild } from 'vite';
-import { isCSSRequest } from '../render/util.js';
-import type { BuildInternals } from './internal';
-import type { PageBuildData, StaticBuildOptions } from './types';
+import { isCSSRequest } from '../../render/util.js';
+import type { BuildInternals } from '../internal';
+import type { PageBuildData, StaticBuildOptions } from '../types';
+import type { AstroBuildPlugin } from '../plugin';
-import { PROPAGATED_ASSET_FLAG } from '../../content/consts.js';
-import * as assetName from './css-asset-name.js';
-import { moduleIsTopLevelPage, walkParentInfos } from './graph.js';
+import { PROPAGATED_ASSET_FLAG } from '../../../content/consts.js';
+import * as assetName from '../css-asset-name.js';
+import { moduleIsTopLevelPage, walkParentInfos } from '../graph.js';
import {
eachPageData,
getPageDataByViteID,
getPageDatasByClientOnlyID,
getPageDatasByHoistedScriptId,
isHoistedScript,
-} from './internal.js';
+} from '../internal.js';
+import { extendManualChunks } from './util.js';
interface PluginOptions {
internals: BuildInternals;
@@ -54,40 +56,30 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
name: 'astro:rollup-plugin-build-css',
outputOptions(outputOptions) {
- const manualChunks = outputOptions.manualChunks || Function.prototype;
const assetFileNames = outputOptions.assetFileNames;
const namingIncludesHash = assetFileNames?.toString().includes('[hash]');
const createNameForParentPages = namingIncludesHash
? assetName.shortHashedName
: assetName.createSlugger(settings);
- outputOptions.manualChunks = function (id, ...args) {
- // Defer to user-provided `manualChunks`, if it was provided.
- if (typeof manualChunks == 'object') {
- if (id in manualChunks) {
- return manualChunks[id];
- }
- } else if (typeof manualChunks === 'function') {
- const outid = manualChunks.call(this, id, ...args);
- if (outid) {
- return outid;
- }
- }
- // For CSS, create a hash of all of the pages that use it.
- // This causes CSS to be built into shared chunks when used by multiple pages.
- if (isCSSRequest(id)) {
- for (const [pageInfo] of walkParentInfos(id, {
- getModuleInfo: args[0].getModuleInfo,
- })) {
- if (new URL(pageInfo.id, 'file://').searchParams.has(PROPAGATED_ASSET_FLAG)) {
- // Split delayed assets to separate modules
- // so they can be injected where needed
- return createNameHash(id, [id]);
+ extendManualChunks(outputOptions, {
+ after(id, meta) {
+ // For CSS, create a hash of all of the pages that use it.
+ // This causes CSS to be built into shared chunks when used by multiple pages.
+ if (isCSSRequest(id)) {
+ for (const [pageInfo] of walkParentInfos(id, {
+ getModuleInfo: meta.getModuleInfo,
+ })) {
+ if (new URL(pageInfo.id, 'file://').searchParams.has(PROPAGATED_ASSET_FLAG)) {
+ // Split delayed assets to separate modules
+ // so they can be injected where needed
+ return createNameHash(id, [id]);
+ }
}
+ return createNameForParentPages(id, meta);
}
- return createNameForParentPages(id, args[0]);
}
- };
+ });
},
async generateBundle(_outputOptions, bundle) {
@@ -272,3 +264,22 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
},
];
}
+
+export function pluginCSS(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'both',
+ hooks: {
+ 'build:before': ({ build }) => {
+ let plugins = rollupPluginAstroBuildCSS({
+ buildOptions: options,
+ internals,
+ target: build === 'ssr' ? 'server' : 'client'
+ });
+
+ return {
+ vitePlugin: plugins
+ };
+ }
+ }
+ }
+}
diff --git a/packages/astro/src/core/build/vite-plugin-hoisted-scripts.ts b/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts
index 65d38ec87..9e21177bd 100644
--- a/packages/astro/src/core/build/vite-plugin-hoisted-scripts.ts
+++ b/packages/astro/src/core/build/plugins/plugin-hoisted-scripts.ts
@@ -1,8 +1,10 @@
import type { Plugin as VitePlugin } from 'vite';
-import type { AstroSettings } from '../../@types/astro';
-import type { BuildInternals } from '../../core/build/internal.js';
-import { viteID } from '../util.js';
-import { getPageDataByViteID } from './internal.js';
+import type { AstroSettings } from '../../../@types/astro';
+import type { BuildInternals } from '../internal.js';
+import { viteID } from '../../util.js';
+import { getPageDataByViteID } from '../internal.js';
+import { StaticBuildOptions } from '../types';
+import { AstroBuildPlugin } from '../plugin';
function virtualHoistedEntry(id: string) {
return id.startsWith('/astro/hoisted.js?q=');
@@ -91,3 +93,16 @@ export function vitePluginHoistedScripts(
},
};
}
+
+export function pluginHoistedScripts(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'client',
+ hooks: {
+ 'build:before': () => {
+ return {
+ vitePlugin: vitePluginHoistedScripts(options.settings, internals)
+ };
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/vite-plugin-internals.ts b/packages/astro/src/core/build/plugins/plugin-internals.ts
index dfd346e77..7177d8bef 100644
--- a/packages/astro/src/core/build/vite-plugin-internals.ts
+++ b/packages/astro/src/core/build/plugins/plugin-internals.ts
@@ -1,5 +1,6 @@
import type { Plugin as VitePlugin, UserConfig } from 'vite';
-import type { BuildInternals } from './internal.js';
+import type { BuildInternals } from '../internal.js';
+import type { AstroBuildPlugin } from '../plugin';
export function vitePluginInternals(input: Set<string>, internals: BuildInternals): VitePlugin {
return {
@@ -60,3 +61,16 @@ export function vitePluginInternals(input: Set<string>, internals: BuildInternal
},
};
}
+
+export function pluginInternals(internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'both',
+ hooks: {
+ 'build:before': ({ input }) => {
+ return {
+ vitePlugin: vitePluginInternals(input, internals)
+ };
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/vite-plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts
index 1d661865c..ac436a364 100644
--- a/packages/astro/src/core/build/vite-plugin-pages.ts
+++ b/packages/astro/src/core/build/plugins/plugin-pages.ts
@@ -1,8 +1,10 @@
import type { Plugin as VitePlugin } from 'vite';
-import { pagesVirtualModuleId, resolvedPagesVirtualModuleId } from '../app/index.js';
-import { addRollupInput } from './add-rollup-input.js';
-import { BuildInternals, eachPageData, hasPrerenderedPages } from './internal.js';
-import type { StaticBuildOptions } from './types';
+import type { StaticBuildOptions } from '../types';
+import type { AstroBuildPlugin } from '../plugin';
+
+import { pagesVirtualModuleId, resolvedPagesVirtualModuleId } from '../../app/index.js';
+import { addRollupInput } from '../add-rollup-input.js';
+import { BuildInternals, eachPageData, hasPrerenderedPages } from '../internal.js';
export function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin {
return {
@@ -53,3 +55,16 @@ export const renderers = [${rendererItems}];`;
},
};
}
+
+export function pluginPages(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'ssr',
+ hooks: {
+ 'build:before': () => {
+ return {
+ vitePlugin: vitePluginPages(opts, internals)
+ };
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/plugins/plugin-prerender.ts b/packages/astro/src/core/build/plugins/plugin-prerender.ts
new file mode 100644
index 000000000..340e84ea6
--- /dev/null
+++ b/packages/astro/src/core/build/plugins/plugin-prerender.ts
@@ -0,0 +1,48 @@
+import type { Plugin as VitePlugin } from 'vite';
+import type { BuildInternals } from '../internal.js';
+import type { AstroBuildPlugin } from '../plugin.js';
+import type { StaticBuildOptions } from '../types';
+import { extendManualChunks } from './util.js';
+
+export function vitePluginPrerender(
+ opts: StaticBuildOptions,
+ internals: BuildInternals
+): VitePlugin {
+ return {
+ name: 'astro:rollup-plugin-prerender',
+
+ outputOptions(outputOptions) {
+ extendManualChunks(outputOptions, {
+ after(id, meta) {
+ // Split the Astro runtime into a separate chunk for readability
+ if (id.includes('astro/dist')) {
+ return 'astro';
+ }
+ const pageInfo = internals.pagesByViteID.get(id);
+ if (pageInfo) {
+ // prerendered pages should be split into their own chunk
+ // Important: this can't be in the `pages/` directory!
+ if (meta.getModuleInfo(id)?.meta.astro?.pageOptions?.prerender) {
+ return 'prerender';
+ }
+ // dynamic pages should all go in their own chunk in the pages/* directory
+ return `pages/all`;
+ }
+ }
+ });
+ },
+ };
+}
+
+export function pluginPrerender(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin {
+ return {
+ build: 'ssr',
+ hooks: {
+ 'build:before': () => {
+ return {
+ vitePlugin: vitePluginPrerender(opts, internals)
+ };
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/vite-plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts
index 817881c36..289703858 100644
--- a/packages/astro/src/core/build/vite-plugin-ssr.ts
+++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts
@@ -1,21 +1,22 @@
import type { Plugin as VitePlugin } from 'vite';
-import type { AstroAdapter } from '../../@types/astro';
-import type { SerializedRouteInfo, SerializedSSRManifest } from '../app/types';
-import type { BuildInternals } from './internal.js';
-import type { StaticBuildOptions } from './types';
+import type { AstroAdapter } from '../../../@types/astro';
+import type { SerializedRouteInfo, SerializedSSRManifest } from '../../app/types';
+import type { BuildInternals } from '../internal.js';
+import type { StaticBuildOptions } from '../types';
import glob from 'fast-glob';
import * as fs from 'fs';
import { fileURLToPath } from 'url';
-import { getContentPaths } from '../../content/index.js';
-import { runHookBuildSsr } from '../../integrations/index.js';
-import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
-import { pagesVirtualModuleId } from '../app/index.js';
-import { removeLeadingForwardSlash, removeTrailingForwardSlash } from '../path.js';
-import { serializeRouteData } from '../routing/index.js';
-import { addRollupInput } from './add-rollup-input.js';
-import { getOutFile, getOutFolder } from './common.js';
-import { eachPrerenderedPageData, eachServerPageData, sortedCSS } from './internal.js';
+import { getContentPaths } from '../../../content/index.js';
+import { runHookBuildSsr } from '../../../integrations/index.js';
+import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../../vite-plugin-scripts/index.js';
+import { pagesVirtualModuleId } from '../../app/index.js';
+import { removeLeadingForwardSlash, removeTrailingForwardSlash } from '../../path.js';
+import { serializeRouteData } from '../../routing/index.js';
+import { addRollupInput } from '../add-rollup-input.js';
+import { getOutFile, getOutFolder } from '../common.js';
+import { eachPrerenderedPageData, eachServerPageData, sortedCSS } from '../internal.js';
+import { AstroBuildPlugin } from '../plugin';
export const virtualModuleId = '@astrojs-ssr-virtual-entry';
const resolvedVirtualModuleId = '\0' + virtualModuleId;
@@ -29,7 +30,7 @@ export function vitePluginSSR(internals: BuildInternals, adapter: AstroAdapter):
options(opts) {
return addRollupInput(opts, [virtualModuleId]);
},
- resolveId(id) {
+ resolveId(id, parent) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
@@ -114,12 +115,10 @@ export async function injectManifest(buildOpts: StaticBuildOptions, internals: B
const chunk = internals.ssrEntryChunk;
const code = chunk.code;
- chunk.code = code.replace(replaceExp, () => {
+
+ return code.replace(replaceExp, () => {
return JSON.stringify(manifest);
});
- const serverEntryURL = new URL(buildOpts.buildConfig.serverEntry, buildOpts.buildConfig.server);
- await fs.promises.mkdir(new URL('./', serverEntryURL), { recursive: true });
- await fs.promises.writeFile(serverEntryURL, chunk.code, 'utf-8');
}
function buildManifest(
@@ -220,3 +219,34 @@ function buildManifest(
return ssrManifest;
}
+
+export function pluginSSR(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin {
+ const ssr = options.settings.config.output === 'server';
+ return {
+ build: 'ssr',
+ hooks: {
+ 'build:before': () => {
+ let vitePlugin = ssr ? vitePluginSSR(internals, options.settings.adapter!) : undefined;
+
+ return {
+ enforce: 'after-user-plugins',
+ vitePlugin
+ }
+ },
+ 'build:post': async ({ mutate }) => {
+ if(!ssr) {
+ return;
+ }
+
+ if(!internals.ssrEntryChunk) {
+ throw new Error(`Did not generate an entry chunk for SSR`);
+ }
+ // Mutate the filename
+ internals.ssrEntryChunk.fileName = options.settings.config.build.serverEntry;
+
+ const code = await injectManifest(options, internals);
+ mutate(internals.ssrEntryChunk, 'server', code);
+ }
+ }
+ };
+}
diff --git a/packages/astro/src/core/build/plugins/util.ts b/packages/astro/src/core/build/plugins/util.ts
new file mode 100644
index 000000000..6129aa1e1
--- /dev/null
+++ b/packages/astro/src/core/build/plugins/util.ts
@@ -0,0 +1,40 @@
+import type { Plugin as VitePlugin } from 'vite';
+
+// eslint-disable-next-line @typescript-eslint/ban-types
+type OutputOptionsHook = Extract<VitePlugin['outputOptions'], Function>;
+type OutputOptions = Parameters<OutputOptionsHook>[0];
+
+type ExtendManualChunksHooks = {
+ before?: (id: string, meta: any) => string | undefined;
+ after?: (id: string, meta: any) => string | undefined;
+}
+
+export function extendManualChunks(outputOptions: OutputOptions, hooks: ExtendManualChunksHooks) {
+ const manualChunks = outputOptions.manualChunks;
+ outputOptions.manualChunks = function(id, meta) {
+ if(hooks.before) {
+ let value = hooks.before(id, meta);
+ if(value) {
+ return value;
+ }
+ }
+
+ // Defer to user-provided `manualChunks`, if it was provided.
+ if (typeof manualChunks == 'object') {
+ if (id in manualChunks) {
+ let value = manualChunks[id];
+ return value[0];
+ }
+ } else if (typeof manualChunks === 'function') {
+ const outid = manualChunks.call(this, id, meta);
+ if (outid) {
+ return outid;
+ }
+ }
+
+ if(hooks.after) {
+ return hooks.after(id, meta) || null;
+ }
+ return null;
+ };
+}
diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts
index b2fab0a14..726c1db88 100644
--- a/packages/astro/src/core/build/static-build.ts
+++ b/packages/astro/src/core/build/static-build.ts
@@ -4,7 +4,6 @@ import fs from 'fs';
import { bgGreen, bgMagenta, black, dim } from 'kleur/colors';
import { fileURLToPath } from 'url';
import * as vite from 'vite';
-import { astroContentProdBundlePlugin } from '../../content/index.js';
import {
BuildInternals,
createBuildInternals,
@@ -23,14 +22,8 @@ import { generatePages } from './generate.js';
import { trackPageData } from './internal.js';
import type { PageBuildData, StaticBuildOptions } from './types';
import { getTimeStat } from './util.js';
-import { vitePluginAliasResolve } from './vite-plugin-alias-resolve.js';
-import { vitePluginAnalyzer } from './vite-plugin-analyzer.js';
-import { rollupPluginAstroBuildCSS } from './vite-plugin-css.js';
-import { vitePluginHoistedScripts } from './vite-plugin-hoisted-scripts.js';
-import { vitePluginInternals } from './vite-plugin-internals.js';
-import { vitePluginPages } from './vite-plugin-pages.js';
-import { vitePluginPrerender } from './vite-plugin-prerender.js';
-import { injectManifest, vitePluginSSR } from './vite-plugin-ssr.js';
+import { AstroBuildPluginContainer, createPluginContainer } from './plugin.js';
+import { registerAllPlugins } from './plugins/index.js';
export async function staticBuild(opts: StaticBuildOptions) {
const { allPages, settings } = opts;
@@ -70,10 +63,15 @@ export async function staticBuild(opts: StaticBuildOptions) {
// condition, so we are doing it ourselves
emptyDir(settings.config.outDir, new Set('.git'));
+ // Register plugins
+ const container = createPluginContainer(opts, internals);
+ registerAllPlugins(container);
+
+
// Build your project (SSR application code, assets, client JS, etc.)
timer.ssr = performance.now();
info(opts.logging, 'build', `Building ${settings.config.output} entrypoints...`);
- await ssrBuild(opts, internals, pageInput);
+ const ssrOutput = await ssrBuild(opts, internals, pageInput, container);
info(opts.logging, 'build', dim(`Completed in ${getTimeStat(timer.ssr, performance.now())}.`));
const rendererClientEntrypoints = settings.renderers
@@ -93,9 +91,11 @@ export async function staticBuild(opts: StaticBuildOptions) {
// Run client build first, so the assets can be fed into the SSR rendered version.
timer.clientBuild = performance.now();
- await clientBuild(opts, internals, clientInput);
+ const clientOutput = await clientBuild(opts, internals, clientInput, container);
timer.generate = performance.now();
+ await runPostBuildHooks(container, ssrOutput, clientOutput);
+
switch (settings.config.output) {
case 'static': {
await generatePages(opts, internals);
@@ -103,7 +103,6 @@ export async function staticBuild(opts: StaticBuildOptions) {
return;
}
case 'server': {
- await injectManifest(opts, internals);
await generatePages(opts, internals);
await cleanStaticOutput(opts, internals);
info(opts.logging, null, `\n${bgMagenta(black(' finalizing server assets '))}\n`);
@@ -113,11 +112,13 @@ export async function staticBuild(opts: StaticBuildOptions) {
}
}
-async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
+async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>, container: AstroBuildPluginContainer) {
const { settings, viteConfig } = opts;
const ssr = settings.config.output === 'server';
const out = ssr ? opts.buildConfig.server : getOutDirWithinCwd(settings.config.outDir);
+ const { lastVitePlugins, vitePlugins } = container.runBeforeHook('ssr', input);
+
const viteBuildConfig: vite.InlineConfig = {
...viteConfig,
mode: viteConfig.mode || 'production',
@@ -155,19 +156,9 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
reportCompressedSize: false,
},
plugins: [
- vitePluginAnalyzer(internals),
- vitePluginInternals(input, internals),
- vitePluginPages(opts, internals),
- rollupPluginAstroBuildCSS({
- buildOptions: opts,
- internals,
- target: 'server',
- }),
- vitePluginPrerender(opts, internals),
+ ...vitePlugins,
...(viteConfig.plugins || []),
- astroContentProdBundlePlugin({ internals }),
- // SSR needs to be last
- ssr && vitePluginSSR(internals, settings.adapter!),
+ ...lastVitePlugins
],
envPrefix: viteConfig.envPrefix ?? 'PUBLIC_',
base: settings.config.base,
@@ -187,7 +178,8 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
async function clientBuild(
opts: StaticBuildOptions,
internals: BuildInternals,
- input: Set<string>
+ input: Set<string>,
+ container: AstroBuildPluginContainer
) {
const { settings, viteConfig } = opts;
const timer = performance.now();
@@ -204,8 +196,10 @@ async function clientBuild(
return null;
}
+ const { lastVitePlugins, vitePlugins } = container.runBeforeHook('client', input);
info(opts.logging, null, `\n${bgGreen(black(' building client '))}`);
+
const viteBuildConfig: vite.InlineConfig = {
...viteConfig,
mode: viteConfig.mode || 'production',
@@ -229,15 +223,9 @@ async function clientBuild(
},
},
plugins: [
- vitePluginAliasResolve(internals),
- vitePluginInternals(input, internals),
- vitePluginHoistedScripts(settings, internals),
- rollupPluginAstroBuildCSS({
- buildOptions: opts,
- internals,
- target: 'client',
- }),
+ ...vitePlugins,
...(viteConfig.plugins || []),
+ ...lastVitePlugins,
],
envPrefix: viteConfig.envPrefix ?? 'PUBLIC_',
base: settings.config.base,
@@ -256,6 +244,21 @@ async function clientBuild(
return buildResult;
}
+async function runPostBuildHooks(
+ container: AstroBuildPluginContainer,
+ ssrReturn: Awaited<ReturnType<typeof ssrBuild>>,
+ clientReturn: Awaited<ReturnType<typeof clientBuild>>
+) {
+ const mutations = await container.runPostHook(ssrReturn, clientReturn);
+ const buildConfig = container.options.settings.config.build;
+ for(const [fileName, mutation] of mutations) {
+ const root = mutation.build === 'server' ? buildConfig.server : buildConfig.client;
+ const fileURL = new URL(fileName, root);
+ await fs.promises.mkdir(new URL('./', fileURL), { recursive: true });
+ await fs.promises.writeFile(fileURL, mutation.code, 'utf-8');
+ }
+}
+
/**
* For each statically prerendered page, replace their SSR file with a noop.
* This allows us to run the SSR build only once, but still remove dependencies for statically rendered routes.
diff --git a/packages/astro/src/core/build/types.ts b/packages/astro/src/core/build/types.ts
index 0c4d9a069..a9ca08a0e 100644
--- a/packages/astro/src/core/build/types.ts
+++ b/packages/astro/src/core/build/types.ts
@@ -11,6 +11,7 @@ import type {
} from '../../@types/astro';
import type { LogOptions } from '../logger/core';
import type { RouteCache } from '../render/route-cache';
+import type { default as vite } from 'vite';
export type ComponentPath = string;
export type ViteID = string;
@@ -44,3 +45,7 @@ export interface SingleFileBuiltModule {
pageMap: Map<ComponentPath, ComponentInstance>;
renderers: SSRLoadedRenderer[];
}
+
+export type ViteBuildReturn = Awaited<ReturnType<typeof vite.build>>;
+export type RollupOutput = Extract<Extract<ViteBuildReturn, Exclude<ViteBuildReturn, Array<any>>>, { output: any }>;
+export type OutputChunk = Extract<RollupOutput['output'][number], { type: 'chunk' }>;
diff --git a/packages/astro/src/core/build/vite-plugin-prerender.ts b/packages/astro/src/core/build/vite-plugin-prerender.ts
deleted file mode 100644
index fdc505378..000000000
--- a/packages/astro/src/core/build/vite-plugin-prerender.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { Plugin as VitePlugin } from 'vite';
-import type { BuildInternals } from './internal.js';
-import type { StaticBuildOptions } from './types';
-
-export function vitePluginPrerender(
- opts: StaticBuildOptions,
- internals: BuildInternals
-): VitePlugin {
- return {
- name: 'astro:rollup-plugin-prerender',
-
- outputOptions(outputOptions) {
- const manualChunks = outputOptions.manualChunks || Function.prototype;
- outputOptions.manualChunks = function (id, api, ...args) {
- // Defer to user-provided `manualChunks`, if it was provided.
- if (typeof manualChunks == 'object') {
- if (id in manualChunks) {
- return manualChunks[id];
- }
- } else if (typeof manualChunks === 'function') {
- const outid = manualChunks.call(this, id, api, ...args);
- if (outid) {
- return outid;
- }
- }
- // Split the Astro runtime into a separate chunk for readability
- if (id.includes('astro/dist')) {
- return 'astro';
- }
- const pageInfo = internals.pagesByViteID.get(id);
- if (pageInfo) {
- // prerendered pages should be split into their own chunk
- // Important: this can't be in the `pages/` directory!
- if (api.getModuleInfo(id)?.meta.astro?.pageOptions?.prerender) {
- return `prerender`;
- }
- // dynamic pages should all go in their own chunk in the pages/* directory
- return `pages/all`;
- }
- };
- },
- };
-}