Refactor routing in App component to enhance navigation and improve error handling by integrating dynamic routes and updating the NotFound route.

This commit is contained in:
becarta
2025-05-23 12:43:00 +02:00
parent f40db0f5c9
commit a544759a3b
11127 changed files with 1647032 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
import { type RenderDestination } from './common.js';
export declare function renderChild(destination: RenderDestination, child: any): Promise<void>;

48
node_modules/astro/dist/runtime/server/render/any.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
import { escapeHTML, isHTMLString, markHTMLString } from "../escape.js";
import { isPromise } from "../util.js";
import { isAstroComponentInstance, isRenderTemplateResult } from "./astro/index.js";
import { isRenderInstance } from "./common.js";
import { SlotString } from "./slot.js";
import { renderToBufferDestination } from "./util.js";
async function renderChild(destination, child) {
if (isPromise(child)) {
child = await child;
}
if (child instanceof SlotString) {
destination.write(child);
} else if (isHTMLString(child)) {
destination.write(child);
} else if (Array.isArray(child)) {
const childRenders = child.map((c) => {
return renderToBufferDestination((bufferDestination) => {
return renderChild(bufferDestination, c);
});
});
for (const childRender of childRenders) {
if (!childRender) continue;
await childRender.renderToFinalDestination(destination);
}
} else if (typeof child === "function") {
await renderChild(destination, child());
} else if (typeof child === "string") {
destination.write(markHTMLString(escapeHTML(child)));
} else if (!child && child !== 0) {
} else if (isRenderInstance(child)) {
await child.render(destination);
} else if (isRenderTemplateResult(child)) {
await child.render(destination);
} else if (isAstroComponentInstance(child)) {
await child.render(destination);
} else if (ArrayBuffer.isView(child)) {
destination.write(child);
} else if (typeof child === "object" && (Symbol.asyncIterator in child || Symbol.iterator in child)) {
for await (const value of child) {
await renderChild(destination, value);
}
} else {
destination.write(child);
}
}
export {
renderChild
};

View File

@@ -0,0 +1,12 @@
import type { PropagationHint, SSRResult } from '../../../../@types/astro.js';
import type { HeadAndContent } from './head-and-content.js';
import type { RenderTemplateResult } from './render-template.js';
export type AstroFactoryReturnValue = RenderTemplateResult | Response | HeadAndContent;
export interface AstroComponentFactory {
(result: any, props: any, slots: any): AstroFactoryReturnValue | Promise<AstroFactoryReturnValue>;
isAstroComponentFactory?: boolean;
moduleId?: string | undefined;
propagation?: PropagationHint;
}
export declare function isAstroComponentFactory(obj: any): obj is AstroComponentFactory;
export declare function isAPropagatingComponent(result: SSRResult, factory: AstroComponentFactory): boolean;

View File

@@ -0,0 +1,14 @@
function isAstroComponentFactory(obj) {
return obj == null ? false : obj.isAstroComponentFactory === true;
}
function isAPropagatingComponent(result, factory) {
let hint = factory.propagation || "none";
if (factory.moduleId && result.componentMetadata.has(factory.moduleId) && hint === "none") {
hint = result.componentMetadata.get(factory.moduleId).propagation;
}
return hint === "in-tree" || hint === "self";
}
export {
isAPropagatingComponent,
isAstroComponentFactory
};

View File

@@ -0,0 +1,10 @@
import type { RenderTemplateResult } from './render-template.js';
declare const headAndContentSym: unique symbol;
export type HeadAndContent = {
[headAndContentSym]: true;
head: string;
content: RenderTemplateResult;
};
export declare function isHeadAndContent(obj: unknown): obj is HeadAndContent;
export declare function createHeadAndContent(head: string, content: RenderTemplateResult): HeadAndContent;
export {};

View File

@@ -0,0 +1,15 @@
const headAndContentSym = Symbol.for("astro.headAndContent");
function isHeadAndContent(obj) {
return typeof obj === "object" && obj !== null && !!obj[headAndContentSym];
}
function createHeadAndContent(head, content) {
return {
[headAndContentSym]: true,
head,
content
};
}
export {
createHeadAndContent,
isHeadAndContent
};

View File

@@ -0,0 +1,7 @@
export { isAstroComponentFactory } from './factory.js';
export type { AstroComponentFactory } from './factory.js';
export { createHeadAndContent, isHeadAndContent } from './head-and-content.js';
export { createAstroComponentInstance, isAstroComponentInstance } from './instance.js';
export type { AstroComponentInstance } from './instance.js';
export { isRenderTemplateResult, renderTemplate } from './render-template.js';
export { renderToReadableStream, renderToString } from './render.js';

View File

@@ -0,0 +1,16 @@
import { isAstroComponentFactory } from "./factory.js";
import { createHeadAndContent, isHeadAndContent } from "./head-and-content.js";
import { createAstroComponentInstance, isAstroComponentInstance } from "./instance.js";
import { isRenderTemplateResult, renderTemplate } from "./render-template.js";
import { renderToReadableStream, renderToString } from "./render.js";
export {
createAstroComponentInstance,
createHeadAndContent,
isAstroComponentFactory,
isAstroComponentInstance,
isHeadAndContent,
isRenderTemplateResult,
renderTemplate,
renderToReadableStream,
renderToString
};

View File

@@ -0,0 +1,20 @@
import type { SSRResult } from '../../../../@types/astro.js';
import type { ComponentSlots } from '../slot.js';
import type { AstroComponentFactory } from './factory.js';
import type { RenderDestination } from '../common.js';
type ComponentProps = Record<string | number, any>;
declare const astroComponentInstanceSym: unique symbol;
export declare class AstroComponentInstance {
[astroComponentInstanceSym]: boolean;
private readonly result;
private readonly props;
private readonly slotValues;
private readonly factory;
private returnValue;
constructor(result: SSRResult, props: ComponentProps, slots: ComponentSlots, factory: AstroComponentFactory);
init(result: SSRResult): Promise<import("./factory.js").AstroFactoryReturnValue>;
render(destination: RenderDestination): Promise<void>;
}
export declare function createAstroComponentInstance(result: SSRResult, displayName: string, factory: AstroComponentFactory, props: ComponentProps, slots?: any): AstroComponentInstance;
export declare function isAstroComponentInstance(obj: unknown): obj is AstroComponentInstance;
export {};

View File

@@ -0,0 +1,76 @@
import { isPromise } from "../../util.js";
import { renderChild } from "../any.js";
import { isAPropagatingComponent } from "./factory.js";
import { isHeadAndContent } from "./head-and-content.js";
const astroComponentInstanceSym = Symbol.for("astro.componentInstance");
class AstroComponentInstance {
[astroComponentInstanceSym] = true;
result;
props;
slotValues;
factory;
returnValue;
constructor(result, props, slots, factory) {
this.result = result;
this.props = props;
this.factory = factory;
this.slotValues = {};
for (const name in slots) {
let didRender = false;
let value = slots[name](result);
this.slotValues[name] = () => {
if (!didRender) {
didRender = true;
return value;
}
return slots[name](result);
};
}
}
async init(result) {
if (this.returnValue !== void 0) return this.returnValue;
this.returnValue = this.factory(result, this.props, this.slotValues);
if (isPromise(this.returnValue)) {
this.returnValue.then((resolved) => {
this.returnValue = resolved;
}).catch(() => {
});
}
return this.returnValue;
}
async render(destination) {
const returnValue = await this.init(this.result);
if (isHeadAndContent(returnValue)) {
await returnValue.content.render(destination);
} else {
await renderChild(destination, returnValue);
}
}
}
function validateComponentProps(props, displayName) {
if (props != null) {
for (const prop of Object.keys(props)) {
if (prop.startsWith("client:")) {
console.warn(
`You are attempting to render <${displayName} ${prop} />, but ${displayName} is an Astro component. Astro components do not render in the client and should not have a hydration directive. Please use a framework component for client rendering.`
);
}
}
}
}
function createAstroComponentInstance(result, displayName, factory, props, slots = {}) {
validateComponentProps(props, displayName);
const instance = new AstroComponentInstance(result, props, slots, factory);
if (isAPropagatingComponent(result, factory)) {
result._metadata.propagators.add(instance);
}
return instance;
}
function isAstroComponentInstance(obj) {
return typeof obj === "object" && obj !== null && !!obj[astroComponentInstanceSym];
}
export {
AstroComponentInstance,
createAstroComponentInstance,
isAstroComponentInstance
};

View File

@@ -0,0 +1,13 @@
import type { RenderDestination } from '../common.js';
declare const renderTemplateResultSym: unique symbol;
export declare class RenderTemplateResult {
[renderTemplateResultSym]: boolean;
private htmlParts;
expressions: any[];
private error;
constructor(htmlParts: TemplateStringsArray, expressions: unknown[]);
render(destination: RenderDestination): Promise<void>;
}
export declare function isRenderTemplateResult(obj: unknown): obj is RenderTemplateResult;
export declare function renderTemplate(htmlParts: TemplateStringsArray, ...expressions: any[]): RenderTemplateResult;
export {};

View File

@@ -0,0 +1,54 @@
import { markHTMLString } from "../../escape.js";
import { isPromise } from "../../util.js";
import { renderChild } from "../any.js";
import { renderToBufferDestination } from "../util.js";
const renderTemplateResultSym = Symbol.for("astro.renderTemplateResult");
class RenderTemplateResult {
[renderTemplateResultSym] = true;
htmlParts;
expressions;
error;
constructor(htmlParts, expressions) {
this.htmlParts = htmlParts;
this.error = void 0;
this.expressions = expressions.map((expression) => {
if (isPromise(expression)) {
return Promise.resolve(expression).catch((err) => {
if (!this.error) {
this.error = err;
throw err;
}
});
}
return expression;
});
}
async render(destination) {
const expRenders = this.expressions.map((exp) => {
return renderToBufferDestination((bufferDestination) => {
if (exp || exp === 0) {
return renderChild(bufferDestination, exp);
}
});
});
for (let i = 0; i < this.htmlParts.length; i++) {
const html = this.htmlParts[i];
const expRender = expRenders[i];
destination.write(markHTMLString(html));
if (expRender) {
await expRender.renderToFinalDestination(destination);
}
}
}
}
function isRenderTemplateResult(obj) {
return typeof obj === "object" && obj !== null && !!obj[renderTemplateResultSym];
}
function renderTemplate(htmlParts, ...expressions) {
return new RenderTemplateResult(htmlParts, expressions);
}
export {
RenderTemplateResult,
isRenderTemplateResult,
renderTemplate
};

View File

@@ -0,0 +1,5 @@
import type { RouteData, SSRResult } from '../../../../@types/astro.js';
import type { AstroComponentFactory } from './factory.js';
export declare function renderToString(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any, isPage?: boolean, route?: RouteData): Promise<string | Response>;
export declare function renderToReadableStream(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any, isPage?: boolean, route?: RouteData): Promise<ReadableStream | Response>;
export declare function renderToAsyncIterable(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any, isPage?: boolean, route?: RouteData): Promise<AsyncIterable<Uint8Array> | Response>;

View File

@@ -0,0 +1,228 @@
import { AstroError, AstroErrorData } from "../../../../core/errors/index.js";
import { chunkToByteArray, chunkToString, encoder } from "../common.js";
import { promiseWithResolvers } from "../util.js";
import { isHeadAndContent } from "./head-and-content.js";
import { isRenderTemplateResult } from "./render-template.js";
const DOCTYPE_EXP = /<!doctype html/i;
async function renderToString(result, componentFactory, props, children, isPage = false, route) {
const templateResult = await callComponentAsTemplateResultOrResponse(
result,
componentFactory,
props,
children,
route
);
if (templateResult instanceof Response) return templateResult;
let str = "";
let renderedFirstPageChunk = false;
if (isPage) {
await bufferHeadContent(result);
}
const destination = {
write(chunk) {
if (isPage && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
const doctype = result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n";
str += doctype;
}
}
if (chunk instanceof Response) return;
str += chunkToString(result, chunk);
}
};
await templateResult.render(destination);
return str;
}
async function renderToReadableStream(result, componentFactory, props, children, isPage = false, route) {
const templateResult = await callComponentAsTemplateResultOrResponse(
result,
componentFactory,
props,
children,
route
);
if (templateResult instanceof Response) return templateResult;
let renderedFirstPageChunk = false;
if (isPage) {
await bufferHeadContent(result);
}
return new ReadableStream({
start(controller) {
const destination = {
write(chunk) {
if (isPage && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
const doctype = result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n";
controller.enqueue(encoder.encode(doctype));
}
}
if (chunk instanceof Response) {
throw new AstroError({
...AstroErrorData.ResponseSentError
});
}
const bytes = chunkToByteArray(result, chunk);
controller.enqueue(bytes);
}
};
(async () => {
try {
await templateResult.render(destination);
controller.close();
} catch (e) {
if (AstroError.is(e) && !e.loc) {
e.setLocation({
file: route?.component
});
}
setTimeout(() => controller.error(e), 0);
}
})();
},
cancel() {
result.cancelled = true;
}
});
}
async function callComponentAsTemplateResultOrResponse(result, componentFactory, props, children, route) {
const factoryResult = await componentFactory(result, props, children);
if (factoryResult instanceof Response) {
return factoryResult;
} else if (isHeadAndContent(factoryResult)) {
if (!isRenderTemplateResult(factoryResult.content)) {
throw new AstroError({
...AstroErrorData.OnlyResponseCanBeReturned,
message: AstroErrorData.OnlyResponseCanBeReturned.message(
route?.route,
typeof factoryResult
),
location: {
file: route?.component
}
});
}
return factoryResult.content;
} else if (!isRenderTemplateResult(factoryResult)) {
throw new AstroError({
...AstroErrorData.OnlyResponseCanBeReturned,
message: AstroErrorData.OnlyResponseCanBeReturned.message(route?.route, typeof factoryResult),
location: {
file: route?.component
}
});
}
return factoryResult;
}
async function bufferHeadContent(result) {
const iterator = result._metadata.propagators.values();
while (true) {
const { value, done } = iterator.next();
if (done) {
break;
}
const returnValue = await value.init(result);
if (isHeadAndContent(returnValue)) {
result._metadata.extraHead.push(returnValue.head);
}
}
}
async function renderToAsyncIterable(result, componentFactory, props, children, isPage = false, route) {
const templateResult = await callComponentAsTemplateResultOrResponse(
result,
componentFactory,
props,
children,
route
);
if (templateResult instanceof Response) return templateResult;
let renderedFirstPageChunk = false;
if (isPage) {
await bufferHeadContent(result);
}
let error = null;
let next = null;
const buffer = [];
let renderingComplete = false;
const iterator = {
async next() {
if (result.cancelled) return { done: true, value: void 0 };
if (next !== null) {
await next.promise;
} else if (!renderingComplete && !buffer.length) {
next = promiseWithResolvers();
await next.promise;
}
if (!renderingComplete) {
next = promiseWithResolvers();
}
if (error) {
throw error;
}
let length = 0;
for (let i = 0, len = buffer.length; i < len; i++) {
length += buffer[i].length;
}
let mergedArray = new Uint8Array(length);
let offset = 0;
for (let i = 0, len = buffer.length; i < len; i++) {
const item = buffer[i];
mergedArray.set(item, offset);
offset += item.length;
}
buffer.length = 0;
const returnValue = {
// The iterator is done when rendering has finished
// and there are no more chunks to return.
done: length === 0 && renderingComplete,
value: mergedArray
};
return returnValue;
},
async return() {
result.cancelled = true;
return { done: true, value: void 0 };
}
};
const destination = {
write(chunk) {
if (isPage && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
const doctype = result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n";
buffer.push(encoder.encode(doctype));
}
}
if (chunk instanceof Response) {
throw new AstroError(AstroErrorData.ResponseSentError);
}
const bytes = chunkToByteArray(result, chunk);
if (bytes.length > 0) {
buffer.push(bytes);
next?.resolve();
} else if (buffer.length > 0) {
next?.resolve();
}
}
};
const renderPromise = templateResult.render(destination);
renderPromise.then(() => {
renderingComplete = true;
next?.resolve();
}).catch((err) => {
error = err;
renderingComplete = true;
next?.resolve();
});
return {
[Symbol.asyncIterator]() {
return iterator;
}
};
}
export {
renderToAsyncIterable,
renderToReadableStream,
renderToString
};

View File

@@ -0,0 +1,30 @@
import type { SSRResult } from '../../../@types/astro.js';
import type { RenderInstruction } from './instruction.js';
import type { HTMLBytes, HTMLString } from '../escape.js';
import { type SlotString } from './slot.js';
/**
* Possible chunk types to be written to the destination, and it'll
* handle stringifying them at the end.
*
* NOTE: Try to reduce adding new types here. If possible, serialize
* the custom types to a string in `renderChild` in `any.ts`.
*/
export type RenderDestinationChunk = string | HTMLBytes | HTMLString | SlotString | ArrayBufferView | RenderInstruction | Response;
export interface RenderDestination {
/**
* Any rendering logic should call this to construct the HTML output.
* See the `chunk` parameter for possible writable values.
*/
write(chunk: RenderDestinationChunk): void;
}
export interface RenderInstance {
render: RenderFunction;
}
export type RenderFunction = (destination: RenderDestination) => Promise<void> | void;
export declare const Fragment: unique symbol;
export declare const Renderer: unique symbol;
export declare const encoder: TextEncoder;
export declare const decoder: TextDecoder;
export declare function chunkToString(result: SSRResult, chunk: Exclude<RenderDestinationChunk, Response>): string;
export declare function chunkToByteArray(result: SSRResult, chunk: Exclude<RenderDestinationChunk, Response>): Uint8Array;
export declare function isRenderInstance(obj: unknown): obj is RenderInstance;

View File

@@ -0,0 +1,96 @@
import { markHTMLString } from "../escape.js";
import {
determineIfNeedsHydrationScript,
determinesIfNeedsDirectiveScript,
getPrescripts
} from "../scripts.js";
import { renderAllHeadContent } from "./head.js";
import { isRenderInstruction } from "./instruction.js";
import { isSlotString } from "./slot.js";
const Fragment = Symbol.for("astro:fragment");
const Renderer = Symbol.for("astro:renderer");
const encoder = new TextEncoder();
const decoder = new TextDecoder();
function stringifyChunk(result, chunk) {
if (isRenderInstruction(chunk)) {
const instruction = chunk;
switch (instruction.type) {
case "directive": {
const { hydration } = instruction;
let needsHydrationScript = hydration && determineIfNeedsHydrationScript(result);
let needsDirectiveScript = hydration && determinesIfNeedsDirectiveScript(result, hydration.directive);
let prescriptType = needsHydrationScript ? "both" : needsDirectiveScript ? "directive" : null;
if (prescriptType) {
let prescripts = getPrescripts(result, prescriptType, hydration.directive);
return markHTMLString(prescripts);
} else {
return "";
}
}
case "head": {
if (result._metadata.hasRenderedHead || result.partial) {
return "";
}
return renderAllHeadContent(result);
}
case "maybe-head": {
if (result._metadata.hasRenderedHead || result._metadata.headInTree || result.partial) {
return "";
}
return renderAllHeadContent(result);
}
case "renderer-hydration-script": {
const { rendererSpecificHydrationScripts } = result._metadata;
const { rendererName } = instruction;
if (!rendererSpecificHydrationScripts.has(rendererName)) {
rendererSpecificHydrationScripts.add(rendererName);
return instruction.render();
}
return "";
}
default: {
throw new Error(`Unknown chunk type: ${chunk.type}`);
}
}
} else if (chunk instanceof Response) {
return "";
} else if (isSlotString(chunk)) {
let out = "";
const c = chunk;
if (c.instructions) {
for (const instr of c.instructions) {
out += stringifyChunk(result, instr);
}
}
out += chunk.toString();
return out;
}
return chunk.toString();
}
function chunkToString(result, chunk) {
if (ArrayBuffer.isView(chunk)) {
return decoder.decode(chunk);
} else {
return stringifyChunk(result, chunk);
}
}
function chunkToByteArray(result, chunk) {
if (ArrayBuffer.isView(chunk)) {
return chunk;
} else {
const stringified = stringifyChunk(result, chunk);
return encoder.encode(stringified.toString());
}
}
function isRenderInstance(obj) {
return !!obj && typeof obj === "object" && "render" in obj && typeof obj.render === "function";
}
export {
Fragment,
Renderer,
chunkToByteArray,
chunkToString,
decoder,
encoder,
isRenderInstance
};

View File

@@ -0,0 +1,11 @@
import type { RouteData, SSRResult } from '../../../@types/astro.js';
import { type RenderInstance } from './common.js';
import { type ComponentSlots } from './slot.js';
declare const needsHeadRenderingSymbol: unique symbol;
export declare function renderComponent(result: SSRResult, displayName: string, Component: unknown, props: Record<string | number, any>, slots?: ComponentSlots): Promise<RenderInstance>;
export declare function renderComponentToString(result: SSRResult, displayName: string, Component: unknown, props: Record<string | number, any>, slots?: any, isPage?: boolean, route?: RouteData): Promise<string>;
export type NonAstroPageComponent = {
name: string;
[needsHeadRenderingSymbol]: boolean;
};
export {};

View File

@@ -0,0 +1,447 @@
import { createRenderInstruction } from "./instruction.js";
import { clsx } from "clsx";
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
import { markHTMLString } from "../escape.js";
import { extractDirectives, generateHydrateScript } from "../hydration.js";
import { serializeProps } from "../serialize.js";
import { shorthash } from "../shorthash.js";
import { isPromise } from "../util.js";
import { isAstroComponentFactory } from "./astro/factory.js";
import { renderTemplate } from "./astro/index.js";
import { createAstroComponentInstance } from "./astro/instance.js";
import {
Fragment,
Renderer,
chunkToString
} from "./common.js";
import { componentIsHTMLElement, renderHTMLElement } from "./dom.js";
import { maybeRenderHead } from "./head.js";
import { containsServerDirective, renderServerIsland } from "./server-islands.js";
import { renderSlotToString, renderSlots } from "./slot.js";
import { formatList, internalSpreadAttributes, renderElement, voidElementNames } from "./util.js";
const needsHeadRenderingSymbol = Symbol.for("astro.needsHeadRendering");
const rendererAliases = /* @__PURE__ */ new Map([["solid", "solid-js"]]);
const clientOnlyValues = /* @__PURE__ */ new Set(["solid-js", "react", "preact", "vue", "svelte", "lit"]);
function guessRenderers(componentUrl) {
const extname = componentUrl?.split(".").pop();
switch (extname) {
case "svelte":
return ["@astrojs/svelte"];
case "vue":
return ["@astrojs/vue"];
case "jsx":
case "tsx":
return ["@astrojs/react", "@astrojs/preact", "@astrojs/solid-js", "@astrojs/vue (jsx)"];
case void 0:
default:
return [
"@astrojs/react",
"@astrojs/preact",
"@astrojs/solid-js",
"@astrojs/vue",
"@astrojs/svelte",
"@astrojs/lit"
];
}
}
function isFragmentComponent(Component) {
return Component === Fragment;
}
function isHTMLComponent(Component) {
return Component && Component["astro:html"] === true;
}
const ASTRO_SLOT_EXP = /<\/?astro-slot\b[^>]*>/g;
const ASTRO_STATIC_SLOT_EXP = /<\/?astro-static-slot\b[^>]*>/g;
function removeStaticAstroSlot(html, supportsAstroStaticSlot = true) {
const exp = supportsAstroStaticSlot ? ASTRO_STATIC_SLOT_EXP : ASTRO_SLOT_EXP;
return html.replace(exp, "");
}
async function renderFrameworkComponent(result, displayName, Component, _props, slots = {}) {
if (!Component && "client:only" in _props === false) {
throw new Error(
`Unable to render ${displayName} because it is ${Component}!
Did you forget to import the component or is it possible there is a typo?`
);
}
const { renderers, clientDirectives } = result;
const metadata = {
astroStaticSlot: true,
displayName
};
const { hydration, isPage, props, propsWithoutTransitionAttributes } = extractDirectives(
_props,
clientDirectives
);
let html = "";
let attrs = void 0;
if (hydration) {
metadata.hydrate = hydration.directive;
metadata.hydrateArgs = hydration.value;
metadata.componentExport = hydration.componentExport;
metadata.componentUrl = hydration.componentUrl;
}
const probableRendererNames = guessRenderers(metadata.componentUrl);
const validRenderers = renderers.filter((r) => r.name !== "astro:jsx");
const { children, slotInstructions } = await renderSlots(result, slots);
let renderer;
if (metadata.hydrate !== "only") {
let isTagged = false;
try {
isTagged = Component && Component[Renderer];
} catch {
}
if (isTagged) {
const rendererName = Component[Renderer];
renderer = renderers.find(({ name }) => name === rendererName);
}
if (!renderer) {
let error;
for (const r of renderers) {
try {
if (await r.ssr.check.call({ result }, Component, props, children)) {
renderer = r;
break;
}
} catch (e) {
error ??= e;
}
}
if (!renderer && error) {
throw error;
}
}
if (!renderer && typeof HTMLElement === "function" && componentIsHTMLElement(Component)) {
const output = await renderHTMLElement(
result,
Component,
_props,
slots
);
return {
render(destination) {
destination.write(output);
}
};
}
} else {
if (metadata.hydrateArgs) {
const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs;
if (clientOnlyValues.has(rendererName)) {
renderer = renderers.find(
({ name }) => name === `@astrojs/${rendererName}` || name === rendererName
);
}
}
if (!renderer && validRenderers.length === 1) {
renderer = validRenderers[0];
}
if (!renderer) {
const extname = metadata.componentUrl?.split(".").pop();
renderer = renderers.find(({ name }) => name === `@astrojs/${extname}` || name === extname);
}
}
let componentServerRenderEndTime;
if (!renderer) {
if (metadata.hydrate === "only") {
const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs;
if (clientOnlyValues.has(rendererName)) {
const plural = validRenderers.length > 1;
throw new AstroError({
...AstroErrorData.NoMatchingRenderer,
message: AstroErrorData.NoMatchingRenderer.message(
metadata.displayName,
metadata?.componentUrl?.split(".").pop(),
plural,
validRenderers.length
),
hint: AstroErrorData.NoMatchingRenderer.hint(
formatList(probableRendererNames.map((r) => "`" + r + "`"))
)
});
} else {
throw new AstroError({
...AstroErrorData.NoClientOnlyHint,
message: AstroErrorData.NoClientOnlyHint.message(metadata.displayName),
hint: AstroErrorData.NoClientOnlyHint.hint(
probableRendererNames.map((r) => r.replace("@astrojs/", "")).join("|")
)
});
}
} else if (typeof Component !== "string") {
const matchingRenderers = validRenderers.filter(
(r) => probableRendererNames.includes(r.name)
);
const plural = validRenderers.length > 1;
if (matchingRenderers.length === 0) {
throw new AstroError({
...AstroErrorData.NoMatchingRenderer,
message: AstroErrorData.NoMatchingRenderer.message(
metadata.displayName,
metadata?.componentUrl?.split(".").pop(),
plural,
validRenderers.length
),
hint: AstroErrorData.NoMatchingRenderer.hint(
formatList(probableRendererNames.map((r) => "`" + r + "`"))
)
});
} else if (matchingRenderers.length === 1) {
renderer = matchingRenderers[0];
({ html, attrs } = await renderer.ssr.renderToStaticMarkup.call(
{ result },
Component,
propsWithoutTransitionAttributes,
children,
metadata
));
} else {
throw new Error(`Unable to render ${metadata.displayName}!
This component likely uses ${formatList(probableRendererNames)},
but Astro encountered an error during server-side rendering.
Please ensure that ${metadata.displayName}:
1. Does not unconditionally access browser-specific globals like \`window\` or \`document\`.
If this is unavoidable, use the \`client:only\` hydration directive.
2. Does not conditionally return \`null\` or \`undefined\` when rendered on the server.
If you're still stuck, please open an issue on GitHub or join us at https://astro.build/chat.`);
}
}
} else {
if (metadata.hydrate === "only") {
const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs;
if (!clientOnlyValues.has(rendererName)) {
console.warn(
`The client:only directive for ${metadata.displayName} is not recognized. The renderer ${renderer.name} will be used. If you intended to use a different renderer, please provide a valid client:only directive.`
);
}
html = await renderSlotToString(result, slots?.fallback);
} else {
const componentRenderStartTime = performance.now();
({ html, attrs } = await renderer.ssr.renderToStaticMarkup.call(
{ result },
Component,
propsWithoutTransitionAttributes,
children,
metadata
));
if (process.env.NODE_ENV === "development")
componentServerRenderEndTime = performance.now() - componentRenderStartTime;
}
}
if (renderer && !renderer.clientEntrypoint && renderer.name !== "@astrojs/lit" && metadata.hydrate) {
throw new AstroError({
...AstroErrorData.NoClientEntrypoint,
message: AstroErrorData.NoClientEntrypoint.message(
displayName,
metadata.hydrate,
renderer.name
)
});
}
if (!html && typeof Component === "string") {
const Tag = sanitizeElementName(Component);
const childSlots = Object.values(children).join("");
const renderTemplateResult = renderTemplate`<${Tag}${internalSpreadAttributes(
props
)}${markHTMLString(
childSlots === "" && voidElementNames.test(Tag) ? `/>` : `>${childSlots}</${Tag}>`
)}`;
html = "";
const destination = {
write(chunk) {
if (chunk instanceof Response) return;
html += chunkToString(result, chunk);
}
};
await renderTemplateResult.render(destination);
}
if (!hydration) {
return {
render(destination) {
if (slotInstructions) {
for (const instruction of slotInstructions) {
destination.write(instruction);
}
}
if (isPage || renderer?.name === "astro:jsx") {
destination.write(html);
} else if (html && html.length > 0) {
destination.write(
markHTMLString(removeStaticAstroSlot(html, renderer?.ssr?.supportsAstroStaticSlot))
);
}
}
};
}
const astroId = shorthash(
`<!--${metadata.componentExport.value}:${metadata.componentUrl}-->
${html}
${serializeProps(
props,
metadata
)}`
);
const island = await generateHydrateScript(
{ renderer, result, astroId, props, attrs },
metadata
);
if (componentServerRenderEndTime && process.env.NODE_ENV === "development")
island.props["server-render-time"] = componentServerRenderEndTime;
let unrenderedSlots = [];
if (html) {
if (Object.keys(children).length > 0) {
for (const key of Object.keys(children)) {
let tagName = renderer?.ssr?.supportsAstroStaticSlot ? !!metadata.hydrate ? "astro-slot" : "astro-static-slot" : "astro-slot";
let expectedHTML = key === "default" ? `<${tagName}>` : `<${tagName} name="${key}">`;
if (!html.includes(expectedHTML)) {
unrenderedSlots.push(key);
}
}
}
} else {
unrenderedSlots = Object.keys(children);
}
const template = unrenderedSlots.length > 0 ? unrenderedSlots.map(
(key) => `<template data-astro-template${key !== "default" ? `="${key}"` : ""}>${children[key]}</template>`
).join("") : "";
island.children = `${html ?? ""}${template}`;
if (island.children) {
island.props["await-children"] = "";
island.children += `<!--astro:end-->`;
}
return {
render(destination) {
if (slotInstructions) {
for (const instruction of slotInstructions) {
destination.write(instruction);
}
}
destination.write(createRenderInstruction({ type: "directive", hydration }));
if (hydration.directive !== "only" && renderer?.ssr.renderHydrationScript) {
destination.write(
createRenderInstruction({
type: "renderer-hydration-script",
rendererName: renderer.name,
render: renderer.ssr.renderHydrationScript
})
);
}
const renderedElement = renderElement("astro-island", island, false);
destination.write(markHTMLString(renderedElement));
}
};
}
function sanitizeElementName(tag) {
const unsafe = /[&<>'"\s]+/;
if (!unsafe.test(tag)) return tag;
return tag.trim().split(unsafe)[0].trim();
}
async function renderFragmentComponent(result, slots = {}) {
const children = await renderSlotToString(result, slots?.default);
return {
render(destination) {
if (children == null) return;
destination.write(children);
}
};
}
async function renderHTMLComponent(result, Component, _props, slots = {}) {
const { slotInstructions, children } = await renderSlots(result, slots);
const html = Component({ slots: children });
const hydrationHtml = slotInstructions ? slotInstructions.map((instr) => chunkToString(result, instr)).join("") : "";
return {
render(destination) {
destination.write(markHTMLString(hydrationHtml + html));
}
};
}
function renderAstroComponent(result, displayName, Component, props, slots = {}) {
if (containsServerDirective(props)) {
return renderServerIsland(result, displayName, props, slots);
}
const instance = createAstroComponentInstance(result, displayName, Component, props, slots);
return {
async render(destination) {
await instance.render(destination);
}
};
}
async function renderComponent(result, displayName, Component, props, slots = {}) {
if (isPromise(Component)) {
Component = await Component.catch(handleCancellation);
}
if (isFragmentComponent(Component)) {
return await renderFragmentComponent(result, slots).catch(handleCancellation);
}
props = normalizeProps(props);
if (isHTMLComponent(Component)) {
return await renderHTMLComponent(result, Component, props, slots).catch(handleCancellation);
}
if (isAstroComponentFactory(Component)) {
return renderAstroComponent(result, displayName, Component, props, slots);
}
return await renderFrameworkComponent(result, displayName, Component, props, slots).catch(
handleCancellation
);
function handleCancellation(e) {
if (result.cancelled)
return {
render() {
}
};
throw e;
}
}
function normalizeProps(props) {
if (props["class:list"] !== void 0) {
const value = props["class:list"];
delete props["class:list"];
props["class"] = clsx(props["class"], value);
if (props["class"] === "") {
delete props["class"];
}
}
return props;
}
async function renderComponentToString(result, displayName, Component, props, slots = {}, isPage = false, route) {
let str = "";
let renderedFirstPageChunk = false;
let head = "";
if (isPage && !result.partial && nonAstroPageNeedsHeadInjection(Component)) {
head += chunkToString(result, maybeRenderHead());
}
try {
const destination = {
write(chunk) {
if (isPage && !result.partial && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!/<!doctype html/i.test(String(chunk))) {
const doctype = result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n";
str += doctype + head;
}
}
if (chunk instanceof Response) return;
str += chunkToString(result, chunk);
}
};
const renderInstance = await renderComponent(result, displayName, Component, props, slots);
await renderInstance.render(destination);
} catch (e) {
if (AstroError.is(e) && !e.loc) {
e.setLocation({
file: route?.component
});
}
throw e;
}
return str;
}
function nonAstroPageNeedsHeadInjection(pageComponent) {
return !!pageComponent?.[needsHeadRenderingSymbol];
}
export {
renderComponent,
renderComponentToString
};

View File

@@ -0,0 +1,3 @@
import type { SSRResult } from '../../../@types/astro.js';
export declare function componentIsHTMLElement(Component: unknown): boolean;
export declare function renderHTMLElement(result: SSRResult, constructor: typeof HTMLElement, props: any, slots: any): Promise<string>;

26
node_modules/astro/dist/runtime/server/render/dom.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
import { markHTMLString } from "../escape.js";
import { renderSlotToString } from "./slot.js";
import { toAttributeString } from "./util.js";
function componentIsHTMLElement(Component) {
return typeof HTMLElement !== "undefined" && HTMLElement.isPrototypeOf(Component);
}
async function renderHTMLElement(result, constructor, props, slots) {
const name = getHTMLElementName(constructor);
let attrHTML = "";
for (const attr in props) {
attrHTML += ` ${attr}="${toAttributeString(await props[attr])}"`;
}
return markHTMLString(
`<${name}${attrHTML}>${await renderSlotToString(result, slots?.default)}</${name}>`
);
}
function getHTMLElementName(constructor) {
const definedName = customElements.getName(constructor);
if (definedName) return definedName;
const assignedName = constructor.name.replace(/^HTML|Element$/g, "").replace(/[A-Z]/g, "-$&").toLowerCase().replace(/^-/, "html-");
return assignedName;
}
export {
componentIsHTMLElement,
renderHTMLElement
};

View File

@@ -0,0 +1,5 @@
import type { SSRResult } from '../../../@types/astro.js';
import type { MaybeRenderHeadInstruction, RenderHeadInstruction } from './instruction.js';
export declare function renderAllHeadContent(result: SSRResult): any;
export declare function renderHead(): RenderHeadInstruction;
export declare function maybeRenderHead(): MaybeRenderHeadInstruction;

37
node_modules/astro/dist/runtime/server/render/head.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
import { markHTMLString } from "../escape.js";
import { createRenderInstruction } from "./instruction.js";
import { renderElement } from "./util.js";
const uniqueElements = (item, index, all) => {
const props = JSON.stringify(item.props);
const children = item.children;
return index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children == children);
};
function renderAllHeadContent(result) {
result._metadata.hasRenderedHead = true;
const styles = Array.from(result.styles).filter(uniqueElements).map(
(style) => style.props.rel === "stylesheet" ? renderElement("link", style) : renderElement("style", style)
);
result.styles.clear();
const scripts = Array.from(result.scripts).filter(uniqueElements).map((script) => {
return renderElement("script", script, false);
});
const links = Array.from(result.links).filter(uniqueElements).map((link) => renderElement("link", link, false));
let content = styles.join("\n") + links.join("\n") + scripts.join("\n");
if (result._metadata.extraHead.length > 0) {
for (const part of result._metadata.extraHead) {
content += part;
}
}
return markHTMLString(content);
}
function renderHead() {
return createRenderInstruction({ type: "head" });
}
function maybeRenderHead() {
return createRenderInstruction({ type: "maybe-head" });
}
export {
maybeRenderHead,
renderAllHeadContent,
renderHead
};

View File

@@ -0,0 +1,12 @@
export { createHeadAndContent, renderTemplate, renderToString } from './astro/index.js';
export type { AstroComponentFactory, AstroComponentInstance } from './astro/index.js';
export { Fragment, Renderer, chunkToByteArray, chunkToString } from './common.js';
export { renderComponent, renderComponentToString } from './component.js';
export { renderScript } from './script.js';
export { renderHTMLElement } from './dom.js';
export { maybeRenderHead, renderHead } from './head.js';
export type { RenderInstruction } from './instruction.js';
export { renderPage } from './page.js';
export { renderSlot, renderSlotToString, type ComponentSlots } from './slot.js';
export { renderScriptElement, renderUniqueStylesheet } from './tags.js';
export { addAttribute, defineScriptVars, voidElementNames } from './util.js';

33
node_modules/astro/dist/runtime/server/render/index.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
import { createHeadAndContent, renderTemplate, renderToString } from "./astro/index.js";
import { Fragment, Renderer, chunkToByteArray, chunkToString } from "./common.js";
import { renderComponent, renderComponentToString } from "./component.js";
import { renderScript } from "./script.js";
import { renderHTMLElement } from "./dom.js";
import { maybeRenderHead, renderHead } from "./head.js";
import { renderPage } from "./page.js";
import { renderSlot, renderSlotToString } from "./slot.js";
import { renderScriptElement, renderUniqueStylesheet } from "./tags.js";
import { addAttribute, defineScriptVars, voidElementNames } from "./util.js";
export {
Fragment,
Renderer,
addAttribute,
chunkToByteArray,
chunkToString,
createHeadAndContent,
defineScriptVars,
maybeRenderHead,
renderComponent,
renderComponentToString,
renderHTMLElement,
renderHead,
renderPage,
renderScript,
renderScriptElement,
renderSlot,
renderSlotToString,
renderTemplate,
renderToString,
renderUniqueStylesheet,
voidElementNames
};

View File

@@ -0,0 +1,26 @@
import type { HydrationMetadata } from '../hydration.js';
export type RenderDirectiveInstruction = {
type: 'directive';
hydration: HydrationMetadata;
};
export type RenderHeadInstruction = {
type: 'head';
};
/**
* Render a renderer-specific hydration script before the first component of that
* framework
*/
export type RendererHydrationScriptInstruction = {
type: 'renderer-hydration-script';
rendererName: string;
render: () => string;
};
export type MaybeRenderHeadInstruction = {
type: 'maybe-head';
};
export type RenderInstruction = RenderDirectiveInstruction | RenderHeadInstruction | MaybeRenderHeadInstruction | RendererHydrationScriptInstruction;
export declare function createRenderInstruction(instruction: RenderDirectiveInstruction): RenderDirectiveInstruction;
export declare function createRenderInstruction(instruction: RendererHydrationScriptInstruction): RendererHydrationScriptInstruction;
export declare function createRenderInstruction(instruction: RenderHeadInstruction): RenderHeadInstruction;
export declare function createRenderInstruction(instruction: MaybeRenderHeadInstruction): MaybeRenderHeadInstruction;
export declare function isRenderInstruction(chunk: any): chunk is RenderInstruction;

View File

@@ -0,0 +1,13 @@
const RenderInstructionSymbol = Symbol.for("astro:render");
function createRenderInstruction(instruction) {
return Object.defineProperty(instruction, RenderInstructionSymbol, {
value: true
});
}
function isRenderInstruction(chunk) {
return chunk && typeof chunk === "object" && chunk[RenderInstructionSymbol];
}
export {
createRenderInstruction,
isRenderInstruction
};

View File

@@ -0,0 +1,4 @@
import type { RouteData, SSRResult } from '../../../@types/astro.js';
import { type NonAstroPageComponent } from './component.js';
import type { AstroComponentFactory } from './index.js';
export declare function renderPage(result: SSRResult, componentFactory: AstroComponentFactory | NonAstroPageComponent, props: any, children: any, streaming: boolean, route?: RouteData): Promise<Response>;

70
node_modules/astro/dist/runtime/server/render/page.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
import { renderComponentToString } from "./component.js";
import { isAstroComponentFactory } from "./astro/index.js";
import { renderToAsyncIterable, renderToReadableStream, renderToString } from "./astro/render.js";
import { encoder } from "./common.js";
import { isDeno, isNode } from "./util.js";
async function renderPage(result, componentFactory, props, children, streaming, route) {
if (!isAstroComponentFactory(componentFactory)) {
result._metadata.headInTree = result.componentMetadata.get(componentFactory.moduleId)?.containsHead ?? false;
const pageProps = { ...props ?? {}, "server:root": true };
const str = await renderComponentToString(
result,
componentFactory.name,
componentFactory,
pageProps,
{},
true,
route
);
const bytes = encoder.encode(str);
return new Response(bytes, {
headers: new Headers([
["Content-Type", "text/html; charset=utf-8"],
["Content-Length", bytes.byteLength.toString()]
])
});
}
result._metadata.headInTree = result.componentMetadata.get(componentFactory.moduleId)?.containsHead ?? false;
let body;
if (streaming) {
if (isNode && !isDeno) {
const nodeBody = await renderToAsyncIterable(
result,
componentFactory,
props,
children,
true,
route
);
body = nodeBody;
} else {
body = await renderToReadableStream(result, componentFactory, props, children, true, route);
}
} else {
body = await renderToString(result, componentFactory, props, children, true, route);
}
if (body instanceof Response) return body;
const init = result.response;
const headers = new Headers(init.headers);
if (!streaming && typeof body === "string") {
body = encoder.encode(body);
headers.set("Content-Length", body.byteLength.toString());
}
if (route?.component.endsWith(".md")) {
headers.set("Content-Type", "text/html; charset=utf-8");
}
let status = init.status;
if (route?.route === "/404") {
status = 404;
} else if (route?.route === "/500") {
status = 500;
}
if (status) {
return new Response(body, { ...init, headers, status });
} else {
return new Response(body, { ...init, headers });
}
}
export {
renderPage
};

View File

@@ -0,0 +1,6 @@
import type { SSRResult } from '../../../@types/astro.js';
/**
* Relies on the `renderScript: true` compiler option
* @experimental
*/
export declare function renderScript(result: SSRResult, id: string): Promise<any>;

View File

@@ -0,0 +1,18 @@
import { markHTMLString } from "../escape.js";
async function renderScript(result, id) {
if (result._metadata.renderedScripts.has(id)) return;
result._metadata.renderedScripts.add(id);
const inlined = result.inlinedScripts.get(id);
if (inlined != null) {
if (inlined) {
return markHTMLString(`<script type="module">${inlined}</script>`);
} else {
return "";
}
}
const resolved = await result.resolve(id);
return markHTMLString(`<script type="module" src="${resolved}"></script>`);
}
export {
renderScript
};

View File

@@ -0,0 +1,5 @@
import type { SSRResult } from '../../../@types/astro.js';
import type { RenderInstance } from './common.js';
import { type ComponentSlots } from './slot.js';
export declare function containsServerDirective(props: Record<string | number, any>): boolean;
export declare function renderServerIsland(result: SSRResult, _displayName: string, props: Record<string | number, any>, slots: ComponentSlots): RenderInstance;

View File

@@ -0,0 +1,83 @@
import { encryptString } from "../../../core/encryption.js";
import { renderChild } from "./any.js";
import { renderSlotToString } from "./slot.js";
const internalProps = /* @__PURE__ */ new Set([
"server:component-path",
"server:component-export",
"server:component-directive",
"server:defer"
]);
function containsServerDirective(props) {
return "server:component-directive" in props;
}
function safeJsonStringify(obj) {
return JSON.stringify(obj).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\//g, "\\u002f");
}
function renderServerIsland(result, _displayName, props, slots) {
return {
async render(destination) {
const componentPath = props["server:component-path"];
const componentExport = props["server:component-export"];
const componentId = result.serverIslandNameMap.get(componentPath);
if (!componentId) {
throw new Error(`Could not find server component name`);
}
for (const key2 of Object.keys(props)) {
if (internalProps.has(key2)) {
delete props[key2];
}
}
destination.write("<!--[if astro]>server-island-start<![endif]-->");
const renderedSlots = {};
for (const name in slots) {
if (name !== "fallback") {
const content = await renderSlotToString(result, slots[name]);
renderedSlots[name] = content.toString();
} else {
await renderChild(destination, slots.fallback(result));
}
}
const key = await result.key;
const propsEncrypted = await encryptString(key, JSON.stringify(props));
const hostId = crypto.randomUUID();
const slash = result.base.endsWith("/") ? "" : "/";
const serverIslandUrl = `${result.base}${slash}_server-islands/${componentId}${result.trailingSlash === "always" ? "/" : ""}`;
destination.write(`<script async type="module" data-island-id="${hostId}">
let componentId = ${safeJsonStringify(componentId)};
let componentExport = ${safeJsonStringify(componentExport)};
let script = document.querySelector('script[data-island-id="${hostId}"]');
let data = {
componentExport,
encryptedProps: ${safeJsonStringify(propsEncrypted)},
slots: ${safeJsonStringify(renderedSlots)},
};
let response = await fetch('${serverIslandUrl}', {
method: 'POST',
body: JSON.stringify(data),
});
if (script) {
if(response.status === 200 && response.headers.get('content-type') === 'text/html') {
let html = await response.text();
// Swap!
while(script.previousSibling &&
script.previousSibling.nodeType !== 8 &&
script.previousSibling.data !== '[if astro]>server-island-start<![endif]') {
script.previousSibling.remove();
}
script.previousSibling?.remove();
let frag = document.createRange().createContextualFragment(html);
script.before(frag);
}
script.remove();
}
</script>`);
}
};
}
export {
containsServerDirective,
renderServerIsland
};

View File

@@ -0,0 +1,24 @@
import type { SSRResult } from '../../../@types/astro.js';
import { renderTemplate } from './astro/render-template.js';
import type { RenderInstruction } from './instruction.js';
import { HTMLString } from '../escape.js';
import { type RenderInstance } from './common.js';
type RenderTemplateResult = ReturnType<typeof renderTemplate>;
export type ComponentSlots = Record<string, ComponentSlotValue>;
export type ComponentSlotValue = (result: SSRResult) => RenderTemplateResult | Promise<RenderTemplateResult>;
declare const slotString: unique symbol;
export declare class SlotString extends HTMLString {
instructions: null | RenderInstruction[];
[slotString]: boolean;
constructor(content: string, instructions: null | RenderInstruction[]);
}
export declare function isSlotString(str: string): str is any;
export declare function renderSlot(result: SSRResult, slotted: ComponentSlotValue | RenderTemplateResult, fallback?: ComponentSlotValue | RenderTemplateResult): RenderInstance;
export declare function renderSlotToString(result: SSRResult, slotted: ComponentSlotValue | RenderTemplateResult, fallback?: ComponentSlotValue | RenderTemplateResult): Promise<string>;
interface RenderSlotsResult {
slotInstructions: null | RenderInstruction[];
children: Record<string, string>;
}
export declare function renderSlots(result: SSRResult, slots?: ComponentSlots): Promise<RenderSlotsResult>;
export declare function createSlotValueFromString(content: string): ComponentSlotValue;
export {};

86
node_modules/astro/dist/runtime/server/render/slot.js generated vendored Normal file
View File

@@ -0,0 +1,86 @@
import { renderTemplate } from "./astro/render-template.js";
import { HTMLString, markHTMLString, unescapeHTML } from "../escape.js";
import { renderChild } from "./any.js";
import { chunkToString } from "./common.js";
const slotString = Symbol.for("astro:slot-string");
class SlotString extends HTMLString {
instructions;
[slotString];
constructor(content, instructions) {
super(content);
this.instructions = instructions;
this[slotString] = true;
}
}
function isSlotString(str) {
return !!str[slotString];
}
function renderSlot(result, slotted, fallback) {
if (!slotted && fallback) {
return renderSlot(result, fallback);
}
return {
async render(destination) {
await renderChild(destination, typeof slotted === "function" ? slotted(result) : slotted);
}
};
}
async function renderSlotToString(result, slotted, fallback) {
let content = "";
let instructions = null;
const temporaryDestination = {
write(chunk) {
if (chunk instanceof SlotString) {
content += chunk;
if (chunk.instructions) {
instructions ??= [];
instructions.push(...chunk.instructions);
}
} else if (chunk instanceof Response) return;
else if (typeof chunk === "object" && "type" in chunk && typeof chunk.type === "string") {
if (instructions === null) {
instructions = [];
}
instructions.push(chunk);
} else {
content += chunkToString(result, chunk);
}
}
};
const renderInstance = renderSlot(result, slotted, fallback);
await renderInstance.render(temporaryDestination);
return markHTMLString(new SlotString(content, instructions));
}
async function renderSlots(result, slots = {}) {
let slotInstructions = null;
let children = {};
if (slots) {
await Promise.all(
Object.entries(slots).map(
([key, value]) => renderSlotToString(result, value).then((output) => {
if (output.instructions) {
if (slotInstructions === null) {
slotInstructions = [];
}
slotInstructions.push(...output.instructions);
}
children[key] = output;
})
)
);
}
return { slotInstructions, children };
}
function createSlotValueFromString(content) {
return function() {
return renderTemplate`${unescapeHTML(content)}`;
};
}
export {
SlotString,
createSlotValueFromString,
isSlotString,
renderSlot,
renderSlotToString,
renderSlots
};

View File

@@ -0,0 +1,4 @@
import type { SSRElement, SSRResult } from '../../../@types/astro.js';
import type { StylesheetAsset } from '../../../core/app/types.js';
export declare function renderScriptElement({ props, children }: SSRElement): string;
export declare function renderUniqueStylesheet(result: SSRResult, sheet: StylesheetAsset): string | undefined;

21
node_modules/astro/dist/runtime/server/render/tags.js generated vendored Normal file
View File

@@ -0,0 +1,21 @@
import { renderElement } from "./util.js";
function renderScriptElement({ props, children }) {
return renderElement("script", {
props,
children
});
}
function renderUniqueStylesheet(result, sheet) {
if (sheet.type === "external") {
if (Array.from(result.styles).some((s) => s.props.href === sheet.src)) return "";
return renderElement("link", { props: { rel: "stylesheet", href: sheet.src }, children: "" });
}
if (sheet.type === "inline") {
if (Array.from(result.styles).some((s) => s.children.includes(sheet.content))) return "";
return renderElement("style", { props: {}, children: sheet.content });
}
}
export {
renderScriptElement,
renderUniqueStylesheet
};

View File

@@ -0,0 +1,39 @@
import type { SSRElement } from '../../../@types/astro.js';
import type { RenderFunction } from './common.js';
export declare const voidElementNames: RegExp;
export declare const toAttributeString: (value: any, shouldEscape?: boolean) => any;
export declare function defineScriptVars(vars: Record<any, any>): any;
export declare function formatList(values: string[]): string;
export declare function addAttribute(value: any, key: string, shouldEscape?: boolean): any;
export declare function internalSpreadAttributes(values: Record<any, any>, shouldEscape?: boolean): any;
export declare function renderElement(name: string, { props: _props, children }: SSRElement, shouldEscape?: boolean): string;
/**
* Executes the `bufferRenderFunction` to prerender it into a buffer destination, and return a promise
* with an object containing the `renderToFinalDestination` function to flush the buffer to the final
* destination.
*
* @example
* ```ts
* // Render components in parallel ahead of time
* const finalRenders = [ComponentA, ComponentB].map((comp) => {
* return renderToBufferDestination(async (bufferDestination) => {
* await renderComponentToDestination(bufferDestination);
* });
* });
* // Render array of components serially
* for (const finalRender of finalRenders) {
* await finalRender.renderToFinalDestination(finalDestination);
* }
* ```
*/
export declare function renderToBufferDestination(bufferRenderFunction: RenderFunction): {
renderToFinalDestination: RenderFunction;
};
export declare const isNode: boolean;
export declare const isDeno: boolean;
export type PromiseWithResolvers<T> = {
promise: Promise<T>;
resolve: (value: T) => void;
reject: (reason?: any) => void;
};
export declare function promiseWithResolvers<T = any>(): PromiseWithResolvers<T>;

170
node_modules/astro/dist/runtime/server/render/util.js generated vendored Normal file
View File

@@ -0,0 +1,170 @@
import { clsx } from "clsx";
import { HTMLString, markHTMLString } from "../escape.js";
const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|checked|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|selected|itemscope)$/i;
const htmlEnumAttributes = /^(?:contenteditable|draggable|spellcheck|value)$/i;
const svgEnumAttributes = /^(?:autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i;
const AMPERSAND_REGEX = /&/g;
const DOUBLE_QUOTE_REGEX = /"/g;
const STATIC_DIRECTIVES = /* @__PURE__ */ new Set(["set:html", "set:text"]);
const toIdent = (k) => k.trim().replace(/(?!^)\b\w|\s+|\W+/g, (match, index) => {
if (/\W/.test(match)) return "";
return index === 0 ? match : match.toUpperCase();
});
const toAttributeString = (value, shouldEscape = true) => shouldEscape ? String(value).replace(AMPERSAND_REGEX, "&#38;").replace(DOUBLE_QUOTE_REGEX, "&#34;") : value;
const kebab = (k) => k.toLowerCase() === k ? k : k.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
const toStyleString = (obj) => Object.entries(obj).filter(([_, v]) => typeof v === "string" && v.trim() || typeof v === "number").map(([k, v]) => {
if (k[0] !== "-" && k[1] !== "-") return `${kebab(k)}:${v}`;
return `${k}:${v}`;
}).join(";");
function defineScriptVars(vars) {
let output = "";
for (const [key, value] of Object.entries(vars)) {
output += `const ${toIdent(key)} = ${JSON.stringify(value)?.replace(
/<\/script>/g,
"\\x3C/script>"
)};
`;
}
return markHTMLString(output);
}
function formatList(values) {
if (values.length === 1) {
return values[0];
}
return `${values.slice(0, -1).join(", ")} or ${values[values.length - 1]}`;
}
function addAttribute(value, key, shouldEscape = true) {
if (value == null) {
return "";
}
if (value === false) {
if (htmlEnumAttributes.test(key) || svgEnumAttributes.test(key)) {
return markHTMLString(` ${key}="false"`);
}
return "";
}
if (STATIC_DIRECTIVES.has(key)) {
console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.
Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the dynamic spread syntax (\`{...{ "${key}": value }}\`).`);
return "";
}
if (key === "class:list") {
const listValue = toAttributeString(clsx(value), shouldEscape);
if (listValue === "") {
return "";
}
return markHTMLString(` ${key.slice(0, -5)}="${listValue}"`);
}
if (key === "style" && !(value instanceof HTMLString)) {
if (Array.isArray(value) && value.length === 2) {
return markHTMLString(
` ${key}="${toAttributeString(`${toStyleString(value[0])};${value[1]}`, shouldEscape)}"`
);
}
if (typeof value === "object") {
return markHTMLString(` ${key}="${toAttributeString(toStyleString(value), shouldEscape)}"`);
}
}
if (key === "className") {
return markHTMLString(` class="${toAttributeString(value, shouldEscape)}"`);
}
if (typeof value === "string" && value.includes("&") && isHttpUrl(value)) {
return markHTMLString(` ${key}="${toAttributeString(value, false)}"`);
}
if (value === true && (key.startsWith("data-") || htmlBooleanAttributes.test(key))) {
return markHTMLString(` ${key}`);
} else {
return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
}
}
function internalSpreadAttributes(values, shouldEscape = true) {
let output = "";
for (const [key, value] of Object.entries(values)) {
output += addAttribute(value, key, shouldEscape);
}
return markHTMLString(output);
}
function renderElement(name, { props: _props, children = "" }, shouldEscape = true) {
const { lang: _, "data-astro-id": astroId, "define:vars": defineVars, ...props } = _props;
if (defineVars) {
if (name === "style") {
delete props["is:global"];
delete props["is:scoped"];
}
if (name === "script") {
delete props.hoist;
children = defineScriptVars(defineVars) + "\n" + children;
}
}
if ((children == null || children == "") && voidElementNames.test(name)) {
return `<${name}${internalSpreadAttributes(props, shouldEscape)}>`;
}
return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}</${name}>`;
}
const noop = () => {
};
class BufferedRenderer {
chunks = [];
renderPromise;
destination;
constructor(bufferRenderFunction) {
this.renderPromise = bufferRenderFunction(this);
Promise.resolve(this.renderPromise).catch(noop);
}
write(chunk) {
if (this.destination) {
this.destination.write(chunk);
} else {
this.chunks.push(chunk);
}
}
async renderToFinalDestination(destination) {
for (const chunk of this.chunks) {
destination.write(chunk);
}
this.destination = destination;
await this.renderPromise;
}
}
function renderToBufferDestination(bufferRenderFunction) {
const renderer = new BufferedRenderer(bufferRenderFunction);
return renderer;
}
const isNode = typeof process !== "undefined" && Object.prototype.toString.call(process) === "[object process]";
const isDeno = typeof Deno !== "undefined";
function promiseWithResolvers() {
let resolve, reject;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return {
promise,
resolve,
reject
};
}
const VALID_PROTOCOLS = ["http:", "https:"];
function isHttpUrl(url) {
try {
const parsedUrl = new URL(url);
return VALID_PROTOCOLS.includes(parsedUrl.protocol);
} catch {
return false;
}
}
export {
addAttribute,
defineScriptVars,
formatList,
internalSpreadAttributes,
isDeno,
isNode,
promiseWithResolvers,
renderElement,
renderToBufferDestination,
toAttributeString,
voidElementNames
};