feat: scaffold Astro + Tailwind project

This commit is contained in:
TerryM
2026-05-12 16:16:03 +08:00
parent 906eb5c763
commit 03d3800c6c
12097 changed files with 1266600 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import type { FetchState } from '../fetch/fetch-state.js';
import type { APIContext } from '../../types/public/context.js';
import { type Pipeline } from '../base-pipeline.js';
/**
* Callback invoked at the bottom of the middleware chain to dispatch the
* request to the matched route (endpoint / redirect / page / fallback).
*
* Callers of `AstroMiddleware.handle` pass their owned `PagesHandler`'s
* `handle` method (bound) so route dispatch logic stays out of the
* middleware layer.
*/
export type RenderRouteCallback = (state: FetchState, ctx: APIContext) => Promise<Response>;
/**
* Handles the execution of Astro's middleware chain (internal + user) for a
* single render. Holds a reference to the `Pipeline` and composes the
* internal and user middleware at render time.
*
* Reads per-request data (componentInstance, slots, props, API contexts)
* off the supplied `FetchState`. The actual route dispatch (endpoint /
* redirect / page / fallback) is supplied by the caller as
* `renderRouteCallback` — typically bound to a `PagesHandler.handle`.
*/
export declare class AstroMiddleware {
#private;
constructor(pipeline: Pipeline);
handle(state: FetchState, renderRouteCallback: RenderRouteCallback): Promise<Response>;
}

View File

@@ -0,0 +1,53 @@
import { PipelineFeatures } from "../base-pipeline.js";
import { ROUTE_TYPE_HEADER } from "../constants.js";
import { attachCookiesToResponse } from "../cookies/index.js";
import { applyRewriteToState } from "../rewrites/handler.js";
import { callMiddleware } from "./callMiddleware.js";
import { sequence } from "./index.js";
class AstroMiddleware {
#pipeline;
constructor(pipeline) {
this.#pipeline = pipeline;
}
async handle(state, renderRouteCallback) {
state.pipeline.usedFeatures |= PipelineFeatures.middleware;
const pipeline = this.#pipeline;
await state.getProps();
const apiContext = state.getAPIContext();
state.counter++;
if (state.counter === 4) {
return new Response("Loop Detected", {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508
status: 508,
statusText: "Astro detected a loop where you tried to call the rewriting logic more than four times."
});
}
const next = async (ctx, payload) => {
if (payload) {
pipeline.logger.debug("router", "Called rewriting to:", payload);
const result = await pipeline.tryRewrite(payload, state.request);
applyRewriteToState(state, payload, result);
}
return renderRouteCallback(state, ctx);
};
let response;
if (state.skipMiddleware) {
response = await next(apiContext);
} else {
const pipelineMiddleware = await pipeline.getMiddleware();
const composed = sequence(...pipeline.internalMiddleware, pipelineMiddleware);
response = await callMiddleware(composed, apiContext, next);
}
return this.#finalize(state, response);
}
#finalize(state, response) {
if (response.headers.get(ROUTE_TYPE_HEADER)) {
response.headers.delete(ROUTE_TYPE_HEADER);
}
attachCookiesToResponse(response, state.cookies);
return response;
}
}
export {
AstroMiddleware
};

View File

@@ -0,0 +1,37 @@
import type { MiddlewareHandler, RewritePayload } from '../../types/public/common.js';
import type { APIContext } from '../../types/public/context.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 cannot 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;
};
const 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
};

View File

@@ -0,0 +1,2 @@
import type { MiddlewareHandler } from '../../types/public/common.js';
export declare function defineMiddleware(fn: MiddlewareHandler): MiddlewareHandler;

View File

@@ -0,0 +1,6 @@
function defineMiddleware(fn) {
return fn;
}
export {
defineMiddleware
};

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

@@ -0,0 +1,60 @@
import type { Params } from '../../types/public/common.js';
import type { APIContext } from '../../types/public/context.js';
import { sequence } from './sequence.js';
/**
* 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;
/**
* Initial value of the locals
*/
locals?: App.Locals;
/**
* The client IP address. Must be provided by the adapter or platform from a
* trusted source (e.g. socket address, platform-provided header).
*
* If not provided, accessing `context.clientAddress` will throw an error.
*/
clientAddress?: string;
};
/**
* Creates a context to be passed to Astro middleware `onRequest` function.
*/
declare function createContext({ request, params, userDefinedLocales, defaultLocale, locals, clientAddress, }: CreateContext): APIContext;
/**
* Checks whether the passed `value` is serializable.
*
* A serializable value contains plain values. For example, `Proxy`, `Set`, `Map`, functions, etc.
* are not accepted because they can't be serialized.
*/
export declare function isLocalsSerializable(value: unknown): boolean;
/**
* 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, sequence, trySerializeLocals };
export { defineMiddleware } from './defineMiddleware.js';

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 { DisabledAstroCache } from "../cache/runtime/noop.js";
import { ASTRO_GENERATOR } from "../constants.js";
import { AstroCookies } from "../cookies/index.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { getOriginPathname } from "../routing/rewrite.js";
import { sequence } from "./sequence.js";
function createContext({
request,
params = {},
userDefinedLocales = [],
defaultLocale = "",
locals = {},
clientAddress
}) {
let preferredLocale = void 0;
let preferredLocaleList = void 0;
let currentLocale = void 0;
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_GENERATOR,
props: {},
rewrite,
routePattern: "",
redirect(path, status) {
return new Response(null, {
status: status || 302,
headers: {
Location: path
}
});
},
isPrerendered: false,
get preferredLocale() {
return preferredLocale ??= computePreferredLocale(request, userDefinedLocales);
},
get preferredLocaleList() {
return preferredLocaleList ??= computePreferredLocaleList(request, userDefinedLocales);
},
get currentLocale() {
return currentLocale ??= computeCurrentLocale(route, userDefinedLocales, defaultLocale);
},
url,
get originPathname() {
return getOriginPathname(request);
},
get clientAddress() {
if (clientAddress) {
return clientAddress;
}
throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
},
get locals() {
if (typeof locals !== "object") {
throw new AstroError(AstroErrorData.LocalsNotAnObject);
}
return locals;
},
set locals(_) {
throw new AstroError(AstroErrorData.LocalsReassigned);
},
session: void 0,
cache: new DisabledAstroCache(),
csp: void 0,
logger: void 0
};
return Object.assign(context, {
getActionResult: createGetActionResult(context.locals),
callAction: createCallAction(context)
});
}
function isLocalsSerializable(value) {
const stack = [value];
while (stack.length > 0) {
const current = stack.pop();
const type = typeof current;
if (current === null || type === "string" || type === "number" || type === "boolean") {
continue;
}
if (Array.isArray(current)) {
stack.push(...current);
continue;
}
if (type === "object" && isPlainObject(current)) {
stack.push(...Object.values(current));
continue;
}
return false;
}
return true;
}
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.");
}
}
import { defineMiddleware } from "./defineMiddleware.js";
export {
createContext,
defineMiddleware,
isLocalsSerializable,
sequence,
trySerializeLocals
};

View File

@@ -0,0 +1,2 @@
import type { MiddlewareHandler } from '../../types/public/common.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/public/common.js';
/**
*
* It accepts one or more middleware handlers and makes sure that they are run in sequence.
*/
export declare function sequence(...handlers: MiddlewareHandler[]): MiddlewareHandler;

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

@@ -0,0 +1,74 @@
import { pipelineSymbol } from "../constants.js";
import { ForbiddenRewrite } from "../errors/errors-data.js";
import { AstroError } from "../errors/index.js";
import { getParams } from "../render/index.js";
import { setOriginPathname } from "../routing/rewrite.js";
import { defineMiddleware } from "./defineMiddleware.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.clone());
} else {
newRequest = new Request(
new URL(payload, handleContext.url.origin),
handleContext.request.clone()
);
}
const oldPathname = handleContext.url.pathname;
const pipeline = Reflect.get(handleContext, pipelineSymbol);
const { routeData, pathname } = await pipeline.tryRewrite(
payload,
handleContext.request
);
if (pipeline.manifest.serverLike === true && handleContext.isPrerendered === false && routeData.prerender === true) {
throw new AstroError({
...ForbiddenRewrite,
message: ForbiddenRewrite.message(
handleContext.url.pathname,
pathname,
routeData.component
),
hint: ForbiddenRewrite.hint(routeData.component)
});
}
carriedPayload = payload;
handleContext.request = newRequest;
handleContext.url = new URL(newRequest.url);
handleContext.params = getParams(routeData, pathname);
handleContext.routePattern = routeData.route;
setOriginPathname(
handleContext.request,
oldPathname,
pipeline.manifest.trailingSlash,
pipeline.manifest.buildFormat
);
}
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 = "virtual:astro:middleware";
export declare function vitePluginMiddleware({ settings }: {
settings: AstroSettings;
}): VitePlugin;
export declare function vitePluginMiddlewareBuild(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin;

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

@@ -0,0 +1,140 @@
import { fileURLToPath } from "node:url";
import {
normalizePath as viteNormalizePath
} from "vite";
import { getServerOutputDirectory } from "../../prerender/utils.js";
import { addRollupInput } from "../build/add-rollup-input.js";
import { ASTRO_VITE_ENVIRONMENT_NAMES, MIDDLEWARE_PATH_SEGMENT_NAME } from "../constants.js";
import { MissingMiddlewareForInternationalization } from "../errors/errors-data.js";
import { AstroError } from "../errors/index.js";
import { normalizePath } from "../viteUtils.js";
const MIDDLEWARE_MODULE_ID = "virtual:astro:middleware";
const MIDDLEWARE_RESOLVED_MODULE_ID = "\0" + MIDDLEWARE_MODULE_ID;
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;
const normalizedSrcDir = viteNormalizePath(fileURLToPath(settings.config.srcDir));
return {
name: "@astro/plugin-middleware",
applyToEnvironment(environment) {
return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.astro || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender;
},
configureServer(server) {
server.watcher.on("change", (path) => {
const normalizedPath = viteNormalizePath(path);
if (!normalizedPath.startsWith(normalizedSrcDir)) return;
const relativePath = normalizedPath.slice(normalizedSrcDir.length);
if (!relativePath.startsWith(`${MIDDLEWARE_PATH_SEGMENT_NAME}.`)) return;
for (const name of [
ASTRO_VITE_ENVIRONMENT_NAMES.ssr,
ASTRO_VITE_ENVIRONMENT_NAMES.astro
]) {
const environment = server.environments[name];
if (!environment) continue;
const virtualMod = environment.moduleGraph.getModuleById(MIDDLEWARE_RESOLVED_MODULE_ID);
if (virtualMod) {
environment.moduleGraph.invalidateModule(virtualMod);
}
environment.hot.send("astro:middleware-updated", {});
}
});
},
resolveId: {
filter: {
id: new RegExp(`^${MIDDLEWARE_MODULE_ID}$`)
},
async handler() {
const middlewareId = await this.resolve(
`${decodeURI(settings.config.srcDir.pathname)}${MIDDLEWARE_PATH_SEGMENT_NAME}`
);
userMiddlewareIsPresent = !!middlewareId;
if (middlewareId) {
resolvedMiddlewareId = middlewareId.id;
return MIDDLEWARE_RESOLVED_MODULE_ID;
} else if (hasIntegrationMiddleware) {
return MIDDLEWARE_RESOLVED_MODULE_ID;
} else {
return NOOP_MIDDLEWARE;
}
}
},
load: {
filter: {
id: new RegExp(`^(${NOOP_MIDDLEWARE}|${MIDDLEWARE_RESOLVED_MODULE_ID})$`)
},
async handler(id) {
if (id === NOOP_MIDDLEWARE) {
if (!userMiddlewareIsPresent && settings.config.i18n?.routing === "manual") {
throw new AstroError(MissingMiddlewareForInternationalization);
}
return { code: "export const onRequest = (_, next) => next()" };
}
if (id === MIDDLEWARE_RESOLVED_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 code = `
${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 { code };
}
}
}
};
}
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) {
let canSplitMiddleware = true;
return {
name: "@astro/plugin-middleware-build",
configResolved(config) {
canSplitMiddleware = config.ssr.target !== "webworker";
},
options(options) {
if (canSplitMiddleware) {
return addRollupInput(options, [MIDDLEWARE_MODULE_ID]);
} else {
}
},
writeBundle(_, bundle) {
for (const [chunkName, chunk] of Object.entries(bundle)) {
if (chunk.type !== "asset" && chunk.facadeModuleId === MIDDLEWARE_RESOLVED_MODULE_ID) {
const outputDirectory = getServerOutputDirectory(opts.settings);
internals.middlewareEntryPoint = new URL(chunkName, outputDirectory);
}
}
}
};
}
export {
MIDDLEWARE_MODULE_ID,
vitePluginMiddleware,
vitePluginMiddlewareBuild
};