mirror of
https://github.com/withastro/astro.git
synced 2025-01-20 22:12:38 -05:00
Node adapter: handle prerendering and serving with query params (#6110)
* Node adapter: handle prerendering and serving with query params * Adding a changeset
This commit is contained in:
parent
f9babc38b4
commit
67ccec9e16
11 changed files with 121 additions and 4 deletions
5
.changeset/shy-cats-heal.md
Normal file
5
.changeset/shy-cats-heal.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@astrojs/node': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes support for prerendering and query params
|
|
@ -31,13 +31,15 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/webapi": "^2.0.0",
|
"@astrojs/webapi": "^2.0.0",
|
||||||
"send": "^0.18.0"
|
"send": "^0.18.0",
|
||||||
|
"server-destroy": "^1.0.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"astro": "workspace:^2.0.6"
|
"astro": "workspace:^2.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/send": "^0.17.1",
|
"@types/send": "^0.17.1",
|
||||||
|
"@types/server-destroy": "^1.0.1",
|
||||||
"astro": "workspace:*",
|
"astro": "workspace:*",
|
||||||
"astro-scripts": "workspace:*",
|
"astro-scripts": "workspace:*",
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import http from 'http';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import send from 'send';
|
import send from 'send';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
import enableDestroy from 'server-destroy';
|
||||||
|
|
||||||
interface CreateServerOptions {
|
interface CreateServerOptions {
|
||||||
client: URL;
|
client: URL;
|
||||||
|
@ -19,6 +20,7 @@ export function createServer(
|
||||||
if (req.url) {
|
if (req.url) {
|
||||||
let pathname = removeBase(req.url);
|
let pathname = removeBase(req.url);
|
||||||
pathname = pathname[0] === '/' ? pathname : '/' + pathname;
|
pathname = pathname[0] === '/' ? pathname : '/' + pathname;
|
||||||
|
pathname = new URL(pathname, `http://${host}:${port}`).pathname;
|
||||||
const stream = send(req, encodeURI(decodeURI(pathname)), {
|
const stream = send(req, encodeURI(decodeURI(pathname)), {
|
||||||
root: fileURLToPath(client),
|
root: fileURLToPath(client),
|
||||||
dotfiles: pathname.startsWith('/.well-known/') ? 'allow' : 'deny',
|
dotfiles: pathname.startsWith('/.well-known/') ? 'allow' : 'deny',
|
||||||
|
@ -63,6 +65,7 @@ export function createServer(
|
||||||
httpServer = http.createServer(listener);
|
httpServer = http.createServer(listener);
|
||||||
}
|
}
|
||||||
httpServer.listen(port, host);
|
httpServer.listen(port, host);
|
||||||
|
enableDestroy(httpServer);
|
||||||
|
|
||||||
// Resolves once the server is closed
|
// Resolves once the server is closed
|
||||||
const closed = new Promise<void>((resolve, reject) => {
|
const closed = new Promise<void>((resolve, reject) => {
|
||||||
|
@ -79,7 +82,7 @@ export function createServer(
|
||||||
server: httpServer,
|
server: httpServer,
|
||||||
stop: async () => {
|
stop: async () => {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
httpServer.close((err) => (err ? reject(err) : resolve(undefined)));
|
httpServer.destroy((err) => (err ? reject(err) : resolve(undefined)));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ export function getAdapter(options: Options): AstroAdapter {
|
||||||
name: '@astrojs/node',
|
name: '@astrojs/node',
|
||||||
serverEntrypoint: '@astrojs/node/server.js',
|
serverEntrypoint: '@astrojs/node/server.js',
|
||||||
previewEntrypoint: '@astrojs/node/preview.js',
|
previewEntrypoint: '@astrojs/node/preview.js',
|
||||||
exports: ['handler'],
|
exports: ['handler', 'startServer'],
|
||||||
args: options,
|
args: options,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ export function createExports(manifest: SSRManifest, options: Options) {
|
||||||
const app = new NodeApp(manifest);
|
const app = new NodeApp(manifest);
|
||||||
return {
|
return {
|
||||||
handler: middleware(app, options.mode),
|
handler: middleware(app, options.mode),
|
||||||
|
startServer: () => startServer(app, options)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,12 @@ export default function startServer(app: NodeApp, options: Options) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const protocol = server.server instanceof https.Server ? 'https' : 'http';
|
const protocol = server.server instanceof https.Server ? 'https' : 'http';
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`Server listening on ${protocol}://${host}:${port}`);
|
console.log(`Server listening on ${protocol}://${host}:${port}`);
|
||||||
|
|
||||||
return server.closed();
|
return {
|
||||||
|
server,
|
||||||
|
done: server.closed()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
9
packages/integrations/node/test/fixtures/prerender/package.json
vendored
Normal file
9
packages/integrations/node/test/fixtures/prerender/package.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "@test/nodejs-encoded",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*",
|
||||||
|
"@astrojs/node": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
10
packages/integrations/node/test/fixtures/prerender/src/pages/one.astro
vendored
Normal file
10
packages/integrations/node/test/fixtures/prerender/src/pages/one.astro
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
---
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>One</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>One</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
packages/integrations/node/test/fixtures/prerender/src/pages/two.astro
vendored
Normal file
11
packages/integrations/node/test/fixtures/prerender/src/pages/two.astro
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
export const prerender = true;
|
||||||
|
---
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Two</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Two</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
60
packages/integrations/node/test/prerender.test.js
Normal file
60
packages/integrations/node/test/prerender.test.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import nodejs from '../dist/index.js';
|
||||||
|
import { loadFixture, createRequestAndResponse } from './test-utils.js';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as cheerio from 'cheerio';
|
||||||
|
import { fetch } from 'undici';
|
||||||
|
|
||||||
|
describe('Prerendering', () => {
|
||||||
|
/** @type {import('./test-utils').Fixture} */
|
||||||
|
let fixture;
|
||||||
|
let server;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
process.env.ASTRO_NODE_AUTOSTART = 'disabled';
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/prerender/',
|
||||||
|
output: 'server',
|
||||||
|
adapter: nodejs({ mode: 'standalone' }),
|
||||||
|
});
|
||||||
|
await fixture.build();
|
||||||
|
const { startServer } = await await load();
|
||||||
|
let res = startServer();
|
||||||
|
server = res.server;
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await server.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
const mod = await import('./fixtures/prerender/dist/server/entry.mjs');
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('Can render SSR route', async () => {
|
||||||
|
const res = await fetch(`http://${server.host}:${server.port}/one`);
|
||||||
|
const html = await res.text();
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
expect(res.status).to.equal(200);
|
||||||
|
expect($('h1').text()).to.equal('One');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Can render prerendered route', async () => {
|
||||||
|
const res = await fetch(`http://${server.host}:${server.port}/two`);
|
||||||
|
const html = await res.text();
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
expect(res.status).to.equal(200);
|
||||||
|
expect($('h1').text()).to.equal('Two');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Can render prerendered route with query params', async () => {
|
||||||
|
const res = await fetch(`http://${server.host}:${server.port}/two?foo=bar`);
|
||||||
|
const html = await res.text();
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
expect(res.status).to.equal(200);
|
||||||
|
expect($('h1').text()).to.equal('Two');
|
||||||
|
});
|
||||||
|
});
|
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
|
@ -3070,6 +3070,7 @@ importers:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@astrojs/webapi': ^2.0.0
|
'@astrojs/webapi': ^2.0.0
|
||||||
'@types/send': ^0.17.1
|
'@types/send': ^0.17.1
|
||||||
|
'@types/server-destroy': ^1.0.1
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
astro-scripts: workspace:*
|
astro-scripts: workspace:*
|
||||||
chai: ^4.3.6
|
chai: ^4.3.6
|
||||||
|
@ -3077,12 +3078,15 @@ importers:
|
||||||
mocha: ^9.2.2
|
mocha: ^9.2.2
|
||||||
node-mocks-http: ^1.11.0
|
node-mocks-http: ^1.11.0
|
||||||
send: ^0.18.0
|
send: ^0.18.0
|
||||||
|
server-destroy: ^1.0.1
|
||||||
undici: ^5.14.0
|
undici: ^5.14.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/webapi': link:../../webapi
|
'@astrojs/webapi': link:../../webapi
|
||||||
send: 0.18.0
|
send: 0.18.0
|
||||||
|
server-destroy: 1.0.1
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/send': 0.17.1
|
'@types/send': 0.17.1
|
||||||
|
'@types/server-destroy': 1.0.1
|
||||||
astro: link:../../astro
|
astro: link:../../astro
|
||||||
astro-scripts: link:../../../scripts
|
astro-scripts: link:../../../scripts
|
||||||
chai: 4.3.7
|
chai: 4.3.7
|
||||||
|
@ -3115,6 +3119,14 @@ importers:
|
||||||
'@astrojs/node': link:../../..
|
'@astrojs/node': link:../../..
|
||||||
astro: link:../../../../../astro
|
astro: link:../../../../../astro
|
||||||
|
|
||||||
|
packages/integrations/node/test/fixtures/prerender:
|
||||||
|
specifiers:
|
||||||
|
'@astrojs/node': workspace:*
|
||||||
|
astro: workspace:*
|
||||||
|
dependencies:
|
||||||
|
'@astrojs/node': link:../../..
|
||||||
|
astro: link:../../../../../astro
|
||||||
|
|
||||||
packages/integrations/node/test/fixtures/url-protocol:
|
packages/integrations/node/test/fixtures/url-protocol:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@astrojs/node': workspace:*
|
'@astrojs/node': workspace:*
|
||||||
|
|
Loading…
Add table
Reference in a new issue