0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-04-07 23:41:43 -05:00

[ci] format

This commit is contained in:
Arsh 2024-02-20 14:41:38 +00:00 committed by astrobot-houston
parent 5acc3135ba
commit c1671dff22
20 changed files with 429 additions and 206 deletions

View file

@ -1,8 +1,4 @@
import type {
ManifestData,
RouteData,
SSRManifest,
} from '../../@types/astro.js';
import type { ManifestData, RouteData, SSRManifest } from '../../@types/astro.js';
import type { SinglePageBuiltModule } from '../build/types.js';
import { getSetCookiesFromResponse } from '../cookies/index.js';
import { consoleLogDestination } from '../logger/console.js';
@ -20,7 +16,13 @@ import { matchRoute } from '../routing/match.js';
import { AppPipeline } from './pipeline.js';
import { normalizeTheLocale } from '../../i18n/index.js';
import { RenderContext } from '../render-context.js';
import { clientAddressSymbol, clientLocalsSymbol, responseSentSymbol, REROUTABLE_STATUS_CODES, REROUTE_DIRECTIVE_HEADER } from '../constants.js';
import {
clientAddressSymbol,
clientLocalsSymbol,
responseSentSymbol,
REROUTABLE_STATUS_CODES,
REROUTE_DIRECTIVE_HEADER,
} from '../constants.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
export { deserializeManifest } from './common.js';
@ -124,7 +126,7 @@ export class App {
},
serverLike: true,
streaming,
})
});
}
set setManifestData(newManifestData: ManifestData) {
@ -294,7 +296,14 @@ export class App {
let response;
try {
const renderContext = RenderContext.create({ pipeline: this.#pipeline, locals, pathname, request, routeData, status: defaultStatus })
const renderContext = RenderContext.create({
pipeline: this.#pipeline,
locals,
pathname,
request,
routeData,
status: defaultStatus,
});
response = await renderContext.render(await mod.page());
} catch (err: any) {
this.#logger.error(null, err.stack || err.message || String(err));
@ -386,7 +395,7 @@ export class App {
request,
routeData: errorRouteData,
status,
})
});
const response = await renderContext.render(await mod.page());
return this.#mergeResponses(response, originalResponse);
} catch {

View file

@ -1,14 +1,25 @@
import type { RouteData, SSRElement, SSRResult } from "../../@types/astro.js";
import { Pipeline } from "../base-pipeline.js";
import { createModuleScriptElement, createStylesheetElementSet } from "../render/ssr-element.js";
import type { RouteData, SSRElement, SSRResult } from '../../@types/astro.js';
import { Pipeline } from '../base-pipeline.js';
import { createModuleScriptElement, createStylesheetElementSet } from '../render/ssr-element.js';
export class AppPipeline extends Pipeline {
static create({ logger, manifest, mode, renderers, resolve, serverLike, streaming }: Pick<AppPipeline, 'logger' | 'manifest' | 'mode' | 'renderers' | 'resolve' | 'serverLike' | 'streaming'>) {
return new AppPipeline(logger, manifest, mode, renderers, resolve, serverLike, streaming);
}
static create({
logger,
manifest,
mode,
renderers,
resolve,
serverLike,
streaming,
}: Pick<
AppPipeline,
'logger' | 'manifest' | 'mode' | 'renderers' | 'resolve' | 'serverLike' | 'streaming'
>) {
return new AppPipeline(logger, manifest, mode, renderers, resolve, serverLike, streaming);
}
headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'> {
const routeInfo = this.manifest.routes.find(route => route.routeData === routeData);
headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'> {
const routeInfo = this.manifest.routes.find((route) => route.routeData === routeData);
// may be used in the future for handling rel=modulepreload, rel=icon, rel=manifest etc.
const links = new Set<never>();
const scripts = new Set<SSRElement>();
@ -26,7 +37,7 @@ export class AppPipeline extends Pipeline {
scripts.add(createModuleScriptElement(script));
}
}
return { links, styles, scripts }
return { links, styles, scripts };
}
componentMetadata() {}

View file

@ -1,4 +1,11 @@
import type { MiddlewareHandler, RouteData, RuntimeMode, SSRLoadedRenderer, SSRManifest, SSRResult } from '../@types/astro.js';
import type {
MiddlewareHandler,
RouteData,
RuntimeMode,
SSRLoadedRenderer,
SSRManifest,
SSRResult,
} from '../@types/astro.js';
import type { Logger } from './logger/core.js';
import { RouteCache } from './render/route-cache.js';
import { createI18nMiddleware } from '../i18n/middleware.js';
@ -6,7 +13,7 @@ import { createI18nMiddleware } from '../i18n/middleware.js';
/**
* The `Pipeline` represents the static parts of rendering that do not change between requests.
* These are mostly known when the server first starts up and do not change.
*
*
* Thus, a `Pipeline` is created once at process start and then used by every `RenderContext`.
*/
export abstract class Pipeline {
@ -38,13 +45,15 @@ export abstract class Pipeline {
/**
* Used for `Astro.site`.
*/
readonly site = manifest.site,
readonly site = manifest.site
) {
this.internalMiddleware = [ createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat) ];
this.internalMiddleware = [
createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat),
];
}
abstract headElements(routeData: RouteData): Promise<HeadElements> | HeadElements
abstract componentMetadata(routeData: RouteData): Promise<SSRResult['componentMetadata']> | void
abstract headElements(routeData: RouteData): Promise<HeadElements> | HeadElements;
abstract componentMetadata(routeData: RouteData): Promise<SSRResult['componentMetadata']> | void;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface

View file

@ -499,7 +499,7 @@ async function generatePath(
logger,
ssr: serverLike,
});
const renderContext = RenderContext.create({ pipeline, pathname, request, routeData: route })
const renderContext = RenderContext.create({ pipeline, pathname, request, routeData: route });
let body: string | Uint8Array;
let response: Response;

View file

@ -4,8 +4,17 @@ import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../vite-plugin-sc
import type { SSRManifest } from '../app/types.js';
import { routeIsFallback, routeIsRedirect } from '../redirects/helpers.js';
import { Pipeline } from '../render/index.js';
import { createAssetLink, createModuleScriptsSet, createStylesheetElementSet } from '../render/ssr-element.js';
import { getPageDataByComponent, type BuildInternals, cssOrder, mergeInlineCss } from './internal.js';
import {
createAssetLink,
createModuleScriptsSet,
createStylesheetElementSet,
} from '../render/ssr-element.js';
import {
getPageDataByComponent,
type BuildInternals,
cssOrder,
mergeInlineCss,
} from './internal.js';
import {
ASTRO_PAGE_RESOLVED_MODULE_ID,
getVirtualModulePageNameFromPath,
@ -47,10 +56,22 @@ export class BuildPipeline extends Pipeline {
}
const serverLike = isServerLikeOutput(config);
const streaming = true;
super(options.logger, manifest, options.mode, manifest.renderers, resolve, serverLike, streaming)
super(
options.logger,
manifest,
options.mode,
manifest.renderers,
resolve,
serverLike,
streaming
);
}
static create({ internals, manifest, options }: Pick<BuildPipeline, 'internals' | 'manifest' | 'options'>) {
static create({
internals,
manifest,
options,
}: Pick<BuildPipeline, 'internals' | 'manifest' | 'options'>) {
return new BuildPipeline(internals, manifest, options);
}
@ -106,17 +127,24 @@ export class BuildPipeline extends Pipeline {
}
headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'> {
const { internals, manifest: { assetsPrefix, base }, settings } = this
const {
internals,
manifest: { assetsPrefix, base },
settings,
} = this;
const links = new Set<never>();
const pageBuildData = getPageDataByComponent(internals, routeData.component)
const pageBuildData = getPageDataByComponent(internals, routeData.component);
const scripts = createModuleScriptsSet(
pageBuildData?.hoistedScript ? [pageBuildData.hoistedScript] : [],
base,
assetsPrefix
);
const sortedCssAssets = pageBuildData?.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []);
const sortedCssAssets = pageBuildData?.styles
.sort(cssOrder)
.map(({ sheet }) => sheet)
.reduce(mergeInlineCss, []);
const styles = createStylesheetElementSet(sortedCssAssets ?? [], base, assetsPrefix);
if (settings.scripts.some((script) => script.stage === 'page')) {
const hashedFilePath = internals.entrySpecifierToBundleMap.get(PAGE_SCRIPT_ID);
if (typeof hashedFilePath !== 'string') {
@ -128,7 +156,7 @@ export class BuildPipeline extends Pipeline {
children: '',
});
}
// Add all injected scripts to the page.
for (const script of settings.scripts) {
if (script.stage === 'head-inline') {
@ -138,7 +166,7 @@ export class BuildPipeline extends Pipeline {
});
}
}
return { scripts, styles, links }
return { scripts, styles, links };
}
componentMetadata() {}

View file

@ -1,8 +1,4 @@
import type {
APIContext,
Locales,
Params,
} from '../../@types/astro.js';
import type { APIContext, Locales, Params } from '../../@types/astro.js';
import { ASTRO_VERSION, clientAddressSymbol, clientLocalsSymbol } from '../constants.js';
import type { AstroCookies } from '../cookies/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
@ -23,7 +19,7 @@ type CreateAPIContext = {
routingStrategy: RoutingStrategies | undefined;
defaultLocale: string | undefined;
route: string;
cookies: AstroCookies
cookies: AstroCookies;
};
/**
@ -41,7 +37,7 @@ export function createAPIContext({
routingStrategy,
defaultLocale,
route,
cookies
cookies,
}: CreateAPIContext): APIContext {
let preferredLocale: string | undefined = undefined;
let preferredLocaleList: string[] | undefined = undefined;

View file

@ -38,12 +38,16 @@ export type CreateContext = {
/**
* Creates a context to be passed to Astro middleware `onRequest` function.
*/
function createContext({ request, params = {}, userDefinedLocales = [] }: CreateContext): APIContext {
function createContext({
request,
params = {},
userDefinedLocales = [],
}: CreateContext): APIContext {
let preferredLocale: string | undefined = undefined;
let preferredLocaleList: string[] | undefined = undefined;
let currentLocale: string | undefined = undefined;
const url = new URL(request.url);
const route = url.pathname
const route = url.pathname;
return {
cookies: new AstroCookies(request),
@ -61,13 +65,18 @@ function createContext({ request, params = {}, userDefinedLocales = [] }: Create
});
},
get preferredLocale(): string | undefined {
return preferredLocale ??= computePreferredLocale(request, userDefinedLocales);
return (preferredLocale ??= computePreferredLocale(request, userDefinedLocales));
},
get preferredLocaleList(): string[] | undefined {
return preferredLocaleList ??= computePreferredLocaleList(request, userDefinedLocales);
return (preferredLocaleList ??= computePreferredLocaleList(request, userDefinedLocales));
},
get currentLocale(): string | undefined {
return currentLocale ??= computeCurrentLocale(route, userDefinedLocales, undefined, undefined);
return (currentLocale ??= computeCurrentLocale(
route,
userDefinedLocales,
undefined,
undefined
));
},
url,
get clientAddress() {

View file

@ -7,4 +7,3 @@ export function routeIsRedirect(route: RouteData | undefined): route is Redirect
export function routeIsFallback(route: RouteData | undefined): route is RedirectRouteData {
return route?.type === 'fallback';
}

View file

@ -1,18 +1,22 @@
import type { RenderContext } from '../render-context.js';
export async function renderRedirect(renderContext: RenderContext) {
const { request: { method }, routeData } = renderContext;
const {
request: { method },
routeData,
} = renderContext;
const { redirect, redirectRoute } = routeData;
const status =
redirectRoute && typeof redirect === "object" ? redirect.status
: method === "GET" ? 301
: 308
const headers = { location: redirectRouteGenerate(renderContext) };
redirectRoute && typeof redirect === 'object' ? redirect.status : method === 'GET' ? 301 : 308;
const headers = { location: redirectRouteGenerate(renderContext) };
return new Response(null, { status, headers });
}
function redirectRouteGenerate(renderContext: RenderContext): string {
const { params, routeData: { redirect, redirectRoute } } = renderContext;
const {
params,
routeData: { redirect, redirectRoute },
} = renderContext;
if (typeof redirectRoute !== 'undefined') {
return redirectRoute?.generate(params) || redirectRoute?.pathname || '/';

View file

@ -1,4 +1,9 @@
import type { APIContext, ComponentInstance, MiddlewareHandler, RouteData } from '../@types/astro.js';
import type {
APIContext,
ComponentInstance,
MiddlewareHandler,
RouteData,
} from '../@types/astro.js';
import { renderEndpoint } from '../runtime/server/endpoint.js';
import { attachCookiesToResponse } from './cookies/index.js';
import { callMiddleware } from './middleware/callMiddleware.js';
@ -6,7 +11,12 @@ import { sequence } from './middleware/index.js';
import { AstroCookies } from './cookies/index.js';
import { createResult } from './render/index.js';
import { renderPage } from '../runtime/server/index.js';
import { ASTRO_VERSION, ROUTE_TYPE_HEADER, clientAddressSymbol, clientLocalsSymbol } from './constants.js';
import {
ASTRO_VERSION,
ROUTE_TYPE_HEADER,
clientAddressSymbol,
clientLocalsSymbol,
} from './constants.js';
import { getParams, getProps, type Pipeline } from './render/index.js';
import { AstroError, AstroErrorData } from './errors/index.js';
import {
@ -26,19 +36,36 @@ export class RenderContext {
readonly routeData: RouteData,
public status: number,
readonly cookies = new AstroCookies(request),
readonly params = getParams(routeData, pathname),
readonly params = getParams(routeData, pathname)
) {}
static create({ locals = {}, middleware, pathname, pipeline, request, routeData, status = 200 }: Pick<RenderContext, 'pathname' |'pipeline' | 'request' | 'routeData'> & Partial<Pick<RenderContext, 'locals' | 'middleware' | 'status'>>) {
return new RenderContext(pipeline, locals, sequence(...pipeline.internalMiddleware, middleware ?? pipeline.middleware), pathname, request, routeData, status);
static create({
locals = {},
middleware,
pathname,
pipeline,
request,
routeData,
status = 200,
}: Pick<RenderContext, 'pathname' | 'pipeline' | 'request' | 'routeData'> &
Partial<Pick<RenderContext, 'locals' | 'middleware' | 'status'>>) {
return new RenderContext(
pipeline,
locals,
sequence(...pipeline.internalMiddleware, middleware ?? pipeline.middleware),
pathname,
request,
routeData,
status
);
}
/**
* The main function of the RenderContext.
*
*
* Use this function to render any route known to Astro.
* It attempts to render a route. A route can be a:
*
*
* - page
* - redirect
* - endpoint
@ -47,25 +74,46 @@ export class RenderContext {
async render(componentInstance: ComponentInstance | undefined): Promise<Response> {
const { cookies, middleware, pathname, pipeline, routeData } = this;
const { logger, routeCache, serverLike, streaming } = pipeline;
const props = await getProps({ mod: componentInstance, routeData, routeCache, pathname, logger, serverLike });
const props = await getProps({
mod: componentInstance,
routeData,
routeCache,
pathname,
logger,
serverLike,
});
const apiContext = this.createAPIContext(props);
const { type } = routeData;
const lastNext =
type === 'endpoint' ? () => renderEndpoint(componentInstance as any, apiContext, serverLike, logger) :
type === 'redirect' ? () => renderRedirect(this) :
type === 'page' ? async () => {
const result = await this.createResult(componentInstance!);
const response = await renderPage(result, componentInstance?.default as any, props, {}, streaming, routeData);
response.headers.set(ROUTE_TYPE_HEADER, "page");
return response;
} :
type === 'fallback' ? () => new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: "fallback" } }) :
() => { throw new Error("Unknown type of route: " + type) }
type === 'endpoint'
? () => renderEndpoint(componentInstance as any, apiContext, serverLike, logger)
: type === 'redirect'
? () => renderRedirect(this)
: type === 'page'
? async () => {
const result = await this.createResult(componentInstance!);
const response = await renderPage(
result,
componentInstance?.default as any,
props,
{},
streaming,
routeData
);
response.headers.set(ROUTE_TYPE_HEADER, 'page');
return response;
}
: type === 'fallback'
? () =>
new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: 'fallback' } })
: () => {
throw new Error('Unknown type of route: ' + type);
};
const response = await callMiddleware(middleware, apiContext, lastNext);
if (response.headers.get(ROUTE_TYPE_HEADER)) {
response.headers.delete(ROUTE_TYPE_HEADER)
response.headers.delete(ROUTE_TYPE_HEADER);
}
// LEGACY: we put cookies on the response object,
// where the adapter might be expecting to read it.
@ -79,11 +127,22 @@ export class RenderContext {
const { cookies, i18nData, params, pipeline, request } = this;
const { currentLocale, preferredLocale, preferredLocaleList } = i18nData;
const generator = `Astro v${ASTRO_VERSION}`;
const redirect = (path: string, status = 302) => new Response(null, { status, headers: { Location: path } });
const redirect = (path: string, status = 302) =>
new Response(null, { status, headers: { Location: path } });
const site = pipeline.site ? new URL(pipeline.site) : undefined;
const url = new URL(request.url);
return {
cookies, currentLocale, generator, params, preferredLocale, preferredLocaleList, props, redirect, request, site, url,
cookies,
currentLocale,
generator,
params,
preferredLocale,
preferredLocaleList,
props,
redirect,
request,
site,
url,
get clientAddress() {
if (clientAddressSymbol in request) {
return Reflect.get(request, clientAddressSymbol) as string;
@ -110,39 +169,80 @@ export class RenderContext {
// where the adapter might be expecting to read it after the response.
Reflect.set(request, clientLocalsSymbol, val);
}
}
}
},
};
}
async createResult(mod: ComponentInstance) {
const { cookies, locals, params, pathname, pipeline, request, routeData, status } = this;
const { adapterName, clientDirectives, compressHTML, i18n, manifest, logger, renderers, resolve, site, serverLike } = pipeline;
const {
adapterName,
clientDirectives,
compressHTML,
i18n,
manifest,
logger,
renderers,
resolve,
site,
serverLike,
} = pipeline;
const { links, scripts, styles } = await pipeline.headElements(routeData);
const componentMetadata = await pipeline.componentMetadata(routeData) ?? manifest.componentMetadata;
const componentMetadata =
(await pipeline.componentMetadata(routeData)) ?? manifest.componentMetadata;
const { defaultLocale, locales, routing: routingStrategy } = i18n ?? {};
const partial = Boolean(mod.partial);
return createResult({ adapterName, clientDirectives, componentMetadata, compressHTML, cookies, defaultLocale, locales, locals, logger, links, params, partial, pathname, renderers, resolve, request, route: routeData.route, routingStrategy, site, scripts, ssr: serverLike, status, styles });
return createResult({
adapterName,
clientDirectives,
componentMetadata,
compressHTML,
cookies,
defaultLocale,
locales,
locals,
logger,
links,
params,
partial,
pathname,
renderers,
resolve,
request,
route: routeData.route,
routingStrategy,
site,
scripts,
ssr: serverLike,
status,
styles,
});
}
/**
* API Context may be created multiple times per request, i18n data needs to be computed only once.
* So, it is computed and saved here on creation of the first APIContext and reused for later ones.
*/
#i18nData?: Pick<APIContext, "currentLocale" | "preferredLocale" | "preferredLocaleList">
#i18nData?: Pick<APIContext, 'currentLocale' | 'preferredLocale' | 'preferredLocaleList'>;
get i18nData() {
if (this.#i18nData) return this.#i18nData
const { pipeline: { i18n }, request, routeData } = this;
if (!i18n) return {
currentLocale: undefined,
preferredLocale: undefined,
preferredLocaleList: undefined
}
const { defaultLocale, locales, routing } = i18n
return this.#i18nData = {
if (this.#i18nData) return this.#i18nData;
const {
pipeline: { i18n },
request,
routeData,
} = this;
if (!i18n)
return {
currentLocale: undefined,
preferredLocale: undefined,
preferredLocaleList: undefined,
};
const { defaultLocale, locales, routing } = i18n;
return (this.#i18nData = {
currentLocale: computeCurrentLocale(routeData.route, locales, routing, defaultLocale),
preferredLocale: computePreferredLocale(request, locales),
preferredLocaleList: computePreferredLocaleList(request, locales)
}
preferredLocaleList: computePreferredLocaleList(request, locales),
});
}
}

View file

@ -24,11 +24,10 @@ export async function getProps(opts: GetParamsAndPropsOptions): Promise<Props> {
return {};
}
if (routeIsRedirect(route) || routeIsFallback(route)) {
return {};
}
// This is a dynamic route, start getting the params
const params = getParams(route, pathname);
if (mod) {

View file

@ -2,7 +2,7 @@ import { appendForwardSlash, joinPaths } from '@astrojs/internal-helpers/path';
import type { APIContext, Locales, MiddlewareHandler, SSRManifest } from '../@types/astro.js';
import { getPathByLocale, normalizeTheLocale } from './index.js';
import { shouldAppendForwardSlash } from '../core/build/util.js';
import type { SSRManifestI18n } from '../core/app/types.js'
import type { SSRManifestI18n } from '../core/app/types.js';
import { ROUTE_TYPE_HEADER } from '../core/constants.js';
// Checks if the pathname has any locale, exception for the defaultLocale, which is ignored on purpose.
@ -102,100 +102,100 @@ export function createI18nMiddleware(
const type = response.headers.get(ROUTE_TYPE_HEADER);
// If the route we're processing is not a page, then we ignore it
if (type !== 'page' && type !== 'fallback') {
return response
return response;
}
const { url, currentLocale } = context;
const { locales, defaultLocale, fallback, routing } = i18n;
switch (i18n.routing) {
case 'domains-prefix-other-locales': {
if (localeHasntDomain(i18n, currentLocale)) {
const result = prefixOtherLocales(url, response);
if (result) {
return result;
}
}
break;
}
case 'pathname-prefix-other-locales': {
switch (i18n.routing) {
case 'domains-prefix-other-locales': {
if (localeHasntDomain(i18n, currentLocale)) {
const result = prefixOtherLocales(url, response);
if (result) {
return result;
}
break;
}
case 'domains-prefix-always-no-redirect': {
if (localeHasntDomain(i18n, currentLocale)) {
const result = prefixAlwaysNoRedirect(url, response);
if (result) {
return result;
}
}
break;
break;
}
case 'pathname-prefix-other-locales': {
const result = prefixOtherLocales(url, response);
if (result) {
return result;
}
break;
}
case 'pathname-prefix-always-no-redirect': {
case 'domains-prefix-always-no-redirect': {
if (localeHasntDomain(i18n, currentLocale)) {
const result = prefixAlwaysNoRedirect(url, response);
if (result) {
return result;
}
break;
}
break;
}
case 'pathname-prefix-always': {
case 'pathname-prefix-always-no-redirect': {
const result = prefixAlwaysNoRedirect(url, response);
if (result) {
return result;
}
break;
}
case 'pathname-prefix-always': {
const result = prefixAlways(url, response, context);
if (result) {
return result;
}
break;
}
case 'domains-prefix-always': {
if (localeHasntDomain(i18n, currentLocale)) {
const result = prefixAlways(url, response, context);
if (result) {
return result;
}
break;
}
case 'domains-prefix-always': {
if (localeHasntDomain(i18n, currentLocale)) {
const result = prefixAlways(url, response, context);
if (result) {
return result;
}
}
break;
}
break;
}
}
if (response.status >= 300 && fallback) {
const fallbackKeys = i18n.fallback ? Object.keys(i18n.fallback) : [];
if (response.status >= 300 && fallback) {
const fallbackKeys = i18n.fallback ? Object.keys(i18n.fallback) : [];
// we split the URL using the `/`, and then check in the returned array we have the locale
const segments = url.pathname.split('/');
const urlLocale = segments.find((segment) => {
for (const locale of locales) {
if (typeof locale === 'string') {
if (locale === segment) {
return true;
}
} else if (locale.path === segment) {
// we split the URL using the `/`, and then check in the returned array we have the locale
const segments = url.pathname.split('/');
const urlLocale = segments.find((segment) => {
for (const locale of locales) {
if (typeof locale === 'string') {
if (locale === segment) {
return true;
}
} else if (locale.path === segment) {
return true;
}
return false;
});
if (urlLocale && fallbackKeys.includes(urlLocale)) {
const fallbackLocale = fallback[urlLocale];
// the user might have configured the locale using the granular locales, so we want to retrieve its corresponding path instead
const pathFallbackLocale = getPathByLocale(fallbackLocale, locales);
let newPathname: string;
// If a locale falls back to the default locale, we want to **remove** the locale because
// the default locale doesn't have a prefix
if (pathFallbackLocale === defaultLocale && routing === 'pathname-prefix-other-locales') {
newPathname = url.pathname.replace(`/${urlLocale}`, ``);
} else {
newPathname = url.pathname.replace(`/${urlLocale}`, `/${pathFallbackLocale}`);
}
return context.redirect(newPathname);
}
return false;
});
if (urlLocale && fallbackKeys.includes(urlLocale)) {
const fallbackLocale = fallback[urlLocale];
// the user might have configured the locale using the granular locales, so we want to retrieve its corresponding path instead
const pathFallbackLocale = getPathByLocale(fallbackLocale, locales);
let newPathname: string;
// If a locale falls back to the default locale, we want to **remove** the locale because
// the default locale doesn't have a prefix
if (pathFallbackLocale === defaultLocale && routing === 'pathname-prefix-other-locales') {
newPathname = url.pathname.replace(`/${urlLocale}`, ``);
} else {
newPathname = url.pathname.replace(`/${urlLocale}`, `/${pathFallbackLocale}`);
}
return context.redirect(newPathname);
}
}
return response;
};

View file

@ -1,5 +1,5 @@
import { bold } from 'kleur/colors';
import { REROUTABLE_STATUS_CODES, REROUTE_DIRECTIVE_HEADER } from '../../core/constants.js';;
import { REROUTABLE_STATUS_CODES, REROUTE_DIRECTIVE_HEADER } from '../../core/constants.js';
import type { APIContext, EndpointHandler } from '../../@types/astro.js';
import type { Logger } from '../../core/logger/core.js';

View file

@ -29,10 +29,7 @@ export function recordServerError(
telemetry.record(eventError({ cmd: 'dev', err: errorWithMetadata, isFatal: false }));
}
logger.error(
null,
formatErrorMessage(errorWithMetadata, logger.level() === 'debug')
);
logger.error(null, formatErrorMessage(errorWithMetadata, logger.level() === 'debug'));
return {
error: err,

View file

@ -1,5 +1,13 @@
import url from 'node:url'
import type { AstroSettings, ComponentInstance, DevToolbarMetadata, RouteData, SSRElement, SSRLoadedRenderer, SSRManifest } from '../@types/astro.js';
import url from 'node:url';
import type {
AstroSettings,
ComponentInstance,
DevToolbarMetadata,
RouteData,
SSRElement,
SSRLoadedRenderer,
SSRManifest,
} from '../@types/astro.js';
import type { Logger } from '../core/logger/core.js';
import type { ModuleLoader } from '../core/module-loader/index.js';
import { Pipeline, loadRenderer } from '../core/render/index.js';
@ -19,28 +27,38 @@ import { getComponentMetadata } from './metadata.js';
export class DevPipeline extends Pipeline {
// renderers are loaded on every request,
// so it needs to be mutable here unlike in other environments
override renderers = new Array<SSRLoadedRenderer>
override renderers = new Array<SSRLoadedRenderer>();
private constructor(
readonly loader: ModuleLoader,
readonly logger: Logger,
readonly manifest: SSRManifest,
readonly settings: AstroSettings,
readonly config = settings.config,
readonly config = settings.config
) {
const mode = 'development'
const mode = 'development';
const resolve = createResolve(loader, config.root);
const serverLike = isServerLikeOutput(config);
const streaming = true;
super(logger, manifest, mode, [], resolve, serverLike, streaming);
}
static create({ loader, logger, manifest, settings }: Pick<DevPipeline, 'loader' | 'logger' | 'manifest' | 'settings'>) {
return new DevPipeline(loader, logger, manifest, settings)
static create({
loader,
logger,
manifest,
settings,
}: Pick<DevPipeline, 'loader' | 'logger' | 'manifest' | 'settings'>) {
return new DevPipeline(loader, logger, manifest, settings);
}
async headElements(routeData: RouteData): Promise<HeadElements> {
const { config: { root }, loader, mode, settings } = this;
const {
config: { root },
loader,
mode,
settings,
} = this;
const filePath = new URL(`./${routeData.component}`, root);
const { scripts } = await getScriptsForURL(filePath, root, loader);
@ -55,9 +73,9 @@ export class DevPipeline extends Pipeline {
settings.config.devToolbar.enabled &&
(await settings.preferences.get('devToolbar.enabled'))
) {
const src = await resolveIdToUrl(loader, 'astro/runtime/client/dev-toolbar/entrypoint.js')
const src = await resolveIdToUrl(loader, 'astro/runtime/client/dev-toolbar/entrypoint.js');
scripts.add({ props: { type: 'module', src }, children: '' });
const additionalMetadata: DevToolbarMetadata['__astro_dev_toolbar__'] = {
root: url.fileURLToPath(settings.config.root),
version: ASTRO_VERSION,
@ -69,7 +87,7 @@ export class DevPipeline extends Pipeline {
scripts.add({ props: {}, children });
}
}
// TODO: We should allow adding generic HTML elements to the head, not just scripts
for (const script of settings.scripts) {
if (script.stage === 'head-inline') {
@ -99,15 +117,18 @@ export class DevPipeline extends Pipeline {
// But we still want to inject the styles to avoid FOUC. The style tags
// should emulate what Vite injects so further HMR works as expected.
styles.add({ props: { 'data-vite-dev-id': id }, children: content });
};
return { scripts, styles, links }
}
return { scripts, styles, links };
}
componentMetadata(routeData: RouteData) {
const { config: { root }, loader } = this;
const {
config: { root },
loader,
} = this;
const filePath = new URL(`./${routeData.component}`, root);
return getComponentMetadata(filePath, loader)
return getComponentMetadata(filePath, loader);
}
async preload(filePath: URL) {
@ -120,13 +141,13 @@ export class DevPipeline extends Pipeline {
try {
// Load the module from the Vite SSR Runtime.
return await loader.import(viteID(filePath)) as ComponentInstance;
return (await loader.import(viteID(filePath))) as ComponentInstance;
} catch (error) {
// If the error came from Markdown or CSS, we already handled it and there's no need to enhance it
if (MarkdownError.is(error) || CSSError.is(error) || AggregateError.is(error)) {
throw error;
}
throw enhanceViteSSRError({ error, filePath, loader });
}
}

View file

@ -1,9 +1,5 @@
import type http from 'node:http';
import type {
ComponentInstance,
ManifestData,
RouteData,
} from '../@types/astro.js';
import type { ComponentInstance, ManifestData, RouteData } from '../@types/astro.js';
import { AstroErrorData, isAstroError } from '../core/errors/index.js';
import { req } from '../core/messages.js';
import { loadMiddleware } from '../core/middleware/loadMiddleware.js';
@ -158,7 +154,7 @@ export async function handleRoute({
let options: SSROptions | undefined = undefined;
let route: RouteData;
const middleware = (await loadMiddleware(loader)).onRequest;
if (!matchedRoute) {
if (config.i18n) {
const locales = config.i18n.locales;
@ -208,7 +204,13 @@ export async function handleRoute({
fallbackRoutes: [],
isIndex: false,
};
renderContext = RenderContext.create({ pipeline: pipeline, pathname, middleware, request, routeData: route });
renderContext = RenderContext.create({
pipeline: pipeline,
pathname,
middleware,
request,
routeData: route,
});
} else {
return handle404Response(origin, incomingRequest, incomingResponse);
}
@ -217,7 +219,7 @@ export async function handleRoute({
const { preloadedComponent } = matchedRoute;
route = matchedRoute.route;
// Allows adapters to pass in locals in dev mode.
const locals = Reflect.get(incomingRequest, clientLocalsSymbol)
const locals = Reflect.get(incomingRequest, clientLocalsSymbol);
request = createRequest({
url,
// Headers are only available when using SSR.
@ -244,7 +246,14 @@ export async function handleRoute({
};
mod = preloadedComponent;
renderContext = RenderContext.create({ locals, pipeline, pathname, middleware, request, routeData: route });
renderContext = RenderContext.create({
locals,
pipeline,
pathname,
middleware,
request,
routeData: route,
});
}
let response = await renderContext.render(mod);

View file

@ -21,9 +21,11 @@ describe('core/render', () => {
before(async () => {
pipeline = createBasicPipeline();
pipeline.headElements = () => ({
links: new Set([{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }]),
scripts: new Set,
styles: new Set
links: new Set([
{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' },
]),
scripts: new Set(),
styles: new Set(),
});
});
@ -95,7 +97,12 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page);
const request = new Request('http://example.com/');
const routeData = { type: 'page', pathname: '/index', component: 'src/pages/index.astro', params: {} };
const routeData = {
type: 'page',
pathname: '/index',
component: 'src/pages/index.astro',
params: {},
};
const renderContext = RenderContext.create({ pipeline, request, routeData });
const response = await renderContext.render(PageModule);
@ -171,7 +178,12 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page);
const request = new Request('http://example.com/');
const routeData = { type: 'page', pathname: '/index', component: 'src/pages/index.astro', params: {} };
const routeData = {
type: 'page',
pathname: '/index',
component: 'src/pages/index.astro',
params: {},
};
const renderContext = RenderContext.create({ pipeline, request, routeData });
const response = await renderContext.render(PageModule);
@ -214,7 +226,12 @@ describe('core/render', () => {
const PageModule = createAstroModule(Page);
const request = new Request('http://example.com/');
const routeData = { type: 'page', pathname: '/index', component: 'src/pages/index.astro', params: {} };
const routeData = {
type: 'page',
pathname: '/index',
component: 'src/pages/index.astro',
params: {},
};
const renderContext = RenderContext.create({ pipeline, request, routeData });
const response = await renderContext.render(PageModule);

View file

@ -43,7 +43,12 @@ describe('core/render', () => {
const mod = createAstroModule(Page);
const request = new Request('http://example.com/');
const routeData = { type: 'page', pathname: '/index', component: 'src/pages/index.mdx', params: {} };
const routeData = {
type: 'page',
pathname: '/index',
component: 'src/pages/index.mdx',
params: {},
};
const renderContext = RenderContext.create({ pipeline, request, routeData });
const response = await renderContext.render(mod);
@ -85,7 +90,12 @@ describe('core/render', () => {
const mod = createAstroModule(Page);
const request = new Request('http://example.com/');
const routeData = { type: 'page', pathname: '/index', component: 'src/pages/index.mdx', params: {} };
const routeData = {
type: 'page',
pathname: '/index',
component: 'src/pages/index.mdx',
params: {},
};
const renderContext = RenderContext.create({ pipeline, request, routeData });
const response = await renderContext.render(mod);
@ -111,7 +121,12 @@ describe('core/render', () => {
const mod = createAstroModule(Page);
const request = new Request('http://example.com/');
const routeData = { type: 'page', pathname: '/index', component: 'src/pages/index.mdx', params: {} };
const routeData = {
type: 'page',
pathname: '/index',
component: 'src/pages/index.mdx',
params: {},
};
const renderContext = RenderContext.create({ pipeline, request, routeData });
const response = await renderContext.render(mod);

View file

@ -191,7 +191,7 @@ export function createBasicPipeline(options = {}) {
options.manifest ?? {},
options.mode ?? 'development',
options.renderers ?? [],
options.resolve ?? (s => Promise.resolve(s)),
options.resolve ?? ((s) => Promise.resolve(s)),
options.serverLike ?? true,
options.streaming ?? true,
options.adapterName,
@ -202,9 +202,9 @@ export function createBasicPipeline(options = {}) {
options.routeCache ?? new RouteCache(options.logging, mode),
options.site
);
pipeline.headElements = () => ({ scripts: new Set, styles: new Set, links: new Set });
pipeline.headElements = () => ({ scripts: new Set(), styles: new Set(), links: new Set() });
pipeline.componentMetadata = () => {};
return pipeline
return pipeline;
}
/**

View file

@ -29,7 +29,7 @@ describe('vite-plugin-astro-server', () => {
loader: createLoader({
import(id) {
if (id === '\0astro-internal:middleware') {
return { onRequest: (_, next) => next() }
return { onRequest: (_, next) => next() };
}
const Page = createComponent(() => {
return render`<div id="test">testing</div>`;
@ -62,7 +62,7 @@ describe('vite-plugin-astro-server', () => {
controller,
incomingRequest: req,
incomingResponse: res,
manifest: {}
manifest: {},
});
} catch (err) {
assert.equal(err.message, undefined);