Skip to content

Commit b2b291d

Browse files
matthewpbluwy
andauthored
Handle base in adapters (#5290)
* Handle `base` in adapters * Use removeBase in the test adapter * Update packages/integrations/node/src/preview.ts Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> * Update packages/integrations/cloudflare/src/server.advanced.ts Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> * Include the subpath for links Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
1 parent 97e2b6a commit b2b291d

17 files changed

Lines changed: 116 additions & 15 deletions

File tree

.changeset/blue-parrots-jam.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@astrojs/cloudflare': major
3+
'@astrojs/deno': major
4+
'@astrojs/node': major
5+
'astro': patch
6+
---
7+
8+
Handle base configuration in adapters
9+
10+
This allows adapters to correctly handle `base` configuration. Internally Astro now matches routes when the URL includes the `base`.
11+
12+
Adapters now also have access to the `removeBase` method which will remove the `base` from a pathname. This is useful to look up files for static assets.

packages/astro/src/@types/astro.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,6 +1426,7 @@ export interface PreviewServerParams {
14261426
serverEntrypoint: URL;
14271427
host: string | undefined;
14281428
port: number;
1429+
base: string;
14291430
}
14301431

14311432
export type CreatePreviewServer = (

packages/astro/src/core/app/index.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { attachToResponse, getSetCookiesFromResponse } from '../cookies/index.js
1313
import { call as callEndpoint } from '../endpoint/index.js';
1414
import { consoleLogDestination } from '../logger/console.js';
1515
import { error } from '../logger/core.js';
16-
import { joinPaths, prependForwardSlash } from '../path.js';
16+
import { joinPaths, prependForwardSlash, removeTrailingForwardSlash } from '../path.js';
1717
import {
1818
createEnvironment,
1919
createRenderContext,
@@ -45,6 +45,8 @@ export class App {
4545
dest: consoleLogDestination,
4646
level: 'info',
4747
};
48+
#base: string;
49+
#baseWithoutTrailingSlash: string;
4850

4951
constructor(manifest: Manifest, streaming = true) {
5052
this.#manifest = manifest;
@@ -78,14 +80,24 @@ export class App {
7880
ssr: true,
7981
streaming,
8082
});
83+
84+
this.#base = this.#manifest.base || '/';
85+
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#base);
86+
}
87+
removeBase(pathname: string) {
88+
if(pathname.startsWith(this.#base)) {
89+
return pathname.slice(this.#baseWithoutTrailingSlash.length + 1);
90+
}
91+
return pathname;
8192
}
8293
match(request: Request, { matchNotFound = false }: MatchOptions = {}): RouteData | undefined {
8394
const url = new URL(request.url);
8495
// ignore requests matching public assets
8596
if (this.#manifest.assets.has(url.pathname)) {
8697
return undefined;
8798
}
88-
let routeData = matchRoute(url.pathname, this.#manifestData);
99+
let pathname = '/' + this.removeBase(url.pathname);
100+
let routeData = matchRoute(pathname, this.#manifestData);
89101

90102
if (routeData) {
91103
return routeData;
@@ -157,7 +169,6 @@ export class App {
157169
): Promise<Response> {
158170
const url = new URL(request.url);
159171
const manifest = this.#manifest;
160-
const renderers = manifest.renderers;
161172
const info = this.#routeDataToRouteInfo.get(routeData!)!;
162173
const links = createLinkStylesheetElementSet(info.links, manifest.site);
163174

packages/astro/src/core/build/vite-plugin-ssr.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { pagesVirtualModuleId } from '../app/index.js';
1313
import { serializeRouteData } from '../routing/index.js';
1414
import { addRollupInput } from './add-rollup-input.js';
1515
import { eachPageData, sortedCSS } from './internal.js';
16+
import { removeLeadingForwardSlash, removeTrailingForwardSlash } from '../path.js';
1617

1718
export const virtualModuleId = '@astrojs-ssr-virtual-entry';
1819
const resolvedVirtualModuleId = '\0' + virtualModuleId;
@@ -141,9 +142,12 @@ function buildManifest(
141142
scripts.push({ type: 'external', value: entryModules[PAGE_SCRIPT_ID] });
142143
}
143144

145+
const bareBase = removeTrailingForwardSlash(removeLeadingForwardSlash(settings.config.base));
146+
const links = sortedCSS(pageData).map(pth => bareBase ? bareBase + '/' + pth : pth);
147+
144148
routes.push({
145149
file: '',
146-
links: sortedCSS(pageData),
150+
links,
147151
scripts: [
148152
...scripts,
149153
...settings.scripts
@@ -172,7 +176,7 @@ function buildManifest(
172176
pageMap: null as any,
173177
renderers: [],
174178
entryModules,
175-
assets: staticFiles.map((s) => '/' + s),
179+
assets: staticFiles.map((s) => settings.config.base + s),
176180
};
177181

178182
return ssrManifest;

packages/astro/src/core/preview/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export default async function preview(
5454
serverEntrypoint: new URL(settings.config.build.serverEntry, settings.config.build.server),
5555
host,
5656
port,
57+
base: settings.config.base,
5758
});
5859

5960
return server;

packages/astro/test/fixtures/ssr-request/src/pages/request.astro

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ const origin = Astro.url.origin;
33
---
44

55
<html>
6-
<head><title>Testing</title></head>
6+
<head>
7+
<title>Testing</title>
8+
<style>
9+
body {
10+
background: orangered;
11+
}
12+
</style>
13+
</head>
714
<body>
815
<h1 id="origin">{origin}</h1>
916
</body>

packages/astro/test/ssr-request.test.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ describe('Using Astro.request in SSR', () => {
1212
root: './fixtures/ssr-request/',
1313
adapter: testAdapter(),
1414
output: 'server',
15+
base: '/subpath/'
1516
});
1617
await fixture.build();
1718
});
1819

19-
it('Gets the request pased in', async () => {
20+
it('Gets the request passed in', async () => {
2021
const app = await fixture.loadTestAdapterApp();
21-
const request = new Request('http://example.com/request');
22+
const request = new Request('http://example.com/subpath/request');
2223
const response = await app.render(request);
24+
expect(response.status).to.equal(200);
2325
const html = await response.text();
2426
const $ = cheerioLoad(html);
2527
expect($('#origin').text()).to.equal('http://example.com');
@@ -29,4 +31,32 @@ describe('Using Astro.request in SSR', () => {
2931
const json = await fixture.readFile('/client/cars.json');
3032
expect(json).to.not.be.undefined;
3133
});
34+
35+
it('CSS assets have their base prefix', async () => {
36+
const app = await fixture.loadTestAdapterApp();
37+
let request = new Request('http://example.com/subpath/request');
38+
let response = await app.render(request);
39+
expect(response.status).to.equal(200);
40+
const html = await response.text();
41+
const $ = cheerioLoad(html);
42+
43+
const linkHref = $('link').attr('href');
44+
expect(linkHref.startsWith('/subpath/')).to.equal(true);
45+
46+
request = new Request('http://example.com' + linkHref);
47+
response = await app.render(request);
48+
49+
expect(response.status).to.equal(200);
50+
const css = await response.text();
51+
expect(css).to.not.be.an('undefined');
52+
});
53+
54+
it('assets can be fetched', async () => {
55+
const app = await fixture.loadTestAdapterApp();
56+
const request = new Request('http://example.com/subpath/cars.json');
57+
const response = await app.render(request);
58+
expect(response.status).to.equal(200);
59+
const data = await response.json();
60+
expect(data).to.be.an('array');
61+
});
3262
});

packages/astro/test/test-adapter.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,23 @@ export default function ({ provideAddress } = { provideAddress: true }) {
2525
if (id === '@my-ssr') {
2626
return `
2727
import { App } from 'astro/app';
28+
import fs from 'fs';
2829
2930
class MyApp extends App {
30-
render(request, routeData) {
31+
#manifest = null;
32+
constructor(manifest, streaming) {
33+
super(manifest, streaming);
34+
this.#manifest = manifest;
35+
}
36+
37+
async render(request, routeData) {
38+
const url = new URL(request.url);
39+
if(this.#manifest.assets.has(url.pathname)) {
40+
const filePath = new URL('../client/' + this.removeBase(url.pathname), import.meta.url);
41+
const data = await fs.promises.readFile(filePath);
42+
return new Response(data);
43+
}
44+
3145
${provideAddress ? `request[Symbol.for('astro.clientAddress')] = '0.0.0.0';` : ''}
3246
return super.render(request, routeData);
3347
}

packages/integrations/cloudflare/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
"dependencies": {
3737
"esbuild": "^0.14.42"
3838
},
39+
"peerDependencies": {
40+
"astro": "^1.6.3"
41+
},
3942
"devDependencies": {
4043
"astro": "workspace:*",
4144
"astro-scripts": "workspace:*",

packages/integrations/cloudflare/src/server.advanced.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function createExports(manifest: SSRManifest) {
1616

1717
// static assets
1818
if (manifest.assets.has(pathname)) {
19-
const assetRequest = new Request(`${origin}/static${pathname}`, request);
19+
const assetRequest = new Request(`${origin}/static/${app.removeBase(pathname)}`, request);
2020
return env.ASSETS.fetch(assetRequest);
2121
}
2222

0 commit comments

Comments
 (0)