diff --git a/.changeset/two-needles-buy.md b/.changeset/two-needles-buy.md new file mode 100644 index 000000000..ebf000cfc --- /dev/null +++ b/.changeset/two-needles-buy.md @@ -0,0 +1,6 @@ +--- +'astro': minor +'@astrojs/node': minor +--- + +Add support for serving well-known URIs with the @astrojs/node SSR adapter diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 13f40c8da..747ef360f 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -191,7 +191,7 @@ async function clientBuild( if (!input.size) { // If SSR, copy public over if (ssr) { - await copyFiles(settings.config.publicDir, out); + await copyFiles(settings.config.publicDir, out, true); } return null; @@ -318,9 +318,10 @@ async function cleanServerOutput(opts: StaticBuildOptions) { } } -async function copyFiles(fromFolder: URL, toFolder: URL) { +async function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles = false) { const files = await glob('**/*', { cwd: fileURLToPath(fromFolder), + dot: includeDotfiles, }); await Promise.all( diff --git a/packages/astro/test/fixtures/static-build-ssr/public/.well-known/apple-app-site-association b/packages/astro/test/fixtures/static-build-ssr/public/.well-known/apple-app-site-association new file mode 100644 index 000000000..daae260f1 --- /dev/null +++ b/packages/astro/test/fixtures/static-build-ssr/public/.well-known/apple-app-site-association @@ -0,0 +1,3 @@ +{ + "applinks": {} +} diff --git a/packages/astro/test/static-build.test.js b/packages/astro/test/static-build.test.js index 7e1c4c872..db35ef991 100644 --- a/packages/astro/test/static-build.test.js +++ b/packages/astro/test/static-build.test.js @@ -191,6 +191,8 @@ describe('Static build SSR', () => { root: './fixtures/static-build-ssr/', }); await fixture.build(); - const asset = await fixture.readFile('/client/nested/asset2.txt'); + + await fixture.readFile('/client/nested/asset2.txt'); + await fixture.readFile('/client/.well-known/apple-app-site-association'); }); }); diff --git a/packages/integrations/node/src/http-server.ts b/packages/integrations/node/src/http-server.ts index bdd628c29..19e33c84a 100644 --- a/packages/integrations/node/src/http-server.ts +++ b/packages/integrations/node/src/http-server.ts @@ -17,10 +17,11 @@ export function createServer( ) { const listener: http.RequestListener = (req, res) => { if (req.url) { - const pathname = '/' + removeBase(req.url); + let pathname = removeBase(req.url); + pathname = pathname[0] === '/' ? pathname : '/' + pathname; const stream = send(req, encodeURI(pathname), { root: fileURLToPath(client), - dotfiles: 'deny', + dotfiles: pathname.startsWith('/.well-known/') ? 'allow' : 'deny', }); let forwardError = false; diff --git a/packages/integrations/node/test/fixtures/well-known-locations/package.json b/packages/integrations/node/test/fixtures/well-known-locations/package.json new file mode 100644 index 000000000..f018b6ec7 --- /dev/null +++ b/packages/integrations/node/test/fixtures/well-known-locations/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/well-known-locations", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*", + "@astrojs/node": "workspace:*" + } +} diff --git a/packages/integrations/node/test/fixtures/well-known-locations/public/.hidden/file.json b/packages/integrations/node/test/fixtures/well-known-locations/public/.hidden/file.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/packages/integrations/node/test/fixtures/well-known-locations/public/.hidden/file.json @@ -0,0 +1 @@ +{} diff --git a/packages/integrations/node/test/fixtures/well-known-locations/public/.well-known/apple-app-site-association b/packages/integrations/node/test/fixtures/well-known-locations/public/.well-known/apple-app-site-association new file mode 100644 index 000000000..daae260f1 --- /dev/null +++ b/packages/integrations/node/test/fixtures/well-known-locations/public/.well-known/apple-app-site-association @@ -0,0 +1,3 @@ +{ + "applinks": {} +} diff --git a/packages/integrations/node/test/well-known-locations.test.js b/packages/integrations/node/test/well-known-locations.test.js new file mode 100644 index 000000000..31f31bacd --- /dev/null +++ b/packages/integrations/node/test/well-known-locations.test.js @@ -0,0 +1,45 @@ +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} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/well-known-locations/', + output: 'server', + adapter: nodejs({ mode: 'standalone' }), + }); + await fixture.build(); + }); + + describe('can load well-known URIs', async () => { + let devPreview; + + before(async () => { + devPreview = await fixture.preview(); + }); + + after(async () => { + await devPreview.stop(); + }); + + 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); + + const json = await res.json(); + + expect(json).to.deep.equal({ 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); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8d3b7ec9..c10f8c04b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3097,6 +3097,14 @@ importers: '@astrojs/node': link:../../.. astro: link:../../../../../astro + packages/integrations/node/test/fixtures/well-known-locations: + specifiers: + '@astrojs/node': workspace:* + astro: workspace:* + dependencies: + '@astrojs/node': link:../../.. + astro: link:../../../../../astro + packages/integrations/partytown: specifiers: '@builder.io/partytown': ^0.7.4