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,36 @@
import type { APIContext, MiddlewareHandler, RewritePayload } from '../../@types/astro.js';
/**
* Utility function that is in charge of calling the middleware.
*
* It accepts a `R` generic, which usually is the `Response` returned.
* It is a generic because endpoints can return a different payload.
*
* When calling a middleware, we provide a `next` function, this function might or
* might not be called.
*
* A middleware, to behave correctly, can:
* - return a `Response`;
* - call `next`;
*
* Failing doing so will result an error. A middleware can call `next` and do not return a
* response. A middleware can not call `next` and return a new `Response` from scratch (maybe with a redirect).
*
* ```js
* const onRequest = async (context, next) => {
* const response = await next(context);
* return response;
* }
* ```
*
* ```js
* const onRequest = async (context, next) => {
* context.locals = "foo";
* next();
* }
* ```
*
* @param onRequest The function called which accepts a `context` and a `resolve` function
* @param apiContext The API context
* @param responseFunction A callback function that should return a promise with the response
*/
export declare function callMiddleware(onRequest: MiddlewareHandler, apiContext: APIContext, responseFunction: (apiContext: APIContext, rewritePayload?: RewritePayload) => Promise<Response> | Response): Promise<Response>;

View File

@@ -0,0 +1,36 @@
import { AstroError, AstroErrorData } from "../errors/index.js";
async function callMiddleware(onRequest, apiContext, responseFunction) {
let nextCalled = false;
let responseFunctionPromise = void 0;
const next = async (payload) => {
nextCalled = true;
responseFunctionPromise = responseFunction(apiContext, payload);
return responseFunctionPromise;
};
let middlewarePromise = onRequest(apiContext, next);
return await Promise.resolve(middlewarePromise).then(async (value) => {
if (nextCalled) {
if (typeof value !== "undefined") {
if (value instanceof Response === false) {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
}
return value;
} else {
if (responseFunctionPromise) {
return responseFunctionPromise;
} else {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
}
}
} else if (typeof value === "undefined") {
throw new AstroError(AstroErrorData.MiddlewareNoDataOrNextCalled);
} else if (value instanceof Response === false) {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
} else {
return value;
}
});
}
export {
callMiddleware
};

41
node_modules/astro/dist/core/middleware/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,41 @@
import type { APIContext, MiddlewareHandler, Params } from '../../@types/astro.js';
import { sequence } from './sequence.js';
declare function defineMiddleware(fn: MiddlewareHandler): MiddlewareHandler;
/**
* Payload for creating a context to be passed to Astro middleware
*/
export type CreateContext = {
/**
* The incoming request
*/
request: Request;
/**
* Optional parameters
*/
params?: Params;
/**
* A list of locales that are supported by the user
*/
userDefinedLocales?: string[];
/**
* User defined default locale
*/
defaultLocale: string;
};
/**
* Creates a context to be passed to Astro middleware `onRequest` function.
*/
declare function createContext({ request, params, userDefinedLocales, defaultLocale, }: CreateContext): APIContext;
/**
* It attempts to serialize `value` and return it as a string.
*
* ## Errors
* If the `value` is not serializable if the function will throw a runtime error.
*
* Something is **not serializable** when it contains properties/values like functions, `Map`, `Set`, `Date`,
* and other types that can't be made a string.
*
* @param value
*/
declare function trySerializeLocals(value: unknown): string;
export { createContext, defineMiddleware, sequence, trySerializeLocals };

129
node_modules/astro/dist/core/middleware/index.js generated vendored Normal file
View File

@@ -0,0 +1,129 @@
import { createCallAction, createGetActionResult } from "../../actions/utils.js";
import {
computeCurrentLocale,
computePreferredLocale,
computePreferredLocaleList
} from "../../i18n/utils.js";
import { ASTRO_VERSION, clientAddressSymbol, clientLocalsSymbol } from "../constants.js";
import { AstroCookies } from "../cookies/index.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { getClientIpAddress } from "../routing/request.js";
import { sequence } from "./sequence.js";
function defineMiddleware(fn) {
return fn;
}
function createContext({
request,
params = {},
userDefinedLocales = [],
defaultLocale = ""
}) {
let preferredLocale = void 0;
let preferredLocaleList = void 0;
let currentLocale = void 0;
let clientIpAddress;
const url = new URL(request.url);
const route = url.pathname;
const rewrite = (_reroutePayload) => {
return Promise.resolve(new Response(null));
};
const context = {
cookies: new AstroCookies(request),
request,
params,
site: void 0,
generator: `Astro v${ASTRO_VERSION}`,
props: {},
rewrite,
redirect(path, status) {
return new Response(null, {
status: status || 302,
headers: {
Location: path
}
});
},
get preferredLocale() {
return preferredLocale ??= computePreferredLocale(request, userDefinedLocales);
},
get preferredLocaleList() {
return preferredLocaleList ??= computePreferredLocaleList(request, userDefinedLocales);
},
get currentLocale() {
return currentLocale ??= computeCurrentLocale(route, userDefinedLocales, defaultLocale);
},
url,
get clientAddress() {
if (clientIpAddress) {
return clientIpAddress;
}
clientIpAddress = getClientIpAddress(request);
if (!clientIpAddress) {
throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
}
return clientIpAddress;
},
get locals() {
let locals = Reflect.get(request, clientLocalsSymbol);
if (locals === void 0) {
locals = {};
Reflect.set(request, clientLocalsSymbol, locals);
}
if (typeof locals !== "object") {
throw new AstroError(AstroErrorData.LocalsNotAnObject);
}
return locals;
},
// We define a custom property, so we can check the value passed to locals
set locals(val) {
if (typeof val !== "object") {
throw new AstroError(AstroErrorData.LocalsNotAnObject);
} else {
Reflect.set(request, clientLocalsSymbol, val);
}
}
};
return Object.assign(context, {
getActionResult: createGetActionResult(context.locals),
callAction: createCallAction(context)
});
}
function isLocalsSerializable(value) {
let type = typeof value;
let plainObject = true;
if (type === "object" && isPlainObject(value)) {
for (const [, nestedValue] of Object.entries(value)) {
if (!isLocalsSerializable(nestedValue)) {
plainObject = false;
break;
}
}
} else {
plainObject = false;
}
let result = value === null || type === "string" || type === "number" || type === "boolean" || Array.isArray(value) || plainObject;
return result;
}
function isPlainObject(value) {
if (typeof value !== "object" || value === null) return false;
let proto = Object.getPrototypeOf(value);
if (proto === null) return true;
let baseProto = proto;
while (Object.getPrototypeOf(baseProto) !== null) {
baseProto = Object.getPrototypeOf(baseProto);
}
return proto === baseProto;
}
function trySerializeLocals(value) {
if (isLocalsSerializable(value)) {
return JSON.stringify(value);
} else {
throw new Error("The passed value can't be serialized.");
}
}
export {
createContext,
defineMiddleware,
sequence,
trySerializeLocals
};

View File

@@ -0,0 +1,7 @@
import type { ModuleLoader } from '../module-loader/index.js';
/**
* It accepts a module loader and the astro settings, and it attempts to load the middlewares defined in the configuration.
*
* If not middlewares were not set, the function returns an empty array.
*/
export declare function loadMiddleware(moduleLoader: ModuleLoader): Promise<Record<string, any>>;

View File

@@ -0,0 +1,14 @@
import { MiddlewareCantBeLoaded } from "../errors/errors-data.js";
import { AstroError } from "../errors/index.js";
import { MIDDLEWARE_MODULE_ID } from "./vite-plugin.js";
async function loadMiddleware(moduleLoader) {
try {
return await moduleLoader.import(MIDDLEWARE_MODULE_ID);
} catch (error) {
const astroError = new AstroError(MiddlewareCantBeLoaded, { cause: error });
throw astroError;
}
}
export {
loadMiddleware
};

View File

@@ -0,0 +1,2 @@
import type { MiddlewareHandler } from '../../@types/astro.js';
export declare const NOOP_MIDDLEWARE_FN: MiddlewareHandler;

View File

@@ -0,0 +1,9 @@
import { NOOP_MIDDLEWARE_HEADER } from "../constants.js";
const NOOP_MIDDLEWARE_FN = async (_ctx, next) => {
const response = await next();
response.headers.set(NOOP_MIDDLEWARE_HEADER, "true");
return response;
};
export {
NOOP_MIDDLEWARE_FN
};

View File

@@ -0,0 +1,6 @@
import type { MiddlewareHandler } from '../../@types/astro.js';
/**
*
* It accepts one or more middleware handlers and makes sure that they are run in sequence.
*/
export declare function sequence(...handlers: MiddlewareHandler[]): MiddlewareHandler;

54
node_modules/astro/dist/core/middleware/sequence.js generated vendored Normal file
View File

@@ -0,0 +1,54 @@
import { AstroCookies } from "../cookies/cookies.js";
import { apiContextRoutesSymbol } from "../render-context.js";
import { getParams } from "../render/index.js";
import { defineMiddleware } from "./index.js";
function sequence(...handlers) {
const filtered = handlers.filter((h) => !!h);
const length = filtered.length;
if (!length) {
return defineMiddleware((_context, next) => {
return next();
});
}
return defineMiddleware((context, next) => {
let carriedPayload = void 0;
return applyHandle(0, context);
function applyHandle(i, handleContext) {
const handle = filtered[i];
const result = handle(handleContext, async (payload) => {
if (i < length - 1) {
if (payload) {
let newRequest;
if (payload instanceof Request) {
newRequest = payload;
} else if (payload instanceof URL) {
newRequest = new Request(payload, handleContext.request);
} else {
newRequest = new Request(
new URL(payload, handleContext.url.origin),
handleContext.request
);
}
const pipeline = Reflect.get(handleContext, apiContextRoutesSymbol);
const { routeData, pathname } = await pipeline.tryRewrite(
payload,
handleContext.request
);
carriedPayload = payload;
handleContext.request = newRequest;
handleContext.url = new URL(newRequest.url);
handleContext.cookies = new AstroCookies(newRequest);
handleContext.params = getParams(routeData, pathname);
}
return applyHandle(i + 1, handleContext);
} else {
return next(payload ?? carriedPayload);
}
});
return result;
}
});
}
export {
sequence
};

View File

@@ -0,0 +1,9 @@
import type { Plugin as VitePlugin } from 'vite';
import type { AstroSettings } from '../../@types/astro.js';
import type { BuildInternals } from '../build/internal.js';
import type { StaticBuildOptions } from '../build/types.js';
export declare const MIDDLEWARE_MODULE_ID = "\0astro-internal:middleware";
export declare function vitePluginMiddleware({ settings }: {
settings: AstroSettings;
}): VitePlugin;
export declare function vitePluginMiddlewareBuild(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin;

98
node_modules/astro/dist/core/middleware/vite-plugin.js generated vendored Normal file
View File

@@ -0,0 +1,98 @@
import { normalizePath } from "vite";
import { getOutputDirectory } from "../../prerender/utils.js";
import { addRollupInput } from "../build/add-rollup-input.js";
import { MIDDLEWARE_PATH_SEGMENT_NAME } from "../constants.js";
import { MissingMiddlewareForInternationalization } from "../errors/errors-data.js";
import { AstroError } from "../errors/index.js";
const MIDDLEWARE_MODULE_ID = "\0astro-internal:middleware";
const NOOP_MIDDLEWARE = "\0noop-middleware";
function vitePluginMiddleware({ settings }) {
let resolvedMiddlewareId = void 0;
const hasIntegrationMiddleware = settings.middlewares.pre.length > 0 || settings.middlewares.post.length > 0;
let userMiddlewareIsPresent = false;
return {
name: "@astro/plugin-middleware",
async resolveId(id) {
if (id === MIDDLEWARE_MODULE_ID) {
const middlewareId = await this.resolve(
`${decodeURI(settings.config.srcDir.pathname)}${MIDDLEWARE_PATH_SEGMENT_NAME}`
);
userMiddlewareIsPresent = !!middlewareId;
if (middlewareId) {
resolvedMiddlewareId = middlewareId.id;
return MIDDLEWARE_MODULE_ID;
} else if (hasIntegrationMiddleware) {
return MIDDLEWARE_MODULE_ID;
} else {
return NOOP_MIDDLEWARE;
}
}
if (id === NOOP_MIDDLEWARE) {
return NOOP_MIDDLEWARE;
}
},
async load(id) {
if (id === NOOP_MIDDLEWARE) {
if (!userMiddlewareIsPresent && settings.config.i18n?.routing === "manual") {
throw new AstroError(MissingMiddlewareForInternationalization);
}
return "export const onRequest = (_, next) => next()";
} else if (id === MIDDLEWARE_MODULE_ID) {
if (!userMiddlewareIsPresent && settings.config.i18n?.routing === "manual") {
throw new AstroError(MissingMiddlewareForInternationalization);
}
const preMiddleware = createMiddlewareImports(settings.middlewares.pre, "pre");
const postMiddleware = createMiddlewareImports(settings.middlewares.post, "post");
const source = `
${userMiddlewareIsPresent ? `import { onRequest as userOnRequest } from '${resolvedMiddlewareId}';` : ""}
import { sequence } from 'astro:middleware';
${preMiddleware.importsCode}${postMiddleware.importsCode}
export const onRequest = sequence(
${preMiddleware.sequenceCode}${preMiddleware.sequenceCode ? "," : ""}
${userMiddlewareIsPresent ? `userOnRequest${postMiddleware.sequenceCode ? "," : ""}` : ""}
${postMiddleware.sequenceCode}
);
`.trim();
return source;
}
}
};
}
function createMiddlewareImports(entrypoints, prefix) {
let importsRaw = "";
let sequenceRaw = "";
let index = 0;
for (const entrypoint of entrypoints) {
const name = `_${prefix}_${index}`;
importsRaw += `import { onRequest as ${name} } from '${normalizePath(entrypoint)}';
`;
sequenceRaw += `${index > 0 ? "," : ""}${name}`;
index++;
}
return {
importsCode: importsRaw,
sequenceCode: sequenceRaw
};
}
function vitePluginMiddlewareBuild(opts, internals) {
return {
name: "@astro/plugin-middleware-build",
options(options) {
return addRollupInput(options, [MIDDLEWARE_MODULE_ID]);
},
writeBundle(_, bundle) {
for (const [chunkName, chunk] of Object.entries(bundle)) {
if (chunk.type !== "asset" && chunk.facadeModuleId === MIDDLEWARE_MODULE_ID) {
const outputDirectory = getOutputDirectory(opts.settings.config);
internals.middlewareEntryPoint = new URL(chunkName, outputDirectory);
}
}
}
};
}
export {
MIDDLEWARE_MODULE_ID,
vitePluginMiddleware,
vitePluginMiddlewareBuild
};