astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
171 lines (170 loc) • 6.36 kB
JavaScript
import { fileURLToPath } from "node:url";
import { getInfoOutput } from "../cli/info/index.js";
import { ASTRO_VERSION } from "../core/constants.js";
import { enhanceViteSSRError } from "../core/errors/dev/index.js";
import { AggregateError, CSSError, MarkdownError } from "../core/errors/index.js";
import { Pipeline, loadRenderer } from "../core/render/index.js";
import { createDefaultRoutes } from "../core/routing/default.js";
import { findRouteToRewrite } from "../core/routing/rewrite.js";
import { isPage, viteID } from "../core/util.js";
import { resolveIdToUrl } from "../core/viteUtils.js";
import { PAGE_SCRIPT_ID } from "../vite-plugin-scripts/index.js";
import { getStylesForURL } from "./css.js";
import { getComponentMetadata } from "./metadata.js";
import { createResolve } from "./resolve.js";
class DevPipeline extends Pipeline {
constructor(loader, logger, manifest, settings, config = settings.config, defaultRoutes = createDefaultRoutes(manifest)) {
const resolve = createResolve(loader, config.root);
const serverLike = settings.buildOutput === "server";
const streaming = true;
super(logger, manifest, "development", [], resolve, serverLike, streaming);
this.loader = loader;
this.logger = logger;
this.manifest = manifest;
this.settings = settings;
this.config = config;
this.defaultRoutes = defaultRoutes;
manifest.serverIslandMap = settings.serverIslandMap;
manifest.serverIslandNameMap = settings.serverIslandNameMap;
}
// renderers are loaded on every request,
// so it needs to be mutable here unlike in other environments
renderers = new Array();
manifestData;
componentInterner = /* @__PURE__ */ new WeakMap();
static create(manifestData, {
loader,
logger,
manifest,
settings
}) {
const pipeline = new DevPipeline(loader, logger, manifest, settings);
pipeline.manifestData = manifestData;
return pipeline;
}
async headElements(routeData) {
const {
config: { root },
loader,
runtimeMode,
settings
} = this;
const filePath = new URL(`${routeData.component}`, root);
const scripts = /* @__PURE__ */ new Set();
if (isPage(filePath, settings) && runtimeMode === "development") {
scripts.add({
props: { type: "module", src: "/@vite/client" },
children: ""
});
if (settings.config.devToolbar.enabled && await settings.preferences.get("devToolbar.enabled")) {
const src = await resolveIdToUrl(loader, "astro/runtime/client/dev-toolbar/entrypoint.js");
scripts.add({ props: { type: "module", src }, children: "" });
const additionalMetadata = {
root: fileURLToPath(settings.config.root),
version: ASTRO_VERSION,
latestAstroVersion: settings.latestAstroVersion,
debugInfo: await getInfoOutput({ userConfig: settings.config, print: false })
};
const children = `window.__astro_dev_toolbar__ = ${JSON.stringify(additionalMetadata)}`;
scripts.add({ props: {}, children });
}
}
for (const script of settings.scripts) {
if (script.stage === "head-inline") {
scripts.add({
props: {},
children: script.content
});
} else if (script.stage === "page" && isPage(filePath, settings)) {
scripts.add({
props: { type: "module", src: `/@id/${PAGE_SCRIPT_ID}` },
children: ""
});
}
}
const links = /* @__PURE__ */ new Set();
const { urls, styles: _styles } = await getStylesForURL(filePath, loader);
for (const href of urls) {
links.add({ props: { rel: "stylesheet", href }, children: "" });
}
const styles = /* @__PURE__ */ new Set();
for (const { id, url: src, content } of _styles) {
scripts.add({ props: { type: "module", src }, children: "" });
styles.add({ props: { "data-vite-dev-id": id }, children: content });
}
return { scripts, styles, links };
}
componentMetadata(routeData) {
const {
config: { root },
loader
} = this;
const filePath = new URL(`${routeData.component}`, root);
return getComponentMetadata(filePath, loader);
}
async preload(routeData, filePath) {
const { loader } = this;
for (const route of this.defaultRoutes) {
if (route.matchesComponent(filePath)) {
return route.instance;
}
}
const renderers__ = this.settings.renderers.map((r) => loadRenderer(r, loader));
const renderers_ = await Promise.all(renderers__);
this.renderers = renderers_.filter((r) => Boolean(r));
try {
const componentInstance = await loader.import(viteID(filePath));
this.componentInterner.set(routeData, componentInstance);
return componentInstance;
} catch (error) {
if (MarkdownError.is(error) || CSSError.is(error) || AggregateError.is(error)) {
throw error;
}
throw enhanceViteSSRError({ error, filePath, loader });
}
}
clearRouteCache() {
this.routeCache.clearAll();
this.componentInterner = /* @__PURE__ */ new WeakMap();
}
async getComponentByRoute(routeData) {
const component = this.componentInterner.get(routeData);
if (component) {
return component;
} else {
const filePath = new URL(`${routeData.component}`, this.config.root);
return await this.preload(routeData, filePath);
}
}
async tryRewrite(payload, request) {
if (!this.manifestData) {
throw new Error("Missing manifest data. This is an internal error, please file an issue.");
}
const { routeData, pathname, newUrl } = findRouteToRewrite({
payload,
request,
routes: this.manifestData?.routes,
trailingSlash: this.config.trailingSlash,
buildFormat: this.config.build.format,
base: this.config.base
});
const componentInstance = await this.getComponentByRoute(routeData);
return { newUrl, pathname, componentInstance, routeData };
}
setManifestData(manifestData) {
this.manifestData = manifestData;
}
rewriteKnownRoute(route, sourceRoute) {
if (this.serverLike && sourceRoute.prerender) {
for (let def of this.defaultRoutes) {
if (route === def.route) {
return def.instance;
}
}
}
throw new Error("Unknown route");
}
}
export {
DevPipeline
};