From 3b104fb8a5c184d253d847998323e6b9c2573bcd Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Fri, 22 Jul 2022 15:22:31 -0400 Subject: Fixes Node adapter receiving a request body (#4023) * Fixes Node adapter receiving a request body * Updated lockfile --- packages/integrations/node/test/api-route.test.js | 37 +++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 packages/integrations/node/test/api-route.test.js (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js new file mode 100644 index 000000000..963e0463a --- /dev/null +++ b/packages/integrations/node/test/api-route.test.js @@ -0,0 +1,37 @@ +import nodejs from '../dist/index.js'; +import { loadFixture, createRequestAndResponse, toPromise } from './test-utils.js'; +import { expect } from 'chai'; + + +describe('API routes', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/api-route/', + experimental: { + ssr: true, + }, + adapter: nodejs(), + }); + await fixture.build(); + }); + + it('Can get the request body', async () => { + const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); + + let { req, res, done } = createRequestAndResponse({ + method: 'POST', + url: '/recipes' + }); + + handler(req, res); + req.send(JSON.stringify({ id: 2 })); + + let [ buffer ] = await done; + let json = JSON.parse(buffer.toString('utf-8')); + expect(json.length).to.equal(1); + expect(json[0].name).to.equal('Broccoli Soup'); + }); +}); -- cgit v1.2.3 From 3f716dba24c5ba4e230977d5209422ba3c85a0be Mon Sep 17 00:00:00 2001 From: matthewp Date: Fri, 22 Jul 2022 19:24:58 +0000 Subject: [ci] format --- packages/integrations/node/src/server.ts | 4 ++-- packages/integrations/node/test/api-route.test.js | 5 ++--- packages/integrations/node/test/test-utils.js | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/src/server.ts b/packages/integrations/node/src/server.ts index 453ecb2d2..12fcf0448 100644 --- a/packages/integrations/node/src/server.ts +++ b/packages/integrations/node/src/server.ts @@ -29,8 +29,8 @@ export function createExports(manifest: SSRManifest) { } else if (next) { return next(); } - } catch(err: unknown) { - if(!res.headersSent) { + } catch (err: unknown) { + if (!res.headersSent) { res.writeHead(500, `Server error`); res.end(); } diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 963e0463a..a28b88e7f 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -2,7 +2,6 @@ import nodejs from '../dist/index.js'; import { loadFixture, createRequestAndResponse, toPromise } from './test-utils.js'; import { expect } from 'chai'; - describe('API routes', () => { /** @type {import('./test-utils').Fixture} */ let fixture; @@ -23,13 +22,13 @@ describe('API routes', () => { let { req, res, done } = createRequestAndResponse({ method: 'POST', - url: '/recipes' + url: '/recipes', }); handler(req, res); req.send(JSON.stringify({ id: 2 })); - let [ buffer ] = await done; + let [buffer] = await done; let json = JSON.parse(buffer.toString('utf-8')); expect(json.length).to.equal(1); expect(json[0].name).to.equal('Broccoli Soup'); diff --git a/packages/integrations/node/test/test-utils.js b/packages/integrations/node/test/test-utils.js index 4bd42d557..0859c6acd 100644 --- a/packages/integrations/node/test/test-utils.js +++ b/packages/integrations/node/test/test-utils.js @@ -23,7 +23,7 @@ export function createRequestAndResponse(reqOptions) { let res = httpMocks.createResponse({ eventEmitter: EventEmitter, - req + req, }); let done = toPromise(res); @@ -32,7 +32,7 @@ export function createRequestAndResponse(reqOptions) { } export function toPromise(res) { - return new Promise(resolve => { + return new Promise((resolve) => { res.on('end', () => { let chunks = res._getChunks(); resolve(chunks); -- cgit v1.2.3 From 42da2e23eb3f677d25b7ed0aab273f219c8cab4e Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Mon, 25 Jul 2022 00:18:02 -0400 Subject: Add the `output` option (#4015) * Start of work on astroConfig.mode === 'server' * Add tests and more * adapter -> deploy in some places * Add fallback for `adapter` config * Update more tests * Update image tests * Fix clientAddress test * Updates based on PR review * Add a changeset * Update integrations tests + readme * Oops * Remove old option * Rename `mode` to `output` * Update Node adapter test * Update test * fred pass * fred pass * fred pass * fix test Co-authored-by: Fred K. Schott --- packages/integrations/node/README.md | 1 + packages/integrations/node/src/index.ts | 6 +++++- packages/integrations/node/test/api-route.test.js | 4 +--- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/README.md b/packages/integrations/node/README.md index 3772a23d8..274352577 100644 --- a/packages/integrations/node/README.md +++ b/packages/integrations/node/README.md @@ -37,6 +37,7 @@ import node from '@astrojs/node'; export default defineConfig({ // ... + output: 'server', adapter: node() }) ``` diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index b90cd9d2e..8ff6fc423 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -12,8 +12,12 @@ export default function createIntegration(): AstroIntegration { return { name: '@astrojs/node', hooks: { - 'astro:config:done': ({ setAdapter }) => { + 'astro:config:done': ({ setAdapter, config }) => { setAdapter(getAdapter()); + + if(config.output === 'static') { + console.warn(`[@astrojs/Node] \`output: "server"\` is required to use this adapter.`); + } }, }, }; diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index a28b88e7f..034b53c07 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -9,9 +9,7 @@ describe('API routes', () => { before(async () => { fixture = await loadFixture({ root: './fixtures/api-route/', - experimental: { - ssr: true, - }, + output: 'server', adapter: nodejs(), }); await fixture.build(); -- cgit v1.2.3 From 8c3de73ebfae60aa0204ef748fd2c124ee64867a Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Tue, 26 Jul 2022 10:31:54 -0400 Subject: Fixes binary data request bodies in the Node adapter (#4055) * Fixes binary data request bodies in the Node adapter * Fix type --- packages/integrations/node/test/api-route.test.js | 16 ++++++++++++++++ .../node/test/fixtures/api-route/src/pages/binary.ts | 11 +++++++++++ 2 files changed, 27 insertions(+) create mode 100644 packages/integrations/node/test/fixtures/api-route/src/pages/binary.ts (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 034b53c07..cd074ef27 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -31,4 +31,20 @@ describe('API routes', () => { expect(json.length).to.equal(1); expect(json[0].name).to.equal('Broccoli Soup'); }); + + it('Can get binary data', async () => { + const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); + + let { req, res, done } = createRequestAndResponse({ + method: 'POST', + url: '/binary', + }); + + handler(req, res); + req.send(Buffer.from(new Uint8Array([1, 2, 3, 4, 5]))); + + let [out] = await done; + let arr = Array.from(new Uint8Array(out.buffer)); + expect(arr).to.deep.equal([5, 4, 3, 2, 1]); + }); }); diff --git a/packages/integrations/node/test/fixtures/api-route/src/pages/binary.ts b/packages/integrations/node/test/fixtures/api-route/src/pages/binary.ts new file mode 100644 index 000000000..6b50bc341 --- /dev/null +++ b/packages/integrations/node/test/fixtures/api-route/src/pages/binary.ts @@ -0,0 +1,11 @@ + +export async function post({ request }: { request: Request }) { + let body = await request.arrayBuffer(); + let data = new Uint8Array(body); + let r = data.reverse(); + return new Response(r, { + headers: { + 'Content-Type': 'application/octet-stream' + } + }); +} -- cgit v1.2.3 From fa399ad7c34a9a3e72515923b0548113dfc65776 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Wed, 12 Oct 2022 17:25:51 -0400 Subject: Node.js standalone mode + support for astro preview (#5056) * wip * Deprecate buildConfig and move to config.build * Implement the standalone server * Stay backwards compat * Add changesets * correctly merge URLs * Get config earlier * update node tests * Return the preview server * update remaining tests * swap usage and config ordering * Update packages/astro/src/@types/astro.ts Co-authored-by: Sarah Rainsberger * Update .changeset/metal-pumas-walk.md Co-authored-by: Sarah Rainsberger * Update .changeset/metal-pumas-walk.md Co-authored-by: Sarah Rainsberger * Update .changeset/stupid-points-refuse.md Co-authored-by: Sarah Rainsberger * Update .changeset/stupid-points-refuse.md Co-authored-by: Sarah Rainsberger * Link to build.server config Co-authored-by: Fred K. Schott Co-authored-by: Sarah Rainsberger --- packages/integrations/node/README.md | 81 +++++++++++++---------- packages/integrations/node/package.json | 5 +- packages/integrations/node/src/http-server.ts | 77 +++++++++++++++++++++ packages/integrations/node/src/index.ts | 30 ++++++++- packages/integrations/node/src/middleware.ts | 53 +++++++++++++++ packages/integrations/node/src/preview.ts | 54 +++++++++++++++ packages/integrations/node/src/server.ts | 53 +++------------ packages/integrations/node/src/standalone.ts | 53 +++++++++++++++ packages/integrations/node/src/types.ts | 17 +++++ packages/integrations/node/test/api-route.test.js | 2 +- 10 files changed, 342 insertions(+), 83 deletions(-) create mode 100644 packages/integrations/node/src/http-server.ts create mode 100644 packages/integrations/node/src/middleware.ts create mode 100644 packages/integrations/node/src/preview.ts create mode 100644 packages/integrations/node/src/standalone.ts create mode 100644 packages/integrations/node/src/types.ts (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/README.md b/packages/integrations/node/README.md index 7c95dd0ea..9e0f8150e 100644 --- a/packages/integrations/node/README.md +++ b/packages/integrations/node/README.md @@ -1,23 +1,23 @@ -# @astrojs/node 🔲 +# @astrojs/node This adapter allows Astro to deploy your SSR site to Node targets. - [Why Astro Node](#why-astro-node) - [Installation](#installation) -- [Usage](#usage) - [Configuration](#configuration) +- [Usage](#usage) - [Troubleshooting](#troubleshooting) - [Contributing](#contributing) - [Changelog](#changelog) -## Why Astro Node +## Why @astrojs/node If you're using Astro as a static site builder—its behavior out of the box—you don't need an adapter. If you wish to [use server-side rendering (SSR)](https://docs.astro.build/en/guides/server-side-rendering/), Astro requires an adapter that matches your deployment runtime. -[Node](https://nodejs.org/en/) is a JavaScript runtime for server-side code. Frameworks like [Express](https://expressjs.com/) are built on top of it and make it easier to write server applications in Node. This adapter provides access to Node's API and creates a script to run your Astro project that can be utilized in Node applications. +[Node.js](https://nodejs.org/en/) is a JavaScript runtime for server-side code. @astrojs/node can be used either in standalone mode or as middleware for other http servers, such as [Express](https://expressjs.com/). ## Installation @@ -42,23 +42,47 @@ If you prefer to install the adapter manually instead, complete the following tw 1. Add two new lines to your `astro.config.mjs` project configuration file. - ```js title="astro.config.mjs" ins={2, 5-6} + ```js title="astro.config.mjs" ins={2, 5-8} import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; export default defineConfig({ output: 'server', - adapter: node(), + adapter: node({ + mode: 'standalone' + }), }); ``` +## Configuration + +@astrojs/node can be configured by passing options into the adapter function. The following options are available: + +### Mode + +Controls whether the adapter builds to `middleware` or `standalone` mode. + +- `middleware` mode allows the built output to be used as middleware for another Node.js server, like Express.js or Fastify. + ```js + import { defineConfig } from 'astro/config'; + import nodejs from '@astrojs/node'; + + export default defineConfig({ + output: 'server', + adapter: node({ + mode: 'middleware' + }), + }); + ``` +- `standalone` mode builds to server that automatically starts with the entry module is run. This allows you to more easily deploy your build to a host without any additional code. + ## Usage -After [performing a build](https://docs.astro.build/en/guides/deploy/#building-your-site-locally) there will be a `dist/server/entry.mjs` module that exposes a `handler` function. This works like a [middleware](https://expressjs.com/en/guide/using-middleware.html) function: it can handle incoming requests and respond accordingly. +First, [performing a build](https://docs.astro.build/en/guides/deploy/#building-your-site-locally). Depending on which `mode` selected (see above) follow the appropriate steps below: +### Middleware -### Using a middleware framework -You can use this `handler` with any framework that supports the Node `request` and `response` objects. +The server entrypoint is built to `./dist/server/entry.mjs` by default. This module exports a `handler` function that can be used with any framework that supports the Node `request` and `response` objects. For example, with Express: @@ -73,40 +97,27 @@ app.use(ssrHandler); app.listen(8080); ``` +Note that middleware mode does not do file servering. You'll need to configure your HTTP framework to do that for you. By default the client assets are written to `./dist/client/`. -### Using `http` - -This output script does not require you use Express and can work with even the built-in `http` and `https` node modules. The handler does follow the convention calling an error function when either +### Standalone -- A route is not found for the request. -- There was an error rendering. +In standalone mode a server starts when the server entrypoint is run. By default it is built to `./dist/server/entry.mjs`. You can run it with: -You can use these to implement your own 404 behavior like so: - -```js -import http from 'http'; -import { handler as ssrHandler } from './dist/server/entry.mjs'; - -http.createServer(function(req, res) { - ssrHandler(req, res, err => { - if(err) { - res.writeHead(500); - res.end(err.toString()); - } else { - // Serve your static assets here maybe? - // 404? - res.writeHead(404); - res.end(); - } - }); -}).listen(8080); +```shell +node ./dist/server/entry.mjs ``` +For standalone mode the server handles file servering in addition to the page and API routes. +#### HTTPS -## Configuration +By default the standalone server uses HTTP. This works well if you have a proxy server in front of it that does HTTPS. If you need the standalone server to run HTTPS itself you need to provide your SSL key and certificate. -This adapter does not expose any configuration options. +You can pass the path to your key and certification via the environment variables `SERVER_CERT_PATH` and `SERVER_KEY_PATH`. This is how you might pass them in bash: + +```bash +SERVER_KEY_PATH=./private/key.pem SERVER_CERT_PATH=./private/cert.pem node ./dist/server/entry.mjs +``` ## Troubleshooting diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index ffe8c07d8..77a027cd9 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -20,6 +20,7 @@ "exports": { ".": "./dist/index.js", "./server.js": "./dist/server.js", + "./preview.js": "./dist/preview.js", "./package.json": "./package.json" }, "scripts": { @@ -29,9 +30,11 @@ "test": "mocha --exit --timeout 20000 test/" }, "dependencies": { - "@astrojs/webapi": "^1.1.0" + "@astrojs/webapi": "^1.1.0", + "send": "^0.18.0" }, "devDependencies": { + "@types/send": "^0.17.1", "astro": "workspace:*", "astro-scripts": "workspace:*", "chai": "^4.3.6", diff --git a/packages/integrations/node/src/http-server.ts b/packages/integrations/node/src/http-server.ts new file mode 100644 index 000000000..34192c5f9 --- /dev/null +++ b/packages/integrations/node/src/http-server.ts @@ -0,0 +1,77 @@ +import fs from 'fs'; +import http from 'http'; +import https from 'https'; +import { fileURLToPath } from 'url'; +import send from 'send'; + +interface CreateServerOptions { + client: URL; + port: number; + host: string | undefined; +} + +export function createServer({ client, port, host }: CreateServerOptions, handler: http.RequestListener) { + const listener: http.RequestListener = (req, res) => { + if(req.url) { + const fileURL = new URL('.' + req.url, client); + + const stream = send(req, fileURLToPath(fileURL), { + dotfiles: 'deny', + }); + + let forwardError = false; + + stream.on('error', err => { + if(forwardError) { + // eslint-disable-next-line no-console + console.error(err.toString()); + res.writeHead(500); + res.end('Internal server error'); + return; + } + // File not found, forward to the SSR handler + handler(req, res); + }); + + stream.on('file', () => { + forwardError = true; + }); + stream.pipe(res); + } else { + handler(req, res); + } + }; + + let httpServer: http.Server | + https.Server; + + if(process.env.SERVER_CERT_PATH && process.env.SERVER_KEY_PATH) { + httpServer = https.createServer({ + key: fs.readFileSync(process.env.SERVER_KEY_PATH), + cert: fs.readFileSync(process.env.SERVER_CERT_PATH), + }, listener); + } else { + httpServer = http.createServer(listener); + } + httpServer.listen(port, host); + + // Resolves once the server is closed + const closed = new Promise((resolve, reject) => { + httpServer.addListener('close', resolve); + httpServer.addListener('error', reject); + }); + + return { + host, + port, + closed() { + return closed; + }, + server: httpServer, + stop: async () => { + await new Promise((resolve, reject) => { + httpServer.close((err) => (err ? reject(err) : resolve(undefined))); + }); + }, + }; +} diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index 53b94b916..80dfacdab 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -1,24 +1,48 @@ import type { AstroAdapter, AstroIntegration } from 'astro'; +import type { Options, UserOptions } from './types'; -export function getAdapter(): AstroAdapter { +export function getAdapter(options: Options): AstroAdapter { return { name: '@astrojs/node', serverEntrypoint: '@astrojs/node/server.js', + previewEntrypoint: '@astrojs/node/preview.js', exports: ['handler'], + args: options }; } -export default function createIntegration(): AstroIntegration { +export default function createIntegration(userOptions: UserOptions): AstroIntegration { + if(!userOptions?.mode) { + throw new Error(`[@astrojs/node] Setting the 'mode' option is required.`) + } + + let needsBuildConfig = false; + let _options: Options; return { name: '@astrojs/node', hooks: { 'astro:config:done': ({ setAdapter, config }) => { - setAdapter(getAdapter()); + needsBuildConfig = !config.build?.server; + _options = { + ...userOptions, + client: config.build.client?.toString(), + server: config.build.server?.toString(), + host: config.server.host, + port: config.server.port, + }; + setAdapter(getAdapter(_options)); if (config.output === 'static') { console.warn(`[@astrojs/node] \`output: "server"\` is required to use this adapter.`); } }, + 'astro:build:start': ({ buildConfig }) => { + // Backwards compat + if(needsBuildConfig) { + _options.client = buildConfig.client.toString(); + _options.server = buildConfig.server.toString(); + } + } }, }; } diff --git a/packages/integrations/node/src/middleware.ts b/packages/integrations/node/src/middleware.ts new file mode 100644 index 000000000..772461f2a --- /dev/null +++ b/packages/integrations/node/src/middleware.ts @@ -0,0 +1,53 @@ +import type { NodeApp } from 'astro/app/node'; +import type { IncomingMessage, ServerResponse } from 'http'; +import type { Readable } from 'stream'; + +export default function(app: NodeApp) { + return async function(req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) { + try { + const route = app.match(req); + + if (route) { + try { + const response = await app.render(req); + await writeWebResponse(app, res, response); + } catch (err: unknown) { + if (next) { + next(err); + } else { + throw err; + } + } + } else if (next) { + return next(); + } else { + res.writeHead(404); + res.end('Not found'); + } + } catch (err: unknown) { + if (!res.headersSent) { + res.writeHead(500, `Server error`); + res.end(); + } + } + }; +} + +async function writeWebResponse(app: NodeApp, res: ServerResponse, webResponse: Response) { + const { status, headers, body } = webResponse; + + if (app.setCookieHeaders) { + const setCookieHeaders: Array = Array.from(app.setCookieHeaders(webResponse)); + if (setCookieHeaders.length) { + res.setHeader('Set-Cookie', setCookieHeaders); + } + } + + res.writeHead(status, Object.fromEntries(headers.entries())); + if (body) { + for await (const chunk of body as unknown as Readable) { + res.write(chunk); + } + } + res.end(); +} diff --git a/packages/integrations/node/src/preview.ts b/packages/integrations/node/src/preview.ts new file mode 100644 index 000000000..33c2f18e2 --- /dev/null +++ b/packages/integrations/node/src/preview.ts @@ -0,0 +1,54 @@ +import type { CreatePreviewServer } from 'astro'; +import type { createExports } from './server'; +import http from 'http'; +import { fileURLToPath } from 'url'; +import { createServer } from './http-server.js'; + +const preview: CreatePreviewServer = async function({ + client, + serverEntrypoint, + host, + port, +}) { + type ServerModule = ReturnType; + type MaybeServerModule = Partial; + let ssrHandler: ServerModule['handler']; + try { + process.env.ASTRO_NODE_AUTOSTART = 'disabled'; + const ssrModule: MaybeServerModule = await import(serverEntrypoint.toString()); + if(typeof ssrModule.handler === 'function') { + ssrHandler = ssrModule.handler; + } else { + throw new Error(`The server entrypoint doesn't have a handler. Are you sure this is the right file?`); + } + } catch(_err) { + throw new Error(`The server entrypoint ${fileURLToPath} does not exist. Have you ran a build yet?`); + } + + const handler: http.RequestListener = (req, res) => { + ssrHandler(req, res, (ssrErr: any) => { + if (ssrErr) { + res.writeHead(500); + res.end(ssrErr.toString()); + } else { + res.writeHead(404); + res.end(); + } + }); + }; + + const server = createServer({ + client, + port, + host, + }, handler); + + // eslint-disable-next-line no-console + console.log(`Preview server listening on http://${host}:${port}`); + + return server; +} + +export { + preview as default +}; diff --git a/packages/integrations/node/src/server.ts b/packages/integrations/node/src/server.ts index 6ecd14931..202e66b7e 100644 --- a/packages/integrations/node/src/server.ts +++ b/packages/integrations/node/src/server.ts @@ -1,8 +1,9 @@ -import { polyfill } from '@astrojs/webapi'; import type { SSRManifest } from 'astro'; +import type { Options } from './types'; +import { polyfill } from '@astrojs/webapi'; import { NodeApp } from 'astro/app/node'; -import type { IncomingMessage, ServerResponse } from 'http'; -import type { Readable } from 'stream'; +import middleware from './middleware.js'; +import startServer from './standalone.js'; polyfill(globalThis, { exclude: 'window document', @@ -11,49 +12,15 @@ polyfill(globalThis, { export function createExports(manifest: SSRManifest) { const app = new NodeApp(manifest); return { - async handler(req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) { - try { - const route = app.match(req); - - if (route) { - try { - const response = await app.render(req); - await writeWebResponse(app, res, response); - } catch (err: unknown) { - if (next) { - next(err); - } else { - throw err; - } - } - } else if (next) { - return next(); - } - } catch (err: unknown) { - if (!res.headersSent) { - res.writeHead(500, `Server error`); - res.end(); - } - } - }, + handler: middleware(app) }; } -async function writeWebResponse(app: NodeApp, res: ServerResponse, webResponse: Response) { - const { status, headers, body } = webResponse; - - if (app.setCookieHeaders) { - const setCookieHeaders: Array = Array.from(app.setCookieHeaders(webResponse)); - if (setCookieHeaders.length) { - res.setHeader('Set-Cookie', setCookieHeaders); - } +export function start(manifest: SSRManifest, options: Options) { + if(options.mode !== 'standalone' || process.env.ASTRO_NODE_AUTOSTART === 'disabled') { + return; } - res.writeHead(status, Object.fromEntries(headers.entries())); - if (body) { - for await (const chunk of body as unknown as Readable) { - res.write(chunk); - } - } - res.end(); + const app = new NodeApp(manifest); + startServer(app, options); } diff --git a/packages/integrations/node/src/standalone.ts b/packages/integrations/node/src/standalone.ts new file mode 100644 index 000000000..8fef96ed5 --- /dev/null +++ b/packages/integrations/node/src/standalone.ts @@ -0,0 +1,53 @@ +import type { NodeApp } from 'astro/app/node'; +import type { Options } from './types'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import middleware from './middleware.js'; +import { createServer } from './http-server.js'; + +function resolvePaths(options: Options) { + const clientURLRaw = new URL(options.client); + const serverURLRaw = new URL(options.server); + const rel = path.relative(fileURLToPath(serverURLRaw), fileURLToPath(clientURLRaw)); + + const serverEntryURL = new URL(import.meta.url); + const clientURL = new URL(appendForwardSlash(rel), serverEntryURL); + + return { + client: clientURL, + }; +} + +function appendForwardSlash(pth: string) { + return pth.endsWith('/') ? pth : pth + '/'; +} + +export function getResolvedHostForHttpServer(host: string | boolean) { + if (host === false) { + // Use a secure default + return '127.0.0.1'; + } else if (host === true) { + // If passed --host in the CLI without arguments + return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs) + } else { + return host; + } +} + +export default function startServer(app: NodeApp, options: Options) { + const port = process.env.PORT ? Number(process.env.port) : (options.port ?? 8080); + const { client } = resolvePaths(options); + const handler = middleware(app); + + const host = getResolvedHostForHttpServer(options.host); + const server = createServer({ + client, + port, + host, + }, handler); + + // eslint-disable-next-line no-console + console.log(`Server listening on http://${host}:${port}`); + + return server.closed(); +} diff --git a/packages/integrations/node/src/types.ts b/packages/integrations/node/src/types.ts new file mode 100644 index 000000000..aaf3be942 --- /dev/null +++ b/packages/integrations/node/src/types.ts @@ -0,0 +1,17 @@ + +export interface UserOptions { + /** + * Specifies the mode that the adapter builds to. + * + * - 'middleware' - Build to middleware, to be used within another Node.js server, such as Express. + * - 'standalone' - Build to a standalone server. The server starts up just by running the built script. + */ + mode: 'middleware' | 'standalone'; +} + +export interface Options extends UserOptions { + host: string | boolean; + port: number; + server: string; + client: string; +} diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index cd074ef27..2cc15c761 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -10,7 +10,7 @@ describe('API routes', () => { fixture = await loadFixture({ root: './fixtures/api-route/', output: 'server', - adapter: nodejs(), + adapter: nodejs({ mode: 'middleware' }), }); await fixture.build(); }); -- cgit v1.2.3 From 2263e889cc68254d3ce08894a1f719ab4ac45d94 Mon Sep 17 00:00:00 2001 From: wulinsheng123 <409187100@qq.com> Date: Wed, 11 Jan 2023 00:59:20 +0800 Subject: can jump 404 when that page does not exist (#5701) --- packages/integrations/node/package.json | 1 + packages/integrations/node/src/middleware.ts | 7 ++-- packages/integrations/node/src/server.ts | 4 +-- packages/integrations/node/src/standalone.ts | 2 +- packages/integrations/node/test/api-route.test.js | 9 ++++-- .../test/fixtures/node-middleware/package.json | 9 ++++++ .../fixtures/node-middleware/src/pages/404.astro | 13 ++++++++ .../fixtures/node-middleware/src/pages/index.astro | 11 +++++++ .../integrations/node/test/node-middleware.test.js | 37 ++++++++++++++++++++++ 9 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 packages/integrations/node/test/fixtures/node-middleware/package.json create mode 100644 packages/integrations/node/test/fixtures/node-middleware/src/pages/404.astro create mode 100644 packages/integrations/node/test/fixtures/node-middleware/src/pages/index.astro create mode 100644 packages/integrations/node/test/node-middleware.test.js (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index 779b8792b..86910a34e 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -41,6 +41,7 @@ "astro": "workspace:*", "astro-scripts": "workspace:*", "chai": "^4.3.6", + "cheerio": "^1.0.0-rc.11", "mocha": "^9.2.2", "node-mocks-http": "^1.11.0", "undici": "^5.14.0" diff --git a/packages/integrations/node/src/middleware.ts b/packages/integrations/node/src/middleware.ts index bfa7b74d5..1af4539a6 100644 --- a/packages/integrations/node/src/middleware.ts +++ b/packages/integrations/node/src/middleware.ts @@ -2,16 +2,17 @@ import type { NodeApp } from 'astro/app/node'; import type { IncomingMessage, ServerResponse } from 'http'; import type { Readable } from 'stream'; import { responseIterator } from './response-iterator'; +import type { Options } from './types'; -export default function (app: NodeApp) { +export default function (app: NodeApp, mode: Options['mode']) { return async function ( req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void ) { try { - const route = app.match(req); - + const route = + mode === 'standalone' ? app.match(req, { matchNotFound: true }) : app.match(req); if (route) { try { const response = await app.render(req); diff --git a/packages/integrations/node/src/server.ts b/packages/integrations/node/src/server.ts index 9cff04cf5..e0ccf6599 100644 --- a/packages/integrations/node/src/server.ts +++ b/packages/integrations/node/src/server.ts @@ -9,10 +9,10 @@ polyfill(globalThis, { exclude: 'window document', }); -export function createExports(manifest: SSRManifest) { +export function createExports(manifest: SSRManifest, options: Options) { const app = new NodeApp(manifest); return { - handler: middleware(app), + handler: middleware(app, options.mode), }; } diff --git a/packages/integrations/node/src/standalone.ts b/packages/integrations/node/src/standalone.ts index 54a3940b4..d68c3a500 100644 --- a/packages/integrations/node/src/standalone.ts +++ b/packages/integrations/node/src/standalone.ts @@ -37,7 +37,7 @@ export function getResolvedHostForHttpServer(host: string | boolean) { export default function startServer(app: NodeApp, options: Options) { const port = process.env.PORT ? Number(process.env.PORT) : options.port ?? 8080; const { client } = resolvePaths(options); - const handler = middleware(app); + const handler = middleware(app, options.mode); // Allow to provide host value at runtime const host = getResolvedHostForHttpServer( diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 2cc15c761..39ba10dbf 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -1,5 +1,5 @@ import nodejs from '../dist/index.js'; -import { loadFixture, createRequestAndResponse, toPromise } from './test-utils.js'; +import { loadFixture, createRequestAndResponse } from './test-utils.js'; import { expect } from 'chai'; describe('API routes', () => { @@ -17,18 +17,21 @@ describe('API routes', () => { it('Can get the request body', async () => { const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ method: 'POST', url: '/recipes', }); handler(req, res); - req.send(JSON.stringify({ id: 2 })); + req.send(JSON.stringify({ id: 2 })); + let [buffer] = await done; + let json = JSON.parse(buffer.toString('utf-8')); + expect(json.length).to.equal(1); + expect(json[0].name).to.equal('Broccoli Soup'); }); diff --git a/packages/integrations/node/test/fixtures/node-middleware/package.json b/packages/integrations/node/test/fixtures/node-middleware/package.json new file mode 100644 index 000000000..7a49010f1 --- /dev/null +++ b/packages/integrations/node/test/fixtures/node-middleware/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/node-middleware", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*", + "@astrojs/node": "workspace:*" + } +} diff --git a/packages/integrations/node/test/fixtures/node-middleware/src/pages/404.astro b/packages/integrations/node/test/fixtures/node-middleware/src/pages/404.astro new file mode 100644 index 000000000..4684c8665 --- /dev/null +++ b/packages/integrations/node/test/fixtures/node-middleware/src/pages/404.astro @@ -0,0 +1,13 @@ +--- +--- + + + + + + + + 404 + +

404!!!!!!!!!!

+ diff --git a/packages/integrations/node/test/fixtures/node-middleware/src/pages/index.astro b/packages/integrations/node/test/fixtures/node-middleware/src/pages/index.astro new file mode 100644 index 000000000..28ff7d223 --- /dev/null +++ b/packages/integrations/node/test/fixtures/node-middleware/src/pages/index.astro @@ -0,0 +1,11 @@ +--- +--- + + +node-middleware + + +
1
+ + diff --git a/packages/integrations/node/test/node-middleware.test.js b/packages/integrations/node/test/node-middleware.test.js new file mode 100644 index 000000000..8a2f947d5 --- /dev/null +++ b/packages/integrations/node/test/node-middleware.test.js @@ -0,0 +1,37 @@ +import nodejs from '../dist/index.js'; +import { loadFixture , createRequestAndResponse} from './test-utils.js'; +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; + +describe('test 404 cant load', () => { + let fixture; + before(async () => { + fixture = await loadFixture({ + root: './fixtures/node-middleware/', + output: 'server', + adapter: nodejs({ mode: 'standalone' }), + }); + await fixture.build(); + }); + describe('test 404', async () => { + let devPreview; + + before(async () => { + devPreview = await fixture.preview(); + }); + after(async () => { + await devPreview.stop(); + }); + it('when mode is standalone', async () => { + const res = await fixture.fetch('/error-page'); + + expect(res.status).to.equal(404); + + const html = await res.text(); + const $ = cheerio.load(html); + + const h1 = $('h1'); + expect(h1.text()).to.equal('404!!!!!!!!!!'); + }); + }) +}) -- cgit v1.2.3 From 37d133275643aec1b67ea574f6950e7793e53b4d Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 10 Jan 2023 17:01:27 +0000 Subject: [ci] format --- packages/integrations/node/test/api-route.test.js | 4 ++-- packages/integrations/node/test/node-middleware.test.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 39ba10dbf..7fbd95776 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -25,13 +25,13 @@ describe('API routes', () => { handler(req, res); req.send(JSON.stringify({ id: 2 })); - + let [buffer] = await done; let json = JSON.parse(buffer.toString('utf-8')); expect(json.length).to.equal(1); - + expect(json[0].name).to.equal('Broccoli Soup'); }); diff --git a/packages/integrations/node/test/node-middleware.test.js b/packages/integrations/node/test/node-middleware.test.js index 8a2f947d5..e96d29ed4 100644 --- a/packages/integrations/node/test/node-middleware.test.js +++ b/packages/integrations/node/test/node-middleware.test.js @@ -1,5 +1,5 @@ import nodejs from '../dist/index.js'; -import { loadFixture , createRequestAndResponse} from './test-utils.js'; +import { loadFixture, createRequestAndResponse } from './test-utils.js'; import { expect } from 'chai'; import * as cheerio from 'cheerio'; @@ -33,5 +33,5 @@ describe('test 404 cant load', () => { const h1 = $('h1'); expect(h1.text()).to.equal('404!!!!!!!!!!'); }); - }) -}) + }); +}); -- cgit v1.2.3 From 6dbf433eddf8f33ecf230aaf9c31ff1d5c14fe21 Mon Sep 17 00:00:00 2001 From: hbgl Date: Tue, 15 Aug 2023 16:26:18 +0200 Subject: Stream request body instead of buffering it in memory (#8084) Co-authored-by: Matthew Phillips --- packages/integrations/node/package.json | 2 +- packages/integrations/node/test/api-route.test.js | 45 ++++++++++++++++++++-- .../node/test/fixtures/api-route/src/pages/hash.ts | 16 ++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index 988246f10..0d7689298 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -49,7 +49,7 @@ "chai": "^4.3.7", "cheerio": "1.0.0-rc.12", "mocha": "^9.2.2", - "node-mocks-http": "^1.12.2", + "node-mocks-http": "^1.13.0", "undici": "^5.22.1" } } diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 7fbd95776..c830eee2d 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -1,6 +1,7 @@ import nodejs from '../dist/index.js'; import { loadFixture, createRequestAndResponse } from './test-utils.js'; import { expect } from 'chai'; +import crypto from 'node:crypto'; describe('API routes', () => { /** @type {import('./test-utils').Fixture} */ @@ -22,9 +23,11 @@ describe('API routes', () => { url: '/recipes', }); - handler(req, res); + req.once('async_iterator', () => { + req.send(JSON.stringify({ id: 2 })); + }); - req.send(JSON.stringify({ id: 2 })); + handler(req, res); let [buffer] = await done; @@ -43,11 +46,47 @@ describe('API routes', () => { url: '/binary', }); + req.once('async_iterator', () => { + req.send(Buffer.from(new Uint8Array([1, 2, 3, 4, 5]))); + }); + handler(req, res); - req.send(Buffer.from(new Uint8Array([1, 2, 3, 4, 5]))); let [out] = await done; let arr = Array.from(new Uint8Array(out.buffer)); expect(arr).to.deep.equal([5, 4, 3, 2, 1]); }); + + it('Can post large binary data', async () => { + const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); + + let { req, res, done } = createRequestAndResponse({ + method: 'POST', + url: '/hash', + }); + + handler(req, res); + + let expectedDigest = null; + req.once('async_iterator', () => { + // Send 256MB of garbage data in 256KB chunks. This should be fast (< 1sec). + let remainingBytes = 256 * 1024 * 1024; + const chunkSize = 256 * 1024; + + const hash = crypto.createHash('sha256'); + while (remainingBytes > 0) { + const size = Math.min(remainingBytes, chunkSize); + const chunk = Buffer.alloc(size, Math.floor(Math.random() * 256)); + hash.update(chunk); + req.emit('data', chunk); + remainingBytes -= size; + } + + req.emit('end'); + expectedDigest = hash.digest(); + }); + + let [out] = await done; + expect(new Uint8Array(out.buffer)).to.deep.equal(expectedDigest); + }); }); diff --git a/packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts b/packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts new file mode 100644 index 000000000..fbf44c547 --- /dev/null +++ b/packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts @@ -0,0 +1,16 @@ +import crypto from 'node:crypto'; + +export async function post({ request }: { request: Request }) { + const hash = crypto.createHash('sha256'); + + const iterable = request.body as unknown as AsyncIterable; + for await (const chunk of iterable) { + hash.update(chunk); + } + + return new Response(hash.digest(), { + headers: { + 'Content-Type': 'application/octet-stream' + } + }); +} -- cgit v1.2.3 From 85ee65e616336161b495b54d4217994b36b672f4 Mon Sep 17 00:00:00 2001 From: pilcrowOnPaper <80624252+pilcrowOnPaper@users.noreply.github.com> Date: Thu, 16 Nov 2023 08:39:41 +0900 Subject: Cancel response stream when connection closes (#9071) * cancel stream on close * add changeset * add test * Update .changeset/modern-ways-develop.md Co-authored-by: Sarah Rainsberger --------- Co-authored-by: lilnasy <69170106+lilnasy@users.noreply.github.com> Co-authored-by: Sarah Rainsberger --- packages/integrations/node/src/nodeMiddleware.ts | 12 +- .../integrations/node/src/response-iterator.ts | 228 --------------------- packages/integrations/node/test/api-route.test.js | 19 ++ .../node/test/fixtures/api-route/src/pages/hash.ts | 2 +- .../test/fixtures/api-route/src/pages/streaming.ts | 22 ++ 5 files changed, 50 insertions(+), 233 deletions(-) delete mode 100644 packages/integrations/node/src/response-iterator.ts create mode 100644 packages/integrations/node/test/fixtures/api-route/src/pages/streaming.ts (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/src/nodeMiddleware.ts b/packages/integrations/node/src/nodeMiddleware.ts index ddaa95deb..7f242809e 100644 --- a/packages/integrations/node/src/nodeMiddleware.ts +++ b/packages/integrations/node/src/nodeMiddleware.ts @@ -1,8 +1,6 @@ import type { NodeApp } from 'astro/app/node'; import type { ServerResponse } from 'node:http'; -import type { Readable } from 'stream'; import { createOutgoingHttpHeaders } from './createOutgoingHttpHeaders.js'; -import { responseIterator } from './response-iterator.js'; import type { ErrorHandlerParams, Options, RequestHandlerParams } from './types.js'; // Disable no-unused-vars to avoid breaking signature change @@ -79,8 +77,14 @@ async function writeWebResponse(app: NodeApp, res: ServerResponse, webResponse: res.writeHead(status, nodeHeaders); if (webResponse.body) { try { - for await (const chunk of responseIterator(webResponse) as unknown as Readable) { - res.write(chunk); + const reader = webResponse.body.getReader(); + res.on("close", () => { + reader.cancel(); + }) + let result = await reader.read(); + while (!result.done) { + res.write(result.value); + result = await reader.read(); } } catch (err: any) { console.error(err?.stack || err?.message || String(err)); diff --git a/packages/integrations/node/src/response-iterator.ts b/packages/integrations/node/src/response-iterator.ts deleted file mode 100644 index b79c3a853..000000000 --- a/packages/integrations/node/src/response-iterator.ts +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Original sources: - * - https://github.com/kmalakoff/response-iterator/blob/master/src/index.ts - * - https://github.com/apollographql/apollo-client/blob/main/src/utilities/common/responseIterator.ts - */ - -import { AstroError } from 'astro/errors'; -import type { ReadableStreamDefaultReadResult } from 'node:stream/web'; -import { Readable as NodeReadableStream } from 'stream'; - -interface NodeStreamIterator { - next(): Promise>; - [Symbol.asyncIterator]?(): AsyncIterator; -} - -interface PromiseIterator { - next(): Promise>; - [Symbol.asyncIterator]?(): AsyncIterator; -} - -interface ReaderIterator { - next(): Promise>; - [Symbol.asyncIterator]?(): AsyncIterator; -} - -const canUseSymbol = typeof Symbol === 'function' && typeof Symbol.for === 'function'; - -const canUseAsyncIteratorSymbol = canUseSymbol && Symbol.asyncIterator; - -function isBuffer(value: any): value is Buffer { - return ( - value?.constructor != null && - typeof value.constructor.isBuffer === 'function' && - value.constructor.isBuffer(value) - ); -} - -function isNodeResponse(value: any): value is Response { - return !!(value as Response).body; -} - -function isReadableStream(value: any): value is ReadableStream { - return !!(value as ReadableStream).getReader; -} - -function isAsyncIterableIterator(value: any): value is AsyncIterableIterator { - return !!( - canUseAsyncIteratorSymbol && (value as AsyncIterableIterator)[Symbol.asyncIterator] - ); -} - -function isStreamableBlob(value: any): value is Blob { - return !!(value as Blob).stream; -} - -function isBlob(value: any): value is Blob { - return !!(value as Blob).arrayBuffer; -} - -function isNodeReadableStream(value: any): value is NodeReadableStream { - return !!(value as NodeReadableStream).pipe; -} - -function readerIterator(reader: ReadableStreamDefaultReader): AsyncIterableIterator { - const iterator: ReaderIterator = { - //@ts-expect-error - next() { - return reader.read(); - }, - }; - - if (canUseAsyncIteratorSymbol) { - iterator[Symbol.asyncIterator] = function (): AsyncIterator { - //@ts-expect-error - return this; - }; - } - - return iterator as AsyncIterableIterator; -} - -function promiseIterator(promise: Promise): AsyncIterableIterator { - let resolved = false; - - const iterator: PromiseIterator = { - next(): Promise> { - if (resolved) - return Promise.resolve({ - value: undefined, - done: true, - }); - resolved = true; - return new Promise(function (resolve, reject) { - promise - .then(function (value) { - resolve({ value: value as unknown as T, done: false }); - }) - .catch(reject); - }); - }, - }; - - if (canUseAsyncIteratorSymbol) { - iterator[Symbol.asyncIterator] = function (): AsyncIterator { - return this; - }; - } - - return iterator as AsyncIterableIterator; -} - -function nodeStreamIterator(stream: NodeReadableStream): AsyncIterableIterator { - let cleanup: (() => void) | null = null; - let error: Error | null = null; - let done = false; - const data: unknown[] = []; - - const waiting: [ - ( - value: - | IteratorResult - | PromiseLike> - ) => void, - (reason?: any) => void, - ][] = []; - - function onData(chunk: any) { - if (error) return; - if (waiting.length) { - const shiftedArr = waiting.shift(); - if (Array.isArray(shiftedArr) && shiftedArr[0]) { - return shiftedArr[0]({ value: chunk, done: false }); - } - } - data.push(chunk); - } - function onError(err: Error) { - error = err; - const all = waiting.slice(); - all.forEach(function (pair) { - pair[1](err); - }); - !cleanup || cleanup(); - } - function onEnd() { - done = true; - const all = waiting.slice(); - all.forEach(function (pair) { - pair[0]({ value: undefined, done: true }); - }); - !cleanup || cleanup(); - } - - cleanup = function () { - cleanup = null; - stream.removeListener('data', onData); - stream.removeListener('error', onError); - stream.removeListener('end', onEnd); - stream.removeListener('finish', onEnd); - stream.removeListener('close', onEnd); - }; - stream.on('data', onData); - stream.on('error', onError); - stream.on('end', onEnd); - stream.on('finish', onEnd); - stream.on('close', onEnd); - - function getNext(): Promise> { - return new Promise(function (resolve, reject) { - if (error) return reject(error); - if (data.length) return resolve({ value: data.shift() as T, done: false }); - if (done) return resolve({ value: undefined, done: true }); - waiting.push([resolve, reject]); - }); - } - - const iterator: NodeStreamIterator = { - next(): Promise> { - return getNext(); - }, - }; - - if (canUseAsyncIteratorSymbol) { - iterator[Symbol.asyncIterator] = function (): AsyncIterator { - return this; - }; - } - - return iterator as AsyncIterableIterator; -} - -function asyncIterator(source: AsyncIterableIterator): AsyncIterableIterator { - const iterator = source[Symbol.asyncIterator](); - return { - next(): Promise> { - return iterator.next(); - }, - [Symbol.asyncIterator](): AsyncIterableIterator { - return this; - }, - }; -} - -export function responseIterator(response: Response | Buffer): AsyncIterableIterator { - let body: unknown = response; - - if (isNodeResponse(response)) body = response.body; - - if (isBuffer(body)) body = NodeReadableStream.from(body); - - if (isAsyncIterableIterator(body)) return asyncIterator(body); - - if (isReadableStream(body)) return readerIterator(body.getReader()); - - // this errors without casting to ReadableStream - // because Blob.stream() returns a NodeJS ReadableStream - if (isStreamableBlob(body)) { - return readerIterator((body.stream() as unknown as ReadableStream).getReader()); - } - - if (isBlob(body)) return promiseIterator(body.arrayBuffer()); - - if (isNodeReadableStream(body)) return nodeStreamIterator(body); - - throw new AstroError( - 'Unknown body type for responseIterator. Please pass a streamable response.' - ); -} diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index c830eee2d..7d9422ab4 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -89,4 +89,23 @@ describe('API routes', () => { let [out] = await done; expect(new Uint8Array(out.buffer)).to.deep.equal(expectedDigest); }); + + it('Can bail on streaming', async () => { + const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); + let { req, res, done } = createRequestAndResponse({ + url: '/streaming', + }); + + let locals = { cancelledByTheServer: false }; + + handler(req, res, () => {}, locals); + req.send(); + + await new Promise((resolve) => setTimeout(resolve, 500)); + res.emit("close"); + + await done; + + expect(locals).to.deep.include({ cancelledByTheServer: true }); + }); }); diff --git a/packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts b/packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts index fbf44c547..3f1b236de 100644 --- a/packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts +++ b/packages/integrations/node/test/fixtures/api-route/src/pages/hash.ts @@ -1,6 +1,6 @@ import crypto from 'node:crypto'; -export async function post({ request }: { request: Request }) { +export async function POST({ request }: { request: Request }) { const hash = crypto.createHash('sha256'); const iterable = request.body as unknown as AsyncIterable; diff --git a/packages/integrations/node/test/fixtures/api-route/src/pages/streaming.ts b/packages/integrations/node/test/fixtures/api-route/src/pages/streaming.ts new file mode 100644 index 000000000..9ecb884bf --- /dev/null +++ b/packages/integrations/node/test/fixtures/api-route/src/pages/streaming.ts @@ -0,0 +1,22 @@ +export const GET = ({ locals }) => { + let sentChunks = 0; + + const readableStream = new ReadableStream({ + async pull(controller) { + if (sentChunks === 3) return controller.close(); + else sentChunks++; + + await new Promise(resolve => setTimeout(resolve, 1000)); + controller.enqueue(new TextEncoder().encode('hello\n')); + }, + cancel() { + locals.cancelledByTheServer = true; + } + }); + + return new Response(readableStream, { + headers: { + "Content-Type": "text/event-stream" + } + }); +} -- cgit v1.2.3 From 35a398a7c32141a1255ddb8ce24a7571aa2d6800 Mon Sep 17 00:00:00 2001 From: pilcrowOnPaper Date: Wed, 15 Nov 2023 23:41:21 +0000 Subject: [ci] format --- packages/integrations/node/src/nodeMiddleware.ts | 4 ++-- packages/integrations/node/test/api-route.test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/src/nodeMiddleware.ts b/packages/integrations/node/src/nodeMiddleware.ts index 7f242809e..4fd0a4bc2 100644 --- a/packages/integrations/node/src/nodeMiddleware.ts +++ b/packages/integrations/node/src/nodeMiddleware.ts @@ -78,9 +78,9 @@ async function writeWebResponse(app: NodeApp, res: ServerResponse, webResponse: if (webResponse.body) { try { const reader = webResponse.body.getReader(); - res.on("close", () => { + res.on('close', () => { reader.cancel(); - }) + }); let result = await reader.read(); while (!result.done) { res.write(result.value); diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 7d9422ab4..313819188 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -102,7 +102,7 @@ describe('API routes', () => { req.send(); await new Promise((resolve) => setTimeout(resolve, 500)); - res.emit("close"); + res.emit('close'); await done; -- cgit v1.2.3 From 4f010d71a1416b5ff5b956cd84bf7afe86bf0530 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 25 Jan 2024 16:17:31 +0000 Subject: chore(@astrojs/node): use Node.js for testing (#9758) * chore(@astrojs/node): use Node.js for testing * revert file * address feedback * feedback * Run tests in a single process (#9823) * Run tests in a single process * Make test less flaky * chore: remove module --------- Co-authored-by: Bjorn Lu --- packages/integrations/node/package.json | 4 +- packages/integrations/node/test/api-route.test.js | 13 ++-- packages/integrations/node/test/assets.test.js | 7 +- packages/integrations/node/test/bad-urls.test.js | 11 ++- packages/integrations/node/test/encoded.test.js | 7 +- packages/integrations/node/test/errors.test.js | 29 ++++---- packages/integrations/node/test/headers.test.js | 5 +- packages/integrations/node/test/image.test.js | 7 +- packages/integrations/node/test/locals.test.js | 11 +-- .../integrations/node/test/node-middleware.test.js | 14 ++-- .../node/test/prerender-404-500.test.js | 83 +++++++++++---------- packages/integrations/node/test/prerender.test.js | 85 ++++++++++++---------- packages/integrations/node/test/test-utils.js | 8 ++ .../integrations/node/test/url-protocol.test.js | 11 +-- .../node/test/well-known-locations.test.js | 9 ++- 15 files changed, 166 insertions(+), 138 deletions(-) (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index d23d1f339..d1811ce6f 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -30,7 +30,7 @@ "build": "astro-scripts build \"src/**/*.ts\" && tsc", "build:ci": "astro-scripts build \"src/**/*.ts\"", "dev": "astro-scripts dev \"src/**/*.ts\"", - "test": "mocha --exit --timeout 20000 test/" + "test": "astro-scripts test \"test/**/*.test.js\"" }, "dependencies": { "send": "^0.18.0", @@ -45,10 +45,8 @@ "@types/server-destroy": "^1.0.3", "astro": "workspace:*", "astro-scripts": "workspace:*", - "chai": "^4.3.7", "cheerio": "1.0.0-rc.12", "express": "^4.18.2", - "mocha": "^10.2.0", "node-mocks-http": "^1.13.0" }, "publishConfig": { diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 313819188..80e99a2cc 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -1,7 +1,8 @@ import nodejs from '../dist/index.js'; import { loadFixture, createRequestAndResponse } from './test-utils.js'; -import { expect } from 'chai'; import crypto from 'node:crypto'; +import { describe, it, before } from 'node:test'; +import * as assert from 'node:assert/strict'; describe('API routes', () => { /** @type {import('./test-utils').Fixture} */ @@ -33,9 +34,9 @@ describe('API routes', () => { let json = JSON.parse(buffer.toString('utf-8')); - expect(json.length).to.equal(1); + assert.equal(json.length, 1); - expect(json[0].name).to.equal('Broccoli Soup'); + assert.equal(json[0].name, 'Broccoli Soup'); }); it('Can get binary data', async () => { @@ -54,7 +55,7 @@ describe('API routes', () => { let [out] = await done; let arr = Array.from(new Uint8Array(out.buffer)); - expect(arr).to.deep.equal([5, 4, 3, 2, 1]); + assert.deepEqual(arr, [5, 4, 3, 2, 1]); }); it('Can post large binary data', async () => { @@ -87,7 +88,7 @@ describe('API routes', () => { }); let [out] = await done; - expect(new Uint8Array(out.buffer)).to.deep.equal(expectedDigest); + assert.deepEqual(new Uint8Array(out.buffer), new Uint8Array(expectedDigest)); }); it('Can bail on streaming', async () => { @@ -106,6 +107,6 @@ describe('API routes', () => { await done; - expect(locals).to.deep.include({ cancelledByTheServer: true }); + assert.deepEqual(locals, { cancelledByTheServer: true }); }); }); diff --git a/packages/integrations/node/test/assets.test.js b/packages/integrations/node/test/assets.test.js index 9e44ab31d..bcd9bb4bd 100644 --- a/packages/integrations/node/test/assets.test.js +++ b/packages/integrations/node/test/assets.test.js @@ -1,4 +1,5 @@ -import { expect } from 'chai'; +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; import * as cheerio from 'cheerio'; @@ -30,7 +31,7 @@ describe('Assets', () => { it('Assets within the _astro folder should be given immutable headers', async () => { let response = await fixture.fetch('/text-file'); let cacheControl = response.headers.get('cache-control'); - expect(cacheControl).to.equal(null); + assert.equal(cacheControl, null); const html = await response.text(); const $ = cheerio.load(html); @@ -38,6 +39,6 @@ describe('Assets', () => { const fileURL = $('a').attr('href'); response = await fixture.fetch(fileURL); cacheControl = response.headers.get('cache-control'); - expect(cacheControl).to.equal('public, max-age=31536000, immutable'); + assert.equal(cacheControl, 'public, max-age=31536000, immutable'); }); }); diff --git a/packages/integrations/node/test/bad-urls.test.js b/packages/integrations/node/test/bad-urls.test.js index bfef81278..6d6c0a2e9 100644 --- a/packages/integrations/node/test/bad-urls.test.js +++ b/packages/integrations/node/test/bad-urls.test.js @@ -1,4 +1,5 @@ -import { expect } from 'chai'; +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; @@ -32,15 +33,17 @@ describe('Bad URLs', () => { '%20foobar%', ]; + const statusCodes = [400, 404, 500]; for (const weirdUrl of weirdURLs) { const fetchResult = await fixture.fetch(weirdUrl); - expect([400, 404, 500]).to.include( - fetchResult.status, + assert.equal( + statusCodes.includes(fetchResult.status), + true, `${weirdUrl} returned something else than 400, 404, or 500` ); } const stillWork = await fixture.fetch('/'); const text = await stillWork.text(); - expect(text).to.equal('Hello!'); + assert.equal(text, 'Hello!'); }); }); diff --git a/packages/integrations/node/test/encoded.test.js b/packages/integrations/node/test/encoded.test.js index bbd264777..2739fcfb7 100644 --- a/packages/integrations/node/test/encoded.test.js +++ b/packages/integrations/node/test/encoded.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture, createRequestAndResponse } from './test-utils.js'; -import { expect } from 'chai'; describe('Encoded Pathname', () => { /** @type {import('./test-utils').Fixture} */ @@ -25,7 +26,7 @@ describe('Encoded Pathname', () => { req.send(); const html = await text(); - expect(html).to.include('什么'); + assert.equal(html.includes('什么'), true); }); it('Can get a Markdown file', async () => { @@ -39,6 +40,6 @@ describe('Encoded Pathname', () => { req.send(); const html = await text(); - expect(html).to.include('什么'); + assert.equal(html.includes('什么'), true); }); }); diff --git a/packages/integrations/node/test/errors.test.js b/packages/integrations/node/test/errors.test.js index 6bb93023a..983187475 100644 --- a/packages/integrations/node/test/errors.test.js +++ b/packages/integrations/node/test/errors.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; -import { expect } from 'chai'; import * as cheerio from 'cheerio'; describe('Errors', () => { @@ -13,21 +14,19 @@ describe('Errors', () => { }); await fixture.build(); }); - describe('Within the stream', async () => { - let devPreview; + let devPreview; - before(async () => { - devPreview = await fixture.preview(); - }); - after(async () => { - await devPreview.stop(); - }); - it('when mode is standalone', async () => { - const res = await fixture.fetch('/in-stream'); - const html = await res.text(); - const $ = cheerio.load(html); + before(async () => { + devPreview = await fixture.preview(); + }); + after(async () => { + await devPreview.stop(); + }); + it('when mode is standalone', async () => { + const res = await fixture.fetch('/in-stream'); + const html = await res.text(); + const $ = cheerio.load(html); - expect($('p').text().trim()).to.equal('Internal server error'); - }); + assert.equal($('p').text().trim(), 'Internal server error'); }); }); diff --git a/packages/integrations/node/test/headers.test.js b/packages/integrations/node/test/headers.test.js index 17cfd3701..6a08dca22 100644 --- a/packages/integrations/node/test/headers.test.js +++ b/packages/integrations/node/test/headers.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture, createRequestAndResponse } from './test-utils.js'; -import { expect } from 'chai'; describe('Node Adapter Headers', () => { /** @type {import('./test-utils').Fixture} */ @@ -143,5 +144,5 @@ async function runTest(url, expectedHeaders) { await done; const headers = res.getHeaders(); - expect(headers).to.deep.equal(expectedHeaders); + assert.deepEqual(headers, expectedHeaders); } diff --git a/packages/integrations/node/test/image.test.js b/packages/integrations/node/test/image.test.js index 7bbdadc12..b315c1a30 100644 --- a/packages/integrations/node/test/image.test.js +++ b/packages/integrations/node/test/image.test.js @@ -1,4 +1,5 @@ -import { expect } from 'chai'; +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; @@ -24,12 +25,12 @@ describe.skip('Image endpoint', () => { it('it returns images', async () => { const res = await fixture.fetch('/'); - expect(res.status).to.equal(200); + assert.equal(res.status, 200); const resImage = await fixture.fetch( '/_image?href=/_astro/some_penguin.97ef5f92.png&w=50&f=webp' ); - expect(resImage.status).to.equal(200); + assert.equal(resImage.status, 200); }); }); diff --git a/packages/integrations/node/test/locals.test.js b/packages/integrations/node/test/locals.test.js index bba8b2d34..e2a531cce 100644 --- a/packages/integrations/node/test/locals.test.js +++ b/packages/integrations/node/test/locals.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture, createRequestAndResponse } from './test-utils.js'; -import { expect } from 'chai'; describe('API routes', () => { /** @type {import('./test-utils').Fixture} */ @@ -28,7 +29,7 @@ describe('API routes', () => { let html = await text(); - expect(html).to.contain('

bar

'); + assert.equal(html.includes('

bar

'), true); }); it('Throws an error when provided non-objects as locals', async () => { @@ -41,7 +42,7 @@ describe('API routes', () => { req.send(); await done; - expect(res).to.deep.include({ statusCode: 500 }); + assert.equal(res.statusCode, 500); }); it('Can use locals added by astro middleware', async () => { @@ -56,7 +57,7 @@ describe('API routes', () => { const html = await text(); - expect(html).to.contain('

baz

'); + assert.equal(html.includes('

baz

'), true); }); it('Can access locals in API', async () => { @@ -75,6 +76,6 @@ describe('API routes', () => { let json = JSON.parse(buffer.toString('utf-8')); - expect(json.foo).to.equal('bar'); + assert.equal(json.foo, 'bar'); }); }); diff --git a/packages/integrations/node/test/node-middleware.test.js b/packages/integrations/node/test/node-middleware.test.js index 6b6785953..889f72315 100644 --- a/packages/integrations/node/test/node-middleware.test.js +++ b/packages/integrations/node/test/node-middleware.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; -import { loadFixture } from './test-utils.js'; -import { expect } from 'chai'; +import { loadFixture, waitServerListen } from './test-utils.js'; import * as cheerio from 'cheerio'; import express from 'express'; @@ -31,6 +32,7 @@ describe('behavior from middleware, standalone', () => { const { startServer } = await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -43,13 +45,13 @@ describe('behavior from middleware, standalone', () => { it('when mode is standalone', async () => { const res = await fetch(`http://${server.host}:${server.port}/error-page`); - expect(res.status).to.equal(404); + assert.equal(res.status, 404); const html = await res.text(); const $ = cheerio.load(html); const body = $('body'); - expect(body.text()).to.equal('Page does not exist'); + assert.equal(body.text().includes('Page does not exist'), true); }); }); }); @@ -82,12 +84,12 @@ describe('behavior from middleware, middleware', () => { it('when mode is standalone', async () => { const res = await fetch(`http://localhost:8888/ssr`); - expect(res.status).to.equal(200); + assert.equal(res.status, 200); const html = await res.text(); const $ = cheerio.load(html); const body = $('body'); - expect(body.text()).to.contain("Here's a random number"); + assert.equal(body.text().includes("Here's a random number"), true); }); }); diff --git a/packages/integrations/node/test/prerender-404-500.test.js b/packages/integrations/node/test/prerender-404-500.test.js index 745a1958c..4195db0ec 100644 --- a/packages/integrations/node/test/prerender-404-500.test.js +++ b/packages/integrations/node/test/prerender-404-500.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; -import { loadFixture } from './test-utils.js'; -import { expect } from 'chai'; +import { loadFixture, waitServerListen } from './test-utils.js'; import * as cheerio from 'cheerio'; /** @@ -37,6 +38,7 @@ describe('Prerender 404', () => { const { startServer } = await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -50,8 +52,8 @@ describe('Prerender 404', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Hello world!'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Hello world!'); }); it('Can handle prerendered 404', async () => { @@ -60,20 +62,20 @@ describe('Prerender 404', () => { const res2 = await fetch(url); const res3 = await fetch(url); - expect(res1.status).to.equal(404); - expect(res2.status).to.equal(404); - expect(res3.status).to.equal(404); + assert.equal(res1.status, 404); + assert.equal(res2.status, 404); + assert.equal(res3.status, 404); const html1 = await res1.text(); const html2 = await res2.text(); const html3 = await res3.text(); - expect(html1).to.equal(html2); - expect(html2).to.equal(html3); + assert.equal(html1, html2); + assert.equal(html2, html3); const $ = cheerio.load(html1); - expect($('body').text()).to.equal('Page does not exist'); + assert.equal($('body').text(), 'Page does not exist'); }); it(' Can handle prerendered 500 called indirectly', async () => { @@ -82,16 +84,16 @@ describe('Prerender 404', () => { const response2 = await fetch(url); const response3 = await fetch(url); - expect(response1.status).to.equal(500); + assert.equal(response1.status, 500); const html1 = await response1.text(); const html2 = await response2.text(); const html3 = await response3.text(); - expect(html1).to.contain('Something went wrong'); + assert.equal(html1.includes('Something went wrong'), true); - expect(html1).to.equal(html2); - expect(html2).to.equal(html3); + assert.equal(html1, html2); + assert.equal(html2, html3); }); it('prerendered 500 page includes expected styles', async () => { @@ -100,7 +102,7 @@ describe('Prerender 404', () => { const $ = cheerio.load(html); // length will be 0 if the stylesheet does not get included - expect($('style')).to.have.a.lengthOf(1); + assert.equal($('style').length, 1); }); }); @@ -121,6 +123,7 @@ describe('Prerender 404', () => { const { startServer } = await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -134,8 +137,8 @@ describe('Prerender 404', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Hello world!'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Hello world!'); }); it('Can handle prerendered 404', async () => { @@ -144,20 +147,20 @@ describe('Prerender 404', () => { const res2 = await fetch(url); const res3 = await fetch(url); - expect(res1.status).to.equal(404); - expect(res2.status).to.equal(404); - expect(res3.status).to.equal(404); + assert.equal(res1.status, 404); + assert.equal(res2.status, 404); + assert.equal(res3.status, 404); const html1 = await res1.text(); const html2 = await res2.text(); const html3 = await res3.text(); - expect(html1).to.equal(html2); - expect(html2).to.equal(html3); + assert.equal(html1, html2); + assert.equal(html2, html3); const $ = cheerio.load(html1); - expect($('body').text()).to.equal('Page does not exist'); + assert.equal($('body').text(), 'Page does not exist'); }); }); }); @@ -184,6 +187,7 @@ describe('Hybrid 404', () => { const { startServer } = await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -197,8 +201,8 @@ describe('Hybrid 404', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Hello world!'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Hello world!'); }); it('Can handle prerendered 404', async () => { @@ -207,20 +211,20 @@ describe('Hybrid 404', () => { const res2 = await fetch(url); const res3 = await fetch(url); - expect(res1.status).to.equal(404); - expect(res2.status).to.equal(404); - expect(res3.status).to.equal(404); + assert.equal(res1.status, 404); + assert.equal(res2.status, 404); + assert.equal(res3.status, 404); const html1 = await res1.text(); const html2 = await res2.text(); const html3 = await res3.text(); - expect(html1).to.equal(html2); - expect(html2).to.equal(html3); + assert.equal(html1, html2); + assert.equal(html2, html3); const $ = cheerio.load(html1); - expect($('body').text()).to.equal('Page does not exist'); + assert.equal($('body').text(), 'Page does not exist'); }); }); @@ -240,6 +244,7 @@ describe('Hybrid 404', () => { const { startServer } = await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -253,8 +258,8 @@ describe('Hybrid 404', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Hello world!'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Hello world!'); }); it('Can handle prerendered 404', async () => { @@ -263,20 +268,20 @@ describe('Hybrid 404', () => { const res2 = await fetch(url); const res3 = await fetch(url); - expect(res1.status).to.equal(404); - expect(res2.status).to.equal(404); - expect(res3.status).to.equal(404); + assert.equal(res1.status, 404); + assert.equal(res2.status, 404); + assert.equal(res3.status, 404); const html1 = await res1.text(); const html2 = await res2.text(); const html3 = await res3.text(); - expect(html1).to.equal(html2); - expect(html2).to.equal(html3); + assert.equal(html1, html2); + assert.equal(html2, html3); const $ = cheerio.load(html1); - expect($('body').text()).to.equal('Page does not exist'); + assert.equal($('body').text(), 'Page does not exist'); }); }); }); diff --git a/packages/integrations/node/test/prerender.test.js b/packages/integrations/node/test/prerender.test.js index 86a7d3a65..ffa5563fa 100644 --- a/packages/integrations/node/test/prerender.test.js +++ b/packages/integrations/node/test/prerender.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; -import { loadFixture } from './test-utils.js'; -import { expect } from 'chai'; +import { loadFixture, waitServerListen } from './test-utils.js'; import * as cheerio from 'cheerio'; /** @@ -30,6 +31,7 @@ describe('Prerendering', () => { const { startServer } = await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -43,8 +45,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route', async () => { @@ -52,8 +54,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); it('Can render prerendered route with redirect and query params', async () => { @@ -61,8 +63,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); it('Can render prerendered route with query params', async () => { @@ -70,8 +72,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); it('Can render prerendered route without trailing slash', async () => { @@ -80,8 +82,9 @@ describe('Prerendering', () => { }); const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 301); + assert.equal(res.headers.get('location'), '/some-base/two/'); + assert.equal($('h1').text(), "Two") }); }); @@ -98,6 +101,7 @@ describe('Prerendering', () => { const { startServer } = await await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -111,8 +115,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route', async () => { @@ -120,8 +124,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); it('Can render prerendered route with redirect and query params', async () => { @@ -129,8 +133,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); it('Can render prerendered route with query params', async () => { @@ -138,8 +142,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); }); @@ -200,6 +204,7 @@ describe('Hybrid rendering', () => { const { startServer } = await await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -212,8 +217,8 @@ describe('Hybrid rendering', () => { const res = await fetch(`http://${server.host}:${server.port}/some-base/two`); const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); it('Can render prerendered route', async () => { @@ -221,8 +226,8 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route with redirect and query params', async () => { @@ -230,8 +235,8 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route with query params', async () => { @@ -239,18 +244,17 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route without trailing slash', async () => { const res = await fetch(`http://${server.host}:${server.port}/some-base/one`, { redirect: 'manual', }); - const html = await res.text(); - const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 301); + assert.equal(res.headers.get('location'), '/some-base/one/'); + assert.equal($('h1').text(), 'One'); }); }); @@ -266,6 +270,7 @@ describe('Hybrid rendering', () => { const { startServer } = await await load(); let res = startServer(); server = res.server; + await waitServerListen(server.server); }); after(async () => { @@ -279,8 +284,8 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); it('Can render prerendered route', async () => { @@ -288,8 +293,8 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route with redirect and query params', async () => { @@ -297,8 +302,8 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route with query params', async () => { @@ -306,8 +311,8 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); }); }); diff --git a/packages/integrations/node/test/test-utils.js b/packages/integrations/node/test/test-utils.js index 8d830fee9..3d582f30b 100644 --- a/packages/integrations/node/test/test-utils.js +++ b/packages/integrations/node/test/test-utils.js @@ -64,3 +64,11 @@ export function buffersToString(buffers) { } return str; } + +export function waitServerListen(server) { + return new Promise((resolve) => { + server.on('listening', () => { + resolve(); + }); + }); +} diff --git a/packages/integrations/node/test/url-protocol.test.js b/packages/integrations/node/test/url-protocol.test.js index a83cb2a41..444d47ed5 100644 --- a/packages/integrations/node/test/url-protocol.test.js +++ b/packages/integrations/node/test/url-protocol.test.js @@ -1,4 +1,5 @@ -import { expect } from 'chai'; +import * as assert from 'node:assert/strict'; +import { describe, it, before } from 'node:test'; import { TLSSocket } from 'node:tls'; import nodejs from '../dist/index.js'; import { createRequestAndResponse, loadFixture } from './test-utils.js'; @@ -26,7 +27,7 @@ describe('URL protocol', () => { req.send(); const html = await text(); - expect(html).to.include('http:'); + assert.equal(html.includes('http:'), true); }); it('return https when secure', async () => { @@ -40,7 +41,7 @@ describe('URL protocol', () => { req.send(); const html = await text(); - expect(html).to.include('https:'); + assert.equal(html.includes('https:'), true); }); it('return http when the X-Forwarded-Proto header is set to http', async () => { @@ -54,7 +55,7 @@ describe('URL protocol', () => { req.send(); const html = await text(); - expect(html).to.include('http:'); + assert.equal(html.includes('http:'), true); }); it('return https when the X-Forwarded-Proto header is set to https', async () => { @@ -68,6 +69,6 @@ describe('URL protocol', () => { req.send(); const html = await text(); - expect(html).to.include('https:'); + assert.equal(html.includes('https:'), true); }); }); diff --git a/packages/integrations/node/test/well-known-locations.test.js b/packages/integrations/node/test/well-known-locations.test.js index 31f31bacd..934673cda 100644 --- a/packages/integrations/node/test/well-known-locations.test.js +++ b/packages/integrations/node/test/well-known-locations.test.js @@ -1,6 +1,7 @@ +import * as assert from 'node:assert/strict'; +import { describe, it, before, after } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; -import { expect } from 'chai'; describe('test URIs beginning with a dot', () => { /** @type {import('./test-utils').Fixture} */ @@ -29,17 +30,17 @@ describe('test URIs beginning with a dot', () => { it('can load a valid well-known URI', async () => { const res = await fixture.fetch('/.well-known/apple-app-site-association'); - expect(res.status).to.equal(200); + assert.equal(res.status, 200); const json = await res.json(); - expect(json).to.deep.equal({ applinks: {} }); + assert.notStrictEqual(json.applinks, {}); }); it('cannot load a dot folder that is not a well-known URI', async () => { const res = await fixture.fetch('/.hidden/file.json'); - expect(res.status).to.equal(404); + assert.equal(res.status, 404); }); }); }); -- cgit v1.2.3 From 4d23caed76081742ddffe99841f5a1cb672f8d99 Mon Sep 17 00:00:00 2001 From: Arsh <69170106+lilnasy@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:24:21 +0000 Subject: fix(NodeApp): fix responses with null bodies never completing (#9931) * fix(NodeApp): fix responses with null bodies never completing * add changeset * add test * chore(tests): restore correct assertions * adjust incorrect test * added Astro.redirect and Response.redirect test cases * updated incorrect HTTP status * adjust api-routes.test.js after cherry-pick * bup markdoc test timeout --------- Co-authored-by: Emanuele Stoppa Co-authored-by: Friedemann Sommer --- packages/integrations/node/test/api-route.test.js | 43 +++++++++++++++++++++- packages/integrations/node/test/errors.test.js | 23 ++++++++---- .../api-route/src/pages/astro-redirect.astro | 3 ++ .../test/fixtures/api-route/src/pages/redirect.ts | 5 +++ .../api-route/src/pages/response-redirect.ts | 5 +++ packages/integrations/node/test/prerender.test.js | 20 +++++----- 6 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 packages/integrations/node/test/fixtures/api-route/src/pages/astro-redirect.astro create mode 100644 packages/integrations/node/test/fixtures/api-route/src/pages/redirect.ts create mode 100644 packages/integrations/node/test/fixtures/api-route/src/pages/response-redirect.ts (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 80e99a2cc..9e0165cb9 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -1,12 +1,16 @@ import nodejs from '../dist/index.js'; import { loadFixture, createRequestAndResponse } from './test-utils.js'; import crypto from 'node:crypto'; -import { describe, it, before } from 'node:test'; +import { describe, it, before, after } from 'node:test'; import * as assert from 'node:assert/strict'; describe('API routes', () => { /** @type {import('./test-utils').Fixture} */ let fixture; + /** @type {import('astro/src/@types/astro.js').PreviewServer} */ + let previewServer; + /** @type {URL} */ + let baseUri; before(async () => { fixture = await loadFixture({ @@ -15,8 +19,12 @@ describe('API routes', () => { adapter: nodejs({ mode: 'middleware' }), }); await fixture.build(); + previewServer = await fixture.preview(); + baseUri = new URL(`http://${previewServer.host ?? 'localhost'}:${previewServer.port}/`); }); + after(() => previewServer.stop()); + it('Can get the request body', async () => { const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); let { req, res, done } = createRequestAndResponse({ @@ -109,4 +117,37 @@ describe('API routes', () => { assert.deepEqual(locals, { cancelledByTheServer: true }); }); + + it('Can respond with SSR redirect', async () => { + const controller = new AbortController(); + setTimeout(() => controller.abort(), 1000); + const response = await fetch(new URL('/redirect', baseUri), { + redirect: 'manual', + signal: controller.signal, + }); + assert.equal(response.status, 302); + assert.equal(response.headers.get('location'), '/destination'); + }); + + it('Can respond with Astro.redirect', async () => { + const controller = new AbortController(); + setTimeout(() => controller.abort(), 1000); + const response = await fetch(new URL('/astro-redirect', baseUri), { + redirect: 'manual', + signal: controller.signal, + }); + assert.equal(response.status, 303); + assert.equal(response.headers.get('location'), '/destination'); + }); + + it('Can respond with Response.redirect', async () => { + const controller = new AbortController(); + setTimeout(() => controller.abort(), 1000); + const response = await fetch(new URL('/response-redirect', baseUri), { + redirect: 'manual', + signal: controller.signal, + }); + assert.equal(response.status, 307); + assert.equal(response.headers.get('location'), String(new URL('/destination', baseUri))); + }); }); diff --git a/packages/integrations/node/test/errors.test.js b/packages/integrations/node/test/errors.test.js index 76bff1326..8d54bcd4e 100644 --- a/packages/integrations/node/test/errors.test.js +++ b/packages/integrations/node/test/errors.test.js @@ -32,16 +32,25 @@ describe('Errors', () => { }); it('generator that throws called in template', async () => { + const result = ['

Astro

1', 'Internal server error']; + /** @type {Response} */ const res = await fixture.fetch('/generator'); const reader = res.body.getReader(); const decoder = new TextDecoder(); - const expect = async ({ done, value }) => { - const result = await reader.read(); - assert.equal(result.done, done); - if (!done) assert.equal(decoder.decode(result.value), value); - }; - await expect({ done: false, value: '

Astro

1Internal server error' }); - await expect({ done: true }); + const chunk1 = await reader.read(); + const chunk2 = await reader.read(); + const chunk3 = await reader.read(); + assert.equal(chunk1.done, false); + if (chunk2.done) { + assert.equal(decoder.decode(chunk1.value), result.join("")); + } + else if (chunk3.done) { + assert.equal(decoder.decode(chunk1.value), result[0]); + assert.equal(decoder.decode(chunk2.value), result[1]); + } + else { + throw new Error('The response should take at most 2 chunks.'); + } }); }); diff --git a/packages/integrations/node/test/fixtures/api-route/src/pages/astro-redirect.astro b/packages/integrations/node/test/fixtures/api-route/src/pages/astro-redirect.astro new file mode 100644 index 000000000..65a8765e8 --- /dev/null +++ b/packages/integrations/node/test/fixtures/api-route/src/pages/astro-redirect.astro @@ -0,0 +1,3 @@ +--- +return Astro.redirect('/destination', 303); +--- diff --git a/packages/integrations/node/test/fixtures/api-route/src/pages/redirect.ts b/packages/integrations/node/test/fixtures/api-route/src/pages/redirect.ts new file mode 100644 index 000000000..baf22c93e --- /dev/null +++ b/packages/integrations/node/test/fixtures/api-route/src/pages/redirect.ts @@ -0,0 +1,5 @@ +import { APIContext } from 'astro'; + +export async function GET({ redirect }: APIContext) { + return redirect('/destination'); +} diff --git a/packages/integrations/node/test/fixtures/api-route/src/pages/response-redirect.ts b/packages/integrations/node/test/fixtures/api-route/src/pages/response-redirect.ts new file mode 100644 index 000000000..1dfa8bb3c --- /dev/null +++ b/packages/integrations/node/test/fixtures/api-route/src/pages/response-redirect.ts @@ -0,0 +1,5 @@ +import { APIContext } from 'astro'; + +export async function GET({ url: requestUrl }: APIContext) { + return Response.redirect(new URL('/destination', requestUrl), 307); +} diff --git a/packages/integrations/node/test/prerender.test.js b/packages/integrations/node/test/prerender.test.js index 597794be0..7036c913c 100644 --- a/packages/integrations/node/test/prerender.test.js +++ b/packages/integrations/node/test/prerender.test.js @@ -82,8 +82,7 @@ describe('Prerendering', () => { }); const html = await res.text(); const $ = cheerio.load(html); - assert.equal(res.status, 301); - assert.equal(res.headers.get('location'), '/some-base/two/'); + assert.equal(res.status, 200); assert.equal($('h1').text(), 'Two'); }); }); @@ -171,8 +170,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('One'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'One'); }); it('Can render prerendered route', async () => { @@ -180,8 +179,8 @@ describe('Prerendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('Two'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'Two'); }); }); }); @@ -252,8 +251,9 @@ describe('Hybrid rendering', () => { const res = await fetch(`http://${server.host}:${server.port}/some-base/one`, { redirect: 'manual', }); - assert.equal(res.status, 301); - assert.equal(res.headers.get('location'), '/some-base/one/'); + const html = await res.text(); + const $ = cheerio.load(html); + assert.equal(res.status, 200); assert.equal($('h1').text(), 'One'); }); }); @@ -342,8 +342,8 @@ describe('Hybrid rendering', () => { const html = await res.text(); const $ = cheerio.load(html); - expect(res.status).to.equal(200); - expect($('h1').text()).to.equal('shared'); + assert.equal(res.status, 200); + assert.equal($('h1').text(), 'shared'); }); }); }); -- cgit v1.2.3 From 7106b81b28ebc782ee886d87dd83f4acb72d0850 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 21 Feb 2024 14:08:19 +0000 Subject: chore: use biome to sort imports - only test files (#10180) * chore: use biome to sort imports * do the sorting * Update package.json Co-authored-by: Bjorn Lu --------- Co-authored-by: Bjorn Lu --- packages/integrations/node/test/api-route.test.js | 8 ++++---- packages/integrations/node/test/assets.test.js | 4 ++-- packages/integrations/node/test/bad-urls.test.js | 2 +- packages/integrations/node/test/encoded.test.js | 4 ++-- packages/integrations/node/test/errors.test.js | 4 ++-- packages/integrations/node/test/headers.test.js | 4 ++-- packages/integrations/node/test/image.test.js | 2 +- packages/integrations/node/test/locals.test.js | 4 ++-- packages/integrations/node/test/node-middleware.test.js | 6 +++--- packages/integrations/node/test/prerender-404-500.test.js | 4 ++-- packages/integrations/node/test/prerender.test.js | 4 ++-- packages/integrations/node/test/url-protocol.test.js | 2 +- packages/integrations/node/test/well-known-locations.test.js | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 9e0165cb9..804a5ccf4 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -1,8 +1,8 @@ -import nodejs from '../dist/index.js'; -import { loadFixture, createRequestAndResponse } from './test-utils.js'; -import crypto from 'node:crypto'; -import { describe, it, before, after } from 'node:test'; import * as assert from 'node:assert/strict'; +import crypto from 'node:crypto'; +import { after, before, describe, it } from 'node:test'; +import nodejs from '../dist/index.js'; +import { createRequestAndResponse, loadFixture } from './test-utils.js'; describe('API routes', () => { /** @type {import('./test-utils').Fixture} */ diff --git a/packages/integrations/node/test/assets.test.js b/packages/integrations/node/test/assets.test.js index bcd9bb4bd..0b71f94cd 100644 --- a/packages/integrations/node/test/assets.test.js +++ b/packages/integrations/node/test/assets.test.js @@ -1,8 +1,8 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; +import { after, before, describe, it } from 'node:test'; +import * as cheerio from 'cheerio'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; -import * as cheerio from 'cheerio'; describe('Assets', () => { /** @type {import('./test-utils').Fixture} */ diff --git a/packages/integrations/node/test/bad-urls.test.js b/packages/integrations/node/test/bad-urls.test.js index 6d6c0a2e9..cdc0158ff 100644 --- a/packages/integrations/node/test/bad-urls.test.js +++ b/packages/integrations/node/test/bad-urls.test.js @@ -1,5 +1,5 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; +import { after, before, describe, it } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; diff --git a/packages/integrations/node/test/encoded.test.js b/packages/integrations/node/test/encoded.test.js index 15b5654b1..edc6ae78b 100644 --- a/packages/integrations/node/test/encoded.test.js +++ b/packages/integrations/node/test/encoded.test.js @@ -1,7 +1,7 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before } from 'node:test'; +import { before, describe, it } from 'node:test'; import nodejs from '../dist/index.js'; -import { loadFixture, createRequestAndResponse } from './test-utils.js'; +import { createRequestAndResponse, loadFixture } from './test-utils.js'; describe('Encoded Pathname', () => { /** @type {import('./test-utils').Fixture} */ diff --git a/packages/integrations/node/test/errors.test.js b/packages/integrations/node/test/errors.test.js index c4212f058..95bb1be85 100644 --- a/packages/integrations/node/test/errors.test.js +++ b/packages/integrations/node/test/errors.test.js @@ -1,8 +1,8 @@ import assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; +import { after, before, describe, it } from 'node:test'; +import * as cheerio from 'cheerio'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; -import * as cheerio from 'cheerio'; describe('Errors', () => { let fixture; diff --git a/packages/integrations/node/test/headers.test.js b/packages/integrations/node/test/headers.test.js index b74af9952..00b1766c7 100644 --- a/packages/integrations/node/test/headers.test.js +++ b/packages/integrations/node/test/headers.test.js @@ -1,7 +1,7 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before } from 'node:test'; +import { before, describe, it } from 'node:test'; import nodejs from '../dist/index.js'; -import { loadFixture, createRequestAndResponse } from './test-utils.js'; +import { createRequestAndResponse, loadFixture } from './test-utils.js'; describe('Node Adapter Headers', () => { /** @type {import('./test-utils').Fixture} */ diff --git a/packages/integrations/node/test/image.test.js b/packages/integrations/node/test/image.test.js index b315c1a30..c4758f96b 100644 --- a/packages/integrations/node/test/image.test.js +++ b/packages/integrations/node/test/image.test.js @@ -1,5 +1,5 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; +import { after, before, describe, it } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; diff --git a/packages/integrations/node/test/locals.test.js b/packages/integrations/node/test/locals.test.js index a310c729d..6d2776079 100644 --- a/packages/integrations/node/test/locals.test.js +++ b/packages/integrations/node/test/locals.test.js @@ -1,7 +1,7 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before } from 'node:test'; +import { before, describe, it } from 'node:test'; import nodejs from '../dist/index.js'; -import { loadFixture, createRequestAndResponse } from './test-utils.js'; +import { createRequestAndResponse, loadFixture } from './test-utils.js'; describe('API routes', () => { /** @type {import('./test-utils').Fixture} */ diff --git a/packages/integrations/node/test/node-middleware.test.js b/packages/integrations/node/test/node-middleware.test.js index 889f72315..fa496e871 100644 --- a/packages/integrations/node/test/node-middleware.test.js +++ b/packages/integrations/node/test/node-middleware.test.js @@ -1,9 +1,9 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; -import nodejs from '../dist/index.js'; -import { loadFixture, waitServerListen } from './test-utils.js'; +import { after, before, describe, it } from 'node:test'; import * as cheerio from 'cheerio'; import express from 'express'; +import nodejs from '../dist/index.js'; +import { loadFixture, waitServerListen } from './test-utils.js'; /** * @typedef {import('../../../astro/test/test-utils').Fixture} Fixture diff --git a/packages/integrations/node/test/prerender-404-500.test.js b/packages/integrations/node/test/prerender-404-500.test.js index 4195db0ec..afa23a151 100644 --- a/packages/integrations/node/test/prerender-404-500.test.js +++ b/packages/integrations/node/test/prerender-404-500.test.js @@ -1,8 +1,8 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; +import { after, before, describe, it } from 'node:test'; +import * as cheerio from 'cheerio'; import nodejs from '../dist/index.js'; import { loadFixture, waitServerListen } from './test-utils.js'; -import * as cheerio from 'cheerio'; /** * @typedef {import('../../../astro/test/test-utils').Fixture} Fixture diff --git a/packages/integrations/node/test/prerender.test.js b/packages/integrations/node/test/prerender.test.js index de570511e..bf34fc99d 100644 --- a/packages/integrations/node/test/prerender.test.js +++ b/packages/integrations/node/test/prerender.test.js @@ -1,8 +1,8 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; +import { after, before, describe, it } from 'node:test'; +import * as cheerio from 'cheerio'; import nodejs from '../dist/index.js'; import { loadFixture, waitServerListen } from './test-utils.js'; -import * as cheerio from 'cheerio'; /** * @typedef {import('../../../astro/test/test-utils').Fixture} Fixture diff --git a/packages/integrations/node/test/url-protocol.test.js b/packages/integrations/node/test/url-protocol.test.js index 444d47ed5..94d53104b 100644 --- a/packages/integrations/node/test/url-protocol.test.js +++ b/packages/integrations/node/test/url-protocol.test.js @@ -1,5 +1,5 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before } from 'node:test'; +import { before, describe, it } from 'node:test'; import { TLSSocket } from 'node:tls'; import nodejs from '../dist/index.js'; import { createRequestAndResponse, loadFixture } from './test-utils.js'; diff --git a/packages/integrations/node/test/well-known-locations.test.js b/packages/integrations/node/test/well-known-locations.test.js index 39d7ccedb..0951d6c27 100644 --- a/packages/integrations/node/test/well-known-locations.test.js +++ b/packages/integrations/node/test/well-known-locations.test.js @@ -1,5 +1,5 @@ import * as assert from 'node:assert/strict'; -import { describe, it, before, after } from 'node:test'; +import { after, before, describe, it } from 'node:test'; import nodejs from '../dist/index.js'; import { loadFixture } from './test-utils.js'; -- cgit v1.2.3 From 1b6f89c5b4b7a3e3c9cc0ca639e0a5dbfca01952 Mon Sep 17 00:00:00 2001 From: Alexander Niebuhr Date: Thu, 29 Aug 2024 08:17:42 +0200 Subject: chore: supress linting --- packages/integrations/node/CHANGELOG.md | 2 +- packages/integrations/node/package.json | 9 ++------- packages/integrations/node/src/index.ts | 2 +- packages/integrations/node/src/log-listening-on.ts | 17 +++++++++++------ packages/integrations/node/src/middleware.ts | 4 +++- packages/integrations/node/src/preview.ts | 10 ++++++---- packages/integrations/node/src/serve-app.ts | 2 +- packages/integrations/node/src/serve-static.ts | 12 +++++++++++- packages/integrations/node/src/standalone.ts | 3 ++- packages/integrations/node/test/api-route.test.js | 20 ++++++++++---------- packages/integrations/node/test/bad-urls.test.js | 2 +- packages/integrations/node/test/encoded.test.js | 4 ++-- packages/integrations/node/test/errors.test.js | 5 +++-- packages/integrations/node/test/headers.test.js | 2 +- packages/integrations/node/test/image.test.js | 2 +- packages/integrations/node/test/locals.test.js | 16 ++++++++-------- .../integrations/node/test/node-middleware.test.js | 5 ++++- .../node/test/prerender-404-500.test.js | 12 ++++++++---- packages/integrations/node/test/prerender.test.js | 21 +++++++++++++++------ packages/integrations/node/test/test-utils.js | 13 +++++++------ .../integrations/node/test/trailing-slash.test.js | 18 ++++++++++++------ packages/integrations/node/test/url.test.js | 12 ++++++------ packages/integrations/node/tsconfig.json | 4 +--- 23 files changed, 117 insertions(+), 80 deletions(-) (limited to 'packages/integrations/node/test/api-route.test.js') diff --git a/packages/integrations/node/CHANGELOG.md b/packages/integrations/node/CHANGELOG.md index 738cfc367..ed830f8de 100644 --- a/packages/integrations/node/CHANGELOG.md +++ b/packages/integrations/node/CHANGELOG.md @@ -734,7 +734,7 @@ { darkMode: true }, { expires: '1 month', - }, + } ); const prefs = Astro.cookies.get('prefs').json(); diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index f8042499b..2fc6c35b1 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -11,10 +11,7 @@ "url": "https://github.com/withastro/astro.git", "directory": "packages/integrations/node" }, - "keywords": [ - "withastro", - "astro-adapter" - ], + "keywords": ["withastro", "astro-adapter"], "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://docs.astro.build/en/guides/integrations-guide/node/", "exports": { @@ -23,9 +20,7 @@ "./preview.js": "./dist/preview.js", "./package.json": "./package.json" }, - "files": [ - "dist" - ], + "files": ["dist"], "scripts": { "build": "tsc", "test": "astro-scripts test \"test/**/*.test.js\"" diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index 36d9ee30f..eb3c98a9b 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -73,7 +73,7 @@ export default function createIntegration(userOptions: UserOptions): AstroIntegr if (config.output === 'static') { logger.warn( - `\`output: "server"\` or \`output: "hybrid"\` is required to use this adapter.`, + `\`output: "server"\` or \`output: "hybrid"\` is required to use this adapter.` ); } }, diff --git a/packages/integrations/node/src/log-listening-on.ts b/packages/integrations/node/src/log-listening-on.ts index 7e299740c..2f774c31c 100644 --- a/packages/integrations/node/src/log-listening-on.ts +++ b/packages/integrations/node/src/log-listening-on.ts @@ -8,20 +8,20 @@ import type { Options } from './types.js'; export async function logListeningOn( logger: AstroIntegrationLogger, server: http.Server | https.Server, - options: Pick, + options: Pick ) { await new Promise((resolve) => server.once('listening', resolve)); const protocol = server instanceof https.Server ? 'https' : 'http'; // Allow to provide host value at runtime const host = getResolvedHostForHttpServer( - process.env.HOST !== undefined && process.env.HOST !== '' ? process.env.HOST : options.host, + process.env.HOST !== undefined && process.env.HOST !== '' ? process.env.HOST : options.host ); const { port } = server.address() as AddressInfo; const address = getNetworkAddress(protocol, host, port); if (host === undefined) { logger.info( - `Server listening on \n local: ${address.local[0]} \t\n network: ${address.network[0]}\n`, + `Server listening on \n local: ${address.local[0]} \t\n network: ${address.network[0]}\n` ); } else { logger.info(`Server listening on ${address.local[0]}`); @@ -32,9 +32,11 @@ function getResolvedHostForHttpServer(host: string | boolean) { if (host === false) { // Use a secure default return 'localhost'; + // biome-ignore lint/style/noUselessElse: } else if (host === true) { // If passed --host in the CLI without arguments return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs) + // biome-ignore lint/style/noUselessElse: } else { return host; } @@ -49,29 +51,32 @@ const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0 // this code from vite https://github.com/vitejs/vite/blob/d09bbd093a4b893e78f0bbff5b17c7cf7821f403/packages/vite/src/node/utils.ts#L892-L914 export function getNetworkAddress( + // biome-ignore lint/style/useDefaultParameterLast: protocol: 'http' | 'https' = 'http', hostname: string | undefined, port: number, - base?: string, + base?: string ) { const NetworkAddress: NetworkAddressOpt = { local: [], network: [], }; + // biome-ignore lint/complexity/noForEach: Object.values(os.networkInterfaces()) .flatMap((nInterface) => nInterface ?? []) .filter( (detail) => + // biome-ignore lint/complexity/useOptionalChain: detail && detail.address && (detail.family === 'IPv4' || // @ts-expect-error Node 18.0 - 18.3 returns number - detail.family === 4), + detail.family === 4) ) .forEach((detail) => { let host = detail.address.replace( '127.0.0.1', - hostname === undefined || wildcardHosts.has(hostname) ? 'localhost' : hostname, + hostname === undefined || wildcardHosts.has(hostname) ? 'localhost' : hostname ); // ipv6 host if (host.includes(':')) { diff --git a/packages/integrations/node/src/middleware.ts b/packages/integrations/node/src/middleware.ts index 5cc4c4a46..5bb104914 100644 --- a/packages/integrations/node/src/middleware.ts +++ b/packages/integrations/node/src/middleware.ts @@ -15,7 +15,7 @@ export default function createMiddleware(app: NodeApp): RequestHandler { const logger = app.getAdapterLogger(); // using spread args because express trips up if the function's // stringified body includes req, res, next, locals directly - return async function (...args) { + return async (...args) => { // assume normal invocation at first const [req, res, next, locals] = args; // short circuit if it is an error invocation @@ -23,6 +23,7 @@ export default function createMiddleware(app: NodeApp): RequestHandler { const error = req; if (next) { return next(error); + // biome-ignore lint/style/noUselessElse: } else { throw error; } @@ -33,6 +34,7 @@ export default function createMiddleware(app: NodeApp): RequestHandler { logger.error(`Could not render ${req.url}`); console.error(err); if (!res.headersSent) { + // biome-ignore lint/style/noUnusedTemplateLiteral: res.writeHead(500, `Server error`); res.end(); } diff --git a/packages/integrations/node/src/preview.ts b/packages/integrations/node/src/preview.ts index 518155c4a..7e9415df8 100644 --- a/packages/integrations/node/src/preview.ts +++ b/packages/integrations/node/src/preview.ts @@ -8,7 +8,7 @@ import { createServer } from './standalone.js'; type ServerModule = ReturnType; type MaybeServerModule = Partial; -const createPreviewServer: CreatePreviewServer = async function (preview) { +const createPreviewServer: CreatePreviewServer = async (preview) => { let ssrHandler: ServerModule['handler']; let options: ServerModule['options']; try { @@ -16,19 +16,21 @@ const createPreviewServer: CreatePreviewServer = async function (preview) { const ssrModule: MaybeServerModule = await import(preview.serverEntrypoint.toString()); if (typeof ssrModule.handler === 'function') { ssrHandler = ssrModule.handler; + // biome-ignore lint/style/noNonNullAssertion: options = ssrModule.options!; } else { throw new AstroError( - `The server entrypoint doesn't have a handler. Are you sure this is the right file?`, + `The server entrypoint doesn't have a handler. Are you sure this is the right file?` ); } } catch (err) { if ((err as any).code === 'ERR_MODULE_NOT_FOUND') { throw new AstroError( `The server entrypoint ${fileURLToPath( - preview.serverEntrypoint, - )} does not exist. Have you ran a build yet?`, + preview.serverEntrypoint + )} does not exist. Have you ran a build yet?` ); + // biome-ignore lint/style/noUselessElse: } else { throw err; } diff --git a/packages/integrations/node/src/serve-app.ts b/packages/integrations/node/src/serve-app.ts index 72b4e0fd6..2934a01ab 100644 --- a/packages/integrations/node/src/serve-app.ts +++ b/packages/integrations/node/src/serve-app.ts @@ -39,7 +39,7 @@ export function createAppHandler(app: NodeApp): RequestHandler { addCookieHeader: true, locals, routeData, - }), + }) ); await NodeApp.writeResponse(response, res); } else if (next) { diff --git a/packages/integrations/node/src/serve-static.ts b/packages/integrations/node/src/serve-static.ts index 725f7afa6..9221594d7 100644 --- a/packages/integrations/node/src/serve-static.ts +++ b/packages/integrations/node/src/serve-static.ts @@ -29,23 +29,28 @@ export function createStaticHandler(app: NodeApp, options: Options) { let isDirectory = false; try { isDirectory = fs.lstatSync(filePath).isDirectory(); - } catch {} + } catch { } const { trailingSlash = 'ignore' } = options; const hasSlash = urlPath.endsWith('/'); switch (trailingSlash) { case 'never': + // biome-ignore lint/suspicious/noDoubleEquals: if (isDirectory && urlPath != '/' && hasSlash) { + // biome-ignore lint/style/useTemplate: + // biome-ignore lint/suspicious/noFallthroughSwitchClause: pathname = urlPath.slice(0, -1) + (urlQuery ? '?' + urlQuery : ''); res.statusCode = 301; res.setHeader('Location', pathname); return res.end(); + // biome-ignore lint/style/noUselessElse: } else pathname = urlPath; // intentionally fall through case 'ignore': { if (isDirectory && !hasSlash) { + // biome-ignore lint/style/useTemplate: pathname = urlPath + '/index.html'; } else pathname = urlPath; } @@ -53,10 +58,12 @@ export function createStaticHandler(app: NodeApp, options: Options) { case 'always': // trailing slash is not added to "subresources" if (!hasSlash && !isSubresourceRegex.test(urlPath)) { + // biome-ignore lint/style/useTemplate: pathname = urlPath + '/' + (urlQuery ? '?' + urlQuery : ''); res.statusCode = 301; res.setHeader('Location', pathname); return res.end(); + // biome-ignore lint/style/noUselessElse: } else pathname = urlPath; break; } @@ -110,6 +117,7 @@ function resolveClientDir(options: Options) { while (!serverEntryFolderURL.endsWith(serverFolder)) { serverEntryFolderURL = path.dirname(serverEntryFolderURL); } + // biome-ignore lint/style/useTemplate: const serverEntryURL = serverEntryFolderURL + '/entry.mjs'; const clientURL = new URL(appendForwardSlash(rel), serverEntryURL); const client = url.fileURLToPath(clientURL); @@ -117,9 +125,11 @@ function resolveClientDir(options: Options) { } function prependForwardSlash(pth: string) { + // biome-ignore lint/style/useTemplate: return pth.startsWith('/') ? pth : '/' + pth; } function appendForwardSlash(pth: string) { + // biome-ignore lint/style/useTemplate: return pth.endsWith('/') ? pth : pth + '/'; } diff --git a/packages/integrations/node/src/standalone.ts b/packages/integrations/node/src/standalone.ts index 76e672d2f..8ae10a9ba 100644 --- a/packages/integrations/node/src/standalone.ts +++ b/packages/integrations/node/src/standalone.ts @@ -39,6 +39,7 @@ export function createStandaloneHandler(app: NodeApp, options: Options) { return (req: http.IncomingMessage, res: http.ServerResponse) => { try { // validate request path + // biome-ignore lint/style/noNonNullAssertion: decodeURI(req.url!); } catch { res.writeHead(400); @@ -59,7 +60,7 @@ export function createServer(listener: http.RequestListener, host: string, port: key: fs.readFileSync(process.env.SERVER_KEY_PATH), cert: fs.readFileSync(process.env.SERVER_CERT_PATH), }, - listener, + listener ); } else { httpServer = http.createServer(listener); diff --git a/packages/integrations/node/test/api-route.test.js b/packages/integrations/node/test/api-route.test.js index 804a5ccf4..5eca5c530 100644 --- a/packages/integrations/node/test/api-route.test.js +++ b/packages/integrations/node/test/api-route.test.js @@ -27,7 +27,7 @@ describe('API routes', () => { it('Can get the request body', async () => { const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ + const { req, res, done } = createRequestAndResponse({ method: 'POST', url: '/recipes', }); @@ -38,9 +38,9 @@ describe('API routes', () => { handler(req, res); - let [buffer] = await done; + const [buffer] = await done; - let json = JSON.parse(buffer.toString('utf-8')); + const json = JSON.parse(buffer.toString('utf-8')); assert.equal(json.length, 1); @@ -50,7 +50,7 @@ describe('API routes', () => { it('Can get binary data', async () => { const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ + const { req, res, done } = createRequestAndResponse({ method: 'POST', url: '/binary', }); @@ -61,15 +61,15 @@ describe('API routes', () => { handler(req, res); - let [out] = await done; - let arr = Array.from(new Uint8Array(out.buffer)); + const [out] = await done; + const arr = Array.from(new Uint8Array(out.buffer)); assert.deepEqual(arr, [5, 4, 3, 2, 1]); }); it('Can post large binary data', async () => { const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ + const { req, res, done } = createRequestAndResponse({ method: 'POST', url: '/hash', }); @@ -95,17 +95,17 @@ describe('API routes', () => { expectedDigest = hash.digest(); }); - let [out] = await done; + const [out] = await done; assert.deepEqual(new Uint8Array(out.buffer), new Uint8Array(expectedDigest)); }); it('Can bail on streaming', async () => { const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ + const { req, res, done } = createRequestAndResponse({ url: '/streaming', }); - let locals = { cancelledByTheServer: false }; + const locals = { cancelledByTheServer: false }; handler(req, res, () => {}, locals); req.send(); diff --git a/packages/integrations/node/test/bad-urls.test.js b/packages/integrations/node/test/bad-urls.test.js index 9323516e8..cdc0158ff 100644 --- a/packages/integrations/node/test/bad-urls.test.js +++ b/packages/integrations/node/test/bad-urls.test.js @@ -39,7 +39,7 @@ describe('Bad URLs', () => { assert.equal( statusCodes.includes(fetchResult.status), true, - `${weirdUrl} returned something else than 400, 404, or 500`, + `${weirdUrl} returned something else than 400, 404, or 500` ); } const stillWork = await fixture.fetch('/'); diff --git a/packages/integrations/node/test/encoded.test.js b/packages/integrations/node/test/encoded.test.js index edc6ae78b..4fc97cf7f 100644 --- a/packages/integrations/node/test/encoded.test.js +++ b/packages/integrations/node/test/encoded.test.js @@ -18,7 +18,7 @@ describe('Encoded Pathname', () => { it('Can get an Astro file', async () => { const { handler } = await import('./fixtures/encoded/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ url: '/什么', }); @@ -32,7 +32,7 @@ describe('Encoded Pathname', () => { it('Can get a Markdown file', async () => { const { handler } = await import('./fixtures/encoded/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ url: '/blog/什么', }); diff --git a/packages/integrations/node/test/errors.test.js b/packages/integrations/node/test/errors.test.js index 802fa6e25..9bf4aa29b 100644 --- a/packages/integrations/node/test/errors.test.js +++ b/packages/integrations/node/test/errors.test.js @@ -20,6 +20,7 @@ describe('Errors', () => { }); let devPreview; + // biome-ignore lint/suspicious/noDuplicateTestHooks: before(async () => { // The two tests that need the server to run are skipped // devPreview = await fixture.preview(); @@ -58,7 +59,7 @@ describe('Errors', () => { const $ = cheerio.load(html); assert.equal($('p').text().trim(), 'Internal server error'); - }, + } ); it( @@ -86,6 +87,6 @@ describe('Errors', () => { } else { throw new Error('The response should take at most 2 chunks.'); } - }, + } ); }); diff --git a/packages/integrations/node/test/headers.test.js b/packages/integrations/node/test/headers.test.js index 00b1766c7..f2753517e 100644 --- a/packages/integrations/node/test/headers.test.js +++ b/packages/integrations/node/test/headers.test.js @@ -132,7 +132,7 @@ describe('Node Adapter Headers', () => { async function runTest(url, expectedHeaders) { const { handler } = await import('./fixtures/headers/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ + const { req, res, done } = createRequestAndResponse({ method: 'GET', url, }); diff --git a/packages/integrations/node/test/image.test.js b/packages/integrations/node/test/image.test.js index 5303fe37e..c4758f96b 100644 --- a/packages/integrations/node/test/image.test.js +++ b/packages/integrations/node/test/image.test.js @@ -28,7 +28,7 @@ describe.skip('Image endpoint', () => { assert.equal(res.status, 200); const resImage = await fixture.fetch( - '/_image?href=/_astro/some_penguin.97ef5f92.png&w=50&f=webp', + '/_image?href=/_astro/some_penguin.97ef5f92.png&w=50&f=webp' ); assert.equal(resImage.status, 200); diff --git a/packages/integrations/node/test/locals.test.js b/packages/integrations/node/test/locals.test.js index 6d2776079..b8e3ed40f 100644 --- a/packages/integrations/node/test/locals.test.js +++ b/packages/integrations/node/test/locals.test.js @@ -18,23 +18,23 @@ describe('API routes', () => { it('Can use locals added by node middleware', async () => { const { handler } = await import('./fixtures/locals/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ url: '/from-node-middleware', }); - let locals = { foo: 'bar' }; + const locals = { foo: 'bar' }; handler(req, res, () => {}, locals); req.send(); - let html = await text(); + const html = await text(); assert.equal(html.includes('

bar

'), true); }); it('Throws an error when provided non-objects as locals', async () => { const { handler } = await import('./fixtures/locals/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ + const { req, res, done } = createRequestAndResponse({ url: '/from-node-middleware', }); @@ -62,19 +62,19 @@ describe('API routes', () => { it('Can access locals in API', async () => { const { handler } = await import('./fixtures/locals/dist/server/entry.mjs'); - let { req, res, done } = createRequestAndResponse({ + const { req, res, done } = createRequestAndResponse({ method: 'POST', url: '/api', }); - let locals = { foo: 'bar' }; + const locals = { foo: 'bar' }; handler(req, res, () => {}, locals); req.send(); - let [buffer] = await done; + const [buffer] = await done; - let json = JSON.parse(buffer.toString('utf-8')); + const json = JSON.parse(buffer.toString('utf-8')); assert.equal(json.foo, 'bar'); }); diff --git a/packages/integrations/node/test/node-middleware.test.js b/packages/integrations/node/test/node-middleware.test.js index d1b016a51..eeb193c73 100644 --- a/packages/integrations/node/test/node-middleware.test.js +++ b/packages/integrations/node/test/node-middleware.test.js @@ -23,7 +23,7 @@ describe('behavior from middleware, standalone', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -31,6 +31,7 @@ describe('behavior from middleware, standalone', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -71,10 +72,12 @@ describe('behavior from middleware, middleware', () => { after(async () => { server.close(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); it('when mode is standalone', async () => { + // biome-ignore lint/style/noUnusedTemplateLiteral: const res = await fetch(`http://localhost:8888/ssr`); assert.equal(res.status, 200); diff --git a/packages/integrations/node/test/prerender-404-500.test.js b/packages/integrations/node/test/prerender-404-500.test.js index 2535fcb35..86226c500 100644 --- a/packages/integrations/node/test/prerender-404-500.test.js +++ b/packages/integrations/node/test/prerender-404-500.test.js @@ -34,7 +34,7 @@ describe('Prerender 404', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -42,6 +42,7 @@ describe('Prerender 404', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -124,7 +125,7 @@ describe('Prerender 404', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -132,6 +133,7 @@ describe('Prerender 404', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -193,7 +195,7 @@ describe('Hybrid 404', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -201,6 +203,7 @@ describe('Hybrid 404', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -255,7 +258,7 @@ describe('Hybrid 404', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -263,6 +266,7 @@ describe('Hybrid 404', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); diff --git a/packages/integrations/node/test/prerender.test.js b/packages/integrations/node/test/prerender.test.js index e699a1b3c..0684ff63a 100644 --- a/packages/integrations/node/test/prerender.test.js +++ b/packages/integrations/node/test/prerender.test.js @@ -30,7 +30,7 @@ describe('Prerendering', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -38,6 +38,7 @@ describe('Prerendering', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -105,7 +106,7 @@ describe('Prerendering', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -113,6 +114,7 @@ describe('Prerendering', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -181,7 +183,7 @@ describe('Prerendering', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -189,6 +191,7 @@ describe('Prerendering', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -233,10 +236,12 @@ describe('Prerendering', () => { after(async () => { await devServer.stop(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); it('Can render SSR route', async () => { + // biome-ignore lint/style/noUnusedTemplateLiteral: const res = await fixture.fetch(`/one`); const html = await res.text(); const $ = cheerio.load(html); @@ -246,6 +251,7 @@ describe('Prerendering', () => { }); it('Can render prerendered route', async () => { + // biome-ignore lint/style/noUnusedTemplateLiteral: const res = await fixture.fetch(`/two`); const html = await res.text(); const $ = cheerio.load(html); @@ -277,7 +283,7 @@ describe('Hybrid rendering', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -285,6 +291,7 @@ describe('Hybrid rendering', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -350,7 +357,7 @@ describe('Hybrid rendering', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -358,6 +365,7 @@ describe('Hybrid rendering', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -415,7 +423,7 @@ describe('Hybrid rendering', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -423,6 +431,7 @@ describe('Hybrid rendering', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); diff --git a/packages/integrations/node/test/test-utils.js b/packages/integrations/node/test/test-utils.js index 10faba5cf..4f273756c 100644 --- a/packages/integrations/node/test/test-utils.js +++ b/packages/integrations/node/test/test-utils.js @@ -20,18 +20,18 @@ export function loadFixture(inlineConfig) { } export function createRequestAndResponse(reqOptions) { - let req = httpMocks.createRequest(reqOptions); + const req = httpMocks.createRequest(reqOptions); - let res = httpMocks.createResponse({ + const res = httpMocks.createResponse({ eventEmitter: EventEmitter, req, }); - let done = toPromise(res); + const done = toPromise(res); // Get the response as text const text = async () => { - let chunks = await done; + const chunks = await done; return buffersToString(chunks); }; @@ -45,19 +45,20 @@ export function toPromise(res) { const write = res.write; res.write = function (data, encoding) { if (ArrayBuffer.isView(data) && !Buffer.isBuffer(data)) { + // biome-ignore lint/style/noParameterAssign: data = Buffer.from(data.buffer); } return write.call(this, data, encoding); }; res.on('end', () => { - let chunks = res._getChunks(); + const chunks = res._getChunks(); resolve(chunks); }); }); } export function buffersToString(buffers) { - let decoder = new TextDecoder(); + const decoder = new TextDecoder(); let str = ''; for (const buffer of buffers) { str += decoder.decode(buffer); diff --git a/packages/integrations/node/test/trailing-slash.test.js b/packages/integrations/node/test/trailing-slash.test.js index 9ea8fcddd..6f6a2a3ba 100644 --- a/packages/integrations/node/test/trailing-slash.test.js +++ b/packages/integrations/node/test/trailing-slash.test.js @@ -32,7 +32,7 @@ describe('Trailing slash', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -40,6 +40,7 @@ describe('Trailing slash', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -103,7 +104,7 @@ describe('Trailing slash', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -111,6 +112,7 @@ describe('Trailing slash', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -177,7 +179,7 @@ describe('Trailing slash', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -185,6 +187,7 @@ describe('Trailing slash', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -241,7 +244,7 @@ describe('Trailing slash', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -249,6 +252,7 @@ describe('Trailing slash', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -308,7 +312,7 @@ describe('Trailing slash', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -316,6 +320,7 @@ describe('Trailing slash', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); @@ -390,7 +395,7 @@ describe('Trailing slash', () => { }); await fixture.build(); const { startServer } = await fixture.loadAdapterEntryModule(); - let res = startServer(); + const res = startServer(); server = res.server; await waitServerListen(server.server); }); @@ -398,6 +403,7 @@ describe('Trailing slash', () => { after(async () => { await server.stop(); await fixture.clean(); + // biome-ignore lint/performance/noDelete: delete process.env.PRERENDER; }); diff --git a/packages/integrations/node/test/url.test.js b/packages/integrations/node/test/url.test.js index 77ca45836..81b357b71 100644 --- a/packages/integrations/node/test/url.test.js +++ b/packages/integrations/node/test/url.test.js @@ -20,7 +20,7 @@ describe('URL', () => { it('return http when non-secure', async () => { const { handler } = await import('./fixtures/url/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ url: '/', }); @@ -33,7 +33,7 @@ describe('URL', () => { it('return https when secure', async () => { const { handler } = await import('./fixtures/url/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ socket: new TLSSocket(), url: '/', }); @@ -47,7 +47,7 @@ describe('URL', () => { it('return http when the X-Forwarded-Proto header is set to http', async () => { const { handler } = await import('./fixtures/url/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ headers: { 'X-Forwarded-Proto': 'http' }, url: '/', }); @@ -61,7 +61,7 @@ describe('URL', () => { it('return https when the X-Forwarded-Proto header is set to https', async () => { const { handler } = await import('./fixtures/url/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ headers: { 'X-Forwarded-Proto': 'https' }, url: '/', }); @@ -75,7 +75,7 @@ describe('URL', () => { it('includes forwarded host and port in the url', async () => { const { handler } = await import('./fixtures/url/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ headers: { 'X-Forwarded-Proto': 'https', 'X-Forwarded-Host': 'abc.xyz', @@ -95,7 +95,7 @@ describe('URL', () => { it('accepts port in forwarded host and forwarded port', async () => { const { handler } = await import('./fixtures/url/dist/server/entry.mjs'); - let { req, res, text } = createRequestAndResponse({ + const { req, res, text } = createRequestAndResponse({ headers: { 'X-Forwarded-Proto': 'https', 'X-Forwarded-Host': 'abc.xyz:444', diff --git a/packages/integrations/node/tsconfig.json b/packages/integrations/node/tsconfig.json index b9feb9b9b..18443cddf 100644 --- a/packages/integrations/node/tsconfig.json +++ b/packages/integrations/node/tsconfig.json @@ -1,8 +1,6 @@ { "extends": "../../tsconfig.base.json", - "include": [ - "src" - ], + "include": ["src"], "compilerOptions": { "outDir": "./dist" } -- cgit v1.2.3