diff --git a/.changeset/spotty-dryers-hear.md b/.changeset/spotty-dryers-hear.md new file mode 100644 index 000000000000..bd1bf1443437 --- /dev/null +++ b/.changeset/spotty-dryers-hear.md @@ -0,0 +1,6 @@ +--- +"@astrojs/node": patch +"astro": patch +--- + +Fixes custom headers are not added to the Node standalone server responses in preview mode diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 51f7b274938d..3b1216de1e94 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -2748,6 +2748,7 @@ export interface PreviewServerParams { port: number; base: string; logger: AstroIntegrationLogger; + headers?: OutgoingHttpHeaders; } export type CreatePreviewServer = ( diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index d34faf3a2104..4ec755e2b526 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -75,6 +75,7 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise< port: settings.config.server.port, base: settings.config.base, logger: new AstroIntegrationLogger(logger.options, settings.adapter.name), + headers: settings.config.server.headers, }); return server; diff --git a/packages/integrations/node/src/preview.ts b/packages/integrations/node/src/preview.ts index e8747ad0d901..519b680821c0 100644 --- a/packages/integrations/node/src/preview.ts +++ b/packages/integrations/node/src/preview.ts @@ -36,6 +36,19 @@ const createPreviewServer: CreatePreviewServer = async function (preview) { const host = preview.host ?? 'localhost'; const port = preview.port ?? 4321; const server = createServer(ssrHandler, host, port); + + // If user specified custom headers append a listener + // to the server to add those headers to response + if (preview.headers) { + server.server.addListener('request', (_, res) => { + if (res.statusCode === 200) { + for (const [name, value] of Object.entries(preview.headers ?? {})) { + if (value) res.setHeader(name, value); + } + } + }); + } + logListeningOn(preview.logger, server.server, options); await new Promise((resolve, reject) => { server.server.once('listening', resolve); diff --git a/packages/integrations/node/test/fixtures/preview-headers/package.json b/packages/integrations/node/test/fixtures/preview-headers/package.json new file mode 100644 index 000000000000..ec2d5e3cfd18 --- /dev/null +++ b/packages/integrations/node/test/fixtures/preview-headers/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/nodejs-preview-headers", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*", + "@astrojs/node": "workspace:*" + } +} diff --git a/packages/integrations/node/test/fixtures/preview-headers/src/pages/index.astro b/packages/integrations/node/test/fixtures/preview-headers/src/pages/index.astro new file mode 100644 index 000000000000..10ddd6d257e0 --- /dev/null +++ b/packages/integrations/node/test/fixtures/preview-headers/src/pages/index.astro @@ -0,0 +1 @@ +Hello! diff --git a/packages/integrations/node/test/preview-headers.test.js b/packages/integrations/node/test/preview-headers.test.js new file mode 100644 index 000000000000..4c199f7e2bcc --- /dev/null +++ b/packages/integrations/node/test/preview-headers.test.js @@ -0,0 +1,38 @@ +import * as assert from 'node:assert/strict'; +import { after, before, describe, it } from 'node:test'; +import nodejs from '../dist/index.js'; +import { loadFixture } from './test-utils.js'; + +describe('Astro preview headers', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + let devPreview; + const headers = { + astro: 'test', + }; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/preview-headers/', + output: 'server', + adapter: nodejs({ mode: 'standalone' }), + server: { + headers + }, + }); + await fixture.build(); + devPreview = await fixture.preview(); + }); + + after(async () => { + await devPreview.stop(); + }); + + describe('Preview Headers', () => { + it('returns custom headers for valid URLs', async () => { + const result = await fixture.fetch('/'); + assert.equal(result.status, 200); + assert.equal(Object.fromEntries(result.headers).astro, headers.astro); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d36088bc2e69..f9e35dae2f7d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,21 @@ importers: specifier: workspace:* version: link:../../../scripts + examples/astro-missing-headers: + dependencies: + '@astrojs/check': + specifier: ^0.5.4 + version: 0.5.5(prettier-plugin-astro@0.12.3)(prettier@3.1.1)(typescript@5.3.3) + '@astrojs/node': + specifier: ^8.2.0 + version: link:../../packages/integrations/node + astro: + specifier: ^4.4.0 + version: link:../../packages/astro + typescript: + specifier: ^5.3.3 + version: 5.3.3 + examples/basics: dependencies: astro: @@ -4583,6 +4598,15 @@ importers: specifier: workspace:* version: link:../../../../../astro + packages/integrations/node/test/fixtures/preview-headers: + dependencies: + '@astrojs/node': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/node/test/fixtures/trailing-slash: dependencies: '@astrojs/node': @@ -5473,6 +5497,23 @@ packages: - prettier-plugin-astro dev: false + /@astrojs/check@0.5.5(prettier-plugin-astro@0.12.3)(prettier@3.1.1)(typescript@5.3.3): + resolution: {integrity: sha512-05LjyUB14Cv2mkLNqY4r2igI2eu0bq/HcKCfFNIoBPLyNW7VUDr9tciD9VJXXT3s0e6JHneIs6bQW5ipjmaRcw==} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + dependencies: + '@astrojs/language-server': 2.7.5(prettier-plugin-astro@0.12.3)(prettier@3.1.1)(typescript@5.3.3) + chokidar: 3.5.3 + fast-glob: 3.3.2 + kleur: 4.1.5 + typescript: 5.3.3 + yargs: 17.7.2 + transitivePeerDependencies: + - prettier + - prettier-plugin-astro + dev: false + /@astrojs/cli-kit@0.2.5: resolution: {integrity: sha512-j6zpNUjtHJGEIKkTrTPvQD3G/sJUKyseJty42iVR3HqytzqHwLK165vptdT4NZKfZ082yLnUtsOXxRyIdfm/AQ==} dependencies: @@ -5566,6 +5607,40 @@ packages: - typescript dev: false + /@astrojs/language-server@2.7.5(prettier-plugin-astro@0.12.3)(prettier@3.1.1)(typescript@5.3.3): + resolution: {integrity: sha512-iMfZ3UaqTgIL+z/eUDOppRa1bGUAteWRihbWq5mGAgvr/hu384ZXUKJcqV3BBux0MBsRXwjxzrC2dJu9IpAaoA==} + hasBin: true + peerDependencies: + prettier: ^3.0.0 + prettier-plugin-astro: '>=0.11.0' + peerDependenciesMeta: + prettier: + optional: true + prettier-plugin-astro: + optional: true + dependencies: + '@astrojs/compiler': 2.5.3 + '@jridgewell/sourcemap-codec': 1.4.15 + '@volar/kit': 2.0.4(typescript@5.3.3) + '@volar/language-core': 2.0.4 + '@volar/language-server': 2.0.4 + '@volar/language-service': 2.0.4 + '@volar/typescript': 2.0.4 + fast-glob: 3.3.2 + prettier: 3.1.1 + prettier-plugin-astro: 0.12.3 + volar-service-css: 0.0.30(@volar/language-service@2.0.4) + volar-service-emmet: 0.0.30(@volar/language-service@2.0.4) + volar-service-html: 0.0.30(@volar/language-service@2.0.4) + volar-service-prettier: 0.0.30(@volar/language-service@2.0.4)(prettier@3.1.1) + volar-service-typescript: 0.0.30(@volar/language-service@2.0.4)(@volar/typescript@2.0.4) + volar-service-typescript-twoslash-queries: 0.0.30(@volar/language-service@2.0.4) + vscode-html-languageservice: 5.1.2 + vscode-uri: 3.0.8 + transitivePeerDependencies: + - typescript + dev: false + /@babel/code-frame@7.23.5: resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} @@ -8523,6 +8598,19 @@ packages: vscode-uri: 3.0.8 dev: false + /@volar/kit@2.0.4(typescript@5.3.3): + resolution: {integrity: sha512-USRx/o0jKz7o8+lEKWMxWqbqvC46XFrf3IE6CZBYzRo9kM7RERQLwUYaoT2bOcHt5DQWublpnTgdgHMm37Gysg==} + peerDependencies: + typescript: '*' + dependencies: + '@volar/language-service': 2.0.4 + '@volar/typescript': 2.0.4 + typesafe-path: 0.2.2 + typescript: 5.3.3 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + dev: false + /@volar/language-core@1.10.10: resolution: {integrity: sha512-nsV1o3AZ5n5jaEAObrS3MWLBWaGwUj/vAsc15FVNIv+DbpizQRISg9wzygsHBr56ELRH8r4K75vkYNMtsSNNWw==} dependencies: @@ -16180,6 +16268,12 @@ packages: hasBin: true dev: false + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + dev: false + /ufo@1.4.0: resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==} dev: false