From 7ee0eacfde99f8fff74274599c3ee7bdf2d656ff Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:30:57 +0000 Subject: [PATCH 01/11] add tests for indirectly called prerendered 500 with imported styles --- .../ssr-prerender/src/external-stylesheet.css | 3 ++ .../ssr-prerender/src/nondeterminism.ts | 17 ++++++++++ .../ssr-prerender/src/pages/500.astro | 6 ++++ .../ssr-prerender/src/pages/fivehundred.astro | 4 +++ packages/astro/test/ssr-prerender.test.js | 33 +++++++++++++++++-- 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 packages/astro/test/fixtures/ssr-prerender/src/external-stylesheet.css create mode 100644 packages/astro/test/fixtures/ssr-prerender/src/nondeterminism.ts create mode 100644 packages/astro/test/fixtures/ssr-prerender/src/pages/500.astro create mode 100644 packages/astro/test/fixtures/ssr-prerender/src/pages/fivehundred.astro diff --git a/packages/astro/test/fixtures/ssr-prerender/src/external-stylesheet.css b/packages/astro/test/fixtures/ssr-prerender/src/external-stylesheet.css new file mode 100644 index 000000000000..5f331948a60d --- /dev/null +++ b/packages/astro/test/fixtures/ssr-prerender/src/external-stylesheet.css @@ -0,0 +1,3 @@ +body { + background-color: ivory; +} diff --git a/packages/astro/test/fixtures/ssr-prerender/src/nondeterminism.ts b/packages/astro/test/fixtures/ssr-prerender/src/nondeterminism.ts new file mode 100644 index 000000000000..8f8024a6083b --- /dev/null +++ b/packages/astro/test/fixtures/ssr-prerender/src/nondeterminism.ts @@ -0,0 +1,17 @@ +// This module is only used by the prerendered 500.astro. +// It exhibits different behavior if it's called more than once, +// which is detected by a test and interpreted as a failure. + +let usedOnce = false +let dynamicMessage = "Page was not prerendered" + +export default function () { + if (usedOnce === false) { + usedOnce = true + return "Something went wrong" + } + + dynamicMessage += "+" + + return dynamicMessage +} diff --git a/packages/astro/test/fixtures/ssr-prerender/src/pages/500.astro b/packages/astro/test/fixtures/ssr-prerender/src/pages/500.astro new file mode 100644 index 000000000000..9b184a3de67f --- /dev/null +++ b/packages/astro/test/fixtures/ssr-prerender/src/pages/500.astro @@ -0,0 +1,6 @@ +--- +import "../external-stylesheet.css" +import message from "../nondeterminism" +export const prerender = true +--- +{message()} \ No newline at end of file diff --git a/packages/astro/test/fixtures/ssr-prerender/src/pages/fivehundred.astro b/packages/astro/test/fixtures/ssr-prerender/src/pages/fivehundred.astro new file mode 100644 index 000000000000..99d103567db2 --- /dev/null +++ b/packages/astro/test/fixtures/ssr-prerender/src/pages/fivehundred.astro @@ -0,0 +1,4 @@ +--- +return new Response(null, { status: 500 }) +--- +

This html will not be served

diff --git a/packages/astro/test/ssr-prerender.test.js b/packages/astro/test/ssr-prerender.test.js index 90ec1b6faa54..4d015552c9d9 100644 --- a/packages/astro/test/ssr-prerender.test.js +++ b/packages/astro/test/ssr-prerender.test.js @@ -30,8 +30,37 @@ describe('SSR: prerender', () => { const app = await fixture.loadTestAdapterApp(); /** @type {Set} */ const assets = app.manifest.assets; - expect(assets.size).to.equal(1); - expect(Array.from(assets)[0].endsWith('static/index.html')).to.be.true; + expect(assets).to.contain('/static/index.html'); + }); + + it('prerenders a 500 route', async () => { + const app = await fixture.loadTestAdapterApp(); + const request = new Request('http://example.com/fivehundred'); + const response1 = await app.render(request); + const response2 = await app.render(request); + const response3 = await app.render(request); + + expect(response1.status).to.equal(500); + + const html1 = await response1.text(); + const html2 = await response2.text(); + const html3 = await response3.text(); + + expect(html1).to.contain("Something went wrong"); + + expect(html1).to.equal(html2); + expect(html2).to.equal(html3); + }); + + it('includes expected styles in a redirected prerendered 500 route', async () => { + const app = await fixture.loadTestAdapterApp(); + const request = new Request('http://example.com/fivehundred'); + const response = await app.render(request); + const html = await response.text(); + const $ = cheerio.load(html); + + // length will be 0 if the stylesheet does not get included + expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1); }); }); From 553be786a029c5b3818930a5804900e98ea9e549 Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:30:57 +0000 Subject: [PATCH 02/11] adjust tests for prerendered 404 routes --- .../prerender-404/src/nondeterminism.ts | 17 ++++ .../prerender-404/src/pages/404.astro | 4 +- .../node/test/prerender-404.test.js | 80 +++++++++++++++---- 3 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 packages/integrations/node/test/fixtures/prerender-404/src/nondeterminism.ts diff --git a/packages/integrations/node/test/fixtures/prerender-404/src/nondeterminism.ts b/packages/integrations/node/test/fixtures/prerender-404/src/nondeterminism.ts new file mode 100644 index 000000000000..1795c26b07af --- /dev/null +++ b/packages/integrations/node/test/fixtures/prerender-404/src/nondeterminism.ts @@ -0,0 +1,17 @@ +// This module is only used by the prerendered 404.astro. +// It exhibits different behavior if it's called more than once, +// which is detected by a test and interpreted as a failure. + +let usedOnce = false +let dynamicMessage = "Page was not prerendered" + +export default function () { + if (usedOnce === false) { + usedOnce = true + return "Page does not exist" + } + + dynamicMessage += "+" + + return dynamicMessage +} diff --git a/packages/integrations/node/test/fixtures/prerender-404/src/pages/404.astro b/packages/integrations/node/test/fixtures/prerender-404/src/pages/404.astro index 230402bbccdb..0f79f7f762a9 100644 --- a/packages/integrations/node/test/fixtures/prerender-404/src/pages/404.astro +++ b/packages/integrations/node/test/fixtures/prerender-404/src/pages/404.astro @@ -1,5 +1,5 @@ --- +import message from "../nondeterminism" export const prerender = true; --- - -Page does not exist +{message()} diff --git a/packages/integrations/node/test/prerender-404.test.js b/packages/integrations/node/test/prerender-404.test.js index 626f584e0c17..bdd39ab67bd6 100644 --- a/packages/integrations/node/test/prerender-404.test.js +++ b/packages/integrations/node/test/prerender-404.test.js @@ -52,11 +52,23 @@ describe('Prerender 404', () => { }); it('Can handle prerendered 404', async () => { - const res = await fetch(`http://${server.host}:${server.port}/some-base/missing`); - const html = await res.text(); - const $ = cheerio.load(html); + const res1 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + const res2 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + const res3 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + + expect(res1.status).to.equal(404); + expect(res2.status).to.equal(404); + expect(res3.status).to.equal(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); + + const $ = cheerio.load(html1); - expect(res.status).to.equal(404); expect($('body').text()).to.equal('Page does not exist'); }); }); @@ -93,11 +105,23 @@ describe('Prerender 404', () => { }); it('Can handle prerendered 404', async () => { - const res = await fetch(`http://${server.host}:${server.port}/missing`); - const html = await res.text(); - const $ = cheerio.load(html); + const res1 = await fetch(`http://${server.host}:${server.port}/missing`); + const res2 = await fetch(`http://${server.host}:${server.port}/missing`); + const res3 = await fetch(`http://${server.host}:${server.port}/missing`); + + expect(res1.status).to.equal(404); + expect(res2.status).to.equal(404); + expect(res3.status).to.equal(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); + + const $ = cheerio.load(html1); - expect(res.status).to.equal(404); expect($('body').text()).to.equal('Page does not exist'); }); }); @@ -140,11 +164,23 @@ describe('Hybrid 404', () => { }); it('Can handle prerendered 404', async () => { - const res = await fetch(`http://${server.host}:${server.port}/some-base/missing`); - const html = await res.text(); - const $ = cheerio.load(html); + const res1 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + const res2 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + const res3 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + + expect(res1.status).to.equal(404); + expect(res2.status).to.equal(404); + expect(res3.status).to.equal(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); + + const $ = cheerio.load(html1); - expect(res.status).to.equal(404); expect($('body').text()).to.equal('Page does not exist'); }); }); @@ -180,11 +216,23 @@ describe('Hybrid 404', () => { }); it('Can handle prerendered 404', async () => { - const res = await fetch(`http://${server.host}:${server.port}/missing`); - const html = await res.text(); - const $ = cheerio.load(html); + const res1 = await fetch(`http://${server.host}:${server.port}/missing`); + const res2 = await fetch(`http://${server.host}:${server.port}/missing`); + const res3 = await fetch(`http://${server.host}:${server.port}/missing`); + + expect(res1.status).to.equal(404); + expect(res2.status).to.equal(404); + expect(res3.status).to.equal(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); + + const $ = cheerio.load(html1); - expect(res.status).to.equal(404); expect($('body').text()).to.equal('Page does not exist'); }); }); From 33b1554ba91c6bcc635fe0112e05630b48a1078d Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:30:58 +0000 Subject: [PATCH 03/11] remove console.log from node image test --- packages/integrations/node/test/image.test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/integrations/node/test/image.test.js b/packages/integrations/node/test/image.test.js index 0834bc175fa4..f3d9ea71f8f4 100644 --- a/packages/integrations/node/test/image.test.js +++ b/packages/integrations/node/test/image.test.js @@ -32,9 +32,6 @@ describe('Image endpoint', () => { '/_image?href=/_astro/some_penguin.97ef5f92.png&w=50&f=webp' ); - console.log(resImage); - const content = resImage.text(); - console.log(content); expect(resImage.status).to.equal(200); }); }); From 25e1090a759cfd0641bad595d355cc1d3bc72bb9 Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:30:58 +0000 Subject: [PATCH 04/11] also query entrySpecifiers for prerendered components that need to turned into no-ops --- packages/astro/src/core/build/static-build.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 28f496d91660..306576159b25 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -308,8 +308,12 @@ async function runPostBuildHooks( async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInternals) { const allStaticFiles = new Set(); for (const pageData of eachPageData(internals)) { - if (pageData.route.prerender) - allStaticFiles.add(internals.pageToBundleMap.get(pageData.moduleSpecifier)); + if (pageData.route.prerender) { + const { moduleSpecifier } = pageData; + const pageBundleId = internals.pageToBundleMap.get(moduleSpecifier); + const entryBundleId = internals.entrySpecifierToBundleMap.get(moduleSpecifier); + allStaticFiles.add(pageBundleId ?? entryBundleId); + } } const ssr = isServerLikeOutput(opts.settings.config); const out = ssr From dd87bb3768df39b8650929e0af026bbe2f5fe7aa Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:30:59 +0000 Subject: [PATCH 05/11] handle default exports when converting prerendered components into no-ops --- packages/astro/src/core/build/static-build.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 306576159b25..706a302c4a45 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -341,7 +341,8 @@ async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInter // Replace exports (only prerendered pages) with a noop let value = 'const noop = () => {};'; for (const e of exports) { - value += `\nexport const ${e.n} = noop;`; + if (e.n === 'default') value += `\n export default noop;` + else value += `\nexport const ${e.n} = noop;`; } await fs.promises.writeFile(url, value, { encoding: 'utf8' }); }) From a9670c101033b0d45217b6cdcced0fc1f02e46b1 Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:23:46 +0000 Subject: [PATCH 06/11] fix tests --- .../test/fixtures/prerender-404/package.json | 1 + .../node/test/prerender-404.test.js | 23 ++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/integrations/node/test/fixtures/prerender-404/package.json b/packages/integrations/node/test/fixtures/prerender-404/package.json index dfd109c9142f..2a0fdc30c835 100644 --- a/packages/integrations/node/test/fixtures/prerender-404/package.json +++ b/packages/integrations/node/test/fixtures/prerender-404/package.json @@ -2,6 +2,7 @@ "name": "@test/nodejs-prerender-404", "version": "0.0.0", "private": true, + "type": "module", "dependencies": { "astro": "workspace:*", "@astrojs/node": "workspace:*" diff --git a/packages/integrations/node/test/prerender-404.test.js b/packages/integrations/node/test/prerender-404.test.js index bdd39ab67bd6..811bbdf764d3 100644 --- a/packages/integrations/node/test/prerender-404.test.js +++ b/packages/integrations/node/test/prerender-404.test.js @@ -14,6 +14,7 @@ async function load() { ); return mod; } + describe('Prerender 404', () => { /** @type {import('./test-utils').Fixture} */ let fixture; @@ -25,6 +26,10 @@ describe('Prerender 404', () => { process.env.PRERENDER = true; fixture = await loadFixture({ + // inconsequential config that differs between tests + // to bust cache and prevent modules and their state + // from being reused + site: 'https://test.dev/', base: '/some-base', root: './fixtures/prerender-404/', output: 'server', @@ -79,12 +84,16 @@ describe('Prerender 404', () => { process.env.PRERENDER = true; fixture = await loadFixture({ + // inconsequential config that differs between tests + // to bust cache and prevent modules and their state + // from being reused + site: 'https://test.info/', root: './fixtures/prerender-404/', output: 'server', adapter: nodejs({ mode: 'standalone' }), }); await fixture.build(); - const { startServer } = await await load(); + const { startServer } = await load(); let res = startServer(); server = res.server; }); @@ -137,13 +146,17 @@ describe('Hybrid 404', () => { process.env.ASTRO_NODE_AUTOSTART = 'disabled'; process.env.PRERENDER = false; fixture = await loadFixture({ + // inconsequential config that differs between tests + // to bust cache and prevent modules and their state + // from being reused + site: 'https://test.com/', base: '/some-base', root: './fixtures/prerender-404/', output: 'hybrid', adapter: nodejs({ mode: 'standalone' }), }); await fixture.build(); - const { startServer } = await await load(); + const { startServer } = await load(); let res = startServer(); server = res.server; }); @@ -190,12 +203,16 @@ describe('Hybrid 404', () => { process.env.ASTRO_NODE_AUTOSTART = 'disabled'; process.env.PRERENDER = false; fixture = await loadFixture({ + // inconsequential config that differs between tests + // to bust cache and prevent modules and their state + // from being reused + site: 'https://test.net/', root: './fixtures/prerender-404/', output: 'hybrid', adapter: nodejs({ mode: 'standalone' }), }); await fixture.build(); - const { startServer } = await await load(); + const { startServer } = await load(); let res = startServer(); server = res.server; }); From 26a5f2516973de443af357ad2172c56687923b03 Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Mon, 14 Aug 2023 18:01:50 +0000 Subject: [PATCH 07/11] move 500 test to node integration --- packages/astro/test/ssr-prerender.test.js | 30 --------- .../package.json | 2 +- .../src/external-stylesheet.css | 0 .../src/nondeterminism-404.ts} | 0 .../src/nondeterminism-500.ts} | 0 .../src/pages/404.astro | 2 +- .../prerender-404-500}/src/pages/500.astro | 2 +- .../src/pages/fivehundred.astro | 0 .../src/pages/static.astro | 0 ...-404.test.js => prerender-404-500.test.js} | 66 ++++++++++++++----- 10 files changed, 52 insertions(+), 50 deletions(-) rename packages/integrations/node/test/fixtures/{prerender-404 => prerender-404-500}/package.json (77%) rename packages/{astro/test/fixtures/ssr-prerender => integrations/node/test/fixtures/prerender-404-500}/src/external-stylesheet.css (100%) rename packages/integrations/node/test/fixtures/{prerender-404/src/nondeterminism.ts => prerender-404-500/src/nondeterminism-404.ts} (100%) rename packages/{astro/test/fixtures/ssr-prerender/src/nondeterminism.ts => integrations/node/test/fixtures/prerender-404-500/src/nondeterminism-500.ts} (100%) rename packages/integrations/node/test/fixtures/{prerender-404 => prerender-404-500}/src/pages/404.astro (53%) rename packages/{astro/test/fixtures/ssr-prerender => integrations/node/test/fixtures/prerender-404-500}/src/pages/500.astro (65%) rename packages/{astro/test/fixtures/ssr-prerender => integrations/node/test/fixtures/prerender-404-500}/src/pages/fivehundred.astro (100%) rename packages/integrations/node/test/fixtures/{prerender-404 => prerender-404-500}/src/pages/static.astro (100%) rename packages/integrations/node/test/{prerender-404.test.js => prerender-404-500.test.js} (77%) diff --git a/packages/astro/test/ssr-prerender.test.js b/packages/astro/test/ssr-prerender.test.js index 4d015552c9d9..567371f0bbaa 100644 --- a/packages/astro/test/ssr-prerender.test.js +++ b/packages/astro/test/ssr-prerender.test.js @@ -32,36 +32,6 @@ describe('SSR: prerender', () => { const assets = app.manifest.assets; expect(assets).to.contain('/static/index.html'); }); - - it('prerenders a 500 route', async () => { - const app = await fixture.loadTestAdapterApp(); - const request = new Request('http://example.com/fivehundred'); - const response1 = await app.render(request); - const response2 = await app.render(request); - const response3 = await app.render(request); - - expect(response1.status).to.equal(500); - - const html1 = await response1.text(); - const html2 = await response2.text(); - const html3 = await response3.text(); - - expect(html1).to.contain("Something went wrong"); - - expect(html1).to.equal(html2); - expect(html2).to.equal(html3); - }); - - it('includes expected styles in a redirected prerendered 500 route', async () => { - const app = await fixture.loadTestAdapterApp(); - const request = new Request('http://example.com/fivehundred'); - const response = await app.render(request); - const html = await response.text(); - const $ = cheerio.load(html); - - // length will be 0 if the stylesheet does not get included - expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1); - }); }); describe('Astro.params in SSR', () => { diff --git a/packages/integrations/node/test/fixtures/prerender-404/package.json b/packages/integrations/node/test/fixtures/prerender-404-500/package.json similarity index 77% rename from packages/integrations/node/test/fixtures/prerender-404/package.json rename to packages/integrations/node/test/fixtures/prerender-404-500/package.json index 2a0fdc30c835..f962fe991e3f 100644 --- a/packages/integrations/node/test/fixtures/prerender-404/package.json +++ b/packages/integrations/node/test/fixtures/prerender-404-500/package.json @@ -1,5 +1,5 @@ { - "name": "@test/nodejs-prerender-404", + "name": "@test/nodejs-prerender-404-500", "version": "0.0.0", "private": true, "type": "module", diff --git a/packages/astro/test/fixtures/ssr-prerender/src/external-stylesheet.css b/packages/integrations/node/test/fixtures/prerender-404-500/src/external-stylesheet.css similarity index 100% rename from packages/astro/test/fixtures/ssr-prerender/src/external-stylesheet.css rename to packages/integrations/node/test/fixtures/prerender-404-500/src/external-stylesheet.css diff --git a/packages/integrations/node/test/fixtures/prerender-404/src/nondeterminism.ts b/packages/integrations/node/test/fixtures/prerender-404-500/src/nondeterminism-404.ts similarity index 100% rename from packages/integrations/node/test/fixtures/prerender-404/src/nondeterminism.ts rename to packages/integrations/node/test/fixtures/prerender-404-500/src/nondeterminism-404.ts diff --git a/packages/astro/test/fixtures/ssr-prerender/src/nondeterminism.ts b/packages/integrations/node/test/fixtures/prerender-404-500/src/nondeterminism-500.ts similarity index 100% rename from packages/astro/test/fixtures/ssr-prerender/src/nondeterminism.ts rename to packages/integrations/node/test/fixtures/prerender-404-500/src/nondeterminism-500.ts diff --git a/packages/integrations/node/test/fixtures/prerender-404/src/pages/404.astro b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/404.astro similarity index 53% rename from packages/integrations/node/test/fixtures/prerender-404/src/pages/404.astro rename to packages/integrations/node/test/fixtures/prerender-404-500/src/pages/404.astro index 0f79f7f762a9..37fd1c1d39c4 100644 --- a/packages/integrations/node/test/fixtures/prerender-404/src/pages/404.astro +++ b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/404.astro @@ -1,5 +1,5 @@ --- -import message from "../nondeterminism" +import message from "../nondeterminism-404" export const prerender = true; --- {message()} diff --git a/packages/astro/test/fixtures/ssr-prerender/src/pages/500.astro b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/500.astro similarity index 65% rename from packages/astro/test/fixtures/ssr-prerender/src/pages/500.astro rename to packages/integrations/node/test/fixtures/prerender-404-500/src/pages/500.astro index 9b184a3de67f..3aa51803459f 100644 --- a/packages/astro/test/fixtures/ssr-prerender/src/pages/500.astro +++ b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/500.astro @@ -1,6 +1,6 @@ --- import "../external-stylesheet.css" -import message from "../nondeterminism" +import message from "../nondeterminism-500" export const prerender = true --- {message()} \ No newline at end of file diff --git a/packages/astro/test/fixtures/ssr-prerender/src/pages/fivehundred.astro b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/fivehundred.astro similarity index 100% rename from packages/astro/test/fixtures/ssr-prerender/src/pages/fivehundred.astro rename to packages/integrations/node/test/fixtures/prerender-404-500/src/pages/fivehundred.astro diff --git a/packages/integrations/node/test/fixtures/prerender-404/src/pages/static.astro b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/static.astro similarity index 100% rename from packages/integrations/node/test/fixtures/prerender-404/src/pages/static.astro rename to packages/integrations/node/test/fixtures/prerender-404-500/src/pages/static.astro diff --git a/packages/integrations/node/test/prerender-404.test.js b/packages/integrations/node/test/prerender-404-500.test.js similarity index 77% rename from packages/integrations/node/test/prerender-404.test.js rename to packages/integrations/node/test/prerender-404-500.test.js index 811bbdf764d3..ab3dd8c149c0 100644 --- a/packages/integrations/node/test/prerender-404.test.js +++ b/packages/integrations/node/test/prerender-404-500.test.js @@ -10,7 +10,7 @@ import { fetch } from 'undici'; async function load() { const mod = await import( - `./fixtures/prerender-404/dist/server/entry.mjs?dropcache=${Date.now()}` + `./fixtures/prerender-404-500/dist/server/entry.mjs?dropcache=${Date.now()}` ); return mod; } @@ -31,7 +31,7 @@ describe('Prerender 404', () => { // from being reused site: 'https://test.dev/', base: '/some-base', - root: './fixtures/prerender-404/', + root: './fixtures/prerender-404-500/', output: 'server', adapter: nodejs({ mode: 'standalone' }), }); @@ -57,9 +57,10 @@ describe('Prerender 404', () => { }); it('Can handle prerendered 404', async () => { - const res1 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); - const res2 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); - const res3 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + const url = `http://${server.host}:${server.port}/some-base/missing`; + const res1 = await fetch(url); + const res2 = await fetch(url); + const res3 = await fetch(url); expect(res1.status).to.equal(404); expect(res2.status).to.equal(404); @@ -76,6 +77,34 @@ describe('Prerender 404', () => { expect($('body').text()).to.equal('Page does not exist'); }); + + it('serves prerendered 500 indirectly', async () => { + const url = `http://${server.host}:${server.port}/some-base/fivehundred`; + const response1 = await fetch(url); + const response2 = await fetch(url); + const response3 = await fetch(url); + + expect(response1.status).to.equal(500); + + const html1 = await response1.text(); + const html2 = await response2.text(); + const html3 = await response3.text(); + + expect(html1).to.contain("Something went wrong"); + + expect(html1).to.equal(html2); + expect(html2).to.equal(html3); + }); + + it('prerendered 500 page includes expected styles', async () => { + const response = await fetch(`http://${server.host}:${server.port}/some-base/fivehundred`); + const html = await response.text(); + const $ = cheerio.load(html); + + // length will be 0 if the stylesheet does not get included + expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1); + }); + }); describe('Without base', async () => { @@ -88,7 +117,7 @@ describe('Prerender 404', () => { // to bust cache and prevent modules and their state // from being reused site: 'https://test.info/', - root: './fixtures/prerender-404/', + root: './fixtures/prerender-404-500/', output: 'server', adapter: nodejs({ mode: 'standalone' }), }); @@ -114,9 +143,10 @@ describe('Prerender 404', () => { }); it('Can handle prerendered 404', async () => { - const res1 = await fetch(`http://${server.host}:${server.port}/missing`); - const res2 = await fetch(`http://${server.host}:${server.port}/missing`); - const res3 = await fetch(`http://${server.host}:${server.port}/missing`); + const url = `http://${server.host}:${server.port}/some-base/missing` + const res1 = await fetch(url); + const res2 = await fetch(url); + const res3 = await fetch(url); expect(res1.status).to.equal(404); expect(res2.status).to.equal(404); @@ -151,7 +181,7 @@ describe('Hybrid 404', () => { // from being reused site: 'https://test.com/', base: '/some-base', - root: './fixtures/prerender-404/', + root: './fixtures/prerender-404-500/', output: 'hybrid', adapter: nodejs({ mode: 'standalone' }), }); @@ -177,9 +207,10 @@ describe('Hybrid 404', () => { }); it('Can handle prerendered 404', async () => { - const res1 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); - const res2 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); - const res3 = await fetch(`http://${server.host}:${server.port}/some-base/missing`); + const url = `http://${server.host}:${server.port}/some-base/missing` + const res1 = await fetch(url); + const res2 = await fetch(url); + const res3 = await fetch(url); expect(res1.status).to.equal(404); expect(res2.status).to.equal(404); @@ -207,7 +238,7 @@ describe('Hybrid 404', () => { // to bust cache and prevent modules and their state // from being reused site: 'https://test.net/', - root: './fixtures/prerender-404/', + root: './fixtures/prerender-404-500/', output: 'hybrid', adapter: nodejs({ mode: 'standalone' }), }); @@ -233,9 +264,10 @@ describe('Hybrid 404', () => { }); it('Can handle prerendered 404', async () => { - const res1 = await fetch(`http://${server.host}:${server.port}/missing`); - const res2 = await fetch(`http://${server.host}:${server.port}/missing`); - const res3 = await fetch(`http://${server.host}:${server.port}/missing`); + const url = `http://${server.host}:${server.port}/missing` + const res1 = await fetch(url); + const res2 = await fetch(url); + const res3 = await fetch(url); expect(res1.status).to.equal(404); expect(res2.status).to.equal(404); From 8325549f33b0ec8ec02ea8a2b5054374d6a51ed9 Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Tue, 15 Aug 2023 00:34:17 +0000 Subject: [PATCH 08/11] make request for static html when an error route is prerendered --- packages/astro/src/core/app/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index bec5368b6ce2..9afc010bd819 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -286,8 +286,9 @@ export class App { const errorRouteData = matchRoute('/' + status, this.#manifestData); const url = new URL(request.url); if (errorRouteData) { - if (errorRouteData.prerender && !errorRouteData.route.endsWith(`/${status}`)) { - const statusURL = new URL(`${this.#baseWithoutTrailingSlash}/${status}`, url); + if (errorRouteData.prerender){ + const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? '.html' : '' + const statusURL = new URL(`${this.#baseWithoutTrailingSlash}/${status}${maybeDotHtml}`, url); const response = await fetch(statusURL.toString()); return this.#mergeResponses(response, originalResponse); } From c11e75ab1c28af7abcbc4e99ddff59e473d63b5c Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Tue, 15 Aug 2023 00:59:09 +0000 Subject: [PATCH 09/11] update pnpm-lock.yaml --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48cd6243367e..6d956cca8a8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4677,7 +4677,7 @@ importers: specifier: workspace:* version: link:../../../../../astro - packages/integrations/node/test/fixtures/prerender-404: + packages/integrations/node/test/fixtures/prerender-404-500: dependencies: '@astrojs/node': specifier: workspace:* From 0e123ee20ed4dc3f34d8caf17b6bae5556b5c1b7 Mon Sep 17 00:00:00 2001 From: lilnasy <69170106+lilnasy@users.noreply.github.com> Date: Tue, 15 Aug 2023 18:28:43 +0000 Subject: [PATCH 10/11] fix status codes from static files --- packages/astro/src/core/app/index.ts | 35 +++++++++++++++---- .../prerender-404-500/src/pages/500.astro | 2 +- .../node/test/prerender-404-500.test.js | 2 +- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index 9afc010bd819..900efc682293 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -290,7 +290,12 @@ export class App { const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? '.html' : '' const statusURL = new URL(`${this.#baseWithoutTrailingSlash}/${status}${maybeDotHtml}`, url); const response = await fetch(statusURL.toString()); - return this.#mergeResponses(response, originalResponse); + + // response for /404.html and 500.html is 200, which is not meaningful + // so we create an override + const override = { status } + + return this.#mergeResponses(response, originalResponse, override); } const mod = await this.#getModuleForRoute(errorRouteData); try { @@ -317,14 +322,30 @@ export class App { return response; } - #mergeResponses(newResponse: Response, oldResponse?: Response) { - if (!oldResponse) return newResponse; - const { status, statusText, headers } = oldResponse; + #mergeResponses(newResponse: Response, oldResponse?: Response, override?: { status : 404 | 500 }) { + if (!oldResponse) { + if (override !== undefined) { + return new Response(newResponse.body, { + status: override.status, + statusText: newResponse.statusText, + headers: newResponse.headers + }) + } + return newResponse; + } + + const { statusText, headers } = oldResponse; + + // If the the new response did not have a meaningful status, an override may have been provided + // If the original status was 200 (default), override it with the new status (probably 404 or 500) + // Otherwise, the user set a specific status while rendering and we should respect that one + const status = + override?.status ? override.status : + oldResponse.status === 200 ? newResponse.status : + oldResponse.status return new Response(newResponse.body, { - // If the original status was 200 (default), override it with the new status (probably 404 or 500) - // Otherwise, the user set a specific status while rendering and we should respect that one - status: status === 200 ? newResponse.status : status, + status, statusText: status === 200 ? newResponse.statusText : statusText, headers: new Headers(Array.from(headers)), }); diff --git a/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/500.astro b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/500.astro index 3aa51803459f..ef91ad0ffa91 100644 --- a/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/500.astro +++ b/packages/integrations/node/test/fixtures/prerender-404-500/src/pages/500.astro @@ -3,4 +3,4 @@ import "../external-stylesheet.css" import message from "../nondeterminism-500" export const prerender = true --- -{message()} \ No newline at end of file +

{message()}

diff --git a/packages/integrations/node/test/prerender-404-500.test.js b/packages/integrations/node/test/prerender-404-500.test.js index ab3dd8c149c0..33cebab5ba4a 100644 --- a/packages/integrations/node/test/prerender-404-500.test.js +++ b/packages/integrations/node/test/prerender-404-500.test.js @@ -78,7 +78,7 @@ describe('Prerender 404', () => { expect($('body').text()).to.equal('Page does not exist'); }); - it('serves prerendered 500 indirectly', async () => { + it(' Can handle prerendered 500 called indirectly', async () => { const url = `http://${server.host}:${server.port}/some-base/fivehundred`; const response1 = await fetch(url); const response2 = await fetch(url); From ae2d417a163f15565c69f8ac508196314cd2b77a Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 15 Aug 2023 15:36:59 -0500 Subject: [PATCH 11/11] Create many-actors-flash.md --- .changeset/many-actors-flash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/many-actors-flash.md diff --git a/.changeset/many-actors-flash.md b/.changeset/many-actors-flash.md new file mode 100644 index 000000000000..92f70c55a024 --- /dev/null +++ b/.changeset/many-actors-flash.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Fix a handful of edge cases with prerendered 404/500 pages