From eb648f3f763f06cb62ab6c076f309e30629b79b8 Mon Sep 17 00:00:00 2001 From: Tudor Date: Wed, 2 Sep 2026 16:08:58 +0100 Subject: [PATCH] build(next): convert the config to ESM so Payload can wrap it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit withPayload() is ESM-only, so next.config.js has to become .mjs. That file also carries the rule that keeps staging out of Google's index, so the conversion goes in on its own, behind a test that asserts the rule survived — along with the standalone output, the opengraph-image font tracing and the analytics frame-ancestors CSP. Jest resolves the .mjs config without extra configuration, so jest.config.js is untouched. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM --- nextjs-app/__tests__/app/nextConfig.test.ts | 49 +++++++++++++++++++ .../{next.config.js => next.config.mjs} | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 nextjs-app/__tests__/app/nextConfig.test.ts rename nextjs-app/{next.config.js => next.config.mjs} (99%) diff --git a/nextjs-app/__tests__/app/nextConfig.test.ts b/nextjs-app/__tests__/app/nextConfig.test.ts new file mode 100644 index 0000000..a378151 --- /dev/null +++ b/nextjs-app/__tests__/app/nextConfig.test.ts @@ -0,0 +1,49 @@ +/** + * next.config.mjs carries the staging noindex rule. Breaking it turns + * stx.schoolcompare.co.uk into a fully crawlable duplicate of production, + * and nothing else in the suite would notice. + * + * The non-null assertions are deliberate: every key asserted here is optional + * on NextConfig, and a missing one is precisely the regression under test, so + * the assertion below should fail the test rather than the compile. + */ +import nextConfig from '@/next.config.mjs'; + +async function headerRules() { + return nextConfig.headers!(); +} + +describe('next.config.mjs', () => { + it('keeps the staging host out of the index', async () => { + const headers = await headerRules(); + const stagingRule = headers.find((rule) => + rule.has?.some( + (cond) => cond.type === 'host' && cond.value === 'stx.schoolcompare.co.uk', + ), + ); + expect(stagingRule).toBeDefined(); + expect(stagingRule!.headers).toContainEqual({ + key: 'X-Robots-Tag', + value: 'noindex, nofollow', + }); + }); + + it('still emits standalone output for the Docker runner', () => { + expect(nextConfig.output).toBe('standalone'); + }); + + it('still traces the share-card fonts into the standalone bundle', () => { + expect(nextConfig.outputFileTracingIncludes!['/opengraph-image']).toEqual([ + './assets/**', + ]); + }); + + it('still allows the analytics subdomain to frame the site', async () => { + const headers = await headerRules(); + const csp = headers + .flatMap((rule) => rule.headers) + .find((header) => header.key === 'Content-Security-Policy'); + expect(csp).toBeDefined(); + expect(csp!.value).toContain('https://analytics.schoolcompare.co.uk'); + }); +}); diff --git a/nextjs-app/next.config.js b/nextjs-app/next.config.mjs similarity index 99% rename from nextjs-app/next.config.js rename to nextjs-app/next.config.mjs index c282b97..d35e4be 100644 --- a/nextjs-app/next.config.js +++ b/nextjs-app/next.config.mjs @@ -127,4 +127,4 @@ const nextConfig = { }, }; -module.exports = nextConfig; +export default nextConfig;