wip stable url
This commit is contained in:
parent
40efae6550
commit
dc1e82d622
16 changed files with 81 additions and 16 deletions
|
@ -107,6 +107,7 @@ export class App {
|
|||
},
|
||||
routeCache: new RouteCache(this.#logging),
|
||||
site: this.#manifest.site,
|
||||
base: this.#manifest.base,
|
||||
ssr: true,
|
||||
streaming,
|
||||
});
|
||||
|
|
|
@ -526,6 +526,7 @@ async function generatePath(
|
|||
},
|
||||
routeCache,
|
||||
site: manifest.site,
|
||||
base: manifest.base,
|
||||
ssr,
|
||||
streaming: true,
|
||||
});
|
||||
|
|
|
@ -13,6 +13,7 @@ import { AstroCookies, attachCookiesToResponse } from '../cookies/index.js';
|
|||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
import { warn } from '../logger/core.js';
|
||||
import { callMiddleware } from '../middleware/callMiddleware.js';
|
||||
import { createRouteUrl } from '../routing/url.js';
|
||||
|
||||
const clientAddressSymbol = Symbol.for('astro.clientAddress');
|
||||
const clientLocalsSymbol = Symbol.for('astro.locals');
|
||||
|
@ -31,6 +32,7 @@ type CreateAPIContext = {
|
|||
request: Request;
|
||||
params: Params;
|
||||
site?: string;
|
||||
routeUrl: URL;
|
||||
props: Record<string, any>;
|
||||
adapterName?: string;
|
||||
};
|
||||
|
@ -44,6 +46,7 @@ export function createAPIContext({
|
|||
request,
|
||||
params,
|
||||
site,
|
||||
routeUrl,
|
||||
props,
|
||||
adapterName,
|
||||
}: CreateAPIContext): APIContext {
|
||||
|
@ -62,7 +65,7 @@ export function createAPIContext({
|
|||
},
|
||||
});
|
||||
},
|
||||
url: new URL(request.url),
|
||||
url: routeUrl,
|
||||
get clientAddress() {
|
||||
if (!(clientAddressSymbol in request)) {
|
||||
if (adapterName) {
|
||||
|
@ -102,11 +105,18 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
|
|||
ctx: RenderContext,
|
||||
onRequest?: MiddlewareHandler<MiddlewareResult> | undefined
|
||||
): Promise<EndpointCallResult> {
|
||||
const routeUrl = createRouteUrl(ctx.route, {
|
||||
params: ctx.params,
|
||||
base: env.base,
|
||||
site: env.site ?? new URL(ctx.request.url).origin,
|
||||
});
|
||||
|
||||
const context = createAPIContext({
|
||||
request: ctx.request,
|
||||
params: ctx.params,
|
||||
props: ctx.props,
|
||||
site: env.site,
|
||||
routeUrl,
|
||||
adapterName: env.adapterName,
|
||||
});
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ function createContext({ request, params }: CreateContext) {
|
|||
params: params ?? {},
|
||||
props: {},
|
||||
site: undefined,
|
||||
routeUrl: new URL(request.url)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import type {
|
|||
} from '../@types/astro';
|
||||
import { callMiddleware } from './middleware/callMiddleware.js';
|
||||
import { renderPage } from './render/core.js';
|
||||
import { createRouteUrl } from './routing/url.js';
|
||||
|
||||
type EndpointResultHandler = (
|
||||
originalRequest: Request,
|
||||
|
@ -95,11 +96,18 @@ export class Pipeline {
|
|||
mod: Readonly<ComponentInstance>,
|
||||
onRequest?: MiddlewareHandler<MiddlewareReturnType>
|
||||
): Promise<Response | EndpointCallResult> {
|
||||
const routeUrl = createRouteUrl(renderContext.route, {
|
||||
params: renderContext.params,
|
||||
base: env.base,
|
||||
site: env.site ?? new URL(renderContext.request.url).origin,
|
||||
});
|
||||
|
||||
const apiContext = createAPIContext({
|
||||
request: renderContext.request,
|
||||
params: renderContext.params,
|
||||
props: renderContext.props,
|
||||
site: env.site,
|
||||
routeUrl,
|
||||
adapterName: env.adapterName,
|
||||
});
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import { attachCookiesToResponse } from '../cookies/index.js';
|
|||
import { callEndpoint, createAPIContext, type EndpointCallResult } from '../endpoint/index.js';
|
||||
import { callMiddleware } from '../middleware/callMiddleware.js';
|
||||
import { redirectRouteGenerate, redirectRouteStatus, routeIsRedirect } from '../redirects/index.js';
|
||||
import { createRouteUrl } from '../routing/url.js';
|
||||
import type { RenderContext } from './context.js';
|
||||
import type { Environment } from './environment.js';
|
||||
import { createResult } from './result.js';
|
||||
|
@ -37,11 +38,18 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag
|
|||
if (!Component)
|
||||
throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`);
|
||||
|
||||
const routeUrl = createRouteUrl(renderContext.route, {
|
||||
params: renderContext.params,
|
||||
base: env.base,
|
||||
site: env.site ?? new URL(renderContext.request.url).origin,
|
||||
});
|
||||
|
||||
const result = createResult({
|
||||
adapterName: env.adapterName,
|
||||
links: renderContext.links,
|
||||
styles: renderContext.styles,
|
||||
logging: env.logging,
|
||||
routeUrl,
|
||||
params: renderContext.params,
|
||||
pathname: renderContext.pathname,
|
||||
componentMetadata: renderContext.componentMetadata,
|
||||
|
@ -93,11 +101,18 @@ export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
|||
mod: Readonly<ComponentInstance>,
|
||||
onRequest?: MiddlewareHandler<MiddlewareReturnType>
|
||||
): Promise<Response | EndpointCallResult> {
|
||||
const routeUrl = createRouteUrl(renderContext.route, {
|
||||
params: renderContext.params,
|
||||
base: env.base,
|
||||
site: env.site ?? new URL(renderContext.request.url).origin,
|
||||
});
|
||||
|
||||
const apiContext = createAPIContext({
|
||||
request: renderContext.request,
|
||||
params: renderContext.params,
|
||||
props: renderContext.props,
|
||||
site: env.site,
|
||||
routeUrl,
|
||||
adapterName: env.adapterName,
|
||||
});
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ export interface Environment {
|
|||
* Used for `Astro.site`
|
||||
*/
|
||||
site?: string;
|
||||
/**
|
||||
* Used to derive `Astro.url`
|
||||
*/
|
||||
base?: string;
|
||||
/**
|
||||
* Value of Astro config's `output` option, true if "server" or "hybrid"
|
||||
*/
|
||||
|
|
|
@ -36,6 +36,10 @@ export interface CreateResultArgs {
|
|||
* Used for `Astro.site`
|
||||
*/
|
||||
site: string | undefined;
|
||||
/**
|
||||
* Used for `Astro.url`. A stable route usually derived from `RouteData`.
|
||||
*/
|
||||
routeUrl: URL;
|
||||
links?: Set<SSRElement>;
|
||||
scripts?: Set<SSRElement>;
|
||||
styles?: Set<SSRElement>;
|
||||
|
@ -126,7 +130,6 @@ class Slots {
|
|||
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 = {
|
||||
|
@ -195,7 +198,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
|
|||
props,
|
||||
locals,
|
||||
request,
|
||||
url,
|
||||
url: args.routeUrl,
|
||||
redirect(path, status) {
|
||||
// If the response is already sent, error as we cannot proceed with the redirect.
|
||||
if ((request as any)[responseSentSymbol]) {
|
||||
|
|
|
@ -2,4 +2,5 @@ export { createRouteManifest } from './manifest/create.js';
|
|||
export { deserializeRouteData, serializeRouteData } from './manifest/serialization.js';
|
||||
export { matchAllRoutes, matchRoute } from './match.js';
|
||||
export { getParams } from './params.js';
|
||||
export { createRouteUrl } from './url.js';
|
||||
export { validateDynamicRouteModule, validateGetStaticPathsResult } from './validation.js';
|
||||
|
|
24
packages/astro/src/core/routing/url.ts
Normal file
24
packages/astro/src/core/routing/url.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import type { Params, RouteData } from '../../@types/astro.js';
|
||||
import { joinPaths } from '../path.js';
|
||||
|
||||
interface CreateUrlOptions {
|
||||
params?: Params;
|
||||
site?: string;
|
||||
base?: string;
|
||||
}
|
||||
|
||||
export function createRouteUrl(route: RouteData, options: CreateUrlOptions) {
|
||||
const site = options.site ?? 'http://localhost:4321';
|
||||
const base = options.base ?? '/';
|
||||
|
||||
// Tests don't implement generate, do a dirty skip here
|
||||
if (route.generate == null) {
|
||||
return new URL(base, site);
|
||||
}
|
||||
|
||||
const pathnameWithoutBase = route.generate(options.params);
|
||||
// If the pathname is empty (root without trailing slash), return it as is so the final
|
||||
// URL also doesn't have a trailing slash
|
||||
const pathname = pathnameWithoutBase === '' ? '' : joinPaths(base, pathnameWithoutBase);
|
||||
return new URL(pathname, site);
|
||||
}
|
|
@ -25,6 +25,7 @@ export function createDevelopmentEnvironment(
|
|||
resolve: createResolve(loader, settings.config.root),
|
||||
routeCache: new RouteCache(logging, mode),
|
||||
site: manifest.site,
|
||||
base: manifest.base,
|
||||
ssr: isServerLikeOutput(settings.config),
|
||||
streaming: true,
|
||||
});
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('getStaticPaths - build calls', () => {
|
|||
const html = await fixture.readFile('/food/tacos/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
expect($('#url').text()).to.equal('/blog/food/tacos/');
|
||||
expect($('#url').text()).to.equal('/blog/food/tacos');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -24,16 +24,16 @@ describe('Astro Global', () => {
|
|||
await devServer.stop();
|
||||
});
|
||||
|
||||
it('Astro.request.url', async () => {
|
||||
it('Astro.url', async () => {
|
||||
const res = await await fixture.fetch('/blog/?foo=42');
|
||||
expect(res.status).to.equal(200);
|
||||
|
||||
const html = await res.text();
|
||||
const $ = cheerio.load(html);
|
||||
expect($('#pathname').text()).to.equal('/blog/');
|
||||
expect($('#pathname').text()).to.equal('/blog');
|
||||
expect($('#searchparams').text()).to.equal('{}');
|
||||
expect($('#child-pathname').text()).to.equal('/blog/');
|
||||
expect($('#nested-child-pathname').text()).to.equal('/blog/');
|
||||
expect($('#child-pathname').text()).to.equal('/blog');
|
||||
expect($('#nested-child-pathname').text()).to.equal('/blog');
|
||||
});
|
||||
|
||||
it('Astro.glob() returned `url` metadata of each markdown file extensions DOES NOT include the extension', async () => {
|
||||
|
|
|
@ -38,7 +38,7 @@ describe('Prerender', () => {
|
|||
const $ = cheerio.load(html);
|
||||
|
||||
expect($('#props').text()).to.equal('10');
|
||||
expect($('#url').text()).to.equal('/blog/food/tacos/');
|
||||
expect($('#url').text()).to.equal('/blog/food/tacos');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -169,7 +169,7 @@ describe('Prerender', () => {
|
|||
const $ = cheerio.load(html);
|
||||
|
||||
expect($('#props').text()).to.equal('10');
|
||||
expect($('#url').text()).to.equal('/blog/food/tacos/');
|
||||
expect($('#url').text()).to.equal('/blog/food/tacos');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -50,8 +50,8 @@ describe('Using Astro.request in SSR', () => {
|
|||
const html = await response.text();
|
||||
const $ = cheerioLoad(html);
|
||||
expect($('#origin').text()).to.equal('http://example.com');
|
||||
expect($('#pathname').text()).to.equal('/subpath/request/');
|
||||
expect($('#request-pathname').text()).to.equal('/subpath/request/');
|
||||
expect($('#pathname').text()).to.equal('/subpath/request');
|
||||
expect($('#request-pathname').text()).to.equal('/subpath/request');
|
||||
});
|
||||
|
||||
it('public file is copied over', async () => {
|
||||
|
|
|
@ -18263,25 +18263,21 @@ packages:
|
|||
file:packages/astro/test/fixtures/css-assets/packages/font-awesome:
|
||||
resolution: {directory: packages/astro/test/fixtures/css-assets/packages/font-awesome, type: directory}
|
||||
name: '@test/astro-font-awesome-package'
|
||||
version: 0.0.1
|
||||
dev: false
|
||||
|
||||
file:packages/astro/test/fixtures/multiple-renderers/renderers/one:
|
||||
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/one, type: directory}
|
||||
name: '@test/astro-renderer-one'
|
||||
version: 1.0.0
|
||||
dev: false
|
||||
|
||||
file:packages/astro/test/fixtures/multiple-renderers/renderers/two:
|
||||
resolution: {directory: packages/astro/test/fixtures/multiple-renderers/renderers/two, type: directory}
|
||||
name: '@test/astro-renderer-two'
|
||||
version: 1.0.0
|
||||
dev: false
|
||||
|
||||
file:packages/astro/test/fixtures/solid-component/deps/solid-jsx-component:
|
||||
resolution: {directory: packages/astro/test/fixtures/solid-component/deps/solid-jsx-component, type: directory}
|
||||
name: '@test/solid-jsx-component'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
solid-js: 1.7.6
|
||||
dev: false
|
||||
|
|
Loading…
Reference in a new issue