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,2 +1,2 @@
import type { SSRManifest, SerializedSSRManifest } from './types.js';
import type { SerializedSSRManifest, SSRManifest } from './types.js';
export declare function deserializeManifest(serializedManifest: SerializedSSRManifest): SSRManifest;

View File

@@ -1,7 +1,9 @@
import type { ManifestData, RouteData, SSRManifest } from '../../@types/astro.js';
import type { RoutesList } from '../../types/astro.js';
import type { RouteData, SSRManifest } from '../../types/public/internal.js';
import { getSetCookiesFromResponse } from '../cookies/index.js';
import { AstroIntegrationLogger } from '../logger/core.js';
export { deserializeManifest } from './common.js';
type ErrorPagePath = `${string}/404` | `${string}/500` | `${string}/404/` | `${string}/500/` | `${string}404.html` | `${string}500.html`;
export interface RenderOptions {
/**
* Whether to automatically add all cookies written by `Astro.cookie.set()` to the response headers.
@@ -23,6 +25,19 @@ export interface RenderOptions {
* The mutable object that will be made available as `Astro.locals` in pages, and as `ctx.locals` in API routes and middleware.
*/
locals?: object;
/**
* A custom fetch function for retrieving prerendered pages - 404 or 500.
*
* If not provided, Astro will fallback to its default behavior for fetching error pages.
*
* When a dynamic route is matched but ultimately results in a 404, this function will be used
* to fetch the prerendered 404 page if available. Similarly, it may be used to fetch a
* prerendered 500 error page when necessary.
*
* @param {ErrorPagePath} url - The URL of the prerendered 404 or 500 error page to fetch.
* @returns {Promise<Response>} A promise resolving to the prerendered response.
*/
prerenderedErrorPageFetch?: (url: ErrorPagePath) => Promise<Response>;
/**
* **Advanced API**: you probably do not need to use this.
*
@@ -43,20 +58,25 @@ export interface RenderErrorOptions {
* Allows passing an error to 500.astro. It will be available through `Astro.props.error`.
*/
error?: unknown;
clientAddress: string | undefined;
prerenderedErrorPageFetch: (url: ErrorPagePath) => Promise<Response>;
}
export declare class App {
#private;
constructor(manifest: SSRManifest, streaming?: boolean);
getAdapterLogger(): AstroIntegrationLogger;
set setManifestData(newManifestData: ManifestData);
set setManifestData(newManifestData: RoutesList);
removeBase(pathname: string): string;
match(request: Request): RouteData | undefined;
render(request: Request, options?: RenderOptions): Promise<Response>;
/**
* @deprecated Instead of passing `RouteData` and locals individually, pass an object with `routeData` and `locals` properties.
* See https://github.com/withastro/astro/pull/9199 for more information.
* Given a `Request`, it returns the `RouteData` that matches its `pathname`. By default, prerendered
* routes aren't returned, even if they are matched.
*
* When `allowPrerenderedRoutes` is `true`, the function returns matched prerendered routes too.
* @param request
* @param allowPrerenderedRoutes
*/
render(request: Request, routeData?: RouteData, locals?: object): Promise<Response>;
match(request: Request, allowPrerenderedRoutes?: boolean): RouteData | undefined;
render(request: Request, renderOptions?: RenderOptions): Promise<Response>;
setCookieHeaders(response: Response): Generator<string, string[], any>;
/**
* Reads all the cookies written by `Astro.cookie.set()` onto the passed response.

View File

@@ -1,9 +1,10 @@
import { collapseDuplicateTrailingSlashes, hasFileExtension } from "@astrojs/internal-helpers/path";
import { normalizeTheLocale } from "../../i18n/index.js";
import {
clientAddressSymbol,
DEFAULT_404_COMPONENT,
REROUTABLE_STATUS_CODES,
REROUTE_DIRECTIVE_HEADER,
clientAddressSymbol,
clientLocalsSymbol,
responseSentSymbol
} from "../constants.js";
import { getSetCookiesFromResponse } from "../cookies/index.js";
@@ -17,10 +18,13 @@ import {
prependForwardSlash,
removeTrailingForwardSlash
} from "../path.js";
import { RenderContext } from "../render-context.js";
import { createAssetLink } from "../render/ssr-element.js";
import { createDefaultRoutes, injectDefaultRoutes } from "../routing/default.js";
import { RenderContext } from "../render-context.js";
import { redirectTemplate } from "../routing/3xx.js";
import { ensure404Route } from "../routing/astro-designed-error-pages.js";
import { createDefaultRoutes } from "../routing/default.js";
import { matchRoute } from "../routing/match.js";
import { PERSIST_SYMBOL } from "../session.js";
import { AppPipeline } from "./pipeline.js";
import { deserializeManifest } from "./common.js";
class App {
@@ -33,14 +37,14 @@ class App {
#baseWithoutTrailingSlash;
#pipeline;
#adapterLogger;
#renderOptionsDeprecationWarningShown = false;
constructor(manifest, streaming = true) {
this.#manifest = manifest;
this.#manifestData = injectDefaultRoutes(manifest, {
this.#manifestData = {
routes: manifest.routes.map((route) => route.routeData)
});
};
ensure404Route(this.#manifestData);
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
this.#pipeline = this.#createPipeline(this.#manifestData, streaming);
this.#pipeline = this.#createPipeline(streaming);
this.#adapterLogger = new AstroIntegrationLogger(
this.#logger.options,
this.#manifest.adapterName
@@ -52,15 +56,14 @@ class App {
/**
* Creates a pipeline by reading the stored manifest
*
* @param manifestData
* @param streaming
* @private
*/
#createPipeline(manifestData, streaming = false) {
return AppPipeline.create(manifestData, {
#createPipeline(streaming = false) {
return AppPipeline.create({
logger: this.#logger,
manifest: this.#manifest,
mode: "production",
runtimeMode: "production",
renderers: this.#manifest.renderers,
defaultRoutes: createDefaultRoutes(this.#manifest),
resolve: async (specifier) => {
@@ -87,20 +90,45 @@ class App {
}
return pathname;
}
/**
* It removes the base from the request URL, prepends it with a forward slash and attempts to decoded it.
*
* If the decoding fails, it logs the error and return the pathname as is.
* @param request
* @private
*/
#getPathnameFromRequest(request) {
const url = new URL(request.url);
const pathname = prependForwardSlash(this.removeBase(url.pathname));
return pathname;
try {
return decodeURI(pathname);
} catch (e) {
this.getAdapterLogger().error(e.toString());
return pathname;
}
}
match(request) {
/**
* Given a `Request`, it returns the `RouteData` that matches its `pathname`. By default, prerendered
* routes aren't returned, even if they are matched.
*
* When `allowPrerenderedRoutes` is `true`, the function returns matched prerendered routes too.
* @param request
* @param allowPrerenderedRoutes
*/
match(request, allowPrerenderedRoutes = false) {
const url = new URL(request.url);
if (this.#manifest.assets.has(url.pathname)) return void 0;
let pathname = this.#computePathnameFromDomain(request);
if (!pathname) {
pathname = prependForwardSlash(this.removeBase(url.pathname));
}
let routeData = matchRoute(pathname, this.#manifestData);
if (!routeData || routeData.prerender) return void 0;
let routeData = matchRoute(decodeURI(pathname), this.#manifestData);
if (!routeData) return void 0;
if (allowPrerenderedRoutes) {
return routeData;
} else if (routeData.prerender) {
return void 0;
}
return routeData;
}
#computePathnameFromDomain(request) {
@@ -150,31 +178,55 @@ class App {
}
return pathname;
}
async render(request, routeDataOrOptions, maybeLocals) {
#redirectTrailingSlash(pathname) {
const { trailingSlash } = this.#manifest;
if (pathname === "/" || pathname.startsWith("/_")) {
return pathname;
}
const path = collapseDuplicateTrailingSlashes(pathname, trailingSlash !== "never");
if (path !== pathname) {
return path;
}
if (trailingSlash === "ignore") {
return pathname;
}
if (trailingSlash === "always" && !hasFileExtension(pathname)) {
return appendForwardSlash(pathname);
}
if (trailingSlash === "never") {
return removeTrailingForwardSlash(pathname);
}
return pathname;
}
async render(request, renderOptions) {
let routeData;
let locals;
let clientAddress;
let addCookieHeader;
if (routeDataOrOptions && ("addCookieHeader" in routeDataOrOptions || "clientAddress" in routeDataOrOptions || "locals" in routeDataOrOptions || "routeData" in routeDataOrOptions)) {
if ("addCookieHeader" in routeDataOrOptions) {
addCookieHeader = routeDataOrOptions.addCookieHeader;
}
if ("clientAddress" in routeDataOrOptions) {
clientAddress = routeDataOrOptions.clientAddress;
}
if ("routeData" in routeDataOrOptions) {
routeData = routeDataOrOptions.routeData;
}
if ("locals" in routeDataOrOptions) {
locals = routeDataOrOptions.locals;
}
} else {
routeData = routeDataOrOptions;
locals = maybeLocals;
if (routeDataOrOptions || locals) {
this.#logRenderOptionsDeprecationWarning();
}
const url = new URL(request.url);
const redirect = this.#redirectTrailingSlash(url.pathname);
const prerenderedErrorPageFetch = renderOptions?.prerenderedErrorPageFetch ?? fetch;
if (redirect !== url.pathname) {
const status = request.method === "GET" ? 301 : 308;
return new Response(
redirectTemplate({
status,
relativeLocation: url.pathname,
absoluteLocation: redirect,
from: request.url
}),
{
status,
headers: {
location: redirect + url.search
}
}
);
}
addCookieHeader = renderOptions?.addCookieHeader;
clientAddress = renderOptions?.clientAddress ?? Reflect.get(request, clientAddressSymbol);
routeData = renderOptions?.routeData;
locals = renderOptions?.locals;
if (routeData) {
this.#logger.debug(
"router",
@@ -187,26 +239,38 @@ class App {
if (typeof locals !== "object") {
const error = new AstroError(AstroErrorData.LocalsNotAnObject);
this.#logger.error(null, error.stack);
return this.#renderError(request, { status: 500, error });
return this.#renderError(request, {
status: 500,
error,
clientAddress,
prerenderedErrorPageFetch
});
}
Reflect.set(request, clientLocalsSymbol, locals);
}
if (clientAddress) {
Reflect.set(request, clientAddressSymbol, clientAddress);
}
if (!routeData) {
routeData = this.match(request);
this.#logger.debug("router", "Astro matched the following route for " + request.url);
this.#logger.debug("router", "RouteData:\n" + routeData);
}
if (!routeData) {
routeData = this.#manifestData.routes.find(
(route) => route.component === "404.astro" || route.component === DEFAULT_404_COMPONENT
);
}
if (!routeData) {
this.#logger.debug("router", "Astro hasn't found routes that match " + request.url);
this.#logger.debug("router", "Here's the available routes:\n", this.#manifestData);
return this.#renderError(request, { locals, status: 404 });
return this.#renderError(request, {
locals,
status: 404,
clientAddress,
prerenderedErrorPageFetch
});
}
const pathname = this.#getPathnameFromRequest(request);
const defaultStatus = this.#getDefaultStatusCode(routeData, pathname);
let response;
let session;
try {
const mod = await this.#pipeline.getModuleForRoute(routeData);
const renderContext = await RenderContext.create({
@@ -215,12 +279,22 @@ class App {
pathname,
request,
routeData,
status: defaultStatus
status: defaultStatus,
clientAddress
});
session = renderContext.session;
response = await renderContext.render(await mod.page());
} catch (err) {
this.#logger.error(null, err.stack || err.message || String(err));
return this.#renderError(request, { locals, status: 500, error: err });
return this.#renderError(request, {
locals,
status: 500,
error: err,
clientAddress,
prerenderedErrorPageFetch
});
} finally {
await session?.[PERSIST_SYMBOL]();
}
if (REROUTABLE_STATUS_CODES.includes(response.status) && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
return this.#renderError(request, {
@@ -229,7 +303,9 @@ class App {
status: response.status,
// We don't have an error to report here. Passing null means we pass nothing intentionally
// while undefined means there's no error
error: response.status === 500 ? null : void 0
error: response.status === 500 ? null : void 0,
clientAddress,
prerenderedErrorPageFetch
});
}
if (response.headers.has(REROUTE_DIRECTIVE_HEADER)) {
@@ -243,14 +319,6 @@ class App {
Reflect.set(response, responseSentSymbol, true);
return response;
}
#logRenderOptionsDeprecationWarning() {
if (this.#renderOptionsDeprecationWarningShown) return;
this.#logger.warn(
"deprecated",
`The adapter ${this.#manifest.adapterName} is using a deprecated signature of the 'app.render()' method. From Astro 4.0, locals and routeData are provided as properties on an optional object to this method. Using the old signature will cause an error in Astro 5.0. See https://github.com/withastro/astro/pull/9199 for more information.`
);
this.#renderOptionsDeprecationWarningShown = true;
}
setCookieHeaders(response) {
return getSetCookiesFromResponse(response);
}
@@ -275,7 +343,9 @@ class App {
status,
response: originalResponse,
skipMiddleware = false,
error
error,
clientAddress,
prerenderedErrorPageFetch
}) {
const errorRoutePath = `/${status}${this.#manifest.trailingSlash === "always" ? "/" : ""}`;
const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
@@ -288,12 +358,13 @@ class App {
url
);
if (statusURL.toString() !== request.url) {
const response2 = await fetch(statusURL.toString());
const override = { status };
const response2 = await prerenderedErrorPageFetch(statusURL.toString());
const override = { status, removeContentEncodingHeaders: true };
return this.#mergeResponses(response2, originalResponse, override);
}
}
const mod = await this.#pipeline.getModuleForRoute(errorRouteData);
let session;
try {
const renderContext = await RenderContext.create({
locals,
@@ -303,8 +374,10 @@ class App {
request,
routeData: errorRouteData,
status,
props: { error }
props: { error },
clientAddress
});
session = renderContext.session;
const response2 = await renderContext.render(await mod.page());
return this.#mergeResponses(response2, originalResponse);
} catch {
@@ -313,9 +386,13 @@ class App {
locals,
status,
response: originalResponse,
skipMiddleware: true
skipMiddleware: true,
clientAddress,
prerenderedErrorPageFetch
});
}
} finally {
await session?.[PERSIST_SYMBOL]();
}
}
const response = this.#mergeResponses(new Response(null, { status }), originalResponse);
@@ -323,12 +400,18 @@ class App {
return response;
}
#mergeResponses(newResponse, originalResponse, override) {
let newResponseHeaders = newResponse.headers;
if (override?.removeContentEncodingHeaders) {
newResponseHeaders = new Headers(newResponseHeaders);
newResponseHeaders.delete("Content-Encoding");
newResponseHeaders.delete("Content-Length");
}
if (!originalResponse) {
if (override !== void 0) {
return new Response(newResponse.body, {
status: override.status,
statusText: newResponse.statusText,
headers: newResponse.headers
headers: newResponseHeaders
});
}
return newResponse;
@@ -338,6 +421,14 @@ class App {
originalResponse.headers.delete("Content-type");
} catch {
}
const mergedHeaders = new Map([
...Array.from(newResponseHeaders),
...Array.from(originalResponse.headers)
]);
const newHeaders = new Headers();
for (const [name, value] of mergedHeaders) {
newHeaders.set(name, value);
}
return new Response(newResponse.body, {
status,
statusText: status === 200 ? newResponse.statusText : originalResponse.statusText,
@@ -346,10 +437,7 @@ class App {
// If users see something weird, it's because they are setting some headers they should not.
//
// Although, we don't want it to replace the content-type, because the error page must return `text/html`
headers: new Headers([
...Array.from(newResponse.headers),
...Array.from(originalResponse.headers)
])
headers: newHeaders
});
}
#getDefaultStatusCode(routeData, pathname) {

View File

@@ -1,4 +1,4 @@
import type { MiddlewareHandler } from '../../@types/astro.js';
import type { MiddlewareHandler } from '../../types/public/common.js';
/**
* Returns a middleware function in charge to check the `origin` header.
*

View File

@@ -4,23 +4,27 @@ const FORM_CONTENT_TYPES = [
"multipart/form-data",
"text/plain"
];
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
function createOriginCheckMiddleware() {
return defineMiddleware((context, next) => {
const { request, url } = context;
if (request.method === "GET") {
const { request, url, isPrerendered } = context;
if (isPrerendered) {
return next();
}
const sameOrigin = (request.method === "POST" || request.method === "PUT" || request.method === "PATCH" || request.method === "DELETE") && request.headers.get("origin") === url.origin;
if (SAFE_METHODS.includes(request.method)) {
return next();
}
const isSameOrigin = request.headers.get("origin") === url.origin;
const hasContentType = request.headers.has("content-type");
if (hasContentType) {
const formLikeHeader = hasFormLikeHeader(request.headers.get("content-type"));
if (formLikeHeader && !sameOrigin) {
if (formLikeHeader && !isSameOrigin) {
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
status: 403
});
}
} else {
if (!sameOrigin) {
if (!isSameOrigin) {
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
status: 403
});

View File

@@ -1,8 +1,8 @@
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { RouteData } from '../../@types/astro.js';
import { App } from './index.js';
import type { RouteData } from '../../types/public/internal.js';
import type { RenderOptions } from './index.js';
import type { SSRManifest } from './types.js';
import { App } from './index.js';
import type { NodeAppHeadersJson, SSRManifest } from './types.js';
export { apply as applyPolyfills } from '../polyfill.js';
/**
* Allow the request body to be explicitly overridden. For example, this
@@ -12,7 +12,9 @@ interface NodeRequest extends IncomingMessage {
body?: unknown;
}
export declare class NodeApp extends App {
match(req: NodeRequest | Request): RouteData | undefined;
headersMap: NodeAppHeadersJson | undefined;
setHeadersMap(headers: NodeAppHeadersJson): void;
match(req: NodeRequest | Request, allowPrerenderedRoutes?: boolean): RouteData | undefined;
render(request: NodeRequest | Request, options?: RenderOptions): Promise<Response>;
/**
* @deprecated Instead of passing `RouteData` and locals individually, pass an object with `routeData` and `locals` properties.

View File

@@ -6,13 +6,17 @@ import { createOutgoingHttpHeaders } from "./createOutgoingHttpHeaders.js";
import { App } from "./index.js";
import { apply } from "../polyfill.js";
class NodeApp extends App {
match(req) {
headersMap = void 0;
setHeadersMap(headers) {
this.headersMap = headers;
}
match(req, allowPrerenderedRoutes = false) {
if (!(req instanceof Request)) {
req = NodeApp.createRequest(req, {
skipBody: true
});
}
return super.match(req);
return super.match(req, allowPrerenderedRoutes);
}
render(req, routeDataOrOptions, maybeLocals) {
if (!(req instanceof Request)) {
@@ -39,13 +43,20 @@ class NodeApp extends App {
return multiValueHeader?.toString()?.split(",").map((e) => e.trim())?.[0];
};
const forwardedProtocol = getFirstForwardedValue(req.headers["x-forwarded-proto"]);
const protocol = forwardedProtocol ?? (isEncrypted ? "https" : "http");
const providedProtocol = isEncrypted ? "https" : "http";
const protocol = forwardedProtocol ?? providedProtocol;
const forwardedHostname = getFirstForwardedValue(req.headers["x-forwarded-host"]);
const hostname = forwardedHostname ?? req.headers.host ?? req.headers[":authority"];
const providedHostname = req.headers.host ?? req.headers[":authority"];
const hostname = forwardedHostname ?? providedHostname;
const port = getFirstForwardedValue(req.headers["x-forwarded-port"]);
const portInHostname = typeof hostname === "string" && /:\d+$/.test(hostname);
const hostnamePort = portInHostname ? hostname : `${hostname}${port ? `:${port}` : ""}`;
const url = `${protocol}://${hostnamePort}${req.url}`;
let url;
try {
const hostnamePort = getHostnamePort(hostname, port);
url = new URL(`${protocol}://${hostnamePort}${req.url}`);
} catch {
const hostnamePort = getHostnamePort(providedHostname, port);
url = new URL(`${providedProtocol}://${hostnamePort}`);
}
const options = {
method: req.method || "GET",
headers: makeRequestHeaders(req)
@@ -107,6 +118,11 @@ class NodeApp extends App {
}
}
}
function getHostnamePort(hostname, port) {
const portInHostname = typeof hostname === "string" && /:\d+$/.test(hostname);
const hostnamePort = portInHostname ? hostname : `${hostname}${port ? `:${port}` : ""}`;
return hostnamePort;
}
function makeRequestHeaders(req) {
const headers = new Headers();
for (const [name, value] of Object.entries(req.headers)) {

View File

@@ -1,9 +1,10 @@
import type { ComponentInstance, ManifestData, RewritePayload, RouteData, SSRResult } from '../../@types/astro.js';
import type { ComponentInstance } from '../../types/astro.js';
import type { RewritePayload } from '../../types/public/common.js';
import type { RouteData, SSRResult } from '../../types/public/internal.js';
import { Pipeline, type TryRewriteResult } from '../base-pipeline.js';
import type { SinglePageBuiltModule } from '../build/types.js';
export declare class AppPipeline extends Pipeline {
#private;
static create(manifestData: ManifestData, { logger, manifest, mode, renderers, resolve, serverLike, streaming, defaultRoutes, }: Pick<AppPipeline, 'logger' | 'manifest' | 'mode' | 'renderers' | 'resolve' | 'serverLike' | 'streaming' | 'defaultRoutes'>): AppPipeline;
static create({ logger, manifest, runtimeMode, renderers, resolve, serverLike, streaming, defaultRoutes, }: Pick<AppPipeline, 'logger' | 'manifest' | 'runtimeMode' | 'renderers' | 'resolve' | 'serverLike' | 'streaming' | 'defaultRoutes'>): AppPipeline;
headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'>;
componentMetadata(): void;
getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;

View File

@@ -3,11 +3,10 @@ import { RedirectSinglePageBuiltModule } from "../redirects/component.js";
import { createModuleScriptElement, createStylesheetElementSet } from "../render/ssr-element.js";
import { findRouteToRewrite } from "../routing/rewrite.js";
class AppPipeline extends Pipeline {
#manifestData;
static create(manifestData, {
static create({
logger,
manifest,
mode,
runtimeMode,
renderers,
resolve,
serverLike,
@@ -17,7 +16,7 @@ class AppPipeline extends Pipeline {
const pipeline = new AppPipeline(
logger,
manifest,
mode,
runtimeMode,
renderers,
resolve,
serverLike,
@@ -32,7 +31,6 @@ class AppPipeline extends Pipeline {
void 0,
defaultRoutes
);
pipeline.#manifestData = manifestData;
return pipeline;
}
headElements(routeData) {
@@ -67,7 +65,8 @@ class AppPipeline extends Pipeline {
routes: this.manifest?.routes.map((r) => r.routeData),
trailingSlash: this.manifest.trailingSlash,
buildFormat: this.manifest.buildFormat,
base: this.manifest.base
base: this.manifest.base,
outDir: this.serverLike ? this.manifest.buildClientDir : this.manifest.outDir
});
const componentInstance = await this.getComponentByRoute(routeData);
return { newUrl, pathname, componentInstance, routeData };

View File

@@ -1,7 +1,13 @@
import type { AstroMiddlewareInstance, ComponentInstance, Locales, RouteData, SSRComponentMetadata, SSRLoadedRenderer, SSRResult, SerializedRouteData } from '../../@types/astro.js';
import type { ZodType } from 'zod';
import type { ActionAccept, ActionClient } from '../../actions/runtime/virtual/server.js';
import type { RoutingStrategies } from '../../i18n/utils.js';
import type { ComponentInstance, SerializedRouteData } from '../../types/astro.js';
import type { AstroMiddlewareInstance } from '../../types/public/common.js';
import type { AstroConfig, CspAlgorithm, Locales, ResolvedSessionConfig } from '../../types/public/config.js';
import type { RouteData, SSRComponentMetadata, SSRLoadedRenderer, SSRResult } from '../../types/public/internal.js';
import type { SinglePageBuiltModule } from '../build/types.js';
export type ComponentPath = string;
import type { CspDirective } from '../csp/config.js';
type ComponentPath = string;
export type StylesheetAsset = {
type: 'inline';
content: string;
@@ -25,7 +31,7 @@ export interface RouteInfo {
export type SerializedRouteInfo = Omit<RouteInfo, 'routeData'> & {
routeData: SerializedRouteData;
};
export type ImportComponentInstance = () => Promise<SinglePageBuiltModule>;
type ImportComponentInstance = () => Promise<SinglePageBuiltModule>;
export type AssetsPrefix = string | ({
fallback: string;
} & Record<string, string>) | undefined;
@@ -35,8 +41,15 @@ export type SSRManifest = {
routes: RouteInfo[];
site?: string;
base: string;
trailingSlash: 'always' | 'never' | 'ignore';
buildFormat: 'file' | 'directory' | 'preserve';
/**
* The base of the assets generated **by the user**. For example, scripts created by the user falls under this category.
*
* The value of this field comes from `vite.base`. We aren't usually this tight to vite in our code base, so probably
* this should be refactored somehow.
*/
userAssetsBase: string | undefined;
trailingSlash: AstroConfig['trailingSlash'];
buildFormat: NonNullable<AstroConfig['build']>['format'];
compressHTML: boolean;
assetsPrefix?: AssetsPrefix;
renderers: SSRLoadedRenderer[];
@@ -55,8 +68,19 @@ export type SSRManifest = {
key: Promise<CryptoKey>;
i18n: SSRManifestI18n | undefined;
middleware?: () => Promise<AstroMiddlewareInstance> | AstroMiddlewareInstance;
actions?: () => Promise<SSRActions> | SSRActions;
checkOrigin: boolean;
experimentalEnvGetSecretEnabled: boolean;
sessionConfig?: ResolvedSessionConfig<any>;
cacheDir: string | URL;
srcDir: string | URL;
outDir: string | URL;
publicDir: string | URL;
buildClientDir: string | URL;
buildServerDir: string | URL;
csp: SSRManifestCSP | undefined;
};
export type SSRActions = {
server: Record<string, ActionClient<unknown, ActionAccept, ZodType>>;
};
export type SSRManifestI18n = {
fallback: Record<string, string> | undefined;
@@ -66,6 +90,17 @@ export type SSRManifestI18n = {
defaultLocale: string;
domainLookupTable: Record<string, string>;
};
export type SSRManifestCSP = {
cspDestination: 'adapter' | 'meta' | 'header' | undefined;
algorithm: CspAlgorithm;
scriptHashes: string[];
scriptResources: string[];
isStrictDynamic: boolean;
styleHashes: string[];
styleResources: string[];
directives: CspDirective[];
};
/** Public type exposed through the `astro:build:ssr` integration hook */
export type SerializedSSRManifest = Omit<SSRManifest, 'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'inlinedScripts' | 'clientDirectives' | 'serverIslandNameMap' | 'key'> & {
routes: SerializedRouteInfo[];
assets: string[];
@@ -75,3 +110,11 @@ export type SerializedSSRManifest = Omit<SSRManifest, 'middleware' | 'routes' |
serverIslandNameMap: [string, string][];
key: string;
};
export type NodeAppHeadersJson = {
pathname: string;
headers: {
key: string;
value: string;
}[];
}[];
export {};

View File

@@ -1,4 +1,9 @@
import type { ComponentInstance, MiddlewareHandler, RewritePayload, RouteData, RuntimeMode, SSRLoadedRenderer, SSRManifest, SSRResult } from '../@types/astro.js';
import type { ZodType } from 'zod';
import type { ActionAccept, ActionClient } from '../actions/runtime/virtual/server.js';
import type { ComponentInstance } from '../types/astro.js';
import type { MiddlewareHandler, RewritePayload } from '../types/public/common.js';
import type { RuntimeMode } from '../types/public/config.js';
import type { RouteData, SSRActions, SSRLoadedRenderer, SSRManifest, SSRResult } from '../types/public/internal.js';
import type { Logger } from './logger/core.js';
import { RouteCache } from './render/route-cache.js';
/**
@@ -11,9 +16,9 @@ export declare abstract class Pipeline {
readonly logger: Logger;
readonly manifest: SSRManifest;
/**
* "development" or "production"
* "development" or "production" only
*/
readonly mode: RuntimeMode;
readonly runtimeMode: RuntimeMode;
readonly renderers: SSRLoadedRenderer[];
readonly resolve: (s: string) => Promise<string>;
/**
@@ -29,7 +34,7 @@ export declare abstract class Pipeline {
readonly inlinedScripts: Map<string, string>;
readonly compressHTML: boolean;
readonly i18n: import("./app/types.js").SSRManifestI18n | undefined;
readonly middleware: (() => Promise<import("../@types/astro.js").AstroMiddlewareInstance> | import("../@types/astro.js").AstroMiddlewareInstance) | undefined;
readonly middleware: (() => Promise<import("../types/public/common.js").AstroMiddlewareInstance> | import("../types/public/common.js").AstroMiddlewareInstance) | undefined;
readonly routeCache: RouteCache;
/**
* Used for `Astro.site`.
@@ -45,13 +50,15 @@ export declare abstract class Pipeline {
route: string;
component: string;
}[];
readonly actions: (() => Promise<SSRActions> | SSRActions) | undefined;
readonly internalMiddleware: MiddlewareHandler[];
resolvedMiddleware: MiddlewareHandler | undefined;
resolvedActions: SSRActions | undefined;
constructor(logger: Logger, manifest: SSRManifest,
/**
* "development" or "production"
* "development" or "production" only
*/
mode: RuntimeMode, renderers: SSRLoadedRenderer[], resolve: (s: string) => Promise<string>,
runtimeMode: RuntimeMode, renderers: SSRLoadedRenderer[], resolve: (s: string) => Promise<string>,
/**
* Based on Astro config's `output` option, `true` if "server" or "hybrid".
*/
@@ -59,7 +66,7 @@ export declare abstract class Pipeline {
/**
* Used to provide better error messages for `Astro.clientAddress`
*/
adapterName?: string, clientDirectives?: Map<string, string>, inlinedScripts?: Map<string, string>, compressHTML?: boolean, i18n?: import("./app/types.js").SSRManifestI18n | undefined, middleware?: (() => Promise<import("../@types/astro.js").AstroMiddlewareInstance> | import("../@types/astro.js").AstroMiddlewareInstance) | undefined, routeCache?: RouteCache,
adapterName?: string, clientDirectives?: Map<string, string>, inlinedScripts?: Map<string, string>, compressHTML?: boolean, i18n?: import("./app/types.js").SSRManifestI18n | undefined, middleware?: (() => Promise<import("../types/public/common.js").AstroMiddlewareInstance> | import("../types/public/common.js").AstroMiddlewareInstance) | undefined, routeCache?: RouteCache,
/**
* Used for `Astro.site`.
*/
@@ -73,7 +80,7 @@ export declare abstract class Pipeline {
matchesComponent(filePath: URL): boolean;
route: string;
component: string;
}[]);
}[], actions?: (() => Promise<SSRActions> | SSRActions) | undefined);
abstract headElements(routeData: RouteData): Promise<HeadElements> | HeadElements;
abstract componentMetadata(routeData: RouteData): Promise<SSRResult['componentMetadata']> | void;
/**
@@ -97,6 +104,9 @@ export declare abstract class Pipeline {
* it returns a no-op function
*/
getMiddleware(): Promise<MiddlewareHandler>;
setActions(actions: SSRActions): void;
getActions(): Promise<SSRActions>;
getAction(path: string): Promise<ActionClient<unknown, ActionAccept, ZodType>>;
}
export interface HeadElements extends Pick<SSRResult, 'scripts' | 'styles' | 'links'> {
}

View File

@@ -1,17 +1,17 @@
import { setGetEnv } from "../env/runtime.js";
import { NOOP_ACTIONS_MOD } from "../actions/noop-actions.js";
import { createI18nMiddleware } from "../i18n/middleware.js";
import { createOriginCheckMiddleware } from "./app/middlewares.js";
import { AstroError } from "./errors/errors.js";
import { AstroErrorData } from "./errors/index.js";
import { sequence } from "./middleware/index.js";
import { ActionNotFoundError } from "./errors/errors-data.js";
import { AstroError } from "./errors/index.js";
import { NOOP_MIDDLEWARE_FN } from "./middleware/noop-middleware.js";
import { sequence } from "./middleware/sequence.js";
import { RouteCache } from "./render/route-cache.js";
import { createDefaultRoutes } from "./routing/default.js";
class Pipeline {
constructor(logger, manifest, mode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, mode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest)) {
constructor(logger, manifest, runtimeMode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, runtimeMode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest), actions = manifest.actions) {
this.logger = logger;
this.manifest = manifest;
this.mode = mode;
this.runtimeMode = runtimeMode;
this.renderers = renderers;
this.resolve = resolve;
this.serverLike = serverLike;
@@ -25,6 +25,7 @@ class Pipeline {
this.routeCache = routeCache;
this.site = site;
this.defaultRoutes = defaultRoutes;
this.actions = actions;
this.internalMiddleware = [];
if (i18n?.strategy !== "manual") {
this.internalMiddleware.push(
@@ -34,6 +35,7 @@ class Pipeline {
}
internalMiddleware;
resolvedMiddleware = void 0;
resolvedActions = void 0;
/**
* Resolves the middleware from the manifest, and returns the `onRequest` function. If `onRequest` isn't there,
* it returns a no-op function
@@ -44,17 +46,52 @@ class Pipeline {
} else if (this.middleware) {
const middlewareInstance = await this.middleware();
const onRequest = middlewareInstance.onRequest ?? NOOP_MIDDLEWARE_FN;
const internalMiddlewares = [onRequest];
if (this.manifest.checkOrigin) {
this.resolvedMiddleware = sequence(createOriginCheckMiddleware(), onRequest);
} else {
this.resolvedMiddleware = onRequest;
internalMiddlewares.unshift(createOriginCheckMiddleware());
}
this.resolvedMiddleware = sequence(...internalMiddlewares);
return this.resolvedMiddleware;
} else {
this.resolvedMiddleware = NOOP_MIDDLEWARE_FN;
return this.resolvedMiddleware;
}
}
setActions(actions) {
this.resolvedActions = actions;
}
async getActions() {
if (this.resolvedActions) {
return this.resolvedActions;
} else if (this.actions) {
return await this.actions();
}
return NOOP_ACTIONS_MOD;
}
async getAction(path) {
const pathKeys = path.split(".").map((key) => decodeURIComponent(key));
let { server } = await this.getActions();
if (!server || !(typeof server === "object")) {
throw new TypeError(
`Expected \`server\` export in actions file to be an object. Received ${typeof server}.`
);
}
for (const key of pathKeys) {
if (!(key in server)) {
throw new AstroError({
...ActionNotFoundError,
message: ActionNotFoundError.message(pathKeys.join("."))
});
}
server = server[key];
}
if (typeof server !== "function") {
throw new TypeError(
`Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof server}.`
);
}
return server;
}
}
export {
Pipeline

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;
/**

View File

@@ -1,4 +1,4 @@
/**
* Build a client directive entrypoint into code that can directly run in a `<script>` tag.
*/
export declare function buildClientDirectiveEntrypoint(name: string, entrypoint: string, root: URL): Promise<string>;
export declare function buildClientDirectiveEntrypoint(name: string, entrypoint: string | URL, root: URL): Promise<string>;

View File

@@ -1,7 +1,7 @@
import type { TransformResult } from '@astrojs/compiler';
import type { ResolvedConfig } from 'vite';
import type { AstroConfig } from '../../@types/astro.js';
import type { AstroPreferences } from '../../preferences/index.js';
import type { AstroConfig } from '../../types/public/config.js';
import type { CompileCssResult } from './types.js';
export interface CompileProps {
astroConfig: AstroConfig;

View File

@@ -1,9 +1,8 @@
import { fileURLToPath } from "node:url";
import { transform } from "@astrojs/compiler";
import { normalizePath } from "vite";
import { AggregateError, CompilerError } from "../errors/errors.js";
import { AstroErrorData } from "../errors/index.js";
import { resolvePath } from "../viteUtils.js";
import { normalizePath, resolvePath } from "../viteUtils.js";
import { createStylePreprocessor } from "./style.js";
async function compile({
astroConfig,
@@ -29,13 +28,14 @@ async function compile({
resultScopedSlot: true,
transitionsAnimationURL: "astro/components/viewtransitions.css",
annotateSourceFile: viteConfig.command === "serve" && astroConfig.devToolbar && astroConfig.devToolbar.enabled && await preferences.get("devToolbar.enabled"),
renderScript: astroConfig.experimental.directRenderScript,
renderScript: true,
preprocessStyle: createStylePreprocessor({
filename,
viteConfig,
cssPartialCompileResults,
cssTransformErrors
}),
experimentalScriptOrder: astroConfig.experimental.preserveScriptOrder ?? false,
async resolvePath(specifier) {
return resolvePath(specifier, filename);
}

View File

@@ -1,2 +1,2 @@
export { compile } from './compile.js';
export type { CompileProps, CompileResult } from './compile.js';
export { compile } from './compile.js';

View File

@@ -1,6 +1,7 @@
import fs from "node:fs";
import { normalizePath, preprocessCSS } from "vite";
import { preprocessCSS } from "vite";
import { AstroErrorData, CSSError, positionAt } from "../errors/index.js";
import { normalizePath } from "../viteUtils.js";
function createStylePreprocessor({
filename,
viteConfig,

View File

@@ -1,7 +1,6 @@
import fs from 'node:fs';
import type { AstroConfig, AstroInlineConfig, AstroUserConfig } from '../../@types/astro.js';
import type { AstroConfig, AstroInlineConfig, AstroUserConfig } from '../../types/public/config.js';
export declare function resolveRoot(cwd?: string | URL): string;
export declare const configPaths: readonly string[];
interface ResolveConfigPathOptions {
root: string;
configFile?: string | false;

View File

@@ -101,7 +101,6 @@ async function resolveConfig(inlineConfig, command, fsMod = fs) {
return { userConfig: mergedConfig, astroConfig };
}
export {
configPaths,
resolveConfig,
resolveConfigPath,
resolveRoot

View File

@@ -1,6 +1,5 @@
export { configPaths, resolveConfig, resolveConfigPath, resolveRoot, } from './config.js';
export { resolveConfig, resolveConfigPath, resolveRoot, } from './config.js';
export { createNodeLogger } from './logging.js';
export { mergeConfig } from './merge.js';
export type { AstroConfigType } from './schema.js';
export { createSettings } from './settings.js';
export { loadTSConfig, updateTSConfigForFramework } from './tsconfig.js';

View File

@@ -1,5 +1,4 @@
import {
configPaths,
resolveConfig,
resolveConfigPath,
resolveRoot
@@ -9,7 +8,6 @@ import { mergeConfig } from "./merge.js";
import { createSettings } from "./settings.js";
import { loadTSConfig, updateTSConfigForFramework } from "./tsconfig.js";
export {
configPaths,
createNodeLogger,
createSettings,
loadTSConfig,

View File

@@ -1,3 +1,3 @@
import type { AstroInlineConfig } from '../../@types/astro.js';
import type { AstroInlineConfig } from '../../types/public/config.js';
import { Logger } from '../logger/core.js';
export declare function createNodeLogger(inlineConfig: AstroInlineConfig): Logger;

View File

@@ -1 +1,3 @@
export declare function mergeConfig(defaults: Record<string, any>, overrides: Record<string, any>, isRoot?: boolean): Record<string, any>;
import type { DeepPartial } from '../../type-utils.js';
import type { AstroConfig, AstroInlineConfig } from '../../types/public/index.js';
export declare function mergeConfig<C extends AstroConfig | AstroInlineConfig>(defaults: C, overrides: DeepPartial<C>): C;

View File

@@ -7,7 +7,7 @@ function mergeConfigRecursively(defaults, overrides, rootPath) {
if (value == null) {
continue;
}
let existing = merged[key];
const existing = merged[key];
if (existing == null) {
merged[key] = value;
continue;
@@ -26,10 +26,8 @@ function mergeConfigRecursively(defaults, overrides, rootPath) {
continue;
}
}
if (key === "data" && rootPath === "db") {
if (!Array.isArray(existing) && !Array.isArray(value)) {
existing = [existing];
}
if (key === "allowedHosts" && rootPath === "server" && typeof existing === "boolean") {
continue;
}
if (Array.isArray(existing) || Array.isArray(value)) {
merged[key] = [...arraify(existing ?? []), ...arraify(value ?? [])];
@@ -47,8 +45,8 @@ function mergeConfigRecursively(defaults, overrides, rootPath) {
}
return merged;
}
function mergeConfig(defaults, overrides, isRoot = true) {
return mergeConfigRecursively(defaults, overrides, isRoot ? "" : ".");
function mergeConfig(defaults, overrides) {
return mergeConfigRecursively(defaults, overrides, "");
}
export {
mergeConfig

File diff suppressed because it is too large Load Diff

View File

@@ -1,446 +0,0 @@
import { markdownConfigDefaults } from "@astrojs/markdown-remark";
import { bundledThemes } from "shiki";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { z } from "zod";
import { EnvSchema } from "../../env/schema.js";
import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from "../path.js";
const ASTRO_CONFIG_DEFAULTS = {
root: ".",
srcDir: "./src",
publicDir: "./public",
outDir: "./dist",
cacheDir: "./node_modules/.astro",
base: "/",
trailingSlash: "ignore",
build: {
format: "directory",
client: "./dist/client/",
server: "./dist/server/",
assets: "_astro",
serverEntry: "entry.mjs",
redirects: true,
inlineStylesheets: "auto",
concurrency: 1
},
image: {
service: { entrypoint: "astro/assets/services/sharp", config: {} }
},
devToolbar: {
enabled: true
},
compressHTML: true,
server: {
host: false,
port: 4321,
open: false
},
integrations: [],
markdown: markdownConfigDefaults,
vite: {},
legacy: {},
redirects: {},
security: {},
experimental: {
directRenderScript: false,
contentCollectionCache: false,
clientPrerender: false,
globalRoutePriority: false,
serverIslands: false,
contentIntellisense: false,
env: {
validateSecrets: false
},
contentLayer: false
}
};
const AstroConfigSchema = z.object({
root: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.root).transform((val) => new URL(val)),
srcDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.srcDir).transform((val) => new URL(val)),
publicDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.publicDir).transform((val) => new URL(val)),
outDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.outDir).transform((val) => new URL(val)),
cacheDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.cacheDir).transform((val) => new URL(val)),
site: z.string().url().optional(),
compressHTML: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.compressHTML),
base: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.base),
trailingSlash: z.union([z.literal("always"), z.literal("never"), z.literal("ignore")]).optional().default(ASTRO_CONFIG_DEFAULTS.trailingSlash),
output: z.union([z.literal("static"), z.literal("server"), z.literal("hybrid")]).optional().default("static"),
scopedStyleStrategy: z.union([z.literal("where"), z.literal("class"), z.literal("attribute")]).optional().default("attribute"),
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
integrations: z.preprocess(
// preprocess
(val) => Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val,
// validate
z.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) })).default(ASTRO_CONFIG_DEFAULTS.integrations)
),
build: z.object({
format: z.union([z.literal("file"), z.literal("directory"), z.literal("preserve")]).optional().default(ASTRO_CONFIG_DEFAULTS.build.format),
client: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.client).transform((val) => new URL(val)),
server: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.server).transform((val) => new URL(val)),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
assetsPrefix: z.string().optional().or(z.object({ fallback: z.string() }).and(z.record(z.string())).optional()).refine(
(value) => {
if (value && typeof value !== "string") {
if (!value.fallback) {
return false;
}
}
return true;
},
{
message: "The `fallback` is mandatory when defining the option as an object."
}
),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
redirects: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.redirects),
inlineStylesheets: z.enum(["always", "auto", "never"]).optional().default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
concurrency: z.number().min(1).optional().default(ASTRO_CONFIG_DEFAULTS.build.concurrency)
}).default({}),
server: z.preprocess(
// preprocess
// NOTE: Uses the "error" command here because this is overwritten by the
// individualized schema parser with the correct command.
(val) => typeof val === "function" ? val({ command: "error" }) : val,
// validate
z.object({
open: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
headers: z.custom().optional()
}).default({})
),
redirects: z.record(
z.string(),
z.union([
z.string(),
z.object({
status: z.union([
z.literal(300),
z.literal(301),
z.literal(302),
z.literal(303),
z.literal(304),
z.literal(307),
z.literal(308)
]),
destination: z.string()
})
])
).default(ASTRO_CONFIG_DEFAULTS.redirects),
prefetch: z.union([
z.boolean(),
z.object({
prefetchAll: z.boolean().optional(),
defaultStrategy: z.enum(["tap", "hover", "viewport", "load"]).optional()
})
]).optional(),
image: z.object({
endpoint: z.string().optional(),
service: z.object({
entrypoint: z.union([
z.literal("astro/assets/services/sharp"),
z.literal("astro/assets/services/squoosh"),
z.string()
]).default(ASTRO_CONFIG_DEFAULTS.image.service.entrypoint),
config: z.record(z.any()).default({})
}).default(ASTRO_CONFIG_DEFAULTS.image.service),
domains: z.array(z.string()).default([]),
remotePatterns: z.array(
z.object({
protocol: z.string().optional(),
hostname: z.string().refine(
(val) => !val.includes("*") || val.startsWith("*.") || val.startsWith("**."),
{
message: "wildcards can only be placed at the beginning of the hostname"
}
).optional(),
port: z.string().optional(),
pathname: z.string().refine((val) => !val.includes("*") || val.endsWith("/*") || val.endsWith("/**"), {
message: "wildcards can only be placed at the end of a pathname"
}).optional()
})
).default([])
}).default(ASTRO_CONFIG_DEFAULTS.image),
devToolbar: z.object({
enabled: z.boolean().default(ASTRO_CONFIG_DEFAULTS.devToolbar.enabled)
}).default(ASTRO_CONFIG_DEFAULTS.devToolbar),
markdown: z.object({
syntaxHighlight: z.union([z.literal("shiki"), z.literal("prism"), z.literal(false)]).default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight),
shikiConfig: z.object({
langs: z.custom().array().transform((langs) => {
for (const lang of langs) {
if (typeof lang === "object") {
if (lang.id) {
lang.name = lang.id;
}
if (lang.grammar) {
Object.assign(lang, lang.grammar);
}
}
}
return langs;
}).default([]),
langAlias: z.record(z.string(), z.string()).optional().default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.langAlias),
theme: z.enum(Object.keys(bundledThemes)).or(z.custom()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme),
themes: z.record(
z.enum(Object.keys(bundledThemes)).or(z.custom())
).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.themes),
defaultColor: z.union([z.literal("light"), z.literal("dark"), z.string(), z.literal(false)]).optional(),
wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap),
transformers: z.custom().array().transform((transformers) => {
for (const transformer of transformers) {
if (transformer.token && !("span" in transformer)) {
transformer.span = transformer.token;
}
}
return transformers;
}).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.transformers)
}).default({}),
remarkPlugins: z.union([
z.string(),
z.tuple([z.string(), z.any()]),
z.custom((data) => typeof data === "function"),
z.tuple([z.custom((data) => typeof data === "function"), z.any()])
]).array().default(ASTRO_CONFIG_DEFAULTS.markdown.remarkPlugins),
rehypePlugins: z.union([
z.string(),
z.tuple([z.string(), z.any()]),
z.custom((data) => typeof data === "function"),
z.tuple([z.custom((data) => typeof data === "function"), z.any()])
]).array().default(ASTRO_CONFIG_DEFAULTS.markdown.rehypePlugins),
remarkRehype: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype),
gfm: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.gfm),
smartypants: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.smartypants)
}).default({}),
vite: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.vite),
i18n: z.optional(
z.object({
defaultLocale: z.string(),
locales: z.array(
z.union([
z.string(),
z.object({
path: z.string(),
codes: z.string().array().nonempty()
})
])
),
domains: z.record(
z.string(),
z.string().url(
"The domain value must be a valid URL, and it has to start with 'https' or 'http'."
)
).optional(),
fallback: z.record(z.string(), z.string()).optional(),
routing: z.literal("manual").or(
z.object({
prefixDefaultLocale: z.boolean().optional().default(false),
redirectToDefaultLocale: z.boolean().optional().default(true),
fallbackType: z.enum(["redirect", "rewrite"]).optional().default("redirect")
}).refine(
({ prefixDefaultLocale, redirectToDefaultLocale }) => {
return !(prefixDefaultLocale === false && redirectToDefaultLocale === false);
},
{
message: "The option `i18n.redirectToDefaultLocale` is only useful when the `i18n.prefixDefaultLocale` is set to `true`. Remove the option `i18n.redirectToDefaultLocale`, or change its value to `true`."
}
)
).optional().default({})
}).optional().superRefine((i18n, ctx) => {
if (i18n) {
const { defaultLocale, locales: _locales, fallback, domains } = i18n;
const locales = _locales.map((locale) => {
if (typeof locale === "string") {
return locale;
} else {
return locale.path;
}
});
if (!locales.includes(defaultLocale)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The default locale \`${defaultLocale}\` is not present in the \`i18n.locales\` array.`
});
}
if (fallback) {
for (const [fallbackFrom, fallbackTo] of Object.entries(fallback)) {
if (!locales.includes(fallbackFrom)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The locale \`${fallbackFrom}\` key in the \`i18n.fallback\` record doesn't exist in the \`i18n.locales\` array.`
});
}
if (fallbackFrom === defaultLocale) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `You can't use the default locale as a key. The default locale can only be used as value.`
});
}
if (!locales.includes(fallbackTo)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The locale \`${fallbackTo}\` value in the \`i18n.fallback\` record doesn't exist in the \`i18n.locales\` array.`
});
}
}
}
if (domains) {
const entries = Object.entries(domains);
const hasDomains = domains ? Object.keys(domains).length > 0 : false;
if (entries.length > 0 && !hasDomains) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `When specifying some domains, the property \`i18n.routingStrategy\` must be set to \`"domains"\`.`
});
}
for (const [domainKey, domainValue] of entries) {
if (!locales.includes(domainKey)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The locale \`${domainKey}\` key in the \`i18n.domains\` record doesn't exist in the \`i18n.locales\` array.`
});
}
if (!domainValue.startsWith("https") && !domainValue.startsWith("http")) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The domain value must be a valid URL, and it has to start with 'https' or 'http'.",
path: ["domains"]
});
} else {
try {
const domainUrl = new URL(domainValue);
if (domainUrl.pathname !== "/") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The URL \`${domainValue}\` must contain only the origin. A subsequent pathname isn't allowed here. Remove \`${domainUrl.pathname}\`.`,
path: ["domains"]
});
}
} catch {
}
}
}
}
}
})
),
security: z.object({
checkOrigin: z.boolean().default(false)
}).optional().default(ASTRO_CONFIG_DEFAULTS.security),
experimental: z.object({
directRenderScript: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.directRenderScript),
contentCollectionCache: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.contentCollectionCache),
clientPrerender: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.clientPrerender),
globalRoutePriority: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.globalRoutePriority),
env: z.object({
schema: EnvSchema.optional(),
validateSecrets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.env.validateSecrets)
}).strict().optional(),
serverIslands: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.serverIslands),
contentIntellisense: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.contentIntellisense),
contentLayer: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.contentLayer)
}).strict(
`Invalid or outdated experimental feature.
Check for incorrect spelling or outdated Astro version.
See https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.`
).default({}),
legacy: z.object({}).default({})
});
function createRelativeSchema(cmd, fileProtocolRoot) {
const AstroConfigRelativeSchema = AstroConfigSchema.extend({
root: z.string().default(ASTRO_CONFIG_DEFAULTS.root).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
srcDir: z.string().default(ASTRO_CONFIG_DEFAULTS.srcDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
compressHTML: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.compressHTML),
publicDir: z.string().default(ASTRO_CONFIG_DEFAULTS.publicDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
outDir: z.string().default(ASTRO_CONFIG_DEFAULTS.outDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
cacheDir: z.string().default(ASTRO_CONFIG_DEFAULTS.cacheDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
build: z.object({
format: z.union([z.literal("file"), z.literal("directory"), z.literal("preserve")]).optional().default(ASTRO_CONFIG_DEFAULTS.build.format),
client: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.client).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
server: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.server).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
assetsPrefix: z.string().optional().or(z.object({ fallback: z.string() }).and(z.record(z.string())).optional()).refine(
(value) => {
if (value && typeof value !== "string") {
if (!value.fallback) {
return false;
}
}
return true;
},
{
message: "The `fallback` is mandatory when defining the option as an object."
}
),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
redirects: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.redirects),
inlineStylesheets: z.enum(["always", "auto", "never"]).optional().default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
concurrency: z.number().min(1).optional().default(ASTRO_CONFIG_DEFAULTS.build.concurrency)
}).optional().default({}),
server: z.preprocess(
// preprocess
(val) => {
if (typeof val === "function") {
return val({ command: cmd === "dev" ? "dev" : "preview" });
} else {
return val;
}
},
// validate
z.object({
open: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
headers: z.custom().optional(),
streaming: z.boolean().optional().default(true)
}).optional().default({})
)
}).transform((config) => {
if (!config.build.server.toString().startsWith(config.outDir.toString()) && config.build.server.toString().endsWith("dist/server/")) {
config.build.server = new URL("./dist/server/", config.outDir);
}
if (!config.build.client.toString().startsWith(config.outDir.toString()) && config.build.client.toString().endsWith("dist/client/")) {
config.build.client = new URL("./dist/client/", config.outDir);
}
if (config.trailingSlash === "never") {
config.base = prependForwardSlash(removeTrailingForwardSlash(config.base));
} else if (config.trailingSlash === "always") {
config.base = prependForwardSlash(appendForwardSlash(config.base));
} else {
config.base = prependForwardSlash(config.base);
}
return config;
}).refine((obj) => !obj.outDir.toString().startsWith(obj.publicDir.toString()), {
message: "The value of `outDir` must not point to a path within the folder set as `publicDir`, this will cause an infinite loop"
}).superRefine((configuration, ctx) => {
const { site, i18n, output } = configuration;
const hasDomains = i18n?.domains ? Object.keys(i18n.domains).length > 0 : false;
if (hasDomains) {
if (!site) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The option `site` isn't set. When using the 'domains' strategy for `i18n`, `site` is required to create absolute URLs for locales that aren't mapped to a domain."
});
}
if (output !== "server") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Domain support is only available when `output` is `"server"`.'
});
}
}
});
return AstroConfigRelativeSchema;
}
function resolveDirAsUrl(dir, root) {
let resolvedDir = path.resolve(root, dir);
if (!resolvedDir.endsWith(path.sep)) {
resolvedDir += path.sep;
}
return pathToFileURL(resolvedDir);
}
export {
ASTRO_CONFIG_DEFAULTS,
AstroConfigSchema,
createRelativeSchema
};

1625
node_modules/astro/dist/core/config/schemas/base.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

301
node_modules/astro/dist/core/config/schemas/base.js generated vendored Normal file
View File

@@ -0,0 +1,301 @@
import { markdownConfigDefaults, syntaxHighlightDefaults } from "@astrojs/markdown-remark";
import { bundledThemes } from "shiki";
import { z } from "zod";
import { localFontFamilySchema, remoteFontFamilySchema } from "../../../assets/fonts/config.js";
import { EnvSchema } from "../../../env/schema.js";
import { allowedDirectivesSchema, cspAlgorithmSchema, cspHashSchema } from "../../csp/config.js";
const ASTRO_CONFIG_DEFAULTS = {
root: ".",
srcDir: "./src",
publicDir: "./public",
outDir: "./dist",
cacheDir: "./node_modules/.astro",
base: "/",
trailingSlash: "ignore",
build: {
format: "directory",
client: "./client/",
server: "./server/",
assets: "_astro",
serverEntry: "entry.mjs",
redirects: true,
inlineStylesheets: "auto",
concurrency: 1
},
image: {
endpoint: { entrypoint: void 0, route: "/_image" },
service: { entrypoint: "astro/assets/services/sharp", config: {} },
responsiveStyles: false
},
devToolbar: {
enabled: true
},
compressHTML: true,
server: {
host: false,
port: 4321,
open: false,
allowedHosts: []
},
integrations: [],
markdown: markdownConfigDefaults,
vite: {},
legacy: {
collections: false
},
redirects: {},
security: {
checkOrigin: true
},
env: {
schema: {},
validateSecrets: false
},
session: void 0,
experimental: {
clientPrerender: false,
contentIntellisense: false,
headingIdCompat: false,
preserveScriptOrder: false,
liveContentCollections: false,
csp: false,
rawEnvValues: false
}
};
const highlighterTypesSchema = z.union([z.literal("shiki"), z.literal("prism")]).default(syntaxHighlightDefaults.type);
const AstroConfigSchema = z.object({
root: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.root).transform((val) => new URL(val)),
srcDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.srcDir).transform((val) => new URL(val)),
publicDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.publicDir).transform((val) => new URL(val)),
outDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.outDir).transform((val) => new URL(val)),
cacheDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.cacheDir).transform((val) => new URL(val)),
site: z.string().url().optional(),
compressHTML: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.compressHTML),
base: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.base),
trailingSlash: z.union([z.literal("always"), z.literal("never"), z.literal("ignore")]).optional().default(ASTRO_CONFIG_DEFAULTS.trailingSlash),
output: z.union([z.literal("static"), z.literal("server")]).optional().default("static"),
scopedStyleStrategy: z.union([z.literal("where"), z.literal("class"), z.literal("attribute")]).optional().default("attribute"),
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
integrations: z.preprocess(
// preprocess
(val) => Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val,
// validate
z.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) })).default(ASTRO_CONFIG_DEFAULTS.integrations)
),
build: z.object({
format: z.union([z.literal("file"), z.literal("directory"), z.literal("preserve")]).optional().default(ASTRO_CONFIG_DEFAULTS.build.format),
client: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.client).transform((val) => new URL(val)),
server: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.server).transform((val) => new URL(val)),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
assetsPrefix: z.string().optional().or(z.object({ fallback: z.string() }).and(z.record(z.string())).optional()),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
redirects: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.redirects),
inlineStylesheets: z.enum(["always", "auto", "never"]).optional().default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
concurrency: z.number().min(1).optional().default(ASTRO_CONFIG_DEFAULTS.build.concurrency)
}).default({}),
server: z.preprocess(
// preprocess
// NOTE: Uses the "error" command here because this is overwritten by the
// individualized schema parser with the correct command.
(val) => typeof val === "function" ? val({ command: "error" }) : val,
// validate
z.object({
open: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
headers: z.custom().optional(),
allowedHosts: z.union([z.array(z.string()), z.literal(true)]).optional().default(ASTRO_CONFIG_DEFAULTS.server.allowedHosts)
}).default({})
),
redirects: z.record(
z.string(),
z.union([
z.string(),
z.object({
status: z.union([
z.literal(300),
z.literal(301),
z.literal(302),
z.literal(303),
z.literal(304),
z.literal(307),
z.literal(308)
]),
destination: z.string()
})
])
).default(ASTRO_CONFIG_DEFAULTS.redirects),
prefetch: z.union([
z.boolean(),
z.object({
prefetchAll: z.boolean().optional(),
defaultStrategy: z.enum(["tap", "hover", "viewport", "load"]).optional()
})
]).optional(),
image: z.object({
endpoint: z.object({
route: z.literal("/_image").or(z.string()).default(ASTRO_CONFIG_DEFAULTS.image.endpoint.route),
entrypoint: z.string().optional()
}).default(ASTRO_CONFIG_DEFAULTS.image.endpoint),
service: z.object({
entrypoint: z.union([z.literal("astro/assets/services/sharp"), z.string()]).default(ASTRO_CONFIG_DEFAULTS.image.service.entrypoint),
config: z.record(z.any()).default({})
}).default(ASTRO_CONFIG_DEFAULTS.image.service),
domains: z.array(z.string()).default([]),
remotePatterns: z.array(
z.object({
protocol: z.string().optional(),
hostname: z.string().optional(),
port: z.string().optional(),
pathname: z.string().optional()
})
).default([]),
layout: z.enum(["constrained", "fixed", "full-width", "none"]).optional(),
objectFit: z.string().optional(),
objectPosition: z.string().optional(),
breakpoints: z.array(z.number()).optional(),
responsiveStyles: z.boolean().default(ASTRO_CONFIG_DEFAULTS.image.responsiveStyles)
}).default(ASTRO_CONFIG_DEFAULTS.image),
devToolbar: z.object({
enabled: z.boolean().default(ASTRO_CONFIG_DEFAULTS.devToolbar.enabled)
}).default(ASTRO_CONFIG_DEFAULTS.devToolbar),
markdown: z.object({
syntaxHighlight: z.union([
z.object({
type: highlighterTypesSchema,
excludeLangs: z.array(z.string()).optional().default(syntaxHighlightDefaults.excludeLangs)
}).default(syntaxHighlightDefaults),
highlighterTypesSchema,
z.literal(false)
]).default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight),
shikiConfig: z.object({
langs: z.custom().array().transform((langs) => {
for (const lang of langs) {
if (typeof lang === "object") {
if (lang.id) {
lang.name = lang.id;
}
if (lang.grammar) {
Object.assign(lang, lang.grammar);
}
}
}
return langs;
}).default([]),
langAlias: z.record(z.string(), z.string()).optional().default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.langAlias),
theme: z.enum(Object.keys(bundledThemes)).or(z.custom()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme),
themes: z.record(
z.enum(Object.keys(bundledThemes)).or(z.custom())
).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.themes),
defaultColor: z.union([z.literal("light"), z.literal("dark"), z.string(), z.literal(false)]).optional(),
wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap),
transformers: z.custom().array().default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.transformers)
}).default({}),
remarkPlugins: z.union([
z.string(),
z.tuple([z.string(), z.any()]),
z.custom((data) => typeof data === "function"),
z.tuple([z.custom((data) => typeof data === "function"), z.any()])
]).array().default(ASTRO_CONFIG_DEFAULTS.markdown.remarkPlugins),
rehypePlugins: z.union([
z.string(),
z.tuple([z.string(), z.any()]),
z.custom((data) => typeof data === "function"),
z.tuple([z.custom((data) => typeof data === "function"), z.any()])
]).array().default(ASTRO_CONFIG_DEFAULTS.markdown.rehypePlugins),
remarkRehype: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype),
gfm: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.gfm),
smartypants: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.smartypants)
}).default({}),
vite: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.vite),
i18n: z.optional(
z.object({
defaultLocale: z.string(),
locales: z.array(
z.union([
z.string(),
z.object({
path: z.string(),
codes: z.string().array().nonempty()
})
])
),
domains: z.record(
z.string(),
z.string().url(
"The domain value must be a valid URL, and it has to start with 'https' or 'http'."
)
).optional(),
fallback: z.record(z.string(), z.string()).optional(),
routing: z.literal("manual").or(
z.object({
prefixDefaultLocale: z.boolean().optional().default(false),
// TODO: Astro 6.0 change to false
redirectToDefaultLocale: z.boolean().optional().default(true),
fallbackType: z.enum(["redirect", "rewrite"]).optional().default("redirect")
})
).optional().default({})
}).optional()
),
security: z.object({
checkOrigin: z.boolean().default(ASTRO_CONFIG_DEFAULTS.security.checkOrigin)
}).optional().default(ASTRO_CONFIG_DEFAULTS.security),
env: z.object({
schema: EnvSchema.optional().default(ASTRO_CONFIG_DEFAULTS.env.schema),
validateSecrets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.env.validateSecrets)
}).strict().optional().default(ASTRO_CONFIG_DEFAULTS.env),
session: z.object({
driver: z.string().optional(),
options: z.record(z.any()).optional(),
cookie: z.object({
name: z.string().optional(),
domain: z.string().optional(),
path: z.string().optional(),
maxAge: z.number().optional(),
sameSite: z.union([z.enum(["strict", "lax", "none"]), z.boolean()]).optional(),
secure: z.boolean().optional()
}).or(z.string()).transform((val) => {
if (typeof val === "string") {
return { name: val };
}
return val;
}).optional(),
ttl: z.number().optional()
}).optional(),
experimental: z.object({
clientPrerender: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.clientPrerender),
contentIntellisense: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.contentIntellisense),
headingIdCompat: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.headingIdCompat),
preserveScriptOrder: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.preserveScriptOrder),
fonts: z.array(z.union([localFontFamilySchema, remoteFontFamilySchema])).optional(),
liveContentCollections: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.liveContentCollections),
csp: z.union([
z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.csp),
z.object({
algorithm: cspAlgorithmSchema,
directives: z.array(allowedDirectivesSchema).optional(),
styleDirective: z.object({
resources: z.array(z.string()).optional(),
hashes: z.array(cspHashSchema).optional()
}).optional(),
scriptDirective: z.object({
resources: z.array(z.string()).optional(),
hashes: z.array(cspHashSchema).optional(),
strictDynamic: z.boolean().optional()
}).optional()
})
]).optional().default(ASTRO_CONFIG_DEFAULTS.experimental.csp),
rawEnvValues: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.rawEnvValues)
}).strict(
`Invalid or outdated experimental feature.
Check for incorrect spelling or outdated Astro version.
See https://docs.astro.build/en/reference/experimental-flags/ for a list of all current experiments.`
).default({}),
legacy: z.object({
collections: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.legacy.collections)
}).default({})
});
export {
ASTRO_CONFIG_DEFAULTS,
AstroConfigSchema
};

View File

@@ -0,0 +1,3 @@
export { ASTRO_CONFIG_DEFAULTS, AstroConfigSchema, type AstroConfigType, } from './base.js';
export { AstroConfigRefinedSchema } from './refined.js';
export { createRelativeSchema } from './relative.js';

12
node_modules/astro/dist/core/config/schemas/index.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
import {
ASTRO_CONFIG_DEFAULTS,
AstroConfigSchema
} from "./base.js";
import { AstroConfigRefinedSchema } from "./refined.js";
import { createRelativeSchema } from "./relative.js";
export {
ASTRO_CONFIG_DEFAULTS,
AstroConfigRefinedSchema,
AstroConfigSchema,
createRelativeSchema
};

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
import type { AstroConfig } from '../../../types/public/config.js';
export declare const AstroConfigRefinedSchema: z.ZodEffects<z.ZodType<AstroConfig, z.ZodTypeDef, AstroConfig>, AstroConfig, AstroConfig>;

146
node_modules/astro/dist/core/config/schemas/refined.js generated vendored Normal file
View File

@@ -0,0 +1,146 @@
import { z } from "zod";
const AstroConfigRefinedSchema = z.custom().superRefine((config, ctx) => {
if (config.build.assetsPrefix && typeof config.build.assetsPrefix !== "string" && !config.build.assetsPrefix.fallback) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The `fallback` is mandatory when defining the option as an object.",
path: ["build", "assetsPrefix"]
});
}
for (let i = 0; i < config.image.remotePatterns.length; i++) {
const { hostname, pathname } = config.image.remotePatterns[i];
if (hostname && hostname.includes("*") && !(hostname.startsWith("*.") || hostname.startsWith("**."))) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "wildcards can only be placed at the beginning of the hostname",
path: ["image", "remotePatterns", i, "hostname"]
});
}
if (pathname && pathname.includes("*") && !(pathname.endsWith("/*") || pathname.endsWith("/**"))) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "wildcards can only be placed at the end of a pathname",
path: ["image", "remotePatterns", i, "pathname"]
});
}
}
if (config.outDir.toString().startsWith(config.publicDir.toString())) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The value of `outDir` must not point to a path within the folder set as `publicDir`, this will cause an infinite loop",
path: ["outDir"]
});
}
if (config.i18n) {
const { defaultLocale, locales: _locales, fallback, domains } = config.i18n;
const locales = _locales.map((locale) => {
if (typeof locale === "string") {
return locale;
} else {
return locale.path;
}
});
if (!locales.includes(defaultLocale)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The default locale \`${defaultLocale}\` is not present in the \`i18n.locales\` array.`,
path: ["i18n", "locales"]
});
}
if (fallback) {
for (const [fallbackFrom, fallbackTo] of Object.entries(fallback)) {
if (!locales.includes(fallbackFrom)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The locale \`${fallbackFrom}\` key in the \`i18n.fallback\` record doesn't exist in the \`i18n.locales\` array.`,
path: ["i18n", "fallbacks"]
});
}
if (fallbackFrom === defaultLocale) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `You can't use the default locale as a key. The default locale can only be used as value.`,
path: ["i18n", "fallbacks"]
});
}
if (!locales.includes(fallbackTo)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The locale \`${fallbackTo}\` value in the \`i18n.fallback\` record doesn't exist in the \`i18n.locales\` array.`,
path: ["i18n", "fallbacks"]
});
}
}
}
if (domains) {
const entries = Object.entries(domains);
const hasDomains = domains ? Object.keys(domains).length > 0 : false;
if (entries.length > 0 && !hasDomains) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `When specifying some domains, the property \`i18n.routing.strategy\` must be set to \`"domains"\`.`,
path: ["i18n", "routing", "strategy"]
});
}
if (hasDomains) {
if (!config.site) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The option `site` isn't set. When using the 'domains' strategy for `i18n`, `site` is required to create absolute URLs for locales that aren't mapped to a domain.",
path: ["site"]
});
}
if (config.output !== "server") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Domain support is only available when `output` is `"server"`.',
path: ["output"]
});
}
}
for (const [domainKey, domainValue] of entries) {
if (!locales.includes(domainKey)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The locale \`${domainKey}\` key in the \`i18n.domains\` record doesn't exist in the \`i18n.locales\` array.`,
path: ["i18n", "domains"]
});
}
if (!domainValue.startsWith("https") && !domainValue.startsWith("http")) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The domain value must be a valid URL, and it has to start with 'https' or 'http'.",
path: ["i18n", "domains"]
});
} else {
try {
const domainUrl = new URL(domainValue);
if (domainUrl.pathname !== "/") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `The URL \`${domainValue}\` must contain only the origin. A subsequent pathname isn't allowed here. Remove \`${domainUrl.pathname}\`.`,
path: ["i18n", "domains"]
});
}
} catch {
}
}
}
}
}
if (config.experimental.fonts && config.experimental.fonts.length > 0) {
for (let i = 0; i < config.experimental.fonts.length; i++) {
const { cssVariable } = config.experimental.fonts[i];
if (!cssVariable.startsWith("--") || cssVariable.includes(" ") || cssVariable.includes(":")) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `**cssVariable** property "${cssVariable}" contains invalid characters for CSS variable generation. It must start with -- and be a valid indent: https://developer.mozilla.org/en-US/docs/Web/CSS/ident.`,
path: ["fonts", i, "cssVariable"]
});
}
}
}
});
export {
AstroConfigRefinedSchema
};

2109
node_modules/astro/dist/core/config/schemas/relative.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { z } from "zod";
import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from "../../path.js";
import { ASTRO_CONFIG_DEFAULTS, AstroConfigSchema } from "./base.js";
function resolveDirAsUrl(dir, root) {
let resolvedDir = path.resolve(root, dir);
if (!resolvedDir.endsWith(path.sep)) {
resolvedDir += path.sep;
}
return pathToFileURL(resolvedDir);
}
function createRelativeSchema(cmd, fileProtocolRoot) {
let originalBuildClient;
let originalBuildServer;
const AstroConfigRelativeSchema = AstroConfigSchema.extend({
root: z.string().default(ASTRO_CONFIG_DEFAULTS.root).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
srcDir: z.string().default(ASTRO_CONFIG_DEFAULTS.srcDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
compressHTML: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.compressHTML),
publicDir: z.string().default(ASTRO_CONFIG_DEFAULTS.publicDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
outDir: z.string().default(ASTRO_CONFIG_DEFAULTS.outDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
cacheDir: z.string().default(ASTRO_CONFIG_DEFAULTS.cacheDir).transform((val) => resolveDirAsUrl(val, fileProtocolRoot)),
build: z.object({
format: z.union([z.literal("file"), z.literal("directory"), z.literal("preserve")]).optional().default(ASTRO_CONFIG_DEFAULTS.build.format),
// NOTE: `client` and `server` are transformed relative to the default outDir first,
// later we'll fix this to be relative to the actual `outDir`
client: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.client).transform((val) => {
originalBuildClient = val;
return resolveDirAsUrl(
val,
path.resolve(fileProtocolRoot, ASTRO_CONFIG_DEFAULTS.outDir)
);
}),
server: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.server).transform((val) => {
originalBuildServer = val;
return resolveDirAsUrl(
val,
path.resolve(fileProtocolRoot, ASTRO_CONFIG_DEFAULTS.outDir)
);
}),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
assetsPrefix: z.string().optional().or(z.object({ fallback: z.string() }).and(z.record(z.string())).optional()),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
redirects: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.redirects),
inlineStylesheets: z.enum(["always", "auto", "never"]).optional().default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
concurrency: z.number().min(1).optional().default(ASTRO_CONFIG_DEFAULTS.build.concurrency)
}).optional().default({}),
server: z.preprocess(
// preprocess
(val) => {
if (typeof val === "function") {
return val({ command: cmd === "dev" ? "dev" : "preview" });
}
return val;
},
// validate
z.object({
open: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.open),
host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host),
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
headers: z.custom().optional(),
streaming: z.boolean().optional().default(true),
allowedHosts: z.union([z.array(z.string()), z.literal(true)]).optional().default(ASTRO_CONFIG_DEFAULTS.server.allowedHosts)
}).optional().default({})
)
}).transform((config) => {
if (config.outDir.toString() !== resolveDirAsUrl(ASTRO_CONFIG_DEFAULTS.outDir, fileProtocolRoot).toString()) {
const outDirPath = fileURLToPath(config.outDir);
config.build.client = resolveDirAsUrl(originalBuildClient, outDirPath);
config.build.server = resolveDirAsUrl(originalBuildServer, outDirPath);
}
if (config.trailingSlash === "never") {
config.base = prependForwardSlash(removeTrailingForwardSlash(config.base));
config.image.endpoint.route = prependForwardSlash(
removeTrailingForwardSlash(config.image.endpoint.route)
);
} else if (config.trailingSlash === "always") {
config.base = prependForwardSlash(appendForwardSlash(config.base));
config.image.endpoint.route = prependForwardSlash(
appendForwardSlash(config.image.endpoint.route)
);
} else {
config.base = prependForwardSlash(config.base);
config.image.endpoint.route = prependForwardSlash(config.image.endpoint.route);
}
return config;
});
return AstroConfigRelativeSchema;
}
export {
createRelativeSchema
};

View File

@@ -1,3 +1,4 @@
import type { AstroConfig, AstroSettings } from '../../@types/astro.js';
import type { AstroSettings } from '../../types/astro.js';
import type { AstroConfig } from '../../types/public/config.js';
export declare function createBaseSettings(config: AstroConfig): AstroSettings;
export declare function createSettings(config: AstroConfig, cwd?: string): Promise<AstroSettings>;

View File

@@ -1,13 +1,19 @@
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import yaml from "js-yaml";
import toml from "smol-toml";
import { getContentPaths } from "../../content/index.js";
import createPreferences from "../../preferences/index.js";
import { markdownContentEntryType } from "../../vite-plugin-markdown/content-entry-type.js";
import { getDefaultClientDirectives } from "../client-directive/index.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { formatYAMLException, isYAMLException } from "../errors/utils.js";
import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from "./../constants.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import {
formatTOMLError,
formatYAMLException,
isTOMLError,
isYAMLException
} from "../errors/utils.js";
import { AstroTimer } from "./timer.js";
import { loadTSConfig } from "./tsconfig.js";
function createBaseSettings(config) {
@@ -86,6 +92,31 @@ function createBaseSettings(config) {
});
}
}
},
{
extensions: [".toml"],
getEntryInfo({ contents, fileUrl }) {
try {
const data = toml.parse(contents);
const rawData = contents;
return { data, rawData };
} catch (e) {
const pathRelToContentDir = path.relative(
fileURLToPath(contentDir),
fileURLToPath(fileUrl)
);
const formattedError = isTOMLError(e) ? formatTOMLError(e) : new Error("contains invalid TOML.");
throw new AstroError({
...AstroErrorData.DataCollectionEntryParseError,
message: AstroErrorData.DataCollectionEntryParseError.message(
pathRelToContentDir,
formattedError.message
),
stack: formattedError.stack,
location: "loc" in formattedError ? { file: fileUrl.pathname, ...formattedError.loc } : { file: fileUrl.pathname }
});
}
}
}
],
renderers: [],
@@ -98,7 +129,8 @@ function createBaseSettings(config) {
dotAstroDir,
latestAstroVersion: void 0,
// Will be set later if applicable when the dev server starts
injectedTypes: []
injectedTypes: [],
buildOutput: void 0
};
}
async function createSettings(config, cwd) {

View File

@@ -1,8 +1,3 @@
export interface Stat {
elapsedTime: number;
heapUsedChange: number;
heapUsedTotal: number;
}
/**
* Timer to track certain operations' performance. Used by Astro's scripts only.
* Set `process.env.ASTRO_TIMER_PATH` truthy to enable.

View File

@@ -13,7 +13,7 @@ export declare function loadTSConfig(root: string | undefined, findUp?: boolean)
rawConfig: TSConfig;
}>>;
export declare function updateTSConfigForFramework(target: TSConfig, framework: frameworkWithTSSettings): TSConfig;
export type StripEnums<T extends Record<string, any>> = {
type StripEnums<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends boolean ? T[K] : T[K] extends string ? T[K] : T[K] extends object ? T[K] : T[K] extends Array<any> ? T[K] : T[K] extends undefined ? undefined : any;
};
export interface TSConfig {

View File

@@ -1,9 +1,9 @@
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import {
TSConfckParseError,
find,
parse,
TSConfckParseError,
toJson
} from "tsconfck";
const defaultTSConfig = { extends: "astro/tsconfigs/base" };

View File

@@ -1,3 +1,9 @@
import type { AstroConfig } from '../../@types/astro.js';
import type { AstroConfig } from '../../types/public/config.js';
/** Turn raw config values into normalized values */
export declare function validateConfig(userConfig: any, root: string, cmd: string): Promise<AstroConfig>;
/**
* Used twice:
* - To validate the user config
* - To validate the config after all integrations (that may have updated it)
*/
export declare function validateConfigRefined(updatedConfig: AstroConfig): Promise<AstroConfig>;

View File

@@ -1,8 +1,15 @@
import { createRelativeSchema } from "./schema.js";
import { errorMap } from "../errors/index.js";
import { AstroConfigRefinedSchema, createRelativeSchema } from "./schemas/index.js";
async function validateConfig(userConfig, root, cmd) {
const AstroConfigRelativeSchema = createRelativeSchema(cmd, root);
return await AstroConfigRelativeSchema.parseAsync(userConfig);
return await validateConfigRefined(
await AstroConfigRelativeSchema.parseAsync(userConfig, { errorMap })
);
}
async function validateConfigRefined(updatedConfig) {
return await AstroConfigRefinedSchema.parseAsync(updatedConfig, { errorMap });
}
export {
validateConfig
validateConfig,
validateConfigRefined
};

View File

@@ -9,20 +9,7 @@ async function createViteServer(root, fs) {
optimizeDeps: { noDiscovery: true },
clearScreen: false,
appType: "custom",
ssr: {
// NOTE: Vite doesn't externalize linked packages by default. During testing locally,
// these dependencies trip up Vite's dev SSR transform. Awaiting upstream feature:
// https://github.com/vitejs/vite/pull/10939
external: [
"@astrojs/tailwind",
"@astrojs/mdx",
"@astrojs/react",
"@astrojs/preact",
"@astrojs/sitemap",
"@astrojs/markdoc",
"@astrojs/db"
]
},
ssr: { external: true },
plugins: [loadFallbackPlugin({ fs, root: pathToFileURL(root) })]
});
return viteServer;
@@ -37,6 +24,9 @@ async function loadConfigWithVite({
const config = await import(pathToFileURL(configPath).toString() + "?t=" + Date.now());
return config.default ?? {};
} catch (e) {
if (e && typeof e === "object" && "code" in e && e.code === "ERR_DLOPEN_DISABLED") {
throw e;
}
debug("Failed to load config with Node", e);
}
}

View File

@@ -38,10 +38,6 @@ export declare const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
* The value of the `component` field of the default 404 page, which is used when there is no user-provided 404.astro page.
*/
export declare const DEFAULT_404_COMPONENT = "astro-default-404.astro";
/**
* The value of the `component` field of the default 500 page, which is used when there is no user-provided 404.astro page.
*/
export declare const DEFAULT_500_COMPONENT = "astro-default-500.astro";
/**
* A response with one of these status codes will create a redirect response.
*/

View File

@@ -1,11 +1,10 @@
const ASTRO_VERSION = "4.16.18";
const ASTRO_VERSION = "5.12.3";
const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
const DEFAULT_404_COMPONENT = "astro-default-404.astro";
const DEFAULT_500_COMPONENT = "astro-default-500.astro";
const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308, 300, 304];
const REROUTABLE_STATUS_CODES = [404, 500];
const clientAddressSymbol = Symbol.for("astro.clientAddress");
@@ -24,7 +23,6 @@ const MIDDLEWARE_PATH_SEGMENT_NAME = "middleware";
export {
ASTRO_VERSION,
DEFAULT_404_COMPONENT,
DEFAULT_500_COMPONENT,
MIDDLEWARE_PATH_SEGMENT_NAME,
NOOP_MIDDLEWARE_HEADER,
REDIRECT_STATUS_CODES,

View File

@@ -1,5 +1,5 @@
import type { CookieSerializeOptions } from 'cookie';
export type AstroCookieSetOptions = Pick<CookieSerializeOptions, 'domain' | 'path' | 'expires' | 'maxAge' | 'httpOnly' | 'sameSite' | 'secure' | 'encode'>;
import type { SerializeOptions } from 'cookie';
export type AstroCookieSetOptions = Pick<SerializeOptions, 'domain' | 'path' | 'expires' | 'maxAge' | 'httpOnly' | 'sameSite' | 'secure' | 'encode'>;
export interface AstroCookieGetOptions {
decode?: (value: string) => string;
}
@@ -45,9 +45,10 @@ declare class AstroCookies implements AstroCookiesInterface {
* Astro.cookies.has(key) returns a boolean indicating whether this cookie is either
* part of the initial request or set via Astro.cookies.set(key)
* @param key The cookie to check for.
* @param _options This parameter is no longer used.
* @returns
*/
has(key: string, options?: AstroCookieGetOptions | undefined): boolean;
has(key: string, _options?: AstroCookieGetOptions): boolean;
/**
* Astro.cookies.set(key, value) is used to set a cookie's value. If provided
* an object it will be stringified via JSON.stringify(value). Additionally you

View File

@@ -3,6 +3,7 @@ import { AstroError, AstroErrorData } from "../errors/index.js";
const DELETED_EXPIRATION = /* @__PURE__ */ new Date(0);
const DELETED_VALUE = "deleted";
const responseSentSymbol = Symbol.for("astro.responseSent");
const identity = (value) => value;
class AstroCookie {
constructor(value) {
this.value = value;
@@ -73,25 +74,29 @@ class AstroCookies {
return void 0;
}
}
const values = this.#ensureParsed(options);
const decode = options?.decode ?? decodeURIComponent;
const values = this.#ensureParsed();
if (key in values) {
const value = values[key];
return new AstroCookie(value);
if (value) {
return new AstroCookie(decode(value));
}
}
}
/**
* Astro.cookies.has(key) returns a boolean indicating whether this cookie is either
* part of the initial request or set via Astro.cookies.set(key)
* @param key The cookie to check for.
* @param _options This parameter is no longer used.
* @returns
*/
has(key, options = void 0) {
has(key, _options) {
if (this.#outgoing?.has(key)) {
let [, , isSetValue] = this.#outgoing.get(key);
return isSetValue;
}
const values = this.#ensureParsed(options);
return !!values[key];
const values = this.#ensureParsed();
return values[key] !== void 0;
}
/**
* Astro.cookies.set(key, value) is used to set a cookie's value. If provided
@@ -168,9 +173,9 @@ class AstroCookies {
cookies.#consumed = true;
return cookies.headers();
}
#ensureParsed(options = void 0) {
#ensureParsed() {
if (!this.#requestValues) {
this.#parse(options);
this.#parse();
}
if (!this.#requestValues) {
this.#requestValues = {};
@@ -183,12 +188,12 @@ class AstroCookies {
}
return this.#outgoing;
}
#parse(options = void 0) {
#parse() {
const raw = this.#request.headers.get("cookie");
if (!raw) {
return;
}
this.#requestValues = parse(raw, options);
this.#requestValues = parse(raw, { decode: identity });
}
}
export {

View File

@@ -1,3 +1,3 @@
export type { AstroCookieGetOptions, AstroCookieSetOptions } from './cookies.js';
export { AstroCookies } from './cookies.js';
export { attachCookiesToResponse, getSetCookiesFromResponse, responseHasCookies, } from './response.js';
export type { AstroCookieSetOptions, AstroCookieGetOptions } from './cookies.js';
export { attachCookiesToResponse, getSetCookiesFromResponse, } from './response.js';

View File

@@ -1,12 +1,10 @@
import { AstroCookies } from "./cookies.js";
import {
attachCookiesToResponse,
getSetCookiesFromResponse,
responseHasCookies
getSetCookiesFromResponse
} from "./response.js";
export {
AstroCookies,
attachCookiesToResponse,
getSetCookiesFromResponse,
responseHasCookies
getSetCookiesFromResponse
};

View File

@@ -1,5 +1,4 @@
import { AstroCookies } from './cookies.js';
export declare function attachCookiesToResponse(response: Response, cookies: AstroCookies): void;
export declare function responseHasCookies(response: Response): boolean;
export declare function getCookiesFromResponse(response: Response): AstroCookies | undefined;
export declare function getSetCookiesFromResponse(response: Response): Generator<string, string[]>;

View File

@@ -3,9 +3,6 @@ const astroCookiesSymbol = Symbol.for("astro.cookies");
function attachCookiesToResponse(response, cookies) {
Reflect.set(response, astroCookiesSymbol, cookies);
}
function responseHasCookies(response) {
return Reflect.has(response, astroCookiesSymbol);
}
function getCookiesFromResponse(response) {
let cookies = Reflect.get(response, astroCookiesSymbol);
if (cookies != null) {
@@ -27,6 +24,5 @@ function* getSetCookiesFromResponse(response) {
export {
attachCookiesToResponse,
getCookiesFromResponse,
getSetCookiesFromResponse,
responseHasCookies
getSetCookiesFromResponse
};

View File

@@ -1,15 +1,23 @@
import nodeFs from 'node:fs';
import * as vite from 'vite';
import type { AstroSettings } from '../@types/astro.js';
import type { AstroSettings, RoutesList } from '../types/astro.js';
import type { SSRManifest } from './app/types.js';
import type { Logger } from './logger/core.js';
interface CreateViteOptions {
type CreateViteOptions = {
settings: AstroSettings;
logger: Logger;
mode: 'dev' | 'build' | string;
command?: 'dev' | 'build';
mode: string;
fs?: typeof nodeFs;
sync: boolean;
}
routesList: RoutesList;
manifest: SSRManifest;
} & ({
command: 'dev';
manifest: SSRManifest;
} | {
command: 'build';
manifest?: SSRManifest;
});
/** Return a base vite config as a common starting point for all Vite commands. */
export declare function createVite(commandConfig: vite.InlineConfig, { settings, logger, mode, command, fs, sync }: CreateViteOptions): Promise<vite.InlineConfig>;
export declare function createVite(commandConfig: vite.InlineConfig, { settings, logger, mode, command, fs, sync, routesList, manifest }: CreateViteOptions): Promise<vite.InlineConfig>;
export {};

Some files were not shown because too many files have changed in this diff Show More