astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
141 lines (139 loc) • 5.46 kB
JavaScript
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
};