diff --git a/apps/site/app/api/site.json/route.ts b/apps/site/app/api/site.json/route.ts new file mode 100644 index 0000000000000..ba2b31007e60e --- /dev/null +++ b/apps/site/app/api/site.json/route.ts @@ -0,0 +1,12 @@ +import { NextResponse } from 'next/server'; + +import { siteConfig } from '#site/next.json.mjs'; + +export const GET = () => + NextResponse.json(siteConfig, { + headers: { + 'Cache-Control': 'public, max-age=300, stale-while-revalidate=3600', + }, + }); + +export const dynamic = 'force-static'; diff --git a/apps/site/pages/en/learn/test-runner/mocking.md b/apps/site/pages/en/learn/test-runner/mocking.md index 1ce756866e546..4e67b826ff14e 100644 --- a/apps/site/pages/en/learn/test-runner/mocking.md +++ b/apps/site/pages/en/learn/test-runner/mocking.md @@ -149,30 +149,27 @@ This leverages [`mock`](https://nodejs.org/api/test.html#class-mocktracker) from ```mjs import assert from 'node:assert/strict'; -import { before, describe, it, mock } from 'node:test'; +import { describe, it, mock } from 'node:test'; -describe('foo', { concurrency: true }, () => { +describe('foo', { concurrency: true }, async () => { const barMock = mock.fn(); - let foo; - - before(async () => { - const barNamedExports = await import('./bar.mjs') - // discard the original default export - .then(({ default: _, ...rest }) => rest); - - // It's usually not necessary to manually call restore() after each - // nor reset() after all (node does this automatically). - mock.module('./bar.mjs', { - defaultExport: barMock, - // Keep the other exports that you don't want to mock. - namedExports: barNamedExports, - }); - // This MUST be a dynamic import because that is the only way to ensure the - // import starts after the mock has been set up. - ({ foo } = await import('./foo.mjs')); + const barNamedExports = await import('./bar.mjs') + // discard the original default export + .then(({ default: _, ...rest }) => rest); + + // It's usually not necessary to manually call restore() after each + // nor reset() after all (node does this automatically). + mock.module('./bar.mjs', { + defaultExport: barMock, + // Keep the other exports that you don't want to mock. + namedExports: barNamedExports, }); + // This MUST be a dynamic import because that is the only way to ensure the + // import starts after the mock has been set up. + const { foo } = await import('./foo.mjs'); + it('should do the thing', () => { barMock.mock.mockImplementationOnce(function bar_mock() { /* … */ @@ -258,12 +255,12 @@ Note the use of time-zone here (`Z` in the time-stamps). Neglecting to include a import assert from 'node:assert/strict'; import { describe, it, mock } from 'node:test'; -import ago from './ago.mjs'; +describe('whatever', { concurrency: true }, async () => { + mock.timers.enable({ now: new Date('2000-01-01T00:02:02Z') }); -describe('whatever', { concurrency: true }, () => { - it('should choose "minutes" when that\'s the closet unit', () => { - mock.timers.enable({ now: new Date('2000-01-01T00:02:02Z') }); + const { default: ago } = await import('./ago.mjs'); + it('should choose "minutes" when that\'s the closest unit', () => { const t = ago('1999-12-01T23:59:59Z'); assert.equal(t, '2 minutes ago'); @@ -271,4 +268,6 @@ describe('whatever', { concurrency: true }, () => { }); ``` +`ago` **must** be imported dynamically _after_ `mock.timers` is enabled. As with all module dependency mocking, this is necessary so that the `ago` module receives the mock before the `ago` module is executed (if the mocking does not occur before, it will be too late). + This is especially useful when comparing against a static fixture (that is checked into a repository), such as in [snapshot testing](https://nodejs.org/api/test.html#snapshot-testing). diff --git a/docs/README.md b/docs/README.md index b0ffe5b82a271..ba1098c6fa157 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,7 @@ New to contributing? Start here: ## Technical Documentation - **[Technologies](./technologies.md)** - Overview of the tech stack and architecture decisions +- **[Site Configuration](./site-config.md)** - How to update `site.json` (banners, badges, RSS feeds, metadata) - **[Downloads Page](./downloads-page.md)** - How to add installation methods and package managers - **[Package Publishing](./package-publishing.md)** - Guidelines for publishing packages in our monorepo - **[Cloudflare build and deployment](./cloudflare-build-and-deployment.md)** - Overview and useful information about the Cloudflare build and deployment diff --git a/docs/site-config.md b/docs/site-config.md new file mode 100644 index 0000000000000..689aac7887aff --- /dev/null +++ b/docs/site-config.md @@ -0,0 +1,127 @@ +# Site Configuration (`site.json`) + +`apps/site/site.json` is a manually maintained JSON file that controls global site metadata, RSS feeds, and time-sensitive UI elements (banners and badges). + +It is imported via `apps/site/next.json.mjs` and exposed as a read-only API endpoint at `/api/site.json`. + +This endpoint is also consumed externally by the [doc-kit](https://github.com/nodejs/doc-kit) to display dynamic banners inside the API docs, for example security announcements or EOL notices, without requiring a doc-kit release. + +## Structure + +### Top-level metadata + +| Field | Description | +| ------------- | ------------------------------------------- | +| `title` | Site title used in `