Skip to content

Commit

Permalink
Handle base in adapters (#5290)
Browse files Browse the repository at this point in the history
* Handle `base` in adapters

* Use removeBase in the test adapter

* Update packages/integrations/node/src/preview.ts

Co-authored-by: Bjorn Lu <[email protected]>

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

Co-authored-by: Bjorn Lu <[email protected]>

* Include the subpath for links

Co-authored-by: Bjorn Lu <[email protected]>
  • Loading branch information
matthewp and bluwy committed Nov 7, 2022
1 parent 97e2b6a commit b2b291d
Show file tree
Hide file tree
Showing 17 changed files with 116 additions and 15 deletions.
12 changes: 12 additions & 0 deletions .changeset/blue-parrots-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@astrojs/cloudflare': major
'@astrojs/deno': major
'@astrojs/node': major
'astro': patch
---

Handle base configuration in adapters

This allows adapters to correctly handle `base` configuration. Internally Astro now matches routes when the URL includes the `base`.

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.
1 change: 1 addition & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,7 @@ export interface PreviewServerParams {
serverEntrypoint: URL;
host: string | undefined;
port: number;
base: string;
}

export type CreatePreviewServer = (
Expand Down
17 changes: 14 additions & 3 deletions packages/astro/src/core/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { attachToResponse, getSetCookiesFromResponse } from '../cookies/index.js
import { call as callEndpoint } from '../endpoint/index.js';
import { consoleLogDestination } from '../logger/console.js';
import { error } from '../logger/core.js';
import { joinPaths, prependForwardSlash } from '../path.js';
import { joinPaths, prependForwardSlash, removeTrailingForwardSlash } from '../path.js';
import {
createEnvironment,
createRenderContext,
Expand Down Expand Up @@ -45,6 +45,8 @@ export class App {
dest: consoleLogDestination,
level: 'info',
};
#base: string;
#baseWithoutTrailingSlash: string;

constructor(manifest: Manifest, streaming = true) {
this.#manifest = manifest;
Expand Down Expand Up @@ -78,14 +80,24 @@ export class App {
ssr: true,
streaming,
});

this.#base = this.#manifest.base || '/';
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#base);
}
removeBase(pathname: string) {
if(pathname.startsWith(this.#base)) {
return pathname.slice(this.#baseWithoutTrailingSlash.length + 1);
}
return pathname;
}
match(request: Request, { matchNotFound = false }: MatchOptions = {}): RouteData | undefined {
const url = new URL(request.url);
// ignore requests matching public assets
if (this.#manifest.assets.has(url.pathname)) {
return undefined;
}
let routeData = matchRoute(url.pathname, this.#manifestData);
let pathname = '/' + this.removeBase(url.pathname);
let routeData = matchRoute(pathname, this.#manifestData);

if (routeData) {
return routeData;
Expand Down Expand Up @@ -157,7 +169,6 @@ export class App {
): Promise<Response> {
const url = new URL(request.url);
const manifest = this.#manifest;
const renderers = manifest.renderers;
const info = this.#routeDataToRouteInfo.get(routeData!)!;
const links = createLinkStylesheetElementSet(info.links, manifest.site);

Expand Down
8 changes: 6 additions & 2 deletions packages/astro/src/core/build/vite-plugin-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { pagesVirtualModuleId } from '../app/index.js';
import { serializeRouteData } from '../routing/index.js';
import { addRollupInput } from './add-rollup-input.js';
import { eachPageData, sortedCSS } from './internal.js';
import { removeLeadingForwardSlash, removeTrailingForwardSlash } from '../path.js';

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

const bareBase = removeTrailingForwardSlash(removeLeadingForwardSlash(settings.config.base));
const links = sortedCSS(pageData).map(pth => bareBase ? bareBase + '/' + pth : pth);

routes.push({
file: '',
links: sortedCSS(pageData),
links,
scripts: [
...scripts,
...settings.scripts
Expand Down Expand Up @@ -172,7 +176,7 @@ function buildManifest(
pageMap: null as any,
renderers: [],
entryModules,
assets: staticFiles.map((s) => '/' + s),
assets: staticFiles.map((s) => settings.config.base + s),
};

return ssrManifest;
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/core/preview/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default async function preview(
serverEntrypoint: new URL(settings.config.build.serverEntry, settings.config.build.server),
host,
port,
base: settings.config.base,
});

return server;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ const origin = Astro.url.origin;
---

<html>
<head><title>Testing</title></head>
<head>
<title>Testing</title>
<style>
body {
background: orangered;
}
</style>
</head>
<body>
<h1 id="origin">{origin}</h1>
</body>
Expand Down
34 changes: 32 additions & 2 deletions packages/astro/test/ssr-request.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ describe('Using Astro.request in SSR', () => {
root: './fixtures/ssr-request/',
adapter: testAdapter(),
output: 'server',
base: '/subpath/'
});
await fixture.build();
});

it('Gets the request pased in', async () => {
it('Gets the request passed in', async () => {
const app = await fixture.loadTestAdapterApp();
const request = new Request('http:https://example.com/request');
const request = new Request('http:https://example.com/subpath/request');
const response = await app.render(request);
expect(response.status).to.equal(200);
const html = await response.text();
const $ = cheerioLoad(html);
expect($('#origin').text()).to.equal('http:https://example.com');
Expand All @@ -29,4 +31,32 @@ describe('Using Astro.request in SSR', () => {
const json = await fixture.readFile('/client/cars.json');
expect(json).to.not.be.undefined;
});

it('CSS assets have their base prefix', async () => {
const app = await fixture.loadTestAdapterApp();
let request = new Request('http:https://example.com/subpath/request');
let response = await app.render(request);
expect(response.status).to.equal(200);
const html = await response.text();
const $ = cheerioLoad(html);

const linkHref = $('link').attr('href');
expect(linkHref.startsWith('/subpath/')).to.equal(true);

request = new Request('http:https://example.com' + linkHref);
response = await app.render(request);

expect(response.status).to.equal(200);
const css = await response.text();
expect(css).to.not.be.an('undefined');
});

it('assets can be fetched', async () => {
const app = await fixture.loadTestAdapterApp();
const request = new Request('http:https://example.com/subpath/cars.json');
const response = await app.render(request);
expect(response.status).to.equal(200);
const data = await response.json();
expect(data).to.be.an('array');
});
});
16 changes: 15 additions & 1 deletion packages/astro/test/test-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,23 @@ export default function ({ provideAddress } = { provideAddress: true }) {
if (id === '@my-ssr') {
return `
import { App } from 'astro/app';
import fs from 'fs';
class MyApp extends App {
render(request, routeData) {
#manifest = null;
constructor(manifest, streaming) {
super(manifest, streaming);
this.#manifest = manifest;
}
async render(request, routeData) {
const url = new URL(request.url);
if(this.#manifest.assets.has(url.pathname)) {
const filePath = new URL('../client/' + this.removeBase(url.pathname), import.meta.url);
const data = await fs.promises.readFile(filePath);
return new Response(data);
}
${provideAddress ? `request[Symbol.for('astro.clientAddress')] = '0.0.0.0';` : ''}
return super.render(request, routeData);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/integrations/cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"dependencies": {
"esbuild": "^0.14.42"
},
"peerDependencies": {
"astro": "^1.6.3"
},
"devDependencies": {
"astro": "workspace:*",
"astro-scripts": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/cloudflare/src/server.advanced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function createExports(manifest: SSRManifest) {

// static assets
if (manifest.assets.has(pathname)) {
const assetRequest = new Request(`${origin}/static${pathname}`, request);
const assetRequest = new Request(`${origin}/static/${app.removeBase(pathname)}`, request);
return env.ASSETS.fetch(assetRequest);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/cloudflare/src/server.directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function createExports(manifest: SSRManifest) {
const { origin, pathname } = new URL(request.url);
// static assets
if (manifest.assets.has(pathname)) {
const assetRequest = new Request(`${origin}/static${pathname}`, request);
const assetRequest = new Request(`${origin}/static/${app.removeBase(pathname)}`, request);
return next(assetRequest);
}

Expand Down
3 changes: 3 additions & 0 deletions packages/integrations/deno/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
"dependencies": {
"esbuild": "^0.14.43"
},
"peerDependencies": {
"astro": "^1.6.3"
},
"devDependencies": {
"astro": "workspace:*",
"astro-scripts": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/deno/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function start(manifest: SSRManifest, options: Options) {
// If the request path wasn't found in astro,
// try to fetch a static file instead
const url = new URL(request.url);
const localPath = new URL('.' + url.pathname, clientRoot);
const localPath = new URL('./' + app.removeBase(url.pathname), clientRoot);
const fileResp = await fetch(localPath.toString());

// If the static file can't be found
Expand Down
3 changes: 3 additions & 0 deletions packages/integrations/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"@astrojs/webapi": "^1.1.1",
"send": "^0.18.0"
},
"peerDependencies": {
"astro": "^1.6.3"
},
"devDependencies": {
"@types/send": "^0.17.1",
"astro": "workspace:*",
Expand Down
6 changes: 4 additions & 2 deletions packages/integrations/node/src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ interface CreateServerOptions {
client: URL;
port: number;
host: string | undefined;
removeBase: (pathname: string) => string;
}

export function createServer(
{ client, port, host }: CreateServerOptions,
{ client, port, host, removeBase }: CreateServerOptions,
handler: http.RequestListener
) {
const listener: http.RequestListener = (req, res) => {
if (req.url) {
const stream = send(req, encodeURI(req.url), {
const pathname = '/' + removeBase(req.url);
const stream = send(req, encodeURI(pathname), {
root: fileURLToPath(client),
dotfiles: 'deny',
});
Expand Down
11 changes: 10 additions & 1 deletion packages/integrations/node/src/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
import { createServer } from './http-server.js';
import type { createExports } from './server';

const preview: CreatePreviewServer = async function ({ client, serverEntrypoint, host, port }) {
const preview: CreatePreviewServer = async function ({ client, serverEntrypoint, host, port, base }) {
type ServerModule = ReturnType<typeof createExports>;
type MaybeServerModule = Partial<ServerModule>;
let ssrHandler: ServerModule['handler'];
Expand Down Expand Up @@ -36,11 +36,20 @@ const preview: CreatePreviewServer = async function ({ client, serverEntrypoint,
});
};

const baseWithoutTrailingSlash: string = base.endsWith('/') ? base.slice(0, base.length - 1) : base;
function removeBase(pathname: string): string {
if(pathname.startsWith(base)) {
return pathname.slice(baseWithoutTrailingSlash.length);
}
return pathname;
}

const server = createServer(
{
client,
port,
host,
removeBase
},
handler
);
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/node/src/standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function startServer(app: NodeApp, options: Options) {
client,
port,
host,
removeBase: app.removeBase.bind(app),
},
handler
);
Expand Down

0 comments on commit b2b291d

Please sign in to comment.