full site update

This commit is contained in:
2025-07-24 18:46:24 +02:00
parent bfe2b90d8d
commit 37a6e0ab31
6912 changed files with 540482 additions and 361712 deletions

View File

@@ -1,5 +1,7 @@
import type { AstroConfig, RouteData } from '../../@types/astro.js';
export declare function getOutFolder(astroConfig: AstroConfig, pathname: string, routeData: RouteData): URL;
import type { AstroSettings } from '../../types/astro.js';
import type { AstroConfig } from '../../types/public/config.js';
import type { RouteData } from '../../types/public/internal.js';
export declare function getOutFolder(astroSettings: AstroSettings, pathname: string, routeData: RouteData): URL;
export declare function getOutFile(astroConfig: AstroConfig, outFolder: URL, pathname: string, routeData: RouteData): URL;
/**
* Ensures the `outDir` is within `process.cwd()`. If not it will fallback to `<cwd>/.astro`.

View File

@@ -3,15 +3,15 @@ import { fileURLToPath, pathToFileURL } from "node:url";
import { appendForwardSlash } from "../../core/path.js";
const STATUS_CODE_PAGES = /* @__PURE__ */ new Set(["/404", "/500"]);
const FALLBACK_OUT_DIR_NAME = "./.astro/";
function getOutRoot(astroConfig) {
if (astroConfig.output === "static") {
return new URL("./", astroConfig.outDir);
function getOutRoot(astroSettings) {
if (astroSettings.buildOutput === "static") {
return new URL("./", astroSettings.config.outDir);
} else {
return new URL("./", astroConfig.build.client);
return new URL("./", astroSettings.config.build.client);
}
}
function getOutFolder(astroConfig, pathname, routeData) {
const outRoot = getOutRoot(astroConfig);
function getOutFolder(astroSettings, pathname, routeData) {
const outRoot = getOutRoot(astroSettings);
const routeType = routeData.type;
switch (routeType) {
case "endpoint":
@@ -19,7 +19,7 @@ function getOutFolder(astroConfig, pathname, routeData) {
case "fallback":
case "page":
case "redirect":
switch (astroConfig.build.format) {
switch (astroSettings.config.build.format) {
case "directory": {
if (STATUS_CODE_PAGES.has(pathname)) {
return new URL("." + appendForwardSlash(npath.dirname(pathname)), outRoot);

View File

@@ -1,2 +1 @@
export declare const CHUNKS_PATH = "chunks/";
export declare const CONTENT_PATH = "content/";

View File

@@ -1,6 +1,4 @@
const CHUNKS_PATH = "chunks/";
const CONTENT_PATH = "content/";
export {
CHUNKS_PATH,
CONTENT_PATH
CHUNKS_PATH
};

View File

@@ -1,5 +1,5 @@
import type { GetModuleInfo } from 'rollup';
import type { AstroSettings } from '../../@types/astro.js';
import type { AstroSettings } from '../../types/astro.js';
export declare function shortHashedName(settings: AstroSettings): (id: string, ctx: {
getModuleInfo: GetModuleInfo;
}) => string;

View File

@@ -1,8 +1,8 @@
import crypto from "node:crypto";
import npath from "node:path";
import { fileURLToPath } from "node:url";
import { normalizePath } from "vite";
import { viteID } from "../util.js";
import { normalizePath } from "../viteUtils.js";
import { getTopLevelPageModuleInfos } from "./graph.js";
const confusingBaseNames = ["404", "500"];
function shortHashedName(settings) {

View File

@@ -1,3 +1,3 @@
import { type BuildInternals } from '../../core/build/internal.js';
import { type BuildInternals } from './internal.js';
import type { StaticBuildOptions } from './types.js';
export declare function generatePages(options: StaticBuildOptions, internals: BuildInternals): Promise<void>;

View File

@@ -1,14 +1,14 @@
import fs from "node:fs";
import os from "node:os";
import { bgGreen, black, blue, bold, dim, green, magenta, red } from "kleur/colors";
import { bgGreen, black, blue, bold, dim, green, magenta, red, yellow } from "kleur/colors";
import PLimit from "p-limit";
import PQueue from "p-queue";
import { NOOP_ACTIONS_MOD } from "../../actions/noop-actions.js";
import {
generateImagesForPath,
getStaticImageList,
prepareAssetsGenerationEnv
} from "../../assets/build/generate.js";
import { hasPrerenderedPages } from "../../core/build/internal.js";
import {
isRelativePath,
joinPaths,
@@ -16,56 +16,67 @@ import {
removeTrailingForwardSlash
} from "../../core/path.js";
import { toFallbackType, toRoutingStrategy } from "../../i18n/utils.js";
import { runHookBuildGenerated } from "../../integrations/hooks.js";
import { getOutputDirectory } from "../../prerender/utils.js";
import { runHookBuildGenerated, toIntegrationResolvedRoute } from "../../integrations/hooks.js";
import { getServerOutputDirectory } from "../../prerender/utils.js";
import {
getAlgorithm,
getDirectives,
getScriptHashes,
getScriptResources,
getStrictDynamic,
getStyleHashes,
getStyleResources,
shouldTrackCspHashes,
trackScriptHashes,
trackStyleHashes
} from "../csp/common.js";
import { NoPrerenderedRoutesWithDomains } from "../errors/errors-data.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { NOOP_MIDDLEWARE_FN } from "../middleware/noop-middleware.js";
import { getRedirectLocationOrThrow, routeIsRedirect } from "../redirects/index.js";
import { RenderContext } from "../render-context.js";
import { callGetStaticPaths } from "../render/route-cache.js";
import { RenderContext } from "../render-context.js";
import { createRequest } from "../request.js";
import { redirectTemplate } from "../routing/3xx.js";
import { matchRoute } from "../routing/match.js";
import { stringifyParams } from "../routing/params.js";
import { getOutputFilename, isServerLikeOutput } from "../util.js";
import { getOutDirWithinCwd, getOutFile, getOutFolder } from "./common.js";
import { cssOrder, mergeInlineCss } from "./internal.js";
import { getOutputFilename } from "../util.js";
import { getOutFile, getOutFolder } from "./common.js";
import { cssOrder, hasPrerenderedPages, mergeInlineCss } from "./internal.js";
import { BuildPipeline } from "./pipeline.js";
import { getTimeStat, shouldAppendForwardSlash } from "./util.js";
function createEntryURL(filePath, outFolder) {
return new URL("./" + filePath + `?time=${Date.now()}`, outFolder);
}
async function generatePages(options, internals) {
const generatePagesTimer = performance.now();
const ssr = isServerLikeOutput(options.settings.config);
const ssr = options.settings.buildOutput === "server";
let manifest;
if (ssr) {
manifest = await BuildPipeline.retrieveManifest(options, internals);
manifest = await BuildPipeline.retrieveManifest(options.settings, internals);
} else {
const baseDirectory = getOutputDirectory(options.settings.config);
const baseDirectory = getServerOutputDirectory(options.settings);
const renderersEntryUrl = new URL("renderers.mjs", baseDirectory);
const renderers = await import(renderersEntryUrl.toString());
const middleware = internals.middlewareEntryPoint ? await import(internals.middlewareEntryPoint.toString()).then((mod) => mod.onRequest) : NOOP_MIDDLEWARE_FN;
manifest = createBuildManifest(
const actions = internals.astroActionsEntryPoint ? await import(internals.astroActionsEntryPoint.toString()).then((mod) => mod) : NOOP_ACTIONS_MOD;
manifest = await createBuildManifest(
options.settings,
internals,
renderers.renderers,
middleware,
actions,
options.key
);
}
const pipeline = BuildPipeline.create({ internals, manifest, options });
const { config, logger } = pipeline;
const outFolder = ssr ? options.settings.config.build.server : getOutDirWithinCwd(options.settings.config.outDir);
if (ssr && !hasPrerenderedPages(internals)) {
delete globalThis?.astroAsset?.addStaticImage;
return;
}
const verb = ssr ? "prerendering" : "generating";
logger.info("SKIP_FORMAT", `
${bgGreen(black(` ${verb} static routes `))}`);
const builtPaths = /* @__PURE__ */ new Set();
const pagesToGenerate = pipeline.retrieveRoutesToGenerate();
const routeToHeaders = /* @__PURE__ */ new Map();
if (ssr) {
for (const [pageData, filePath] of pagesToGenerate) {
if (pageData.route.prerender) {
@@ -76,26 +87,14 @@ ${bgGreen(black(` ${verb} static routes `))}`);
});
}
const ssrEntryPage = await pipeline.retrieveSsrEntry(pageData.route, filePath);
if (options.settings.adapter?.adapterFeatures?.functionPerRoute) {
const ssrEntry = ssrEntryPage?.pageModule;
if (ssrEntry) {
await generatePage(pageData, ssrEntry, builtPaths, pipeline);
} else {
const ssrEntryURLPage = createEntryURL(filePath, outFolder);
throw new Error(
`Unable to find the manifest for the module ${ssrEntryURLPage.toString()}. This is unexpected and likely a bug in Astro, please report.`
);
}
} else {
const ssrEntry = ssrEntryPage;
await generatePage(pageData, ssrEntry, builtPaths, pipeline);
}
const ssrEntry = ssrEntryPage;
await generatePage(pageData, ssrEntry, builtPaths, pipeline, routeToHeaders);
}
}
} else {
for (const [pageData, filePath] of pagesToGenerate) {
const entry = await pipeline.retrieveSsrEntry(pageData.route, filePath);
await generatePage(pageData, entry, builtPaths, pipeline);
await generatePage(pageData, entry, builtPaths, pipeline, routeToHeaders);
}
}
logger.info(
@@ -112,7 +111,9 @@ ${bgGreen(black(` ${verb} static routes `))}`);
const queue = new PQueue({ concurrency: Math.max(cpuCount, 1) });
const assetsTimer = performance.now();
for (const [originalPath, transforms] of staticImageList) {
await generateImagesForPath(originalPath, transforms, assetsCreationPipeline, queue);
queue.add(() => generateImagesForPath(originalPath, transforms, assetsCreationPipeline)).catch((e) => {
throw e;
});
}
await queue.onIdle();
const assetsTimeEnd = performance.now();
@@ -120,15 +121,18 @@ ${bgGreen(black(` ${verb} static routes `))}`);
`));
delete globalThis?.astroAsset?.addStaticImage;
}
await runHookBuildGenerated({ config, logger });
await runHookBuildGenerated({
settings: options.settings,
logger,
experimentalRouteToHeaders: routeToHeaders
});
}
const THRESHOLD_SLOW_RENDER_TIME_MS = 500;
async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
async function generatePage(pageData, ssrEntry, builtPaths, pipeline, routeToHeaders) {
const { config, logger } = pipeline;
const pageModulePromise = ssrEntry.page;
const styles = pageData.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []);
const linkIds = [];
const scripts = pageData.hoistedScript ?? null;
if (!pageModulePromise) {
throw new Error(
`Unable to find the module for ${pageData.component}. This is unexpected and likely a bug in Astro, please report.`
@@ -138,29 +142,38 @@ async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
const generationOptions = {
pageData,
linkIds,
scripts,
scripts: null,
styles,
mod: pageModule
};
async function generatePathWithLogs(path, route, index, paths, isConcurrent) {
async function generatePathWithLogs(path, route, integrationRoute, index, paths, isConcurrent) {
const timeStart = performance.now();
pipeline.logger.debug("build", `Generating: ${path}`);
const filePath = getOutputFilename(config, path, pageData.route.type);
const filePath = getOutputFilename(config, path, pageData.route);
const lineIcon = index === paths.length - 1 && !isConcurrent || paths.length === 1 ? "\u2514\u2500" : "\u251C\u2500";
if (!isConcurrent) {
logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)}`, false);
}
await generatePath(path, pipeline, generationOptions, route);
const created = await generatePath(
path,
pipeline,
generationOptions,
route,
integrationRoute,
routeToHeaders
);
const timeEnd = performance.now();
const isSlow = timeEnd - timeStart > THRESHOLD_SLOW_RENDER_TIME_MS;
const timeIncrease = (isSlow ? red : dim)(`(+${getTimeStat(timeStart, timeEnd)})`);
const notCreated = created === false ? yellow("(file not created, response body was empty)") : "";
if (isConcurrent) {
logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)} ${timeIncrease}`);
logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)} ${timeIncrease} ${notCreated}`);
} else {
logger.info("SKIP_FORMAT", ` ${timeIncrease}`);
logger.info("SKIP_FORMAT", ` ${timeIncrease} ${notCreated}`);
}
}
for (const route of eachRouteInRouteData(pageData)) {
const integrationRoute = toIntegrationResolvedRoute(route);
const icon = route.type === "page" || route.type === "redirect" || route.type === "fallback" ? green("\u25B6") : magenta("\u03BB");
logger.info(null, `${icon} ${getPrettyRouteName(route)}`);
const paths = await getPathsForRoute(route, pageModule, pipeline, builtPaths);
@@ -169,13 +182,15 @@ async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
const promises = [];
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
promises.push(limit(() => generatePathWithLogs(path, route, i, paths, true)));
promises.push(
limit(() => generatePathWithLogs(path, route, integrationRoute, i, paths, true))
);
}
await Promise.allSettled(promises);
await Promise.all(promises);
} else {
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
await generatePathWithLogs(path, route, i, paths, false);
await generatePathWithLogs(path, route, integrationRoute, i, paths, false);
}
}
}
@@ -187,7 +202,7 @@ function* eachRouteInRouteData(data) {
}
}
async function getPathsForRoute(route, mod, pipeline, builtPaths) {
const { logger, options, routeCache, serverLike } = pipeline;
const { logger, options, routeCache, serverLike, config } = pipeline;
let paths = [];
if (route.pathname) {
paths.push(route.pathname);
@@ -198,7 +213,8 @@ async function getPathsForRoute(route, mod, pipeline, builtPaths) {
route,
routeCache,
logger,
ssr: serverLike
ssr: serverLike,
base: config.base
}).catch((err) => {
logger.error("build", `Failed to call getStaticPaths for ${route.component}`);
throw err;
@@ -221,7 +237,7 @@ async function getPathsForRoute(route, mod, pipeline, builtPaths) {
if (!builtPaths.has(removeTrailingForwardSlash(staticPath))) {
return true;
}
const matchedRoute = matchRoute(staticPath, options.manifest);
const matchedRoute = matchRoute(decodeURI(staticPath), options.routesList);
return matchedRoute === route;
});
for (const staticPath of paths) {
@@ -283,21 +299,29 @@ function getUrlForPath(pathname, base, origin, format, trailingSlash, routeType)
const buildPathRelative = removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending;
buildPathname = joinPaths(base, buildPathRelative);
}
const url = new URL(buildPathname, origin);
return url;
return new URL(buildPathname, origin);
}
async function generatePath(pathname, pipeline, gopts, route) {
async function generatePath(pathname, pipeline, gopts, route, integrationRoute, routeToHeaders) {
const { mod } = gopts;
const { config, logger, options } = pipeline;
logger.debug("build", `Generating: ${pathname}`);
if (route.type === "page") {
addPageName(pathname, options);
}
if (route.type === "fallback" && // If route is index page, continue rendering. The index page should
// always be rendered
route.pathname !== "/" && // Check if there is a translated page with the same path
Object.values(options.allPages).some((val) => val.route.pattern.test(pathname))) {
return;
if (route.type === "fallback" && route.pathname !== "/") {
let locale = removeLeadingForwardSlash(pathname).split("/")[0];
if (Object.values(options.allPages).some((val) => {
if (val.route.pattern.test(pathname)) {
if (val.route.segments && val.route.segments.length !== 0) {
if (val.route.segments[0][0].content !== locale) return false;
}
return true;
} else {
return false;
}
})) {
return void 0;
}
}
const url = getUrlForPath(
pathname,
@@ -308,17 +332,18 @@ async function generatePath(pathname, pipeline, gopts, route) {
route.type
);
const request = createRequest({
base: config.base,
url,
headers: new Headers(),
logger,
staticLike: true
isPrerendered: true,
routePattern: route.component
});
const renderContext = await RenderContext.create({
pipeline,
pathname,
request,
routeData: route
routeData: route,
clientAddress: void 0
});
let body;
let response;
@@ -330,23 +355,21 @@ async function generatePath(pathname, pipeline, gopts, route) {
}
throw err;
}
const responseHeaders = response.headers;
if (response.status >= 300 && response.status < 400) {
if (routeIsRedirect(route) && !config.build.redirects) {
return;
return void 0;
}
const locationSite = getRedirectLocationOrThrow(response.headers);
const locationSite = getRedirectLocationOrThrow(responseHeaders);
const siteURL = config.site;
const location = siteURL ? new URL(locationSite, siteURL) : locationSite;
const fromPath = new URL(request.url).pathname;
const delay = response.status === 302 ? 2 : 0;
body = `<!doctype html>
<title>Redirecting to: ${location}</title>
<meta http-equiv="refresh" content="${delay};url=${location}">
<meta name="robots" content="noindex">
<link rel="canonical" href="${location}">
<body>
<a href="${location}">Redirecting from <code>${fromPath}</code> to <code>${location}</code></a>
</body>`;
body = redirectTemplate({
status: response.status,
absoluteLocation: location,
relativeLocation: locationSite,
from: fromPath
});
if (config.compressHTML === true) {
body = body.replaceAll("\n", "");
}
@@ -354,26 +377,36 @@ async function generatePath(pathname, pipeline, gopts, route) {
route.redirect = location.toString();
}
} else {
if (!response.body) return;
if (!response.body) return false;
body = Buffer.from(await response.arrayBuffer());
}
const outFolder = getOutFolder(config, pathname, route);
const outFile = getOutFile(config, outFolder, pathname, route);
route.distURL = outFile;
const encodedPath = encodeURI(pathname);
const outFolder = getOutFolder(pipeline.settings, encodedPath, route);
const outFile = getOutFile(config, outFolder, encodedPath, route);
if (route.distURL) {
route.distURL.push(outFile);
} else {
route.distURL = [outFile];
}
if (pipeline.settings.adapter?.adapterFeatures?.experimentalStaticHeaders && pipeline.settings.config.experimental?.csp) {
routeToHeaders.set(pathname, { headers: responseHeaders, route: integrationRoute });
}
await fs.promises.mkdir(outFolder, { recursive: true });
await fs.promises.writeFile(outFile, body);
return true;
}
function getPrettyRouteName(route) {
if (isRelativePath(route.component)) {
return route.route;
} else if (route.component.includes("node_modules/")) {
return /.*node_modules\/(.+)/.exec(route.component)?.[1] ?? route.component;
} else {
return route.component;
}
if (route.component.includes("node_modules/")) {
return /.*node_modules\/(.+)/.exec(route.component)?.[1] ?? route.component;
}
return route.component;
}
function createBuildManifest(settings, internals, renderers, middleware, key) {
async function createBuildManifest(settings, internals, renderers, middleware, actions, key) {
let i18nManifest = void 0;
let csp = void 0;
if (settings.config.i18n) {
i18nManifest = {
fallback: settings.config.i18n.fallback,
@@ -384,18 +417,46 @@ function createBuildManifest(settings, internals, renderers, middleware, key) {
domainLookupTable: {}
};
}
if (shouldTrackCspHashes(settings.config.experimental.csp)) {
const algorithm = getAlgorithm(settings.config.experimental.csp);
const scriptHashes = [
...getScriptHashes(settings.config.experimental.csp),
...await trackScriptHashes(internals, settings, algorithm)
];
const styleHashes = [
...getStyleHashes(settings.config.experimental.csp),
...await trackStyleHashes(internals, settings, algorithm)
];
csp = {
cspDestination: settings.adapter?.adapterFeatures?.experimentalStaticHeaders ? "adapter" : void 0,
styleHashes,
styleResources: getStyleResources(settings.config.experimental.csp),
scriptHashes,
scriptResources: getScriptResources(settings.config.experimental.csp),
algorithm,
directives: getDirectives(settings.config.experimental.csp),
isStrictDynamic: getStrictDynamic(settings.config.experimental.csp)
};
}
return {
hrefRoot: settings.config.root.toString(),
srcDir: settings.config.srcDir,
buildClientDir: settings.config.build.client,
buildServerDir: settings.config.build.server,
publicDir: settings.config.publicDir,
outDir: settings.config.outDir,
cacheDir: settings.config.cacheDir,
trailingSlash: settings.config.trailingSlash,
assets: /* @__PURE__ */ new Set(),
entryModules: Object.fromEntries(internals.entrySpecifierToBundleMap.entries()),
inlinedScripts: internals.inlinedScripts,
routes: [],
adapterName: "",
adapterName: settings.adapter?.name ?? "",
clientDirectives: settings.clientDirectives,
compressHTML: settings.config.compressHTML,
renderers,
base: settings.config.base,
userAssetsBase: settings.config?.vite?.base,
assetsPrefix: settings.config.build.assetsPrefix,
site: settings.config.site,
componentMetadata: internals.componentMetadata,
@@ -406,9 +467,10 @@ function createBuildManifest(settings, internals, renderers, middleware, key) {
onRequest: middleware
};
},
checkOrigin: settings.config.security?.checkOrigin ?? false,
actions: () => actions,
checkOrigin: (settings.config.security?.checkOrigin && settings.buildOutput === "server") ?? false,
key,
experimentalEnvGetSecretEnabled: false
csp
};
}
export {

View File

@@ -1,5 +1,22 @@
import type { AstroInlineConfig } from '../../@types/astro.js';
export interface BuildOptions {
import type { AstroInlineConfig } from '../../types/public/config.js';
interface BuildOptions {
/**
* Output a development-based build similar to code transformed in `astro dev`. This
* can be useful to test build-only issues with additional debugging information included.
*
* @default false
*/
devOutput?: boolean;
/**
* Teardown the compiler WASM instance after build. This can improve performance when
* building once, but may cause a performance hit if building multiple times in a row.
*
* When building multiple projects in the same execution (e.g. during tests), disabling
* this option can greatly improve performance at the cost of some extra memory usage.
*
* @default true
*/
teardownCompiler?: boolean;
}
/**
* Builds your site for deployment. By default, this will generate static files and place them in a dist/ directory.
@@ -8,3 +25,4 @@ export interface BuildOptions {
* @experimental The JavaScript API is experimental
*/
export default function build(inlineConfig: AstroInlineConfig, options?: BuildOptions): Promise<void>;
export {};

View File

@@ -2,7 +2,6 @@ import fs from "node:fs";
import { performance } from "node:perf_hooks";
import { fileURLToPath } from "node:url";
import { blue, bold, green } from "kleur/colors";
import { injectImageEndpoint } from "../../assets/endpoint/config.js";
import { telemetry } from "../../events/index.js";
import { eventCliSession } from "../../events/session.js";
import {
@@ -11,62 +10,57 @@ import {
runHookConfigDone,
runHookConfigSetup
} from "../../integrations/hooks.js";
import { createDevelopmentManifest } from "../../vite-plugin-astro-server/plugin.js";
import { resolveConfig } from "../config/config.js";
import { createNodeLogger } from "../config/logging.js";
import { createSettings } from "../config/settings.js";
import { createVite } from "../create-vite.js";
import { createKey, getEnvironmentKey, hasEnvironmentKey } from "../encryption.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { levels, timerMessage } from "../logger/core.js";
import { apply as applyPolyfill } from "../polyfill.js";
import { createRouteManifest } from "../routing/index.js";
import { createRoutesList } from "../routing/index.js";
import { getServerIslandRouteData } from "../server-islands/endpoint.js";
import { clearContentLayerCache } from "../sync/index.js";
import { ensureProcessNodeEnv, isServerLikeOutput } from "../util.js";
import { ensureProcessNodeEnv } from "../util.js";
import { collectPagesData } from "./page-data.js";
import { staticBuild, viteBuild } from "./static-build.js";
import { getTimeStat } from "./util.js";
async function build(inlineConfig, options = {}) {
ensureProcessNodeEnv("production");
ensureProcessNodeEnv(options.devOutput ? "development" : "production");
applyPolyfill();
const logger = createNodeLogger(inlineConfig);
const { userConfig, astroConfig } = await resolveConfig(inlineConfig, "build");
telemetry.record(eventCliSession("build", userConfig));
const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
if (inlineConfig.force) {
if (astroConfig.experimental.contentCollectionCache) {
const contentCacheDir = new URL("./content/", astroConfig.cacheDir);
if (fs.existsSync(contentCacheDir)) {
logger.debug("content", "clearing content cache");
await fs.promises.rm(contentCacheDir, { force: true, recursive: true });
logger.warn("content", "content cache cleared (force)");
}
}
await clearContentLayerCache({ settings, logger, fs });
await clearContentLayerCache({ settings, logger, fs, isDev: false });
}
const builder = new AstroBuilder(settings, {
...options,
logger,
mode: inlineConfig.mode
mode: inlineConfig.mode ?? "production",
runtimeMode: options.devOutput ? "development" : "production"
});
await builder.run();
}
class AstroBuilder {
settings;
logger;
mode = "production";
mode;
runtimeMode;
origin;
manifest;
routesList;
timer;
teardownCompiler;
constructor(settings, options) {
if (options.mode) {
this.mode = options.mode;
}
this.mode = options.mode;
this.runtimeMode = options.runtimeMode;
this.settings = settings;
this.logger = options.logger;
this.teardownCompiler = options.teardownCompiler ?? true;
this.origin = settings.config.site ? new URL(settings.config.site).origin : `http://localhost:${settings.config.server.port}`;
this.manifest = { routes: [] };
this.routesList = { routes: [] };
this.timer = {};
}
/** Setup Vite and run any async setup logic that couldn't run inside of the constructor. */
@@ -79,13 +73,14 @@ class AstroBuilder {
command: "build",
logger
});
if (isServerLikeOutput(this.settings.config)) {
this.settings = injectImageEndpoint(this.settings, "build");
const manifest = createDevelopmentManifest(this.settings);
this.routesList = await createRoutesList({ settings: this.settings }, this.logger);
await runHookConfigDone({ settings: this.settings, logger, command: "build" });
if (!this.settings.config.adapter && this.settings.buildOutput === "server") {
throw new AstroError(AstroErrorData.NoAdapterInstalled);
}
this.manifest = createRouteManifest({ settings: this.settings }, this.logger);
const viteConfig = await createVite(
{
mode: this.mode,
server: {
hmr: false,
middlewareMode: true
@@ -94,25 +89,31 @@ class AstroBuilder {
{
settings: this.settings,
logger: this.logger,
mode: "build",
mode: this.mode,
command: "build",
sync: false
sync: false,
routesList: this.routesList,
manifest
}
);
await runHookConfigDone({ settings: this.settings, logger });
const { syncInternal } = await import("../sync/index.js");
await syncInternal({
mode: this.mode,
settings: this.settings,
logger,
fs
fs,
routesList: this.routesList,
command: "build",
manifest
});
return { viteConfig };
}
/** Run the build logic. build() is marked private because usage should go through ".run()" */
async build({ viteConfig }) {
await runHookBuildStart({ config: this.settings.config, logging: this.logger });
await runHookBuildStart({ config: this.settings.config, logger: this.logger });
this.validateConfig();
this.logger.info("build", `output: ${blue('"' + this.settings.config.output + '"')}`);
this.logger.info("build", `mode: ${blue('"' + this.settings.buildOutput + '"')}`);
this.logger.info("build", `directory: ${blue(fileURLToPath(this.settings.config.outDir))}`);
if (this.settings.adapter) {
this.logger.info("build", `adapter: ${green(this.settings.adapter.name)}`);
@@ -122,7 +123,7 @@ class AstroBuilder {
const { assets, allPages } = collectPagesData({
settings: this.settings,
logger: this.logger,
manifest: this.manifest
manifest: this.routesList
});
this.logger.debug("build", timerMessage("All pages loaded", this.timer.loadStart));
const pageNames = [];
@@ -137,8 +138,8 @@ class AstroBuilder {
allPages,
settings: this.settings,
logger: this.logger,
manifest: this.manifest,
mode: this.mode,
routesList: this.routesList,
runtimeMode: this.runtimeMode,
origin: this.origin,
pageNames,
teardownCompiler: this.teardownCompiler,
@@ -146,6 +147,10 @@ class AstroBuilder {
key: keyPromise
};
const { internals, ssrOutputChunkNames } = await viteBuild(opts);
const hasServerIslands = this.settings.serverIslandNameMap.size > 0;
if (hasServerIslands && this.settings.buildOutput !== "server") {
throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands);
}
await staticBuild(opts, internals, ssrOutputChunkNames);
this.timer.assetsStart = performance.now();
Object.keys(assets).map((k) => {
@@ -157,20 +162,18 @@ class AstroBuilder {
});
this.logger.debug("build", timerMessage("Additional assets copied", this.timer.assetsStart));
await runHookBuildDone({
config: this.settings.config,
settings: this.settings,
pages: pageNames,
routes: Object.values(allPages).flat().map((pageData) => pageData.route).concat(
this.settings.config.experimental.serverIslands ? [getServerIslandRouteData(this.settings.config)] : []
),
logging: this.logger,
cacheManifest: internals.cacheManifestUsed
routes: Object.values(allPages).flat().map((pageData) => pageData.route).concat(hasServerIslands ? getServerIslandRouteData(this.settings.config) : []),
logger: this.logger
});
if (this.logger.level && levels[this.logger.level()] <= levels["info"]) {
await this.printStats({
logger: this.logger,
timeStart: this.timer.init,
pageCount: pageNames.length,
buildMode: this.settings.config.output
buildMode: this.settings.buildOutput
// buildOutput is always set at this point
});
}
}

View File

@@ -1,5 +1,5 @@
import type { Rollup } from 'vite';
import type { RouteData, SSRResult } from '../../@types/astro.js';
import type { RouteData, SSRResult } from '../../types/public/internal.js';
import type { PageBuildData, StylesheetAsset, ViteID } from './types.js';
export interface BuildInternals {
/**
@@ -10,12 +10,10 @@ export interface BuildInternals {
* build so the client can pick up the same information and use the same chunk ids.
*/
cssModuleToChunkIdMap: Map<string, string>;
hoistedScriptIdToHoistedMap: Map<string, Set<string>>;
hoistedScriptIdToPagesMap: Map<string, Set<string>>;
/**
* Used by the `directRenderScript` option. If script is inlined, its id and
* inlined code is mapped here. The resolved id is an URL like "/_astro/something.js"
* but will no longer exist as the content is now inlined in this map.
* If script is inlined, its id and inlined code is mapped here. The resolved id is
* an URL like "/_astro/something.js" but will no longer exist as the content is now
* inlined in this map.
*/
inlinedScripts: Map<string, string>;
entrySpecifierToBundleMap: Map<string, string>;
@@ -56,29 +54,24 @@ export interface BuildInternals {
*/
discoveredClientOnlyComponents: Map<string, string[]>;
/**
* A list of hoisted scripts that are discovered during the SSR build
* A list of scripts that are discovered during the SSR build.
* These will be used as the top-level entrypoints for the client build.
*/
discoveredScripts: Set<string>;
cachedClientEntries: string[];
cacheManifestUsed: boolean;
/**
* Map of propagated module ids (usually something like `/Users/...blog.mdx?astroPropagatedAssets`)
* to a set of stylesheets that it uses.
*/
propagatedStylesMap: Map<string, Set<StylesheetAsset>>;
/**
* Map of propagated module ids (usually something like `/Users/...blog.mdx?astroPropagatedAssets`)
* to a set of hoisted scripts that it uses.
*/
propagatedScriptsMap: Map<string, Set<string>>;
staticFiles: Set<string>;
clientChunksAndAssets: Set<string>;
ssrEntryChunk?: Rollup.OutputChunk;
manifestEntryChunk?: Rollup.OutputChunk;
manifestFileName?: string;
entryPoints: Map<RouteData, URL>;
componentMetadata: SSRResult['componentMetadata'];
middlewareEntryPoint?: URL;
middlewareEntryPoint: URL | undefined;
astroActionsEntryPoint: URL | undefined;
/**
* Chunks in the bundle that are only used in prerendering that we can delete later
*/
@@ -95,7 +88,7 @@ export declare function trackPageData(internals: BuildInternals, _component: str
*/
export declare function trackClientOnlyPageDatas(internals: BuildInternals, pageData: PageBuildData, clientOnlys: string[]): void;
/**
* Tracks scripts to the pages they are associated with. (experimental.directRenderScript)
* Tracks scripts to the pages they are associated with.
*/
export declare function trackScriptPageDatas(internals: BuildInternals, pageData: PageBuildData, scriptIds: string[]): void;
export declare function getPageDatasByClientOnlyID(internals: BuildInternals, viteid: ViteID): Generator<PageBuildData, void, unknown>;
@@ -106,16 +99,6 @@ export declare function getPageDatasByClientOnlyID(internals: BuildInternals, vi
* @param component The component of the page, used to identify the page
*/
export declare function getPageData(internals: BuildInternals, route: string, component: string): PageBuildData | undefined;
/**
* Map internals.pagesByKeys to a new map with the public key instead of the internal key.
* This function is only used to avoid breaking changes in the Integrations API, after we changed the way
* we identify pages, from the entrypoint component to an internal key.
* If the page component is unique -> the public key is the component path. (old behavior)
* If the page component is shared -> the public key is the internal key. (new behavior)
* The new behavior on shared entrypoint it's not a breaking change, because it was not supported before.
* @param pagesByKeys A map of all page data by their internal key
*/
export declare function getPageDatasWithPublicKey(pagesByKeys: Map<string, PageBuildData>): Map<string, PageBuildData>;
export declare function getPageDataByViteID(internals: BuildInternals, viteid: ViteID): PageBuildData | undefined;
export declare function hasPrerenderedPages(internals: BuildInternals): boolean;
interface OrderInfo {
@@ -130,10 +113,4 @@ interface OrderInfo {
*/
export declare function cssOrder(a: OrderInfo, b: OrderInfo): 1 | -1;
export declare function mergeInlineCss(acc: Array<StylesheetAsset>, current: StylesheetAsset): Array<StylesheetAsset>;
/**
* Get all pages data from the build internals, using a specific hoisted script id.
* @param internals Build Internals with all the pages
* @param id Hoisted script id, used to identify the pages using it
*/
export declare function getPageDatasByHoistedScriptId(internals: BuildInternals, id: string): PageBuildData[];
export {};

View File

@@ -2,13 +2,8 @@ import { prependForwardSlash, removeFileExtension } from "../path.js";
import { viteID } from "../util.js";
import { makePageDataKey } from "./plugins/util.js";
function createBuildInternals() {
const hoistedScriptIdToHoistedMap = /* @__PURE__ */ new Map();
const hoistedScriptIdToPagesMap = /* @__PURE__ */ new Map();
return {
cachedClientEntries: [],
cssModuleToChunkIdMap: /* @__PURE__ */ new Map(),
hoistedScriptIdToHoistedMap,
hoistedScriptIdToPagesMap,
inlinedScripts: /* @__PURE__ */ new Map(),
entrySpecifierToBundleMap: /* @__PURE__ */ new Map(),
pagesByKeys: /* @__PURE__ */ new Map(),
@@ -16,15 +11,16 @@ function createBuildInternals() {
pagesByClientOnly: /* @__PURE__ */ new Map(),
pagesByScriptId: /* @__PURE__ */ new Map(),
propagatedStylesMap: /* @__PURE__ */ new Map(),
propagatedScriptsMap: /* @__PURE__ */ new Map(),
discoveredHydratedComponents: /* @__PURE__ */ new Map(),
discoveredClientOnlyComponents: /* @__PURE__ */ new Map(),
discoveredScripts: /* @__PURE__ */ new Set(),
staticFiles: /* @__PURE__ */ new Set(),
componentMetadata: /* @__PURE__ */ new Map(),
entryPoints: /* @__PURE__ */ new Map(),
cacheManifestUsed: false,
prerenderOnlyChunks: []
prerenderOnlyChunks: [],
astroActionsEntryPoint: void 0,
middlewareEntryPoint: void 0,
clientChunksAndAssets: /* @__PURE__ */ new Set()
};
}
function trackPageData(internals, _component, pageData, componentModuleId, componentURL) {
@@ -82,32 +78,6 @@ function getPageData(internals, route, component) {
}
return void 0;
}
function getPagesDatasByComponent(internals, component) {
const pageDatas = [];
internals.pagesByKeys.forEach((pageData) => {
if (component === pageData.component) pageDatas.push(pageData);
});
return pageDatas;
}
function getPageDatasWithPublicKey(pagesByKeys) {
const pagesWithPublicKey = /* @__PURE__ */ new Map();
const pagesByComponentsArray = Array.from(pagesByKeys.values()).map((pageData) => {
return { component: pageData.component, pageData };
});
const pagesWithUniqueComponent = pagesByComponentsArray.filter((page) => {
return pagesByComponentsArray.filter((p) => p.component === page.component).length === 1;
});
pagesWithUniqueComponent.forEach((page) => {
pagesWithPublicKey.set(page.component, page.pageData);
});
const pagesWithSharedComponent = pagesByComponentsArray.filter((page) => {
return pagesByComponentsArray.filter((p) => p.component === page.component).length > 1;
});
pagesWithSharedComponent.forEach((page) => {
pagesWithPublicKey.set(page.pageData.key, page.pageData);
});
return pagesWithPublicKey;
}
function getPageDataByViteID(internals, viteid) {
if (internals.pagesByViteID.has(viteid)) {
return internals.pagesByViteID.get(viteid);
@@ -154,26 +124,12 @@ function mergeInlineCss(acc, current) {
acc.push(current);
return acc;
}
function getPageDatasByHoistedScriptId(internals, id) {
const set = internals.hoistedScriptIdToPagesMap.get(id);
const pageDatas = [];
if (set) {
for (const pageId of set) {
getPagesDatasByComponent(internals, pageId.slice(1)).forEach((pageData) => {
pageDatas.push(pageData);
});
}
}
return pageDatas;
}
export {
createBuildInternals,
cssOrder,
getPageData,
getPageDataByViteID,
getPageDatasByClientOnlyID,
getPageDatasByHoistedScriptId,
getPageDatasWithPublicKey,
hasPrerenderedPages,
mergeInlineCss,
trackClientOnlyPageDatas,

View File

@@ -1,13 +1,14 @@
import type { AstroSettings, ManifestData } from '../../@types/astro.js';
import type { AstroSettings, RoutesList } from '../../types/astro.js';
import type { Logger } from '../logger/core.js';
import type { AllPagesData } from './types.js';
export interface CollectPagesDataOptions {
interface CollectPagesDataOptions {
settings: AstroSettings;
logger: Logger;
manifest: ManifestData;
manifest: RoutesList;
}
export interface CollectPagesDataResult {
interface CollectPagesDataResult {
assets: Record<string, string>;
allPages: AllPagesData;
}
export declare function collectPagesData(opts: CollectPagesDataOptions): CollectPagesDataResult;
export {};

View File

@@ -1,11 +1,15 @@
import * as colors from "kleur/colors";
import { debug } from "../logger/core.js";
import { DEFAULT_COMPONENTS } from "../routing/default.js";
import { makePageDataKey } from "./plugins/util.js";
function collectPagesData(opts) {
const { settings, manifest } = opts;
const assets = {};
const allPages = {};
for (const route of manifest.routes) {
if (DEFAULT_COMPONENTS.some((component) => route.component === component)) {
continue;
}
const key = makePageDataKey(route.route, route.component);
if (route.pathname) {
allPages[key] = {
@@ -13,10 +17,9 @@ function collectPagesData(opts) {
component: route.component,
route,
moduleSpecifier: "",
styles: [],
hoistedScript: void 0
styles: []
};
if (settings.config.output === "static") {
if (settings.buildOutput === "static") {
const html = `${route.pathname}`.replace(/\/?$/, "/index.html");
debug(
"build",
@@ -32,8 +35,7 @@ function collectPagesData(opts) {
component: route.component,
route,
moduleSpecifier: "",
styles: [],
hoistedScript: void 0
styles: []
};
}
return { assets, allPages };

View File

@@ -1,4 +1,6 @@
import type { ComponentInstance, RewritePayload, RouteData, SSRResult } from '../../@types/astro.js';
import type { AstroSettings, ComponentInstance } from '../../types/astro.js';
import type { RewritePayload } from '../../types/public/common.js';
import type { RouteData, SSRResult } from '../../types/public/internal.js';
import type { SSRManifest } from '../app/types.js';
import type { TryRewriteResult } from '../base-pipeline.js';
import { Pipeline } from '../render/index.js';
@@ -12,8 +14,8 @@ export declare class BuildPipeline extends Pipeline {
readonly internals: BuildInternals;
readonly manifest: SSRManifest;
readonly options: StaticBuildOptions;
readonly config: import("../../@types/astro.js").AstroConfig;
readonly settings: import("../../@types/astro.js").AstroSettings;
readonly config: import("../../types/public/config.js").AstroConfig;
readonly settings: AstroSettings;
readonly defaultRoutes: {
instance: ComponentInstance;
matchesComponent(filePath: URL): boolean;
@@ -37,7 +39,7 @@ export declare class BuildPipeline extends Pipeline {
*
* @param staticBuildOptions
*/
static retrieveManifest(staticBuildOptions: StaticBuildOptions, internals: BuildInternals): Promise<SSRManifest>;
static retrieveManifest(settings: AstroSettings, internals: BuildInternals): Promise<SSRManifest>;
headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'>;
componentMetadata(): void;
/**

View File

@@ -1,20 +1,14 @@
import { getOutputDirectory } from "../../prerender/utils.js";
import { getServerOutputDirectory } from "../../prerender/utils.js";
import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
import { routeIsFallback, routeIsRedirect } from "../redirects/helpers.js";
import { RedirectSinglePageBuiltModule } from "../redirects/index.js";
import { Pipeline } from "../render/index.js";
import {
createAssetLink,
createModuleScriptsSet,
createStylesheetElementSet
} from "../render/ssr-element.js";
import { createAssetLink, createStylesheetElementSet } from "../render/ssr-element.js";
import { createDefaultRoutes } from "../routing/default.js";
import { findRouteToRewrite } from "../routing/rewrite.js";
import { isServerLikeOutput } from "../util.js";
import { getOutDirWithinCwd } from "./common.js";
import { cssOrder, getPageData, mergeInlineCss } from "./internal.js";
import { ASTRO_PAGE_MODULE_ID, ASTRO_PAGE_RESOLVED_MODULE_ID } from "./plugins/plugin-pages.js";
import { RESOLVED_SPLIT_MODULE_ID } from "./plugins/plugin-ssr.js";
import { getPagesFromVirtualModulePageName, getVirtualModulePageName } from "./plugins/util.js";
import { i18nHasFallback } from "./util.js";
class BuildPipeline extends Pipeline {
@@ -36,12 +30,12 @@ class BuildPipeline extends Pipeline {
resolveCache.set(specifier, assetLink);
return assetLink;
}
const serverLike = isServerLikeOutput(config);
const serverLike = settings.buildOutput === "server";
const streaming = serverLike;
super(
options.logger,
manifest,
options.mode,
options.runtimeMode,
manifest.renderers,
resolve,
serverLike,
@@ -61,11 +55,10 @@ class BuildPipeline extends Pipeline {
*/
#routesByFilePath = /* @__PURE__ */ new WeakMap();
get outFolder() {
const ssr = isServerLikeOutput(this.settings.config);
return ssr ? this.settings.config.build.server : getOutDirWithinCwd(this.settings.config.outDir);
return this.settings.buildOutput === "server" ? this.settings.config.build.server : getOutDirWithinCwd(this.settings.config.outDir);
}
getRoutes() {
return this.options.manifest.routes;
return this.options.routesList.routes;
}
static create({
internals,
@@ -87,9 +80,8 @@ class BuildPipeline extends Pipeline {
*
* @param staticBuildOptions
*/
static async retrieveManifest(staticBuildOptions, internals) {
const config = staticBuildOptions.settings.config;
const baseDirectory = getOutputDirectory(config);
static async retrieveManifest(settings, internals) {
const baseDirectory = getServerOutputDirectory(settings);
const manifestEntryUrl = new URL(
`${internals.manifestFileName}?time=${Date.now()}`,
baseDirectory
@@ -102,11 +94,10 @@ class BuildPipeline extends Pipeline {
}
const renderersEntryUrl = new URL(`renderers.mjs?time=${Date.now()}`, baseDirectory);
const renderers = await import(renderersEntryUrl.toString());
const middleware = internals.middlewareEntryPoint ? await import(internals.middlewareEntryPoint.toString()).then((mod) => {
return function() {
return { onRequest: mod.onRequest };
};
}) : manifest.middleware;
const middleware = internals.middlewareEntryPoint ? async function() {
const mod = await import(internals.middlewareEntryPoint.toString());
return { onRequest: mod.onRequest };
} : manifest.middleware;
if (!renderers) {
throw new Error(
"Astro couldn't find the emitted renderers. This is an internal error, please file an issue."
@@ -126,11 +117,7 @@ class BuildPipeline extends Pipeline {
} = this;
const links = /* @__PURE__ */ new Set();
const pageBuildData = getPageData(internals, routeData.route, routeData.component);
const scripts = createModuleScriptsSet(
pageBuildData?.hoistedScript ? [pageBuildData.hoistedScript] : [],
base,
assetsPrefix
);
const scripts = /* @__PURE__ */ new Set();
const sortedCssAssets = pageBuildData?.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []);
const styles = createStylesheetElementSet(sortedCssAssets ?? [], base, assetsPrefix);
if (settings.scripts.some((script) => script.stage === "page")) {
@@ -163,26 +150,15 @@ class BuildPipeline extends Pipeline {
retrieveRoutesToGenerate() {
const pages = /* @__PURE__ */ new Map();
for (const [virtualModulePageName, filePath] of this.internals.entrySpecifierToBundleMap) {
if (virtualModulePageName.includes(ASTRO_PAGE_RESOLVED_MODULE_ID) || virtualModulePageName.includes(RESOLVED_SPLIT_MODULE_ID)) {
if (virtualModulePageName.includes(ASTRO_PAGE_RESOLVED_MODULE_ID)) {
let pageDatas = [];
if (virtualModulePageName.includes(ASTRO_PAGE_RESOLVED_MODULE_ID)) {
pageDatas.push(
...getPagesFromVirtualModulePageName(
this.internals,
ASTRO_PAGE_RESOLVED_MODULE_ID,
virtualModulePageName
)
);
}
if (virtualModulePageName.includes(RESOLVED_SPLIT_MODULE_ID)) {
pageDatas.push(
...getPagesFromVirtualModulePageName(
this.internals,
RESOLVED_SPLIT_MODULE_ID,
virtualModulePageName
)
);
}
pageDatas.push(
...getPagesFromVirtualModulePageName(
this.internals,
ASTRO_PAGE_RESOLVED_MODULE_ID,
virtualModulePageName
)
);
for (const pageData of pageDatas) {
pages.set(pageData, filePath);
}
@@ -222,10 +198,11 @@ class BuildPipeline extends Pipeline {
const { routeData, pathname, newUrl } = findRouteToRewrite({
payload,
request,
routes: this.options.manifest.routes,
routes: this.options.routesList.routes,
trailingSlash: this.config.trailingSlash,
buildFormat: this.config.build.format,
base: this.config.base
base: this.config.base,
outDir: this.serverLike ? this.manifest.buildClientDir : this.manifest.outDir
});
const componentInstance = await this.getComponentByRoute(routeData);
return { routeData, componentInstance, newUrl, pathname };
@@ -236,9 +213,9 @@ class BuildPipeline extends Pipeline {
}
let entry;
if (routeIsRedirect(route)) {
entry = await this.#getEntryForRedirectRoute(route, this.internals, this.outFolder);
entry = await this.#getEntryForRedirectRoute(route, this.outFolder);
} else if (routeIsFallback(route)) {
entry = await this.#getEntryForFallbackRoute(route, this.internals, this.outFolder);
entry = await this.#getEntryForFallbackRoute(route, this.outFolder);
} else {
const ssrEntryURLPage = createEntryURL(filePath, this.outFolder);
entry = await import(ssrEntryURLPage.toString());
@@ -246,7 +223,7 @@ class BuildPipeline extends Pipeline {
this.#componentsInterner.set(route, entry);
return entry;
}
async #getEntryForFallbackRoute(route, _internals, outFolder) {
async #getEntryForFallbackRoute(route, outFolder) {
if (route.type !== "fallback") {
throw new Error(`Expected a redirect route.`);
}
@@ -260,7 +237,7 @@ class BuildPipeline extends Pipeline {
}
return RedirectSinglePageBuiltModule;
}
async #getEntryForRedirectRoute(route, _internals, outFolder) {
async #getEntryForRedirectRoute(route, outFolder) {
if (route.type !== "redirect") {
throw new Error(`Expected a redirect route.`);
}

View File

@@ -8,7 +8,7 @@ type OutputChunk = Extract<OutputChunkorAsset, {
}>;
export type BuildTarget = 'server' | 'client';
type MutateChunk = (chunk: OutputChunk, targets: BuildTarget[], newCode: string) => void;
export interface BuildBeforeHookResult {
interface BuildBeforeHookResult {
enforce?: 'after-user-plugins';
vitePlugin: VitePlugin | VitePlugin[] | undefined;
}

View File

@@ -1,11 +1,10 @@
import { astroConfigBuildPlugin } from "../../../content/vite-plugin-content-assets.js";
import { astroHeadBuildPlugin } from "../../../vite-plugin-head/index.js";
import { pluginActions } from "./plugin-actions.js";
import { pluginAnalyzer } from "./plugin-analyzer.js";
import { pluginChunks } from "./plugin-chunks.js";
import { pluginComponentEntry } from "./plugin-component-entry.js";
import { pluginContent } from "./plugin-content.js";
import { pluginCSS } from "./plugin-css.js";
import { pluginHoistedScripts } from "./plugin-hoisted-scripts.js";
import { pluginInternals } from "./plugin-internals.js";
import { pluginManifest } from "./plugin-manifest.js";
import { pluginMiddleware } from "./plugin-middleware.js";
@@ -13,27 +12,22 @@ import { pluginPages } from "./plugin-pages.js";
import { pluginPrerender } from "./plugin-prerender.js";
import { pluginRenderers } from "./plugin-renderers.js";
import { pluginScripts } from "./plugin-scripts.js";
import { pluginSSR, pluginSSRSplit } from "./plugin-ssr.js";
import { pluginSSR } from "./plugin-ssr.js";
function registerAllPlugins({ internals, options, register }) {
register(pluginComponentEntry(internals));
register(pluginAnalyzer(options, internals));
register(pluginInternals(internals));
register(pluginAnalyzer(internals));
register(pluginInternals(options, internals));
register(pluginManifest(options, internals));
register(pluginRenderers(options));
register(pluginMiddleware(options, internals));
register(pluginActions(options, internals));
register(pluginPages(options, internals));
register(pluginContent(options, internals));
register(pluginCSS(options, internals));
register(astroHeadBuildPlugin(internals));
register(pluginPrerender(options, internals));
register(astroConfigBuildPlugin(options, internals));
if (options.settings.config.experimental.directRenderScript) {
register(pluginScripts(internals));
} else {
register(pluginHoistedScripts(internals));
}
register(pluginScripts(internals));
register(pluginSSR(options, internals));
register(pluginSSRSplit(options, internals));
register(pluginChunks());
}
export {

View File

@@ -1,5 +1,4 @@
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { StaticBuildOptions } from '../types.js';
export declare function copyContentToCache(opts: StaticBuildOptions): Promise<string[]>;
export declare function pluginContent(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;
export declare function pluginActions(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;

View File

@@ -0,0 +1,16 @@
import { vitePluginActionsBuild } from "../../../actions/plugins.js";
function pluginActions(opts, internals) {
return {
targets: ["server"],
hooks: {
"build:before": () => {
return {
vitePlugin: vitePluginActionsBuild(opts, internals)
};
}
}
};
}
export {
pluginActions
};

View File

@@ -1,6 +1,3 @@
import type { Plugin as VitePlugin } from 'vite';
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { StaticBuildOptions } from '../types.js';
export declare function vitePluginAnalyzer(options: StaticBuildOptions, internals: BuildInternals): VitePlugin;
export declare function pluginAnalyzer(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;
export declare function pluginAnalyzer(internals: BuildInternals): AstroBuildPlugin;

View File

@@ -1,94 +1,13 @@
import { PROPAGATED_ASSET_FLAG } from "../../../content/consts.js";
import { prependForwardSlash } from "../../../core/path.js";
import {
getParentModuleInfos,
getTopLevelPageModuleInfos,
moduleIsTopLevelPage
} from "../graph.js";
import { getTopLevelPageModuleInfos } from "../graph.js";
import {
getPageDataByViteID,
trackClientOnlyPageDatas,
trackScriptPageDatas
} from "../internal.js";
function isPropagatedAsset(id) {
try {
return new URL("file://" + id).searchParams.has(PROPAGATED_ASSET_FLAG);
} catch {
return false;
}
}
function vitePluginAnalyzer(options, internals) {
function hoistedScriptScanner() {
const uniqueHoistedIds = /* @__PURE__ */ new Map();
const pageScriptsMap = /* @__PURE__ */ new Map();
return {
async scan(scripts, from) {
const hoistedScripts = /* @__PURE__ */ new Set();
for (let i = 0; i < scripts.length; i++) {
const hid = `${from.replace("/@fs", "")}?astro&type=script&index=${i}&lang.ts`;
hoistedScripts.add(hid);
}
if (hoistedScripts.size) {
for (const parentInfo of getParentModuleInfos(from, this, isPropagatedAsset)) {
if (isPropagatedAsset(parentInfo.id)) {
if (!internals.propagatedScriptsMap.has(parentInfo.id)) {
internals.propagatedScriptsMap.set(parentInfo.id, /* @__PURE__ */ new Set());
}
const propagatedScripts = internals.propagatedScriptsMap.get(parentInfo.id);
for (const hid of hoistedScripts) {
propagatedScripts.add(hid);
}
} else if (moduleIsTopLevelPage(parentInfo)) {
if (!pageScriptsMap.has(parentInfo.id)) {
pageScriptsMap.set(parentInfo.id, {
hoistedSet: /* @__PURE__ */ new Set()
});
}
const pageScripts = pageScriptsMap.get(parentInfo.id);
for (const hid of hoistedScripts) {
pageScripts.hoistedSet.add(hid);
}
}
}
}
},
finalize() {
for (const propagatedScripts of internals.propagatedScriptsMap.values()) {
for (const propagatedScript of propagatedScripts) {
internals.discoveredScripts.add(propagatedScript);
}
}
for (const [pageId, { hoistedSet }] of pageScriptsMap) {
const pageData = getPageDataByViteID(internals, pageId);
if (!pageData) continue;
const { component } = pageData;
const astroModuleId = prependForwardSlash(component);
const uniqueHoistedId = JSON.stringify(Array.from(hoistedSet).sort());
let moduleId;
if (uniqueHoistedIds.has(uniqueHoistedId)) {
moduleId = uniqueHoistedIds.get(uniqueHoistedId);
} else {
moduleId = `/astro/hoisted.js?q=${uniqueHoistedIds.size}`;
uniqueHoistedIds.set(uniqueHoistedId, moduleId);
}
internals.discoveredScripts.add(moduleId);
if (internals.hoistedScriptIdToPagesMap.has(moduleId)) {
const pages = internals.hoistedScriptIdToPagesMap.get(moduleId);
pages.add(astroModuleId);
} else {
internals.hoistedScriptIdToPagesMap.set(moduleId, /* @__PURE__ */ new Set([astroModuleId]));
internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedSet);
}
}
}
};
}
function vitePluginAnalyzer(internals) {
return {
name: "@astro/rollup-plugin-astro-analyzer",
async generateBundle() {
const hoistScanner = options.settings.config.experimental.directRenderScript ? { scan: async () => {
}, finalize: () => {
} } : hoistedScriptScanner();
const ids = this.getModuleIds();
for (const id of ids) {
const info = this.getModuleInfo(id);
@@ -103,7 +22,6 @@ function vitePluginAnalyzer(options, internals) {
internals.discoveredHydratedComponents.set(rid, [c.exportName]);
}
}
await hoistScanner.scan.call(this, astro.scripts, id);
if (astro.clientOnlyComponents.length) {
const clientOnlys = [];
for (const c of astro.clientOnlyComponents) {
@@ -126,7 +44,7 @@ function vitePluginAnalyzer(options, internals) {
trackClientOnlyPageDatas(internals, newPageData, clientOnlys);
}
}
if (options.settings.config.experimental.directRenderScript && astro.scripts.length) {
if (astro.scripts.length) {
const scriptIds = astro.scripts.map(
(_, i) => `${id.replace("/@fs", "")}?astro&type=script&index=${i}&lang.ts`
);
@@ -140,23 +58,21 @@ function vitePluginAnalyzer(options, internals) {
}
}
}
hoistScanner.finalize();
}
};
}
function pluginAnalyzer(options, internals) {
function pluginAnalyzer(internals) {
return {
targets: ["server"],
hooks: {
"build:before": () => {
return {
vitePlugin: vitePluginAnalyzer(options, internals)
vitePlugin: vitePluginAnalyzer(internals)
};
}
}
};
}
export {
pluginAnalyzer,
vitePluginAnalyzer
pluginAnalyzer
};

View File

@@ -1,4 +1,2 @@
import type { Plugin as VitePlugin } from 'vite';
import type { AstroBuildPlugin } from '../plugin.js';
export declare function vitePluginChunks(): VitePlugin;
export declare function pluginChunks(): AstroBuildPlugin;

View File

@@ -11,9 +11,6 @@ function vitePluginChunks() {
if (id.includes("astro/dist/runtime")) {
return "astro";
}
if (id.includes("astro/dist/env/setup")) {
return "astro/env-setup";
}
}
});
}
@@ -32,6 +29,5 @@ function pluginChunks() {
};
}
export {
pluginChunks,
vitePluginChunks
pluginChunks
};

View File

@@ -1,12 +1,4 @@
import type { Plugin as VitePlugin } from 'vite';
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
export declare const astroEntryPrefix = "\0astro-entry:";
/**
* When adding hydrated or client:only components as Rollup inputs, sometimes we're not using all
* of the export names, e.g. `import { Counter } from './ManyComponents.jsx'`. This plugin proxies
* entries to re-export only the names the user is using.
*/
export declare function vitePluginComponentEntry(internals: BuildInternals): VitePlugin;
export declare function normalizeEntryId(id: string): string;
export declare function pluginComponentEntry(internals: BuildInternals): AstroBuildPlugin;

View File

@@ -44,7 +44,9 @@ function vitePluginComponentEntry(internals) {
const componentId = id.slice(astroEntryPrefix.length);
const exportNames = componentToExportNames.get(componentId);
if (exportNames) {
return `export { ${exportNames.join(", ")} } from ${JSON.stringify(componentId)}`;
return {
code: `export { ${exportNames.join(", ")} } from ${JSON.stringify(componentId)}`
};
}
}
}
@@ -66,8 +68,6 @@ function pluginComponentEntry(internals) {
};
}
export {
astroEntryPrefix,
normalizeEntryId,
pluginComponentEntry,
vitePluginComponentEntry
pluginComponentEntry
};

View File

@@ -1,394 +0,0 @@
import { createHash } from "node:crypto";
import fsMod from "node:fs";
import { fileURLToPath } from "node:url";
import glob from "fast-glob";
import pLimit from "p-limit";
import { normalizePath } from "vite";
import { CONTENT_RENDER_FLAG, PROPAGATED_ASSET_FLAG } from "../../../content/consts.js";
import { hasContentFlag } from "../../../content/utils.js";
import {
generateContentEntryFile,
generateLookupMap
} from "../../../content/vite-plugin-content-virtual-mod.js";
import { configPaths } from "../../config/index.js";
import { emptyDir } from "../../fs/index.js";
import {
appendForwardSlash,
joinPaths,
removeFileExtension,
removeLeadingForwardSlash
} from "../../path.js";
import { isContentCollectionsCacheEnabled } from "../../util.js";
import { addRollupInput } from "../add-rollup-input.js";
import { CHUNKS_PATH, CONTENT_PATH } from "../consts.js";
import { copyFiles } from "../static-build.js";
import { encodeName } from "../util.js";
import { extendManualChunks } from "./util.js";
const CONTENT_CACHE_DIR = "./" + CONTENT_PATH;
const CONTENT_MANIFEST_FILE = "./manifest.json";
const CONTENT_MANIFEST_VERSION = 1;
const virtualEmptyModuleId = `virtual:empty-content`;
const resolvedVirtualEmptyModuleId = `\0${virtualEmptyModuleId}`;
const NO_MANIFEST_VERSION = -1;
function createContentManifest() {
return {
version: NO_MANIFEST_VERSION,
entries: [],
serverEntries: [],
clientEntries: [],
lockfiles: "",
configs: ""
};
}
const getContentRoot = (config) => new URL("./content/", config.outDir);
const getContentCacheDir = (config) => new URL(CONTENT_CACHE_DIR, config.cacheDir);
const getCacheTmp = (contentCacheDir) => new URL("./.tmp/", contentCacheDir);
function vitePluginContent(opts, lookupMap, internals, cachedBuildOutput) {
const { config } = opts.settings;
const distContentRoot = getContentRoot(config);
const contentCacheDir = getContentCacheDir(config);
const contentManifestFile = new URL(CONTENT_MANIFEST_FILE, contentCacheDir);
let oldManifest = createContentManifest();
let newManifest = createContentManifest();
let entries;
let injectedEmptyFile = false;
let currentManifestState = "valid";
if (fsMod.existsSync(contentManifestFile)) {
try {
const data = fsMod.readFileSync(contentManifestFile, { encoding: "utf8" });
oldManifest = JSON.parse(data);
} catch {
}
}
return {
name: "@astro/plugin-build-content",
async options(options) {
let newOptions = Object.assign({}, options);
newManifest = await generateContentManifest(opts, lookupMap);
entries = getEntriesFromManifests(oldManifest, newManifest);
currentManifestState = manifestState(oldManifest, newManifest);
if (currentManifestState === "valid") {
internals.cachedClientEntries = oldManifest.clientEntries;
} else {
let logReason = "";
switch (currentManifestState) {
case "config-mismatch":
logReason = "Astro config has changed";
break;
case "lockfile-mismatch":
logReason = "Lockfiles have changed";
break;
case "no-entries":
logReason = "No content collections entries cached";
break;
case "version-mismatch":
logReason = "The cache manifest version has changed";
break;
case "no-manifest":
logReason = "No content manifest was found in the cache";
break;
}
opts.logger.info("build", `Cache invalid, rebuilding from source. Reason: ${logReason}.`);
}
for (const { type, entry } of entries.buildFromSource) {
const fileURL = encodeURI(joinPaths(opts.settings.config.root.toString(), entry));
const input = fileURLToPath(fileURL);
const inputs = [`${input}?${collectionTypeToFlag(type)}`];
if (type === "content") {
inputs.push(`${input}?${CONTENT_RENDER_FLAG}`);
}
newOptions = addRollupInput(newOptions, inputs);
}
if (currentManifestState === "valid") {
for (const { cached, dist } of cachedBuildOutput) {
if (fsMod.existsSync(cached)) {
await copyFiles(cached, dist, true);
}
}
const cacheExists = fsMod.existsSync(contentCacheDir);
if (cacheExists) {
await copyFiles(contentCacheDir, distContentRoot, false);
}
}
if (entries.buildFromSource.length === 0) {
newOptions = addRollupInput(newOptions, [virtualEmptyModuleId]);
injectedEmptyFile = true;
}
return newOptions;
},
outputOptions(outputOptions) {
const rootPath = normalizePath(fileURLToPath(opts.settings.config.root));
const srcPath = normalizePath(fileURLToPath(opts.settings.config.srcDir));
const entryCache = /* @__PURE__ */ new Map();
extendManualChunks(outputOptions, {
before(id, meta) {
if (id.startsWith(srcPath) && id.slice(srcPath.length).startsWith("content")) {
const info = meta.getModuleInfo(id);
if (info?.dynamicImporters.length === 1 && hasContentFlag(info.dynamicImporters[0], PROPAGATED_ASSET_FLAG)) {
const [srcRelativePath2] = id.replace(rootPath, "/").split("?");
const resultId = encodeName(
`${removeLeadingForwardSlash(removeFileExtension(srcRelativePath2))}.render.mjs`
);
return resultId;
}
const [srcRelativePath, flag] = id.replace(rootPath, "/").split("?");
const collectionEntry = findEntryFromSrcRelativePath(
lookupMap,
srcRelativePath,
entryCache
);
if (collectionEntry) {
let suffix = ".mjs";
if (flag === PROPAGATED_ASSET_FLAG) {
suffix = ".entry.mjs";
}
id = removeLeadingForwardSlash(
removeFileExtension(encodeName(id.replace(srcPath, "/")))
) + suffix;
return id;
}
}
}
});
},
resolveId(id) {
if (id === virtualEmptyModuleId) {
return resolvedVirtualEmptyModuleId;
}
},
async load(id) {
if (id === resolvedVirtualEmptyModuleId) {
return {
code: `// intentionally left empty!
export default {}`
};
}
},
async generateBundle(_options, bundle) {
const code = await generateContentEntryFile({
settings: opts.settings,
fs: fsMod,
lookupMap,
IS_DEV: false,
IS_SERVER: false,
isClient: false
});
this.emitFile({
type: "prebuilt-chunk",
code,
fileName: "content/entry.mjs"
});
if (!injectedEmptyFile) return;
Object.keys(bundle).forEach((key) => {
const mod = bundle[key];
if (mod.type === "asset") return;
if (mod.facadeModuleId === resolvedVirtualEmptyModuleId) {
delete bundle[key];
}
});
},
async writeBundle() {
const clientComponents = /* @__PURE__ */ new Set([
...oldManifest.clientEntries,
...internals.discoveredHydratedComponents.keys(),
...internals.discoveredClientOnlyComponents.keys(),
...internals.discoveredScripts
]);
const serverComponents = /* @__PURE__ */ new Set([
...oldManifest.serverEntries,
...internals.discoveredHydratedComponents.keys()
]);
newManifest.serverEntries = Array.from(serverComponents);
newManifest.clientEntries = Array.from(clientComponents);
const cacheExists = fsMod.existsSync(contentCacheDir);
if (cacheExists && currentManifestState !== "valid") {
emptyDir(contentCacheDir);
}
await fsMod.promises.mkdir(contentCacheDir, { recursive: true });
await fsMod.promises.writeFile(contentManifestFile, JSON.stringify(newManifest), {
encoding: "utf8"
});
}
};
}
function findEntryFromSrcRelativePath(lookupMap, srcRelativePath, entryCache) {
let value = entryCache.get(srcRelativePath);
if (value) return value;
for (const collection of Object.values(lookupMap)) {
for (const entry of Object.values(collection)) {
for (const entryFile of Object.values(entry)) {
if (entryFile === srcRelativePath) {
value = entryFile;
entryCache.set(srcRelativePath, entryFile);
return value;
}
}
}
}
}
function getEntriesFromManifests(oldManifest, newManifest) {
const { entries: oldEntries } = oldManifest;
const { entries: newEntries } = newManifest;
let entries = { restoreFromCache: [], buildFromSource: [] };
const newEntryMap = new Map(newEntries);
if (manifestState(oldManifest, newManifest) !== "valid") {
entries.buildFromSource = Array.from(newEntryMap.keys());
return entries;
}
const oldEntryHashMap = new Map(
oldEntries.map(([key, hash]) => [hash, key])
);
for (const [entry, hash] of newEntryMap) {
if (oldEntryHashMap.has(hash)) {
entries.restoreFromCache.push(entry);
} else {
entries.buildFromSource.push(entry);
}
}
return entries;
}
function manifestState(oldManifest, newManifest) {
if (oldManifest.version === NO_MANIFEST_VERSION) {
return "no-manifest";
}
if (oldManifest.version !== newManifest.version) {
return "version-mismatch";
}
if (oldManifest.entries.length === 0) {
return "no-entries";
}
if (oldManifest.lockfiles !== newManifest.lockfiles || newManifest.lockfiles === "") {
return "lockfile-mismatch";
}
if (oldManifest.configs !== newManifest.configs) {
return "config-mismatch";
}
return "valid";
}
async function generateContentManifest(opts, lookupMap) {
let manifest = createContentManifest();
manifest.version = CONTENT_MANIFEST_VERSION;
const limit = pLimit(10);
const promises = [];
for (const [collection, { type, entries }] of Object.entries(lookupMap)) {
for (const entry of Object.values(entries)) {
const key = { collection, type, entry };
const fileURL = new URL(encodeURI(joinPaths(opts.settings.config.root.toString(), entry)));
promises.push(
limit(async () => {
const data = await fsMod.promises.readFile(fileURL, { encoding: "utf8" });
manifest.entries.push([key, checksum(data, fileURL.toString())]);
})
);
}
}
const [lockfiles, configs] = await Promise.all([
lockfilesHash(opts.settings.config.root),
configHash(opts.settings.config.root)
]);
manifest.lockfiles = lockfiles;
manifest.configs = configs;
await Promise.all(promises);
return manifest;
}
async function pushBufferInto(fileURL, buffers) {
try {
const handle = await fsMod.promises.open(fileURL, "r");
const data = await handle.readFile();
buffers.push(data);
await handle.close();
} catch {
}
}
async function lockfilesHash(root) {
const lockfiles = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb"];
const datas = [];
const promises = [];
for (const lockfileName of lockfiles) {
const fileURL = new URL(`./${lockfileName}`, root);
promises.push(pushBufferInto(fileURL, datas));
}
await Promise.all(promises);
return checksum(...datas);
}
async function configHash(root) {
const configFileNames = configPaths;
for (const configPath of configFileNames) {
try {
const fileURL = new URL(`./${configPath}`, root);
const data = await fsMod.promises.readFile(fileURL);
const hash = checksum(data);
return hash;
} catch {
}
}
return checksum(`export default {}`);
}
function checksum(...datas) {
const hash = createHash("sha1");
datas.forEach((data) => hash.update(data));
return hash.digest("base64");
}
function collectionTypeToFlag(type) {
const name = type[0].toUpperCase() + type.slice(1);
return `astro${name}CollectionEntry`;
}
async function copyContentToCache(opts) {
const { config } = opts.settings;
const distContentRoot = getContentRoot(config);
const contentCacheDir = getContentCacheDir(config);
const cacheTmp = getCacheTmp(contentCacheDir);
await fsMod.promises.mkdir(cacheTmp, { recursive: true });
await copyFiles(distContentRoot, cacheTmp, true);
await copyFiles(cacheTmp, contentCacheDir);
let files = [];
await Promise.all([
glob(`**/*.{mjs,json}`, {
cwd: fileURLToPath(cacheTmp)
}).then((f) => files.push(...f.map((file) => CONTENT_PATH + file))),
glob(`**/*.{mjs,json}`, {
cwd: fileURLToPath(new URL("./" + CHUNKS_PATH, config.outDir))
}).then((f) => files.push(...f.map((file) => CHUNKS_PATH + file)))
]);
await fsMod.promises.rm(cacheTmp, { recursive: true, force: true });
return files;
}
function pluginContent(opts, internals) {
const { cacheDir, outDir } = opts.settings.config;
const chunksFolder = "./" + CHUNKS_PATH;
const assetsFolder = "./" + appendForwardSlash(opts.settings.config.build.assets);
const cachedBuildOutput = [
{ cached: new URL(chunksFolder, cacheDir), dist: new URL(chunksFolder, outDir) },
{ cached: new URL(assetsFolder, cacheDir), dist: new URL(assetsFolder, outDir) }
];
return {
targets: ["server"],
hooks: {
async "build:before"() {
if (!isContentCollectionsCacheEnabled(opts.settings.config)) {
return { vitePlugin: void 0 };
}
const lookupMap = await generateLookupMap({ settings: opts.settings, fs: fsMod });
return {
vitePlugin: vitePluginContent(opts, lookupMap, internals, cachedBuildOutput)
};
},
async "build:post"() {
if (!isContentCollectionsCacheEnabled(opts.settings.config)) {
return;
}
const promises = [];
for (const { cached, dist } of cachedBuildOutput) {
if (fsMod.existsSync(dist)) {
promises.push(copyFiles(dist, cached, true));
}
}
if (promises.length) await Promise.all(promises);
}
}
};
}
export {
copyContentToCache,
pluginContent
};

View File

@@ -1,16 +1,12 @@
import { isBuildableCSSRequest } from "../../../vite-plugin-astro-server/util.js";
import { hasAssetPropagationFlag } from "../../../content/index.js";
import { isBuildableCSSRequest } from "../../../vite-plugin-astro-server/util.js";
import * as assetName from "../css-asset-name.js";
import {
getParentExtendedModuleInfos,
getParentModuleInfos,
moduleIsTopLevelPage
} from "../graph.js";
import {
getPageDataByViteID,
getPageDatasByClientOnlyID,
getPageDatasByHoistedScriptId
} from "../internal.js";
import { getPageDataByViteID, getPageDatasByClientOnlyID } from "../internal.js";
import { extendManualChunks, shouldInlineAsset } from "./util.js";
function pluginCSS(options, internals) {
return {
@@ -93,18 +89,10 @@ function rollupPluginAstroBuildCSS(options) {
appendCSSToPage(pageData, meta, pagesToCss, depth, order);
}
} else if (options.target === "client") {
if (buildOptions.settings.config.experimental.directRenderScript) {
const pageDatas = internals.pagesByScriptId.get(pageInfo.id);
if (pageDatas) {
for (const pageData of pageDatas) {
appendCSSToPage(pageData, meta, pagesToCss, -1, order);
}
}
} else {
if (internals.hoistedScriptIdToPagesMap.has(pageInfo.id)) {
for (const pageData of getPageDatasByHoistedScriptId(internals, pageInfo.id)) {
appendCSSToPage(pageData, meta, pagesToCss, -1, order);
}
const pageDatas = internals.pagesByScriptId.get(pageInfo.id);
if (pageDatas) {
for (const pageData of pageDatas) {
appendCSSToPage(pageData, meta, pagesToCss, -1, order);
}
}
}
@@ -113,22 +101,6 @@ function rollupPluginAstroBuildCSS(options) {
}
}
};
const cssScopeToPlugin = {
name: "astro:rollup-plugin-css-scope-to",
renderChunk(_, chunk, __, meta) {
for (const id in chunk.modules) {
const modMeta = this.getModuleInfo(id)?.meta;
const cssScopeTo = modMeta?.astroCss?.cssScopeTo;
if (cssScopeTo && !isCssScopeToRendered(cssScopeTo, Object.values(meta.chunks))) {
delete chunk.modules[id];
const moduleIdsIndex = chunk.moduleIds.indexOf(id);
if (moduleIdsIndex > -1) {
chunk.moduleIds.splice(moduleIdsIndex, 1);
}
}
}
}
};
const singleCssPlugin = {
name: "astro:rollup-plugin-single-css",
enforce: "post",
@@ -190,7 +162,7 @@ function rollupPluginAstroBuildCSS(options) {
});
}
};
return [cssBuildPlugin, cssScopeToPlugin, singleCssPlugin, inlineStylesheetsPlugin];
return [cssBuildPlugin, singleCssPlugin, inlineStylesheetsPlugin];
}
function* getParentClientOnlys(id, ctx, internals) {
for (const info of getParentModuleInfos(id, ctx)) {
@@ -215,16 +187,6 @@ function appendCSSToPage(pageData, meta, pagesToCss, depth, order) {
}
}
}
function isCssScopeToRendered(cssScopeTo, chunks) {
for (const moduleId in cssScopeTo) {
const exports = cssScopeTo[moduleId];
const renderedModule = chunks.find((c) => c.moduleIds.includes(moduleId))?.modules[moduleId];
if (renderedModule?.renderedExports.some((e) => exports.includes(e))) {
return true;
}
}
return false;
}
export {
pluginCSS
};

View File

@@ -1,5 +0,0 @@
import type { Plugin as VitePlugin } from 'vite';
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
export declare function vitePluginHoistedScripts(internals: BuildInternals): VitePlugin;
export declare function pluginHoistedScripts(internals: BuildInternals): AstroBuildPlugin;

View File

@@ -1,83 +0,0 @@
import { getPageDatasByHoistedScriptId } from "../internal.js";
import { shouldInlineAsset } from "./util.js";
function virtualHoistedEntry(id) {
return id.startsWith("/astro/hoisted.js?q=");
}
function vitePluginHoistedScripts(internals) {
let assetsInlineLimit;
return {
name: "@astro/rollup-plugin-astro-hoisted-scripts",
configResolved(config) {
assetsInlineLimit = config.build.assetsInlineLimit;
},
resolveId(id) {
if (virtualHoistedEntry(id)) {
return id;
}
},
load(id) {
if (virtualHoistedEntry(id)) {
let code = "";
for (let path of internals.hoistedScriptIdToHoistedMap.get(id)) {
let importPath = path;
if (importPath.startsWith("/@fs")) {
importPath = importPath.slice("/@fs".length);
}
code += `import "${importPath}";`;
}
return {
code
};
}
return void 0;
},
async generateBundle(_options, bundle) {
const considerInlining = /* @__PURE__ */ new Map();
const importedByOtherScripts = /* @__PURE__ */ new Set();
Object.entries(bundle).forEach(([id, output]) => {
if (output.type === "chunk" && output.facadeModuleId && virtualHoistedEntry(output.facadeModuleId)) {
considerInlining.set(id, output);
output.imports.forEach((imported) => importedByOtherScripts.add(imported));
}
});
for (const [id, output] of considerInlining.entries()) {
const canBeInlined = importedByOtherScripts.has(output.fileName) === false && output.imports.length === 0 && output.dynamicImports.length === 0 && shouldInlineAsset(output.code, output.fileName, assetsInlineLimit);
let removeFromBundle = false;
const facadeId = output.facadeModuleId;
for (const pageData of getPageDatasByHoistedScriptId(internals, facadeId)) {
if (canBeInlined) {
pageData.hoistedScript = {
type: "inline",
value: output.code
};
removeFromBundle = true;
} else {
pageData.hoistedScript = {
type: "external",
value: id
};
}
}
if (removeFromBundle) {
delete bundle[id];
}
}
}
};
}
function pluginHoistedScripts(internals) {
return {
targets: ["client"],
hooks: {
"build:before": () => {
return {
vitePlugin: vitePluginHoistedScripts(internals)
};
}
}
};
}
export {
pluginHoistedScripts,
vitePluginHoistedScripts
};

View File

@@ -1,5 +1,4 @@
import type { Plugin as VitePlugin } from 'vite';
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
export declare function vitePluginInternals(input: Set<string>, internals: BuildInternals): VitePlugin;
export declare function pluginInternals(internals: BuildInternals): AstroBuildPlugin;
import type { StaticBuildOptions } from '../types.js';
export declare function pluginInternals(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;

View File

@@ -1,5 +1,5 @@
import { normalizeEntryId } from "./plugin-component-entry.js";
function vitePluginInternals(input, internals) {
function vitePluginInternals(input, opts, internals) {
return {
name: "@astro/plugin-build-internals",
config(config, options) {
@@ -35,7 +35,10 @@ function vitePluginInternals(input, internals) {
);
}
await Promise.all(promises);
for (const [, chunk] of Object.entries(bundle)) {
for (const [_, chunk] of Object.entries(bundle)) {
if (chunk.fileName.startsWith(opts.settings.config.build.assets)) {
internals.clientChunksAndAssets.add(chunk.fileName);
}
if (chunk.type === "chunk" && chunk.facadeModuleId) {
const specifiers = mapping.get(chunk.facadeModuleId) || /* @__PURE__ */ new Set([chunk.facadeModuleId]);
for (const specifier of specifiers) {
@@ -46,19 +49,18 @@ function vitePluginInternals(input, internals) {
}
};
}
function pluginInternals(internals) {
function pluginInternals(options, internals) {
return {
targets: ["client", "server"],
hooks: {
"build:before": ({ input }) => {
return {
vitePlugin: vitePluginInternals(input, internals)
vitePlugin: vitePluginInternals(input, options, internals)
};
}
}
};
}
export {
pluginInternals,
vitePluginInternals
pluginInternals
};

View File

@@ -1,12 +1,27 @@
import { fileURLToPath } from "node:url";
import glob from "fast-glob";
import { resolve as importMetaResolve } from "import-meta-resolve";
import { glob } from "tinyglobby";
import { builtinDrivers } from "unstorage";
import { getAssetsPrefix } from "../../../assets/utils/getAssetsPrefix.js";
import { normalizeTheLocale } from "../../../i18n/index.js";
import { toFallbackType, toRoutingStrategy } from "../../../i18n/utils.js";
import { runHookBuildSsr } from "../../../integrations/hooks.js";
import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from "../../../vite-plugin-scripts/index.js";
import {
getAlgorithm,
getDirectives,
getScriptHashes,
getScriptResources,
getStrictDynamic,
getStyleHashes,
getStyleResources,
shouldTrackCspHashes,
trackScriptHashes,
trackStyleHashes
} from "../../csp/common.js";
import { encodeKey } from "../../encryption.js";
import { fileExtension, joinPaths, prependForwardSlash } from "../../path.js";
import { DEFAULT_COMPONENTS } from "../../routing/default.js";
import { serializeRouteData } from "../../routing/index.js";
import { addRollupInput } from "../add-rollup-input.js";
import { getOutFile, getOutFolder } from "../common.js";
@@ -16,7 +31,23 @@ const manifestReplace = "@@ASTRO_MANIFEST_REPLACE@@";
const replaceExp = new RegExp(`['"]${manifestReplace}['"]`, "g");
const SSR_MANIFEST_VIRTUAL_MODULE_ID = "@astrojs-manifest";
const RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID = "\0" + SSR_MANIFEST_VIRTUAL_MODULE_ID;
function vitePluginManifest(_options, internals) {
function resolveSessionDriver(driver) {
if (!driver) {
return null;
}
try {
if (driver === "fs") {
return importMetaResolve(builtinDrivers.fsLite, import.meta.url);
}
if (driver in builtinDrivers) {
return importMetaResolve(builtinDrivers[driver], import.meta.url);
}
} catch {
return null;
}
return driver;
}
function vitePluginManifest(options, internals) {
return {
name: "@astro/plugin-build-manifest",
enforce: "post",
@@ -33,18 +64,20 @@ function vitePluginManifest(_options, internals) {
return Date.now().toString();
}
},
async load(id) {
load(id) {
if (id === RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID) {
const imports = [
`import { deserializeManifest as _deserializeManifest } from 'astro/app'`,
`import { _privateSetManifestDontUseThis } from 'astro:ssr-manifest'`
];
const resolvedDriver = resolveSessionDriver(options.settings.config.session?.driver);
const contents = [
`const manifest = _deserializeManifest('${manifestReplace}');`,
`if (manifest.sessionConfig) manifest.sessionConfig.driverModule = ${resolvedDriver ? `() => import(${JSON.stringify(resolvedDriver)})` : "null"};`,
`_privateSetManifestDontUseThis(manifest);`
];
const exports = [`export { manifest }`];
return [...imports, ...contents, ...exports].join("\n");
return { code: [...imports, ...contents, ...exports].join("\n") };
}
},
async generateBundle(_opts, bundle) {
@@ -105,7 +138,7 @@ async function createManifest(buildOpts, internals) {
}
const staticFiles = internals.staticFiles;
const encodedKey = await encodeKey(await buildOpts.key);
return buildManifest(buildOpts, internals, Array.from(staticFiles), encodedKey);
return await buildManifest(buildOpts, internals, Array.from(staticFiles), encodedKey);
}
function injectManifest(manifest, chunk) {
const code = chunk.code;
@@ -113,7 +146,7 @@ function injectManifest(manifest, chunk) {
return JSON.stringify(manifest);
});
}
function buildManifest(opts, internals, staticFiles, encodedKey) {
async function buildManifest(opts, internals, staticFiles, encodedKey) {
const { settings } = opts;
const routes = [];
const domainLookupTable = {};
@@ -129,10 +162,22 @@ function buildManifest(opts, internals, staticFiles, encodedKey) {
return prependForwardSlash(joinPaths(settings.config.base, pth));
}
};
for (const route of opts.manifest.routes) {
for (const route of opts.routesList.routes) {
if (!DEFAULT_COMPONENTS.find((component) => route.component === component)) {
continue;
}
routes.push({
file: "",
links: [],
scripts: [],
styles: [],
routeData: serializeRouteData(route, settings.config.trailingSlash)
});
}
for (const route of opts.routesList.routes) {
if (!route.prerender) continue;
if (!route.pathname) continue;
const outFolder = getOutFolder(opts.settings.config, route.pathname, route);
const outFolder = getOutFolder(opts.settings, route.pathname, route);
const outFile = getOutFile(opts.settings.config, outFolder, route.pathname, route);
const file = outFile.toString().replace(opts.settings.config.build.client.toString(), "");
routes.push({
@@ -144,20 +189,14 @@ function buildManifest(opts, internals, staticFiles, encodedKey) {
});
staticFiles.push(file);
}
for (const route of opts.manifest.routes) {
const needsStaticHeaders = settings.adapter?.adapterFeatures?.experimentalStaticHeaders ?? false;
for (const route of opts.routesList.routes) {
const pageData = internals.pagesByKeys.get(makePageDataKey(route.route, route.component));
if (route.prerender || !pageData) continue;
const scripts = [];
if (pageData.hoistedScript) {
const shouldPrefixAssetPath = pageData.hoistedScript.type === "external";
const hoistedValue = pageData.hoistedScript.value;
const value = shouldPrefixAssetPath ? prefixAssetPath(hoistedValue) : hoistedValue;
scripts.unshift(
Object.assign({}, pageData.hoistedScript, {
value
})
);
if (!pageData) continue;
if (route.prerender && !needsStaticHeaders) {
continue;
}
const scripts = [];
if (settings.scripts.some((script) => script.stage === "page")) {
const src = entryModules[PAGE_SCRIPT_ID];
scripts.push({
@@ -198,12 +237,41 @@ function buildManifest(opts, internals, staticFiles, encodedKey) {
domainLookupTable
};
}
let csp = void 0;
if (shouldTrackCspHashes(settings.config.experimental.csp)) {
const algorithm = getAlgorithm(settings.config.experimental.csp);
const scriptHashes = [
...getScriptHashes(settings.config.experimental.csp),
...await trackScriptHashes(internals, settings, algorithm)
];
const styleHashes = [
...getStyleHashes(settings.config.experimental.csp),
...await trackStyleHashes(internals, settings, algorithm)
];
csp = {
cspDestination: settings.adapter?.adapterFeatures?.experimentalStaticHeaders ? "adapter" : void 0,
scriptHashes,
scriptResources: getScriptResources(settings.config.experimental.csp),
styleHashes,
styleResources: getStyleResources(settings.config.experimental.csp),
algorithm,
directives: getDirectives(settings.config.experimental.csp),
isStrictDynamic: getStrictDynamic(settings.config.experimental.csp)
};
}
return {
hrefRoot: opts.settings.config.root.toString(),
cacheDir: opts.settings.config.cacheDir.toString(),
outDir: opts.settings.config.outDir.toString(),
srcDir: opts.settings.config.srcDir.toString(),
publicDir: opts.settings.config.publicDir.toString(),
buildClientDir: opts.settings.config.build.client.toString(),
buildServerDir: opts.settings.config.build.server.toString(),
adapterName: opts.settings.adapter?.name ?? "",
routes,
site: settings.config.site,
base: settings.config.base,
userAssetsBase: settings.config?.vite?.base,
trailingSlash: settings.config.trailingSlash,
compressHTML: settings.config.compressHTML,
assetsPrefix: settings.config.build.assetsPrefix,
@@ -215,10 +283,11 @@ function buildManifest(opts, internals, staticFiles, encodedKey) {
assets: staticFiles.map(prefixAssetPath),
i18n: i18nManifest,
buildFormat: settings.config.build.format,
checkOrigin: settings.config.security?.checkOrigin ?? false,
checkOrigin: (settings.config.security?.checkOrigin && settings.buildOutput === "server") ?? false,
serverIslandNameMap: Array.from(settings.serverIslandNameMap),
key: encodedKey,
experimentalEnvGetSecretEnabled: settings.config.experimental.env !== void 0 && (settings.adapter?.supportedAstroFeatures.envGetSecret ?? "unsupported") !== "unsupported"
sessionConfig: settings.config.session,
csp
};
}
export {

View File

@@ -1,5 +1,4 @@
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { StaticBuildOptions } from '../types.js';
export { MIDDLEWARE_MODULE_ID } from '../../middleware/vite-plugin.js';
export declare function pluginMiddleware(opts: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;

View File

@@ -1,5 +1,4 @@
import { vitePluginMiddlewareBuild } from "../../middleware/vite-plugin.js";
import { MIDDLEWARE_MODULE_ID } from "../../middleware/vite-plugin.js";
function pluginMiddleware(opts, internals) {
return {
targets: ["server"],
@@ -13,6 +12,5 @@ function pluginMiddleware(opts, internals) {
};
}
export {
MIDDLEWARE_MODULE_ID,
pluginMiddleware
};

View File

@@ -8,7 +8,7 @@ function vitePluginPages(opts, internals) {
return {
name: "@astro/plugin-build-pages",
options(options) {
if (opts.settings.config.output === "static") {
if (opts.settings.buildOutput === "static") {
const inputs = /* @__PURE__ */ new Set();
for (const pageData of Object.values(opts.allPages)) {
if (routeIsRedirect(pageData.route)) {
@@ -40,7 +40,7 @@ function vitePluginPages(opts, internals) {
exports.push(`export const page = () => _page`);
imports.push(`import { renderers } from "${RENDERERS_MODULE_ID}";`);
exports.push(`export { renderers };`);
return `${imports.join("\n")}${exports.join("\n")}`;
return { code: `${imports.join("\n")}${exports.join("\n")}` };
}
}
}

View File

@@ -60,7 +60,7 @@ function getNonPrerenderOnlyChunks(bundle, internals) {
return nonPrerenderOnlyChunks;
}
function pluginPrerender(opts, internals) {
if (opts.settings.config.output === "static") {
if (opts.settings.buildOutput === "static") {
return { targets: ["server"] };
}
return {

View File

@@ -1,7 +1,5 @@
import type { Plugin as VitePlugin } from 'vite';
import type { AstroBuildPlugin } from '../plugin.js';
import type { StaticBuildOptions } from '../types.js';
export declare const RENDERERS_MODULE_ID = "@astro-renderers";
export declare const RESOLVED_RENDERERS_MODULE_ID = "\0@astro-renderers";
export declare function vitePluginRenderers(opts: StaticBuildOptions): VitePlugin;
export declare function pluginRenderers(opts: StaticBuildOptions): AstroBuildPlugin;

View File

@@ -21,15 +21,15 @@ function vitePluginRenderers(opts) {
let rendererItems = "";
for (const renderer of opts.settings.renderers) {
const variable = `_renderer${i}`;
imports.push(`import ${variable} from '${renderer.serverEntrypoint}';`);
imports.push(`import ${variable} from ${JSON.stringify(renderer.serverEntrypoint)};`);
rendererItems += `Object.assign(${JSON.stringify(renderer)}, { ssr: ${variable} }),`;
i++;
}
exports.push(`export const renderers = [${rendererItems}];`);
return `${imports.join("\n")}
${exports.join("\n")}`;
return { code: `${imports.join("\n")}
${exports.join("\n")}` };
} else {
return `export const renderers = [];`;
return { code: `export const renderers = [];` };
}
}
}
@@ -50,6 +50,5 @@ function pluginRenderers(opts) {
export {
RENDERERS_MODULE_ID,
RESOLVED_RENDERERS_MODULE_ID,
pluginRenderers,
vitePluginRenderers
pluginRenderers
};

View File

@@ -1,8 +1,3 @@
import type { Plugin as VitePlugin } from 'vite';
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
/**
* Used by the `experimental.directRenderScript` option to inline scripts directly into the HTML.
*/
export declare function vitePluginScripts(internals: BuildInternals): VitePlugin;
export declare function pluginScripts(internals: BuildInternals): AstroBuildPlugin;

View File

@@ -38,6 +38,5 @@ function pluginScripts(internals) {
};
}
export {
pluginScripts,
vitePluginScripts
pluginScripts
};

View File

@@ -1,9 +1,5 @@
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { StaticBuildOptions } from '../types.js';
export declare const SSR_VIRTUAL_MODULE_ID = "@astrojs-ssr-virtual-entry";
export declare const RESOLVED_SSR_VIRTUAL_MODULE_ID: string;
export declare function pluginSSR(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;
export declare const SPLIT_MODULE_ID = "@astro-page-split:";
export declare const RESOLVED_SPLIT_MODULE_ID = "\0@astro-page-split:";
export declare function pluginSSRSplit(options: StaticBuildOptions, internals: BuildInternals): AstroBuildPlugin;

View File

@@ -1,15 +1,12 @@
import { join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { isFunctionPerRouteEnabled } from "../../../integrations/hooks.js";
import { ASTRO_ACTIONS_INTERNAL_MODULE_ID } from "../../../actions/consts.js";
import { MIDDLEWARE_MODULE_ID } from "../../middleware/vite-plugin.js";
import { routeIsRedirect } from "../../redirects/index.js";
import { VIRTUAL_ISLAND_MAP_ID } from "../../server-islands/vite-plugin-server-islands.js";
import { isServerLikeOutput } from "../../util.js";
import { addRollupInput } from "../add-rollup-input.js";
import { SSR_MANIFEST_VIRTUAL_MODULE_ID } from "./plugin-manifest.js";
import { MIDDLEWARE_MODULE_ID } from "./plugin-middleware.js";
import { ASTRO_PAGE_MODULE_ID } from "./plugin-pages.js";
import { RENDERERS_MODULE_ID } from "./plugin-renderers.js";
import { getComponentFromVirtualModulePageName, getVirtualModulePageName } from "./util.js";
import { getVirtualModulePageName } from "./util.js";
const SSR_VIRTUAL_MODULE_ID = "@astrojs-ssr-virtual-entry";
const RESOLVED_SSR_VIRTUAL_MODULE_ID = "\0" + SSR_VIRTUAL_MODULE_ID;
const ADAPTER_VIRTUAL_MODULE_ID = "@astrojs-ssr-adapter";
@@ -25,7 +22,7 @@ function vitePluginAdapter(adapter) {
},
async load(id) {
if (id === RESOLVED_ADAPTER_VIRTUAL_MODULE_ID) {
return `export * from '${adapter.serverEntrypoint}';`;
return { code: `export * from ${JSON.stringify(adapter.serverEntrypoint)};` };
}
}
};
@@ -86,10 +83,10 @@ function vitePluginSSR(internals, adapter, options) {
]);`);
exports.push(`export { pageMap }`);
const middleware = await this.resolve(MIDDLEWARE_MODULE_ID);
const ssrCode = generateSSRCode(options.settings, adapter, middleware.id);
const ssrCode = generateSSRCode(adapter, middleware.id);
imports.push(...ssrCode.imports);
contents.push(...ssrCode.contents);
return [...imports, ...contents, ...exports].join("\n");
return { code: [...imports, ...contents, ...exports].join("\n") };
}
},
async generateBundle(_opts, bundle) {
@@ -110,14 +107,13 @@ function vitePluginSSR(internals, adapter, options) {
};
}
function pluginSSR(options, internals) {
const ssr = isServerLikeOutput(options.settings.config);
const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter);
const ssr = options.settings.buildOutput === "server";
return {
targets: ["server"],
hooks: {
"build:before": () => {
const adapter = options.settings.adapter;
let ssrPlugin = ssr && functionPerRouteEnabled === false ? vitePluginSSR(internals, adapter, options) : void 0;
const ssrPlugin = ssr && vitePluginSSR(internals, adapter, options);
const vitePlugin = [vitePluginAdapter(adapter)];
if (ssrPlugin) {
vitePlugin.unshift(ssrPlugin);
@@ -131,9 +127,6 @@ function pluginSSR(options, internals) {
if (!ssr) {
return;
}
if (functionPerRouteEnabled) {
return;
}
if (!internals.ssrEntryChunk) {
throw new Error(`Did not generate an entry chunk for SSR`);
}
@@ -142,101 +135,21 @@ function pluginSSR(options, internals) {
}
};
}
const SPLIT_MODULE_ID = "@astro-page-split:";
const RESOLVED_SPLIT_MODULE_ID = "\0@astro-page-split:";
function vitePluginSSRSplit(internals, adapter, options) {
return {
name: "@astrojs/vite-plugin-astro-ssr-split",
enforce: "post",
options(opts) {
const inputs = /* @__PURE__ */ new Set();
for (const pageData of Object.values(options.allPages)) {
if (routeIsRedirect(pageData.route)) {
continue;
}
inputs.add(getVirtualModulePageName(SPLIT_MODULE_ID, pageData.component));
}
return addRollupInput(opts, Array.from(inputs));
},
resolveId(id) {
if (id.startsWith(SPLIT_MODULE_ID)) {
return "\0" + id;
}
},
async load(id) {
if (id.startsWith(RESOLVED_SPLIT_MODULE_ID)) {
const imports = [];
const contents = [];
const exports = [];
const componentPath = getComponentFromVirtualModulePageName(RESOLVED_SPLIT_MODULE_ID, id);
const virtualModuleName = getVirtualModulePageName(ASTRO_PAGE_MODULE_ID, componentPath);
let module = await this.resolve(virtualModuleName);
if (module) {
imports.push(`import * as pageModule from "${virtualModuleName}";`);
}
const middleware = await this.resolve(MIDDLEWARE_MODULE_ID);
const ssrCode = generateSSRCode(options.settings, adapter, middleware.id);
imports.push(...ssrCode.imports);
contents.push(...ssrCode.contents);
exports.push("export { pageModule }");
return [...imports, ...contents, ...exports].join("\n");
}
},
async generateBundle(_opts, bundle) {
for (const [, chunk] of Object.entries(bundle)) {
if (chunk.type === "asset") {
internals.staticFiles.add(chunk.fileName);
}
}
for (const [, chunk] of Object.entries(bundle)) {
if (chunk.type === "asset") {
continue;
}
for (const moduleKey of Object.keys(chunk.modules)) {
if (moduleKey.startsWith(RESOLVED_SPLIT_MODULE_ID)) {
storeEntryPoint(moduleKey, options, internals, chunk.fileName);
}
}
}
}
};
}
function pluginSSRSplit(options, internals) {
const ssr = isServerLikeOutput(options.settings.config);
const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter);
return {
targets: ["server"],
hooks: {
"build:before": () => {
const adapter = options.settings.adapter;
let ssrPlugin = ssr && functionPerRouteEnabled ? vitePluginSSRSplit(internals, adapter, options) : void 0;
const vitePlugin = [vitePluginAdapter(adapter)];
if (ssrPlugin) {
vitePlugin.unshift(ssrPlugin);
}
return {
enforce: "after-user-plugins",
vitePlugin
};
}
}
};
}
function generateSSRCode(settings, adapter, middlewareId) {
function generateSSRCode(adapter, middlewareId) {
const edgeMiddleware = adapter?.adapterFeatures?.edgeMiddleware ?? false;
const pageMap = isFunctionPerRouteEnabled(adapter) ? "pageModule" : "pageMap";
const imports = [
`import { renderers } from '${RENDERERS_MODULE_ID}';`,
`import * as serverEntrypointModule from '${ADAPTER_VIRTUAL_MODULE_ID}';`,
`import { manifest as defaultManifest } from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';`,
settings.config.experimental.serverIslands ? `import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';` : ""
`import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';`
];
const contents = [
settings.config.experimental.serverIslands ? "" : `const serverIslandMap = new Map()`,
edgeMiddleware ? `const middleware = (_, next) => next()` : "",
`const _manifest = Object.assign(defaultManifest, {`,
` ${pageMap},`,
` pageMap,`,
` serverIslandMap,`,
` renderers,`,
` actions: () => import("${ASTRO_ACTIONS_INTERNAL_MODULE_ID}"),`,
` middleware: ${edgeMiddleware ? "undefined" : `() => import("${middlewareId}")`}`,
`});`,
`const _args = ${adapter.args ? JSON.stringify(adapter.args, null, 4) : "undefined"};`,
@@ -262,20 +175,7 @@ if (_start in serverEntrypointModule) {
contents
};
}
function storeEntryPoint(moduleKey, options, internals, fileName) {
const componentPath = getComponentFromVirtualModulePageName(RESOLVED_SPLIT_MODULE_ID, moduleKey);
for (const pageData of Object.values(options.allPages)) {
if (componentPath == pageData.component) {
const publicPath = fileURLToPath(options.settings.config.build.server);
internals.entryPoints.set(pageData.route, pathToFileURL(join(publicPath, fileName)));
}
}
}
export {
RESOLVED_SPLIT_MODULE_ID,
RESOLVED_SSR_VIRTUAL_MODULE_ID,
SPLIT_MODULE_ID,
SSR_VIRTUAL_MODULE_ID,
pluginSSR,
pluginSSRSplit
pluginSSR
};

View File

@@ -9,7 +9,6 @@ type ExtendManualChunksHooks = {
};
export declare function extendManualChunks(outputOptions: OutputOptions, hooks: ExtendManualChunksHooks): void;
export declare const ASTRO_PAGE_EXTENSION_POST_PATTERN = "@_@";
export declare const ASTRO_PAGE_KEY_SEPARATOR = "&";
/**
* Generate a unique key to identify each page in the build process.
* @param route Usually pageData.route.route
@@ -30,13 +29,5 @@ export declare function getVirtualModulePageName(virtualModulePrefix: string, pa
* @param id Virtual module name
*/
export declare function getPagesFromVirtualModulePageName(internals: BuildInternals, virtualModulePrefix: string, id: string): PageBuildData[];
/**
* From the VirtualModulePageName, get the component path.
* Remember that the component can be use by multiple routes.
* Inverse function of getVirtualModulePageName() above.
* @param virtualModulePrefix The prefix at the beginning of the virtual module
* @param id Virtual module name
*/
export declare function getComponentFromVirtualModulePageName(virtualModulePrefix: string, id: string): string;
export declare function shouldInlineAsset(assetContent: string, assetPath: string, assetsInlineLimit: NonNullable<BuildOptions['assetsInlineLimit']>): boolean;
export {};

View File

@@ -60,9 +60,7 @@ function shouldInlineAsset(assetContent, assetPath, assetsInlineLimit) {
}
export {
ASTRO_PAGE_EXTENSION_POST_PATTERN,
ASTRO_PAGE_KEY_SEPARATOR,
extendManualChunks,
getComponentFromVirtualModulePageName,
getPagesFromVirtualModulePageName,
getVirtualModulePageName,
makePageDataKey,

View File

@@ -1,12 +1,11 @@
import type { RouteData } from '../../@types/astro.js';
import { type BuildInternals } from '../../core/build/internal.js';
import type { RouteData } from '../../types/public/internal.js';
import type { StaticBuildOptions } from './types.js';
export declare function viteBuild(opts: StaticBuildOptions): Promise<{
internals: BuildInternals;
ssrOutputChunkNames: string[];
}>;
export declare function staticBuild(opts: StaticBuildOptions, internals: BuildInternals, ssrOutputChunkNames: string[]): Promise<void>;
export declare function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles?: boolean): Promise<void[] | undefined>;
/**
* This function takes the virtual module name of any page entrypoint and
* transforms it to generate a final `.mjs` output file.
@@ -26,16 +25,3 @@ export declare function copyFiles(fromFolder: URL, toFolder: URL, includeDotfile
* @param pages AllPagesData
*/
export declare function makeAstroPageEntryPointFileName(prefix: string, facadeModuleId: string, routes: RouteData[]): string;
/**
* The `facadeModuleId` has a shape like: \0@astro-serverless-page:src/pages/index@_@astro.
*
* 1. We call `makeAstroPageEntryPointFileName` which normalise its name, making it like a file path
* 2. We split the file path using the file system separator and attempt to retrieve the last entry
* 3. The last entry should be the file
* 4. We prepend the file name with `entry.`
* 5. We built the file path again, using the new en3built in the previous step
*
* @param facadeModuleId
* @param opts
*/
export declare function makeSplitEntryPointFileName(facadeModuleId: string, routes: RouteData[]): string;

View File

@@ -1,27 +1,16 @@
import fs from "node:fs";
import path, { extname } from "node:path";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { teardown } from "@astrojs/compiler";
import glob from "fast-glob";
import { bgGreen, bgMagenta, black, green } from "kleur/colors";
import { bgGreen, black, green } from "kleur/colors";
import { glob } from "tinyglobby";
import * as vite from "vite";
import { PROPAGATED_ASSET_FLAG } from "../../content/consts.js";
import {
getSymlinkedContentCollections,
hasAnyContentFlag,
reverseSymlink
} from "../../content/utils.js";
import {
createBuildInternals,
getPageDatasWithPublicKey
} from "../../core/build/internal.js";
import { createBuildInternals } from "../../core/build/internal.js";
import { emptyDir, removeEmptyDirs } from "../../core/fs/index.js";
import { appendForwardSlash, prependForwardSlash, removeFileExtension } from "../../core/path.js";
import { isModeServerWithNoAdapter, isServerLikeOutput } from "../../core/util.js";
import { appendForwardSlash, prependForwardSlash } from "../../core/path.js";
import { runHookBuildSetup } from "../../integrations/hooks.js";
import { getOutputDirectory } from "../../prerender/utils.js";
import { getServerOutputDirectory } from "../../prerender/utils.js";
import { PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { routeIsRedirect } from "../redirects/index.js";
import { getOutDirWithinCwd } from "./common.js";
import { CHUNKS_PATH } from "./consts.js";
@@ -29,18 +18,14 @@ import { generatePages } from "./generate.js";
import { trackPageData } from "./internal.js";
import { createPluginContainer } from "./plugin.js";
import { registerAllPlugins } from "./plugins/index.js";
import { copyContentToCache } from "./plugins/plugin-content.js";
import { RESOLVED_SSR_MANIFEST_VIRTUAL_MODULE_ID } from "./plugins/plugin-manifest.js";
import { ASTRO_PAGE_RESOLVED_MODULE_ID } from "./plugins/plugin-pages.js";
import { RESOLVED_RENDERERS_MODULE_ID } from "./plugins/plugin-renderers.js";
import { RESOLVED_SPLIT_MODULE_ID, RESOLVED_SSR_VIRTUAL_MODULE_ID } from "./plugins/plugin-ssr.js";
import { RESOLVED_SSR_VIRTUAL_MODULE_ID } from "./plugins/plugin-ssr.js";
import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from "./plugins/util.js";
import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from "./util.js";
async function viteBuild(opts) {
const { allPages, settings, logger } = opts;
if (isModeServerWithNoAdapter(opts.settings)) {
throw new AstroError(AstroErrorData.NoAdapterInstalled);
}
const { allPages, settings } = opts;
settings.timer.start("SSR build");
const pageInput = /* @__PURE__ */ new Set();
const internals = createBuildInternals();
@@ -58,14 +43,13 @@ async function viteBuild(opts) {
const container = createPluginContainer(opts, internals);
registerAllPlugins(container);
const ssrTime = performance.now();
opts.logger.info("build", `Building ${settings.config.output} entrypoints...`);
const ssrOutput = await ssrBuild(opts, internals, pageInput, container, logger);
opts.logger.info("build", `Building ${settings.buildOutput} entrypoints...`);
const ssrOutput = await ssrBuild(opts, internals, pageInput, container);
opts.logger.info("build", green(`\u2713 Completed in ${getTimeStat(ssrTime, performance.now())}.`));
settings.timer.end("SSR build");
settings.timer.start("Client build");
const rendererClientEntrypoints = settings.renderers.map((r) => r.clientEntrypoint).filter((a) => typeof a === "string");
const clientInput = /* @__PURE__ */ new Set([
...internals.cachedClientEntries,
...internals.discoveredHydratedComponents.keys(),
...internals.discoveredClientOnlyComponents.keys(),
...rendererClientEntrypoints,
@@ -95,12 +79,12 @@ async function viteBuild(opts) {
}
async function staticBuild(opts, internals, ssrOutputChunkNames) {
const { settings } = opts;
if (settings.config.output === "static") {
if (settings.buildOutput === "static") {
settings.timer.start("Static generate");
await generatePages(opts, internals);
await cleanServerOutput(opts, ssrOutputChunkNames, internals);
settings.timer.end("Static generate");
} else if (isServerLikeOutput(settings.config)) {
} else if (settings.buildOutput === "server") {
settings.timer.start("Server generate");
await generatePages(opts, internals);
await cleanStaticOutput(opts, internals);
@@ -108,19 +92,14 @@ async function staticBuild(opts, internals, ssrOutputChunkNames) {
settings.timer.end("Server generate");
}
}
async function ssrBuild(opts, internals, input, container, logger) {
const buildID = Date.now().toString();
async function ssrBuild(opts, internals, input, container) {
const { allPages, settings, viteConfig } = opts;
const ssr = isServerLikeOutput(settings.config);
const out = getOutputDirectory(settings.config);
const ssr = settings.buildOutput === "server";
const out = getServerOutputDirectory(settings);
const routes = Object.values(allPages).flatMap((pageData) => pageData.route);
const isContentCache = !ssr && settings.config.experimental.contentCollectionCache;
const { lastVitePlugins, vitePlugins } = await container.runBeforeHook("server", input);
const contentDir = new URL("./src/content", settings.config.root);
const symlinks = await getSymlinkedContentCollections({ contentDir, logger, fs });
const viteBuildConfig = {
...viteConfig,
mode: viteConfig.mode || "production",
logLevel: viteConfig.logLevel ?? "error",
build: {
target: "esnext",
@@ -138,25 +117,15 @@ async function ssrBuild(opts, internals, input, container, logger) {
preserveEntrySignatures: "exports-only",
input: [],
output: {
hoistTransitiveImports: isContentCache,
hoistTransitiveImports: false,
format: "esm",
minifyInternalExports: !isContentCache,
minifyInternalExports: true,
// Server chunks can't go in the assets (_astro) folder
// We need to keep these separate
chunkFileNames(chunkInfo) {
const { name } = chunkInfo;
let prefix = CHUNKS_PATH;
let suffix = "_[hash].mjs";
if (isContentCache) {
prefix += `${buildID}/`;
suffix = ".mjs";
if (name.includes("/content/")) {
const parts = name.split("/");
if (parts.at(1) === "content") {
return encodeName(parts.slice(1).join("/"));
}
}
}
if (name.includes(ASTRO_PAGE_EXTENSION_POST_PATTERN)) {
const [sanitizedName] = name.split(ASTRO_PAGE_EXTENSION_POST_PATTERN);
return [prefix, sanitizedName, suffix].join("");
@@ -177,8 +146,6 @@ async function ssrBuild(opts, internals, input, container, logger) {
chunkInfo.facadeModuleId,
routes
);
} else if (chunkInfo.facadeModuleId?.startsWith(RESOLVED_SPLIT_MODULE_ID)) {
return makeSplitEntryPointFileName(chunkInfo.facadeModuleId, routes);
} else if (chunkInfo.facadeModuleId === RESOLVED_SSR_VIRTUAL_MODULE_ID) {
return opts.settings.config.build.serverEntry;
} else if (chunkInfo.facadeModuleId === RESOLVED_RENDERERS_MODULE_ID) {
@@ -187,17 +154,6 @@ async function ssrBuild(opts, internals, input, container, logger) {
return "manifest_[hash].mjs";
} else if (chunkInfo.facadeModuleId === settings.adapter?.serverEntrypoint) {
return "adapter_[hash].mjs";
} else if (settings.config.experimental.contentCollectionCache && chunkInfo.facadeModuleId && hasAnyContentFlag(chunkInfo.facadeModuleId)) {
const moduleId = reverseSymlink({
symlinks,
entry: chunkInfo.facadeModuleId,
contentDir
});
const [srcRelative, flag] = moduleId.split("/src/")[1].split("?");
if (flag === PROPAGATED_ASSET_FLAG) {
return encodeName(`${removeFileExtension(srcRelative)}.entry.mjs`);
}
return encodeName(`${removeFileExtension(srcRelative)}.mjs`);
} else {
return "[name].mjs";
}
@@ -217,7 +173,7 @@ async function ssrBuild(opts, internals, input, container, logger) {
};
const updatedViteBuildConfig = await runHookBuildSetup({
config: settings.config,
pages: getPageDatasWithPublicKey(internals.pagesByKeys),
pages: internals.pagesByKeys,
vite: viteBuildConfig,
target: "server",
logger: opts.logger
@@ -226,11 +182,11 @@ async function ssrBuild(opts, internals, input, container, logger) {
}
async function clientBuild(opts, internals, input, container) {
const { settings, viteConfig } = opts;
const ssr = isServerLikeOutput(settings.config);
const ssr = settings.buildOutput === "server";
const out = ssr ? settings.config.build.client : getOutDirWithinCwd(settings.config.outDir);
if (!input.size) {
if (ssr) {
await copyFiles(settings.config.publicDir, out, true);
if (ssr && fs.existsSync(settings.config.publicDir)) {
await fs.promises.cp(settings.config.publicDir, out, { recursive: true, force: true });
}
return null;
}
@@ -239,7 +195,6 @@ async function clientBuild(opts, internals, input, container) {
${bgGreen(black(" building client (vite) "))}`);
const viteBuildConfig = {
...viteConfig,
mode: viteConfig.mode || "production",
build: {
target: "esnext",
...viteConfig.build,
@@ -263,14 +218,14 @@ ${bgGreen(black(" building client (vite) "))}`);
envPrefix: viteConfig.envPrefix ?? "PUBLIC_",
base: settings.config.base
};
await runHookBuildSetup({
const updatedViteBuildConfig = await runHookBuildSetup({
config: settings.config,
pages: getPageDatasWithPublicKey(internals.pagesByKeys),
pages: internals.pagesByKeys,
vite: viteBuildConfig,
target: "client",
logger: opts.logger
});
const buildResult = await vite.build(viteBuildConfig);
const buildResult = await vite.build(updatedViteBuildConfig);
return buildResult;
}
async function runPostBuildHooks(container, ssrOutputs, clientOutputs) {
@@ -278,7 +233,7 @@ async function runPostBuildHooks(container, ssrOutputs, clientOutputs) {
const config = container.options.settings.config;
const build = container.options.settings.config.build;
for (const [fileName, mutation] of mutations) {
const root = isServerLikeOutput(config) ? mutation.targets.includes("server") ? build.server : build.client : getOutDirWithinCwd(config.outDir);
const root = container.options.settings.buildOutput === "server" ? mutation.targets.includes("server") ? build.server : build.client : getOutDirWithinCwd(config.outDir);
const fullPath = path.join(fileURLToPath(root), fileName);
const fileURL = pathToFileURL(fullPath);
await fs.promises.mkdir(new URL("./", fileURL), { recursive: true });
@@ -286,7 +241,7 @@ async function runPostBuildHooks(container, ssrOutputs, clientOutputs) {
}
}
async function cleanStaticOutput(opts, internals) {
const ssr = isServerLikeOutput(opts.settings.config);
const ssr = opts.settings.buildOutput === "server";
const out = ssr ? opts.settings.config.build.server : getOutDirWithinCwd(opts.settings.config.outDir);
await Promise.all(
internals.prerenderOnlyChunks.map(async (chunk) => {
@@ -317,43 +272,25 @@ async function cleanServerOutput(opts, ssrOutputChunkNames, internals) {
files.map(async (filename) => {
const url = new URL(filename, out);
const map = new URL(url + ".map");
await Promise.all([fs.promises.rm(url), fs.promises.rm(new URL(map)).catch(() => {
await Promise.all([fs.promises.rm(url), fs.promises.rm(map).catch(() => {
})]);
})
);
removeEmptyDirs(out);
removeEmptyDirs(fileURLToPath(out));
}
if (out.toString() !== opts.settings.config.outDir.toString()) {
const fileNames = await fs.promises.readdir(out);
await Promise.all(
fileNames.filter((fileName) => fileName.endsWith(".d.ts")).map((fileName) => fs.promises.rm(new URL(fileName, out)))
);
await copyFiles(out, opts.settings.config.outDir, true);
await fs.promises.cp(out, opts.settings.config.outDir, { recursive: true, force: true });
await fs.promises.rm(out, { recursive: true });
return;
}
}
async function copyFiles(fromFolder, toFolder, includeDotfiles = false) {
const files = await glob("**/*", {
cwd: fileURLToPath(fromFolder),
dot: includeDotfiles
});
if (files.length === 0) return;
return await Promise.all(
files.map(async function copyFile(filename) {
const from = new URL(filename, fromFolder);
const to = new URL(filename, toFolder);
const lastFolder = new URL("./", to);
return fs.promises.mkdir(lastFolder, { recursive: true }).then(async function fsCopyFile() {
const p = await fs.promises.copyFile(from, to, fs.constants.COPYFILE_FICLONE);
return p;
});
})
);
}
async function ssrMoveAssets(opts) {
opts.logger.info("build", "Rearranging server assets...");
const serverRoot = opts.settings.config.output === "static" ? opts.settings.config.build.client : opts.settings.config.build.server;
const serverRoot = opts.settings.buildOutput === "static" ? opts.settings.config.build.client : opts.settings.config.build.server;
const clientRoot = opts.settings.config.build.client;
const assets = opts.settings.config.build.assets;
const serverAssets = new URL(`./${assets}/`, appendForwardSlash(serverRoot.toString()));
@@ -371,7 +308,7 @@ async function ssrMoveAssets(opts) {
return fs.promises.rename(currentUrl, clientUrl);
})
);
removeEmptyDirs(serverAssets);
removeEmptyDirs(fileURLToPath(serverRoot));
}
}
function makeAstroPageEntryPointFileName(prefix, facadeModuleId, routes) {
@@ -380,27 +317,8 @@ function makeAstroPageEntryPointFileName(prefix, facadeModuleId, routes) {
const name = route?.route ?? pageModuleId;
return `pages${name.replace(/\/$/, "/index").replaceAll(/[[\]]/g, "_").replaceAll("...", "---")}.astro.mjs`;
}
function makeSplitEntryPointFileName(facadeModuleId, routes) {
const filePath = `${makeAstroPageEntryPointFileName(
RESOLVED_SPLIT_MODULE_ID,
facadeModuleId,
routes
)}`;
const pathComponents = filePath.split(path.sep);
const lastPathComponent = pathComponents.pop();
if (lastPathComponent) {
const extension = extname(lastPathComponent);
if (extension.length > 0) {
const newFileName = `entry.${lastPathComponent}`;
return [...pathComponents, newFileName].join(path.sep);
}
}
return filePath;
}
export {
copyFiles,
makeAstroPageEntryPointFileName,
makeSplitEntryPointFileName,
staticBuild,
viteBuild
};

View File

@@ -1,8 +1,11 @@
import type * as vite from 'vite';
import type { InlineConfig } from 'vite';
import type { AstroSettings, ComponentInstance, ManifestData, MiddlewareHandler, RouteData, RuntimeMode, SSRLoadedRenderer } from '../../@types/astro.js';
import type { AstroSettings, ComponentInstance, RoutesList } from '../../types/astro.js';
import type { MiddlewareHandler } from '../../types/public/common.js';
import type { RuntimeMode } from '../../types/public/config.js';
import type { RouteData, SSRLoadedRenderer } from '../../types/public/internal.js';
import type { Logger } from '../logger/core.js';
export type ComponentPath = string;
type ComponentPath = string;
export type ViteID = string;
export type StylesheetAsset = {
type: 'inline';
@@ -11,16 +14,12 @@ export type StylesheetAsset = {
type: 'external';
src: string;
};
export type HoistedScriptAsset = {
type: 'inline' | 'external';
value: string;
};
/** Public type exposed through the `astro:build:setup` integration hook */
export interface PageBuildData {
key: string;
component: ComponentPath;
route: RouteData;
moduleSpecifier: string;
hoistedScript: HoistedScriptAsset | undefined;
styles: Array<{
depth: number;
order: number;
@@ -33,8 +32,8 @@ export interface StaticBuildOptions {
allPages: AllPagesData;
settings: AstroSettings;
logger: Logger;
manifest: ManifestData;
mode: RuntimeMode;
routesList: RoutesList;
runtimeMode: RuntimeMode;
origin: string;
pageNames: string[];
viteConfig: InlineConfig;

View File

@@ -1,5 +1,5 @@
import type { Rollup } from 'vite';
import type { AstroConfig } from '../../@types/astro.js';
import type { AstroConfig } from '../../types/public/config.js';
import type { ViteBuildReturn } from './types.js';
export declare function getTimeStat(timeStart: number, timeEnd: number): string;
/**