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

20
node_modules/astro/dist/core/render/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
import type { ComponentInstance, RouteData } from '../../@types/astro.js';
import type { Pipeline } from '../base-pipeline.js';
export { Pipeline } from '../base-pipeline.js';
export { getParams, getProps } from './params-and-props.js';
export { loadRenderer } from './renderer.js';
export { Slots } from './slots.js';
export interface SSROptions {
/** The pipeline instance */
pipeline: Pipeline;
/** location of file on disk */
filePath: URL;
/** the web request (needed for dynamic routes) */
pathname: string;
/** The runtime component instance */
preload: ComponentInstance;
/** Request */
request: Request;
/** optional, in case we need to render something outside a dev server */
route: RouteData;
}

11
node_modules/astro/dist/core/render/index.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
import { Pipeline } from "../base-pipeline.js";
import { getParams, getProps } from "./params-and-props.js";
import { loadRenderer } from "./renderer.js";
import { Slots } from "./slots.js";
export {
Pipeline,
Slots,
getParams,
getProps,
loadRenderer
};

2
node_modules/astro/dist/core/render/paginate.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import type { PaginateFunction, RouteData } from '../../@types/astro.js';
export declare function generatePaginateFunction(routeMatch: RouteData): (...args: Parameters<PaginateFunction>) => ReturnType<PaginateFunction>;

72
node_modules/astro/dist/core/render/paginate.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import { AstroError, AstroErrorData } from "../errors/index.js";
function generatePaginateFunction(routeMatch) {
return function paginateUtility(data, args = {}) {
let { pageSize: _pageSize, params: _params, props: _props } = args;
const pageSize = _pageSize || 10;
const paramName = "page";
const additionalParams = _params || {};
const additionalProps = _props || {};
let includesFirstPageNumber;
if (routeMatch.params.includes(`...${paramName}`)) {
includesFirstPageNumber = false;
} else if (routeMatch.params.includes(`${paramName}`)) {
includesFirstPageNumber = true;
} else {
throw new AstroError({
...AstroErrorData.PageNumberParamNotFound,
message: AstroErrorData.PageNumberParamNotFound.message(paramName)
});
}
const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
const result = [...Array(lastPage).keys()].map((num) => {
const pageNum = num + 1;
const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize;
const end = Math.min(start + pageSize, data.length);
const params = {
...additionalParams,
[paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : void 0
};
const current = correctIndexRoute(routeMatch.generate({ ...params }));
const next = pageNum === lastPage ? void 0 : correctIndexRoute(routeMatch.generate({ ...params, page: String(pageNum + 1) }));
const prev = pageNum === 1 ? void 0 : correctIndexRoute(
routeMatch.generate({
...params,
page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
})
);
const first = pageNum === 1 ? void 0 : correctIndexRoute(
routeMatch.generate({
...params,
page: includesFirstPageNumber ? "1" : void 0
})
);
const last = pageNum === lastPage ? void 0 : correctIndexRoute(routeMatch.generate({ ...params, page: String(lastPage) }));
return {
params,
props: {
...additionalProps,
page: {
data: data.slice(start, end),
start,
end: end - 1,
size: pageSize,
total: data.length,
currentPage: pageNum,
lastPage,
url: { current, next, prev, first, last }
}
}
};
});
return result;
};
}
function correctIndexRoute(route) {
if (route === "") {
return "/";
}
return route;
}
export {
generatePaginateFunction
};

View File

@@ -0,0 +1,18 @@
import type { ComponentInstance, Params, Props, RouteData } from '../../@types/astro.js';
import type { Logger } from '../logger/core.js';
import type { RouteCache } from './route-cache.js';
interface GetParamsAndPropsOptions {
mod: ComponentInstance | undefined;
routeData?: RouteData | undefined;
routeCache: RouteCache;
pathname: string;
logger: Logger;
serverLike: boolean;
}
export declare function getProps(opts: GetParamsAndPropsOptions): Promise<Props>;
/**
* When given a route with the pattern `/[x]/[y]/[z]/svelte`, and a pathname `/a/b/c/svelte`,
* returns the params object: { x: "a", y: "b", z: "c" }.
*/
export declare function getParams(route: RouteData, pathname: string): Params;
export {};

View File

@@ -0,0 +1,70 @@
import { DEFAULT_404_COMPONENT } from "../constants.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { routeIsFallback } from "../redirects/helpers.js";
import { routeIsRedirect } from "../redirects/index.js";
import { callGetStaticPaths, findPathItemByKey } from "./route-cache.js";
async function getProps(opts) {
const { logger, mod, routeData: route, routeCache, pathname, serverLike } = opts;
if (!route || route.pathname) {
return {};
}
if (routeIsRedirect(route) || routeIsFallback(route) || route.component === DEFAULT_404_COMPONENT) {
return {};
}
const staticPaths = await callGetStaticPaths({
mod,
route,
routeCache,
logger,
ssr: serverLike
});
const params = getParams(route, pathname);
const matchedStaticPath = findPathItemByKey(staticPaths, params, route, logger);
if (!matchedStaticPath && (serverLike ? route.prerender : true)) {
throw new AstroError({
...AstroErrorData.NoMatchingStaticPathFound,
message: AstroErrorData.NoMatchingStaticPathFound.message(pathname),
hint: AstroErrorData.NoMatchingStaticPathFound.hint([route.component])
});
}
if (mod) {
validatePrerenderEndpointCollision(route, mod, params);
}
const props = matchedStaticPath?.props ? { ...matchedStaticPath.props } : {};
return props;
}
function getParams(route, pathname) {
if (!route.params.length) return {};
const paramsMatch = route.pattern.exec(decodeURIComponent(pathname));
if (!paramsMatch) return {};
const params = {};
route.params.forEach((key, i) => {
if (key.startsWith("...")) {
params[key.slice(3)] = paramsMatch[i + 1] ? paramsMatch[i + 1] : void 0;
} else {
params[key] = paramsMatch[i + 1];
}
});
return params;
}
function validatePrerenderEndpointCollision(route, mod, params) {
if (route.type === "endpoint" && mod.getStaticPaths) {
const lastSegment = route.segments[route.segments.length - 1];
const paramValues = Object.values(params);
const lastParam = paramValues[paramValues.length - 1];
if (lastSegment.length === 1 && lastSegment[0].dynamic && lastParam === void 0) {
throw new AstroError({
...AstroErrorData.PrerenderDynamicEndpointPathCollide,
message: AstroErrorData.PrerenderDynamicEndpointPathCollide.message(route.route),
hint: AstroErrorData.PrerenderDynamicEndpointPathCollide.hint(route.component),
location: {
file: route.component
}
});
}
}
}
export {
getParams,
getProps
};

3
node_modules/astro/dist/core/render/renderer.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
import type { AstroRenderer, SSRLoadedRenderer } from '../../@types/astro.js';
import type { ModuleLoader } from '../module-loader/index.js';
export declare function loadRenderer(renderer: AstroRenderer, moduleLoader: ModuleLoader): Promise<SSRLoadedRenderer | undefined>;

13
node_modules/astro/dist/core/render/renderer.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
async function loadRenderer(renderer, moduleLoader) {
const mod = await moduleLoader.import(renderer.serverEntrypoint);
if (typeof mod.default !== "undefined") {
return {
...renderer,
ssr: mod.default
};
}
return void 0;
}
export {
loadRenderer
};

31
node_modules/astro/dist/core/render/route-cache.d.ts generated vendored Normal file
View File

@@ -0,0 +1,31 @@
import type { ComponentInstance, GetStaticPathsItem, GetStaticPathsResultKeyed, Params, RouteData, RuntimeMode } from '../../@types/astro.js';
import type { Logger } from '../logger/core.js';
interface CallGetStaticPathsOptions {
mod: ComponentInstance | undefined;
route: RouteData;
routeCache: RouteCache;
logger: Logger;
ssr: boolean;
}
export declare function callGetStaticPaths({ mod, route, routeCache, logger, ssr, }: CallGetStaticPathsOptions): Promise<GetStaticPathsResultKeyed>;
interface RouteCacheEntry {
staticPaths: GetStaticPathsResultKeyed;
}
/**
* Manage the route cache, responsible for caching data related to each route,
* including the result of calling getStaticPath() so that it can be reused across
* responses during dev and only ever called once during build.
*/
export declare class RouteCache {
private logger;
private cache;
private mode;
constructor(logger: Logger, mode?: RuntimeMode);
/** Clear the cache. */
clearAll(): void;
set(route: RouteData, entry: RouteCacheEntry): void;
get(route: RouteData): RouteCacheEntry | undefined;
key(route: RouteData): string;
}
export declare function findPathItemByKey(staticPaths: GetStaticPathsResultKeyed, params: Params, route: RouteData, logger: Logger): GetStaticPathsItem | undefined;
export {};

81
node_modules/astro/dist/core/render/route-cache.js generated vendored Normal file
View File

@@ -0,0 +1,81 @@
import { stringifyParams } from "../routing/params.js";
import { validateDynamicRouteModule, validateGetStaticPathsResult } from "../routing/validation.js";
import { generatePaginateFunction } from "./paginate.js";
async function callGetStaticPaths({
mod,
route,
routeCache,
logger,
ssr
}) {
const cached = routeCache.get(route);
if (!mod) {
throw new Error("This is an error caused by Astro and not your code. Please file an issue.");
}
if (cached?.staticPaths) {
return cached.staticPaths;
}
validateDynamicRouteModule(mod, { ssr, route });
if (ssr && !route.prerender) {
const entry = Object.assign([], { keyed: /* @__PURE__ */ new Map() });
routeCache.set(route, { ...cached, staticPaths: entry });
return entry;
}
let staticPaths = [];
if (!mod.getStaticPaths) {
throw new Error("Unexpected Error.");
}
staticPaths = await mod.getStaticPaths({
// Q: Why the cast?
// A: So users downstream can have nicer typings, we have to make some sacrifice in our internal typings, which necessitate a cast here
paginate: generatePaginateFunction(route)
});
validateGetStaticPathsResult(staticPaths, logger, route);
const keyedStaticPaths = staticPaths;
keyedStaticPaths.keyed = /* @__PURE__ */ new Map();
for (const sp of keyedStaticPaths) {
const paramsKey = stringifyParams(sp.params, route);
keyedStaticPaths.keyed.set(paramsKey, sp);
}
routeCache.set(route, { ...cached, staticPaths: keyedStaticPaths });
return keyedStaticPaths;
}
class RouteCache {
logger;
cache = {};
mode;
constructor(logger, mode = "production") {
this.logger = logger;
this.mode = mode;
}
/** Clear the cache. */
clearAll() {
this.cache = {};
}
set(route, entry) {
const key = this.key(route);
if (this.mode === "production" && this.cache[key]?.staticPaths) {
this.logger.warn(null, `Internal Warning: route cache overwritten. (${key})`);
}
this.cache[key] = entry;
}
get(route) {
return this.cache[this.key(route)];
}
key(route) {
return `${route.route}_${route.component}`;
}
}
function findPathItemByKey(staticPaths, params, route, logger) {
const paramsKey = stringifyParams(params, route);
const matchedStaticPath = staticPaths.keyed.get(paramsKey);
if (matchedStaticPath) {
return matchedStaticPath;
}
logger.debug("router", `findPathItemByKey() - Unexpected cache miss looking for ${paramsKey}`);
}
export {
RouteCache,
callGetStaticPaths,
findPathItemByKey
};

9
node_modules/astro/dist/core/render/slots.d.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import type { SSRResult } from '../../@types/astro.js';
import { type ComponentSlots } from '../../runtime/server/index.js';
import type { Logger } from '../logger/core.js';
export declare class Slots {
#private;
constructor(result: SSRResult, slots: ComponentSlots | null, logger: Logger);
has(name: string): boolean;
render(name: string, args?: any[]): Promise<any>;
}

72
node_modules/astro/dist/core/render/slots.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import { renderSlotToString } from "../../runtime/server/index.js";
import { renderJSX } from "../../runtime/server/jsx.js";
import { chunkToString } from "../../runtime/server/render/index.js";
import { isRenderInstruction } from "../../runtime/server/render/instruction.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
function getFunctionExpression(slot) {
if (!slot) return;
const expressions = slot?.expressions?.filter((e) => isRenderInstruction(e) === false);
if (expressions?.length !== 1) return;
return expressions[0];
}
class Slots {
#result;
#slots;
#logger;
constructor(result, slots, logger) {
this.#result = result;
this.#slots = slots;
this.#logger = logger;
if (slots) {
for (const key of Object.keys(slots)) {
if (this[key] !== void 0) {
throw new AstroError({
...AstroErrorData.ReservedSlotName,
message: AstroErrorData.ReservedSlotName.message(key)
});
}
Object.defineProperty(this, key, {
get() {
return true;
},
enumerable: true
});
}
}
}
has(name) {
if (!this.#slots) return false;
return Boolean(this.#slots[name]);
}
async render(name, args = []) {
if (!this.#slots || !this.has(name)) return;
const result = this.#result;
if (!Array.isArray(args)) {
this.#logger.warn(
null,
`Expected second parameter to be an array, received a ${typeof args}. If you're trying to pass an array as a single argument and getting unexpected results, make sure you're passing your array as a item of an array. Ex: Astro.slots.render('default', [["Hello", "World"]])`
);
} else if (args.length > 0) {
const slotValue = this.#slots[name];
const component = typeof slotValue === "function" ? await slotValue(result) : await slotValue;
const expression = getFunctionExpression(component);
if (expression) {
const slot = async () => typeof expression === "function" ? expression(...args) : expression;
return await renderSlotToString(result, slot).then((res) => {
return res;
});
}
if (typeof component === "function") {
return await renderJSX(result, component(...args)).then(
(res) => res != null ? String(res) : res
);
}
}
const content = await renderSlotToString(result, this.#slots[name]);
const outHTML = chunkToString(result, content);
return outHTML;
}
}
export {
Slots
};

14
node_modules/astro/dist/core/render/ssr-element.d.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import type { AssetsPrefix, SSRElement } from '../../@types/astro.js';
import type { StylesheetAsset } from '../app/types.js';
export declare function createAssetLink(href: string, base?: string, assetsPrefix?: AssetsPrefix): string;
export declare function createStylesheetElement(stylesheet: StylesheetAsset, base?: string, assetsPrefix?: AssetsPrefix): SSRElement;
export declare function createStylesheetElementSet(stylesheets: StylesheetAsset[], base?: string, assetsPrefix?: AssetsPrefix): Set<SSRElement>;
export declare function createModuleScriptElement(script: {
type: 'inline' | 'external';
value: string;
}, base?: string, assetsPrefix?: AssetsPrefix): SSRElement;
export declare function createModuleScriptElementWithSrc(src: string, base?: string, assetsPrefix?: AssetsPrefix): SSRElement;
export declare function createModuleScriptsSet(scripts: {
type: 'inline' | 'external';
value: string;
}[], base?: string, assetsPrefix?: AssetsPrefix): Set<SSRElement>;

65
node_modules/astro/dist/core/render/ssr-element.js generated vendored Normal file
View File

@@ -0,0 +1,65 @@
import { getAssetsPrefix } from "../../assets/utils/getAssetsPrefix.js";
import { fileExtension, joinPaths, prependForwardSlash, slash } from "../../core/path.js";
function createAssetLink(href, base, assetsPrefix) {
if (assetsPrefix) {
const pf = getAssetsPrefix(fileExtension(href), assetsPrefix);
return joinPaths(pf, slash(href));
} else if (base) {
return prependForwardSlash(joinPaths(base, slash(href)));
} else {
return href;
}
}
function createStylesheetElement(stylesheet, base, assetsPrefix) {
if (stylesheet.type === "inline") {
return {
props: {},
children: stylesheet.content
};
} else {
return {
props: {
rel: "stylesheet",
href: createAssetLink(stylesheet.src, base, assetsPrefix)
},
children: ""
};
}
}
function createStylesheetElementSet(stylesheets, base, assetsPrefix) {
return new Set(stylesheets.map((s) => createStylesheetElement(s, base, assetsPrefix)));
}
function createModuleScriptElement(script, base, assetsPrefix) {
if (script.type === "external") {
return createModuleScriptElementWithSrc(script.value, base, assetsPrefix);
} else {
return {
props: {
type: "module"
},
children: script.value
};
}
}
function createModuleScriptElementWithSrc(src, base, assetsPrefix) {
return {
props: {
type: "module",
src: createAssetLink(src, base, assetsPrefix)
},
children: ""
};
}
function createModuleScriptsSet(scripts, base, assetsPrefix) {
return new Set(
scripts.map((script) => createModuleScriptElement(script, base, assetsPrefix))
);
}
export {
createAssetLink,
createModuleScriptElement,
createModuleScriptElementWithSrc,
createModuleScriptsSet,
createStylesheetElement,
createStylesheetElementSet
};