summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Arsh <69170106+lilnasy@users.noreply.github.com> 2024-03-07 20:14:53 +0530
committerGravatar GitHub <noreply@github.com> 2024-03-07 14:44:53 +0000
commitf33cce8f6c3a2e17847658cdedb015bd93cc1ee3 (patch)
treea0abf466c0e810d78edfcb2398e325b440c86bd1
parentfdd5bf277e5c1cfa30c1bd2ca123f4e90e8d09d9 (diff)
downloadastro-f33cce8f6c3a2e17847658cdedb015bd93cc1ee3.tar.gz
astro-f33cce8f6c3a2e17847658cdedb015bd93cc1ee3.tar.zst
astro-f33cce8f6c3a2e17847658cdedb015bd93cc1ee3.zip
fix(Astro global): parity with `ctx.site` (#10325)
* move `createResult()` into `RenderContext` * `Astro.site`-`ctx.site` parity * shared `clientAdress()` implementation * remove base from `manifest.site` * astro global tests * add api context tests * add changeset * flaky test * error on `Astro.response.headers` reassignment
-rw-r--r--.changeset/sour-apples-try.md5
-rw-r--r--packages/astro/src/@types/astro.ts2
-rw-r--r--packages/astro/src/core/base-pipeline.ts2
-rw-r--r--packages/astro/src/core/build/generate.ts4
-rw-r--r--packages/astro/src/core/compile/compile.ts2
-rw-r--r--packages/astro/src/core/errors/errors-data.ts15
-rw-r--r--packages/astro/src/core/middleware/index.ts2
-rw-r--r--packages/astro/src/core/render-context.ts194
-rw-r--r--packages/astro/src/core/render/index.ts2
-rw-r--r--packages/astro/src/core/render/result.ts209
-rw-r--r--packages/astro/src/i18n/utils.ts12
-rw-r--r--packages/astro/src/runtime/server/astro-global.ts2
-rw-r--r--packages/astro/src/vite-plugin-astro-server/plugin.ts4
-rw-r--r--packages/astro/test/astro-global.test.js29
-rw-r--r--packages/astro/test/ssr-api-route.test.js46
-rw-r--r--packages/astro/test/units/test-utils.js2
16 files changed, 229 insertions, 303 deletions
diff --git a/.changeset/sour-apples-try.md b/.changeset/sour-apples-try.md
new file mode 100644
index 000000000..a9d517603
--- /dev/null
+++ b/.changeset/sour-apples-try.md
@@ -0,0 +1,5 @@
+---
+"astro": patch
+---
+
+Fixes an issue where `ctx.site` included the configured `base` in API routes and middleware, unlike `Astro.site` in astro pages.
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 8e0803410..6d15aeddd 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -2701,7 +2701,7 @@ export interface SSRResult {
slots: Record<string, any> | null
): AstroGlobal;
resolve: (s: string) => Promise<string>;
- response: ResponseInit;
+ response: AstroGlobal["response"];
renderers: SSRLoadedRenderer[];
/**
* Map of directive name (e.g. `load`) to the directive script code
diff --git a/packages/astro/src/core/base-pipeline.ts b/packages/astro/src/core/base-pipeline.ts
index 2d4a8277c..f0ae81d14 100644
--- a/packages/astro/src/core/base-pipeline.ts
+++ b/packages/astro/src/core/base-pipeline.ts
@@ -45,7 +45,7 @@ export abstract class Pipeline {
/**
* Used for `Astro.site`.
*/
- readonly site = manifest.site
+ readonly site = manifest.site ? new URL(manifest.site) : undefined,
) {
this.internalMiddleware = [
createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat),
diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts
index 3a1b2939a..e52896109 100644
--- a/packages/astro/src/core/build/generate.ts
+++ b/packages/astro/src/core/build/generate.ts
@@ -609,9 +609,7 @@ function createBuildManifest(
renderers,
base: settings.config.base,
assetsPrefix: settings.config.build.assetsPrefix,
- site: settings.config.site
- ? new URL(settings.config.base, settings.config.site).toString()
- : settings.config.site,
+ site: settings.config.site,
componentMetadata: internals.componentMetadata,
i18n: i18nManifest,
buildFormat: settings.config.build.format,
diff --git a/packages/astro/src/core/compile/compile.ts b/packages/astro/src/core/compile/compile.ts
index 09647402c..4d8793be3 100644
--- a/packages/astro/src/core/compile/compile.ts
+++ b/packages/astro/src/core/compile/compile.ts
@@ -56,6 +56,8 @@ export async function compile({
normalizedFilename: normalizeFilename(filename, astroConfig.root),
sourcemap: 'both',
internalURL: 'astro/compiler-runtime',
+ // TODO: this is no longer neccessary for `Astro.site`
+ // but it somehow allows working around caching issues in content collections for some tests
astroGlobalArgs: JSON.stringify(astroConfig.site),
scopedStyleStrategy: astroConfig.scopedStyleStrategy,
resultScopedSlot: true,
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index 40bcab111..67cd94b98 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -792,7 +792,7 @@ export const MiddlewareNotAResponse = {
* @docs
* @description
*
- * Thrown in development mode when `locals` is overwritten with something that is not an object
+ * Thrown when `locals` is overwritten with something that is not an object
*
* For example:
* ```ts
@@ -814,6 +814,19 @@ export const LocalsNotAnObject = {
/**
* @docs
* @description
+ * Thrown when a value is being set as the `headers` field on the `ResponseInit` object available as `Astro.response`.
+ */
+export const AstroResponseHeadersReassigned = {
+ name: 'AstroResponseHeadersReassigned',
+ title: '`Astro.response.headers` must not be reassigned.',
+ message:
+ 'Individual headers can be added to and removed from `Astro.response.headers`, but it must not be replaced with another instance of `Headers` altogether.',
+ hint: 'Consider using `Astro.response.headers.add()`, and `Astro.response.headers.delete()`.',
+} satisfies ErrorData;
+
+/**
+ * @docs
+ * @description
* Thrown in development mode when middleware throws an error while attempting to loading it.
*
* For example:
diff --git a/packages/astro/src/core/middleware/index.ts b/packages/astro/src/core/middleware/index.ts
index 813f5ae48..acfe7f056 100644
--- a/packages/astro/src/core/middleware/index.ts
+++ b/packages/astro/src/core/middleware/index.ts
@@ -74,8 +74,6 @@ function createContext({
return (currentLocale ??= computeCurrentLocale(
route,
userDefinedLocales,
- undefined,
- undefined
));
},
url,
diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts
index fde4509ac..a20636b8a 100644
--- a/packages/astro/src/core/render-context.ts
+++ b/packages/astro/src/core/render-context.ts
@@ -1,8 +1,11 @@
import type {
APIContext,
+ AstroGlobal,
+ AstroGlobalPartial,
ComponentInstance,
MiddlewareHandler,
RouteData,
+ SSRResult,
} from '../@types/astro.js';
import {
computeCurrentLocale,
@@ -11,21 +14,20 @@ import {
} from '../i18n/utils.js';
import { renderEndpoint } from '../runtime/server/endpoint.js';
import { renderPage } from '../runtime/server/index.js';
+import { renderRedirect } from './redirects/render.js';
import {
ASTRO_VERSION,
REROUTE_DIRECTIVE_HEADER,
ROUTE_TYPE_HEADER,
clientAddressSymbol,
clientLocalsSymbol,
+ responseSentSymbol,
} from './constants.js';
-import { attachCookiesToResponse } from './cookies/index.js';
-import { AstroCookies } from './cookies/index.js';
+import { attachCookiesToResponse, AstroCookies } from './cookies/index.js';
import { AstroError, AstroErrorData } from './errors/index.js';
import { callMiddleware } from './middleware/callMiddleware.js';
import { sequence } from './middleware/index.js';
-import { renderRedirect } from './redirects/render.js';
-import { createResult } from './render/index.js';
-import { type Pipeline, getParams, getProps } from './render/index.js';
+import { type Pipeline, getParams, getProps, Slots } from './render/index.js';
export class RenderContext {
private constructor(
@@ -135,38 +137,15 @@ export class RenderContext {
const generator = `Astro v${ASTRO_VERSION}`;
const redirect = (path: string, status = 302) =>
new Response(null, { status, headers: { Location: path } });
- const site = pipeline.site ? new URL(pipeline.site) : undefined;
return {
cookies,
+ get clientAddress() {
+ return renderContext.clientAddress()
+ },
get currentLocale() {
return renderContext.computeCurrentLocale();
},
generator,
- params,
- get preferredLocale() {
- return renderContext.computePreferredLocale();
- },
- get preferredLocaleList() {
- return renderContext.computePreferredLocaleList();
- },
- props,
- redirect,
- request,
- site,
- url,
- get clientAddress() {
- if (clientAddressSymbol in request) {
- return Reflect.get(request, clientAddressSymbol) as string;
- }
- if (pipeline.adapterName) {
- throw new AstroError({
- ...AstroErrorData.ClientAddressNotAvailable,
- message: AstroErrorData.ClientAddressNotAvailable.message(pipeline.adapterName),
- });
- } else {
- throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
- }
- },
get locals() {
return renderContext.locals;
},
@@ -181,53 +160,142 @@ export class RenderContext {
Reflect.set(request, clientLocalsSymbol, val);
}
},
+ params,
+ get preferredLocale() {
+ return renderContext.computePreferredLocale();
+ },
+ get preferredLocaleList() {
+ return renderContext.computePreferredLocaleList();
+ },
+ props,
+ redirect,
+ request,
+ site: pipeline.site,
+ url,
};
}
async createResult(mod: ComponentInstance) {
- const { cookies, locals, params, pathname, pipeline, request, routeData, status } = this;
+ const { cookies, pathname, pipeline, routeData, status } = this;
const {
- adapterName,
clientDirectives,
compressHTML,
- i18n,
manifest,
- logger,
renderers,
- resolve,
- site,
- serverLike,
+ resolve
} = pipeline;
const { links, scripts, styles } = await pipeline.headElements(routeData);
const componentMetadata =
(await pipeline.componentMetadata(routeData)) ?? manifest.componentMetadata;
- const { defaultLocale, locales, strategy } = i18n ?? {};
+ const headers = new Headers({ 'Content-Type': 'text/html' });
const partial = Boolean(mod.partial);
- return createResult({
- adapterName,
+ const response = {
+ status,
+ statusText: 'OK',
+ get headers() {
+ return headers
+ },
+ // Disallow `Astro.response.headers = new Headers`
+ set headers(_) {
+ throw new AstroError(AstroErrorData.AstroResponseHeadersReassigned);
+ }
+ } satisfies AstroGlobal["response"];
+
+ // Create the result object that will be passed into the renderPage function.
+ // This object starts here as an empty shell (not yet the result) but then
+ // calling the render() function will populate the object with scripts, styles, etc.
+ const result: SSRResult = {
clientDirectives,
componentMetadata,
compressHTML,
cookies,
- defaultLocale,
- locales,
- locals,
- logger,
+ /** This function returns the `Astro` faux-global */
+ createAstro: (astroGlobal, props, slots) => this.createAstro(result, astroGlobal, props, slots),
links,
- params,
partial,
pathname,
renderers,
resolve,
- request,
- route: routeData.route,
- strategy,
- site,
+ response,
scripts,
- ssr: serverLike,
- status,
styles,
- });
+ _metadata: {
+ hasHydrationScript: false,
+ rendererSpecificHydrationScripts: new Set(),
+ hasRenderedHead: false,
+ hasDirectives: new Set(),
+ headInTree: false,
+ extraHead: [],
+ propagators: new Set(),
+ },
+ };
+
+ return result;
+ }
+
+ createAstro(
+ result: SSRResult,
+ astroGlobalPartial: AstroGlobalPartial,
+ props: Record<string, any>,
+ slotValues: Record<string, any> | null
+ ): AstroGlobal {
+ const renderContext = this;
+ const { cookies, locals, params, pipeline, request, url } = this;
+ const { response } = result;
+ const redirect = (path: string, status = 302) => {
+ // If the response is already sent, error as we cannot proceed with the redirect.
+ if ((request as any)[responseSentSymbol]) {
+ throw new AstroError({
+ ...AstroErrorData.ResponseSentError,
+ });
+ }
+ return new Response(null, { status, headers: { Location: path } });
+ }
+ const slots = new Slots(result, slotValues, pipeline.logger) as unknown as AstroGlobal['slots'];
+
+ // `Astro.self` is added by the compiler
+ const astroGlobalCombined: Omit<AstroGlobal, 'self'> = {
+ ...astroGlobalPartial,
+ cookies,
+ get clientAddress() {
+ return renderContext.clientAddress()
+ },
+ get currentLocale() {
+ return renderContext.computeCurrentLocale();
+ },
+ params,
+ get preferredLocale() {
+ return renderContext.computePreferredLocale();
+ },
+ get preferredLocaleList() {
+ return renderContext.computePreferredLocaleList();
+ },
+ props,
+ locals,
+ redirect,
+ request,
+ response,
+ slots,
+ site: pipeline.site,
+ url,
+ };
+
+ return astroGlobalCombined as AstroGlobal
+ }
+
+ clientAddress() {
+ const { pipeline, request } = this;
+ if (clientAddressSymbol in request) {
+ return Reflect.get(request, clientAddressSymbol) as string;
+ }
+ if (pipeline.adapterName) {
+ throw new AstroError({
+ ...AstroErrorData.ClientAddressNotAvailable,
+ message: AstroErrorData.ClientAddressNotAvailable.message(pipeline.adapterName),
+ });
+ } else {
+ throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
+ }
}
/**
@@ -242,13 +310,21 @@ export class RenderContext {
routeData,
} = this;
if (!i18n) return;
+
const { defaultLocale, locales, strategy } = i18n;
- return (this.#currentLocale ??= computeCurrentLocale(
- routeData.route,
- locales,
- strategy,
- defaultLocale
- ));
+
+ const fallbackTo = (
+ strategy === 'pathname-prefix-other-locales' ||
+ strategy === 'domains-prefix-other-locales'
+ ) ? defaultLocale : undefined
+
+ // TODO: look into why computeCurrentLocale() needs routeData.route to pass ctx.currentLocale tests,
+ // and url.pathname to pass Astro.currentLocale tests.
+ // A single call with `routeData.pathname ?? routeData.route` as the pathname still fails.
+ return (this.#currentLocale ??=
+ computeCurrentLocale(routeData.route, locales) ??
+ computeCurrentLocale(url.pathname, locales) ??
+ fallbackTo);
}
#preferredLocale: APIContext['preferredLocale'];
diff --git a/packages/astro/src/core/render/index.ts b/packages/astro/src/core/render/index.ts
index 266abf696..35b5ce76e 100644
--- a/packages/astro/src/core/render/index.ts
+++ b/packages/astro/src/core/render/index.ts
@@ -3,7 +3,7 @@ import type { Pipeline } from '../base-pipeline.js';
export { Pipeline } from '../base-pipeline.js';
export { getParams, getProps } from './params-and-props.js';
export { loadRenderer } from './renderer.js';
-export { createResult } from './result.js';
+export { Slots } from './result.js';
export interface SSROptions {
/** The pipeline instance */
diff --git a/packages/astro/src/core/render/result.ts b/packages/astro/src/core/render/result.ts
index f56ec253c..e23e87adf 100644
--- a/packages/astro/src/core/render/result.ts
+++ b/packages/astro/src/core/render/result.ts
@@ -1,68 +1,17 @@
-import type {
- AstroGlobal,
- AstroGlobalPartial,
- Locales,
- Params,
- SSRElement,
- SSRLoadedRenderer,
- SSRResult,
-} from '../../@types/astro.js';
-import {
- type RoutingStrategies,
- computeCurrentLocale,
- computePreferredLocale,
- computePreferredLocaleList,
-} from '../../i18n/utils.js';
+import type { SSRResult } from '../../@types/astro.js';
import { type ComponentSlots, renderSlotToString } from '../../runtime/server/index.js';
import { renderJSX } from '../../runtime/server/jsx.js';
import { chunkToString } from '../../runtime/server/render/index.js';
-import { clientAddressSymbol, responseSentSymbol } from '../constants.js';
-import { AstroCookies } from '../cookies/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import type { Logger } from '../logger/core.js';
-export interface CreateResultArgs {
- /**
- * Used to provide better error messages for `Astro.clientAddress`
- */
- adapterName: string | undefined;
- /**
- * Value of Astro config's `output` option, true if "server" or "hybrid"
- */
- ssr: boolean;
- logger: Logger;
- params: Params;
- pathname: string;
- renderers: SSRLoadedRenderer[];
- clientDirectives: Map<string, string>;
- compressHTML: boolean;
- partial: boolean;
- resolve: (s: string) => Promise<string>;
- /**
- * Used for `Astro.site`
- */
- site: string | undefined;
- links: Set<SSRElement>;
- scripts: Set<SSRElement>;
- styles: Set<SSRElement>;
- componentMetadata: SSRResult['componentMetadata'];
- request: Request;
- status: number;
- locals: App.Locals;
- cookies: AstroCookies;
- locales: Locales | undefined;
- defaultLocale: string | undefined;
- route: string;
- strategy: RoutingStrategies | undefined;
-}
-
function getFunctionExpression(slot: any) {
if (!slot) return;
if (slot.expressions?.length !== 1) return;
return slot.expressions[0] as (...args: any[]) => any;
}
-class Slots {
+export class Slots {
#result: SSRResult;
#slots: ComponentSlots | null;
#logger: Logger;
@@ -131,157 +80,3 @@ class Slots {
return outHTML;
}
}
-
-export function createResult(args: CreateResultArgs): SSRResult {
- const { params, request, resolve, locals } = args;
-
- const url = new URL(request.url);
- const headers = new Headers();
- headers.set('Content-Type', 'text/html');
- const response: ResponseInit = {
- status: args.status,
- statusText: 'OK',
- headers,
- };
-
- // Make headers be read-only
- Object.defineProperty(response, 'headers', {
- value: response.headers,
- enumerable: true,
- writable: false,
- });
-
- // Astro.cookies is defined lazily to avoid the cost on pages that do not use it.
- let cookies: AstroCookies | undefined = args.cookies;
- let preferredLocale: string | undefined = undefined;
- let preferredLocaleList: string[] | undefined = undefined;
- let currentLocale: string | undefined = undefined;
-
- // Create the result object that will be passed into the render function.
- // This object starts here as an empty shell (not yet the result) but then
- // calling the render() function will populate the object with scripts, styles, etc.
- const result: SSRResult = {
- styles: args.styles ?? new Set<SSRElement>(),
- scripts: args.scripts ?? new Set<SSRElement>(),
- links: args.links ?? new Set<SSRElement>(),
- componentMetadata: args.componentMetadata ?? new Map(),
- renderers: args.renderers,
- clientDirectives: args.clientDirectives,
- compressHTML: args.compressHTML,
- partial: args.partial,
- pathname: args.pathname,
- cookies,
- /** This function returns the `Astro` faux-global */
- createAstro(
- astroGlobal: AstroGlobalPartial,
- props: Record<string, any>,
- slots: Record<string, any> | null
- ) {
- const astroSlots = new Slots(result, slots, args.logger);
-
- const Astro: AstroGlobal = {
- // @ts-expect-error
- __proto__: astroGlobal,
- get clientAddress() {
- if (!(clientAddressSymbol in request)) {
- if (args.adapterName) {
- throw new AstroError({
- ...AstroErrorData.ClientAddressNotAvailable,
- message: AstroErrorData.ClientAddressNotAvailable.message(args.adapterName),
- });
- } else {
- throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
- }
- }
-
- return Reflect.get(request, clientAddressSymbol) as string;
- },
- get cookies() {
- if (cookies) {
- return cookies;
- }
- cookies = new AstroCookies(request);
- result.cookies = cookies;
- return cookies;
- },
- get preferredLocale(): string | undefined {
- if (preferredLocale) {
- return preferredLocale;
- }
- if (args.locales) {
- preferredLocale = computePreferredLocale(request, args.locales);
- return preferredLocale;
- }
-
- return undefined;
- },
- get preferredLocaleList(): string[] | undefined {
- if (preferredLocaleList) {
- return preferredLocaleList;
- }
- if (args.locales) {
- preferredLocaleList = computePreferredLocaleList(request, args.locales);
- return preferredLocaleList;
- }
-
- return undefined;
- },
- get currentLocale(): string | undefined {
- if (currentLocale) {
- return currentLocale;
- }
- if (args.locales) {
- currentLocale = computeCurrentLocale(
- url.pathname,
- args.locales,
- args.strategy,
- args.defaultLocale
- );
- if (currentLocale) {
- return currentLocale;
- }
- }
-
- return undefined;
- },
- params,
- props,
- locals,
- request,
- url,
- redirect(path, status) {
- // If the response is already sent, error as we cannot proceed with the redirect.
- if ((request as any)[responseSentSymbol]) {
- throw new AstroError({
- ...AstroErrorData.ResponseSentError,
- });
- }
-
- return new Response(null, {
- status: status || 302,
- headers: {
- Location: path,
- },
- });
- },
- response: response as AstroGlobal['response'],
- slots: astroSlots as unknown as AstroGlobal['slots'],
- };
-
- return Astro;
- },
- resolve,
- response,
- _metadata: {
- hasHydrationScript: false,
- rendererSpecificHydrationScripts: new Set(),
- hasRenderedHead: false,
- hasDirectives: new Set(),
- headInTree: false,
- extraHead: [],
- propagators: new Set(),
- },
- };
-
- return result;
-}
diff --git a/packages/astro/src/i18n/utils.ts b/packages/astro/src/i18n/utils.ts
index d3dddba1f..b120a8953 100644
--- a/packages/astro/src/i18n/utils.ts
+++ b/packages/astro/src/i18n/utils.ts
@@ -155,8 +155,6 @@ export function computePreferredLocaleList(request: Request, locales: Locales):
export function computeCurrentLocale(
pathname: string,
locales: Locales,
- routingStrategy: RoutingStrategies | undefined,
- defaultLocale: string | undefined
): undefined | string {
for (const segment of pathname.split('/')) {
for (const locale of locales) {
@@ -179,15 +177,7 @@ export function computeCurrentLocale(
}
}
}
- }
-
- if (
- routingStrategy === 'pathname-prefix-other-locales' ||
- routingStrategy === 'domains-prefix-other-locales'
- ) {
- return defaultLocale;
- }
- return undefined;
+ };
}
export type RoutingStrategies =
diff --git a/packages/astro/src/runtime/server/astro-global.ts b/packages/astro/src/runtime/server/astro-global.ts
index c15cd8a4a..3f3a2a95f 100644
--- a/packages/astro/src/runtime/server/astro-global.ts
+++ b/packages/astro/src/runtime/server/astro-global.ts
@@ -30,6 +30,8 @@ function createAstroGlobFn() {
// inside of getStaticPaths. See the `astroGlobalArgs` option for parameter type.
export function createAstro(site: string | undefined): AstroGlobalPartial {
return {
+ // TODO: this is no longer neccessary for `Astro.site`
+ // but it somehow allows working around caching issues in content collections for some tests
site: site ? new URL(site) : undefined,
generator: `Astro v${ASTRO_VERSION}`,
glob: createAstroGlobFn(),
diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts
index d688de381..3e55ea03c 100644
--- a/packages/astro/src/vite-plugin-astro-server/plugin.ts
+++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts
@@ -136,9 +136,7 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest
renderers: [],
base: settings.config.base,
assetsPrefix: settings.config.build.assetsPrefix,
- site: settings.config.site
- ? new URL(settings.config.base, settings.config.site).toString()
- : settings.config.site,
+ site: settings.config.site,
componentMetadata: new Map(),
i18n: i18nManifest,
middleware(_, next) {
diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js
index 119a8413f..804797399 100644
--- a/packages/astro/test/astro-global.test.js
+++ b/packages/astro/test/astro-global.test.js
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
+import testAdapter from './test-adapter.js';
describe('Astro Global', () => {
let fixture;
@@ -9,7 +10,7 @@ describe('Astro Global', () => {
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-global/',
- site: 'https://mysite.dev/blog/',
+ site: 'https://mysite.dev/subsite/',
base: '/blog',
});
});
@@ -65,7 +66,7 @@ describe('Astro Global', () => {
it('Astro.site', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
- assert.equal($('#site').attr('href'), 'https://mysite.dev/blog/');
+ assert.equal($('#site').attr('href'), 'https://mysite.dev/subsite/');
});
it('Astro.glob() correctly returns an array of all posts', async () => {
@@ -81,6 +82,30 @@ describe('Astro Global', () => {
assert.equal($('.post-url[href]').length, 8);
});
});
+
+ describe('app', () => {
+ /** @type {import('../dist/core/app/index.js').App} */
+ let app;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/astro-global/',
+ site: 'https://mysite.dev/subsite/',
+ base: '/new',
+ output: "server",
+ adapter: testAdapter()
+ });
+ await fixture.build();
+ app = await fixture.loadTestAdapterApp();
+ });
+
+ it('Astro.site', async () => {
+ const response = await app.render(new Request("https://example.com/"));
+ const html = await response.text();
+ const $ = cheerio.load(html);
+ assert.equal($('#site').attr('href'), 'https://mysite.dev/subsite/');
+ });
+ });
});
describe('Astro Global Defaults', () => {
diff --git a/packages/astro/test/ssr-api-route.test.js b/packages/astro/test/ssr-api-route.test.js
index 495381eac..21b1129bc 100644
--- a/packages/astro/test/ssr-api-route.test.js
+++ b/packages/astro/test/ssr-api-route.test.js
@@ -5,20 +5,25 @@ import testAdapter from './test-adapter.js';
import { loadFixture } from './test-utils.js';
describe('API routes in SSR', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
+ const config = {
+ root: './fixtures/ssr-api-route/',
+ output: 'server',
+ site: 'https://mysite.dev/subsite/',
+ base: '/blog',
+ adapter: testAdapter(),
+ };
+
+ describe('Build', () => {
+ /** @type {import('./test-utils.js').App} */
+ let app;
before(async () => {
- fixture = await loadFixture({
- root: './fixtures/ssr-api-route/',
- output: 'server',
- adapter: testAdapter(),
- });
+ const fixture = await loadFixture(config);
await fixture.build();
+ app = await fixture.loadTestAdapterApp();
});
it('Basic pages work', async () => {
- const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/');
const response = await app.render(request);
const html = await response.text();
@@ -26,7 +31,6 @@ describe('API routes in SSR', () => {
});
it('Can load the API route too', async () => {
- const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/food.json');
const response = await app.render(request);
assert.equal(response.status, 200);
@@ -35,7 +39,6 @@ describe('API routes in SSR', () => {
});
it('Has valid api context', async () => {
- const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/context/any');
const response = await app.render(request);
assert.equal(response.status, 200);
@@ -48,11 +51,17 @@ describe('API routes in SSR', () => {
assert.match(data.generator, /^Astro v/);
assert.equal(data.url, 'http://example.com/context/any');
assert.equal(data.clientAddress, '0.0.0.0');
+ assert.equal(data.site, "https://mysite.dev/subsite/");
});
+ })
- describe('API Routes - Dev', () => {
+ describe('Dev', () => {
+ /** @type {import('./test-utils.js').DevServer} */
let devServer;
+ /** @type {import('./test-utils.js').Fixture} */
+ let fixture;
before(async () => {
+ fixture = await loadFixture(config);
devServer = await fixture.startDevServer();
});
@@ -116,5 +125,20 @@ describe('API routes in SSR', () => {
assert.equal(count, 2, 'Found two seperate set-cookie response headers');
});
+
+ it('Has valid api context', async () => {
+ const response = await fixture.fetch('/context/any');
+ assert.equal(response.status, 200);
+ const data = await response.json();
+ assert.equal(data.cookiesExist, true);
+ assert.equal(data.requestExist, true);
+ assert.equal(data.redirectExist, true);
+ assert.equal(data.propsExist, true);
+ assert.deepEqual(data.params, { param: 'any' });
+ assert.match(data.generator, /^Astro v/);
+ assert.equal(data.url, 'http://[::1]:4321/blog/context/any');
+ assert.equal(data.clientAddress, '::1');
+ assert.equal(data.site, "https://mysite.dev/subsite/");
+ });
});
});
diff --git a/packages/astro/test/units/test-utils.js b/packages/astro/test/units/test-utils.js
index c223ff073..c29740128 100644
--- a/packages/astro/test/units/test-utils.js
+++ b/packages/astro/test/units/test-utils.js
@@ -203,7 +203,7 @@ export function createBasicPipeline(options = {}) {
options.site
);
pipeline.headElements = () => ({ scripts: new Set(), styles: new Set(), links: new Set() });
- pipeline.componentMetadata = () => {};
+ pipeline.componentMetadata = () => new Map;
return pipeline;
}