From ea189d91118e19a97de31d7b455d620c870862a2 Mon Sep 17 00:00:00 2001
From: Aviv Keller
Date: Sat, 4 Apr 2026 13:46:10 -0400
Subject: [PATCH 1/2] feat(doc-kit): bump version
---
components/Footer/footer.json | 62 -----
components/Footer/index.jsx | 6 +-
components/Navigation/index.jsx | 20 +-
components/Sidebar/index.jsx | 56 +----
doc-kit.config.mjs | 11 +-
package-lock.json | 8 +-
package.json | 2 +-
site.json | 386 ++++++++++++++++++++++++++++++++
template.html | 61 +++++
vercel.json | 13 +-
10 files changed, 478 insertions(+), 147 deletions(-)
delete mode 100644 components/Footer/footer.json
create mode 100644 site.json
create mode 100644 template.html
diff --git a/components/Footer/footer.json b/components/Footer/footer.json
deleted file mode 100644
index cc1cfa6..0000000
--- a/components/Footer/footer.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
- "socialLinks": [
- {
- "icon": "github",
- "link": "https://github.com/nodejs/node",
- "alt": "GitHub"
- },
- {
- "icon": "discord",
- "link": "https://discord.gg/nodejs",
- "alt": "Discord"
- },
- {
- "icon": "mastodon",
- "link": "https://social.lfx.dev/@nodejs",
- "alt": "Mastodon"
- },
- {
- "icon": "bluesky",
- "link": "https://bsky.app/profile/nodejs.org",
- "alt": "Bluesky"
- },
- {
- "icon": "twitter",
- "link": "https://twitter.com/nodejs",
- "alt": "Twitter"
- },
- {
- "icon": "slack",
- "link": "https://slack-invite.openjsf.org/",
- "alt": "Slack"
- },
- {
- "icon": "linkedin",
- "link": "https://www.linkedin.com/company/node-js",
- "alt": "LinkedIn"
- }
- ],
- "footerLinks": [
- { "link": "https://openjsf.org/", "text": "OpenJS Foundation" },
- { "link": "https://terms-of-use.openjsf.org/", "text": "Terms of Use" },
- { "link": "https://privacy-policy.openjsf.org/", "text": "Privacy Policy" },
- { "link": "https://bylaws.openjsf.org/", "text": "Bylaws" },
- {
- "link": "https://github.com/openjs-foundation/cross-project-council/blob/main/CODE_OF_CONDUCT.md",
- "text": "Code of Conduct"
- },
- {
- "link": "https://trademark-policy.openjsf.org/",
- "text": "Trademark Policy"
- },
- { "link": "https://trademark-list.openjsf.org/", "text": "Trademark List" },
- {
- "link": "https://www.linuxfoundation.org/cookies/",
- "text": "Cookie Policy"
- },
- {
- "link": "https://github.com/nodejs/node/security/policy",
- "text": "Security Policy"
- }
- ]
-}
diff --git a/components/Footer/index.jsx b/components/Footer/index.jsx
index f27d37a..e3f9fe9 100644
--- a/components/Footer/index.jsx
+++ b/components/Footer/index.jsx
@@ -1,7 +1,7 @@
import Footer from '@node-core/ui-components/Containers/Footer';
import NavItem from '@node-core/ui-components/Containers/NavBar/NavItem';
-import { socialLinks, footerLinks } from './footer.json';
+import { footer } from '../../site.json' with { type: 'json' };
// The Node.js Project is legally obligated to include the following text.
// It should not be modified unless explicitly requested by OpenJS staff.
@@ -24,7 +24,7 @@ const LegalSlot = (
- {footerLinks.map(({ link, text }) => (
+ {footer.links.map(({ link, text }) => (
{text}
@@ -39,7 +39,7 @@ const LegalSlot = (
export default ({ metadata }) => (
);
diff --git a/components/Navigation/index.jsx b/components/Navigation/index.jsx
index c2c9903..5a3f369 100644
--- a/components/Navigation/index.jsx
+++ b/components/Navigation/index.jsx
@@ -5,7 +5,7 @@ import GitHubIcon from '@node-core/ui-components/Icons/Social/GitHub';
import SearchBox from '@node-core/doc-kit/src/generators/web/ui/components/SearchBox';
import { useTheme } from '@node-core/doc-kit/src/generators/web/ui/hooks/useTheme.mjs';
-
+import { navigation } from '../../site.json' with { type: 'json' };
import Logo from '#theme/Logo';
/**
@@ -18,23 +18,7 @@ export default ({ metadata }) => {
} */
-const categories = [
- ['getting-started', 'Getting Started'],
- ['command-line', 'Command Line'],
- ['http', 'HTTP'],
- ['manipulating-files', 'Manipulating Files'],
- ['asynchronous-work', 'Asynchronous Work'],
- ['typescript', 'TypeScript'],
- ['modules', 'Modules'],
- ['diagnostics', 'Diagnostics'],
- ['test-runner', 'Test Runner'],
-];
-
-/** @type {Map>} */
-const byDir = new Map();
-for (const [heading, path] of pages) {
- const dir = path.split('/')[1];
- if (!byDir.has(dir)) byDir.set(dir, []);
- byDir.get(dir).push({ heading, path });
-}
+import { sidebar } from '../../site.json' with { type: 'json' };
/** @param {string} url */
const redirect = url => (window.location.href = url);
@@ -31,26 +9,12 @@ const PrefetchLink = props => ;
/**
* Sidebar component for MDX documentation with page navigation
*/
-export default ({ metadata }) => {
- const { path: currentPath, basename } = metadata;
- const pathname = `${basename}.html`;
-
- const groups = categories.map(([dir, title]) => ({
- groupName: title,
- items: byDir.get(dir).map(({ heading, path }) => ({
- label: heading,
- link:
- currentPath === path ? pathname : `${relative(path, currentPath)}.html`,
- })),
- }));
-
- return (
-
- );
-};
+export default ({ metadata }) => (
+
+);
diff --git a/doc-kit.config.mjs b/doc-kit.config.mjs
index 7eceff7..de0bb3d 100644
--- a/doc-kit.config.mjs
+++ b/doc-kit.config.mjs
@@ -8,9 +8,16 @@ export default {
input: ['pages/**/*.md'],
},
web: {
- title: '',
- pageURL: 'https://nodejs.org/learn{path}.html',
+ // Important Configuration
+ project: 'Node.js',
+ title: '{project} Learn',
+ baseURL: 'https://nodejs.org/learn',
+ pageURL: '{baseURL}{path}.html',
editURL: 'https://github.com/nodejs/learn/edit/main/pages{path}.md',
+ useAbsoluteURLs: true,
+ templatePath: join(import.meta.dirname, 'template.html'),
+
+ // Imports
imports: {
...web.defaultConfiguration.imports,
'#theme/Layout': join(import.meta.dirname, 'components/Layout/index.jsx'),
diff --git a/package-lock.json b/package-lock.json
index 1e503ea..437bbc3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
- "@node-core/doc-kit": "1.3.1",
+ "@node-core/doc-kit": "1.3.3",
"@node-core/remark-lint": "^1.3.0",
"eslint": "^10.1.0",
"eslint-plugin-mdx": "^3.7.0",
@@ -827,9 +827,9 @@
}
},
"node_modules/@node-core/doc-kit": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@node-core/doc-kit/-/doc-kit-1.3.1.tgz",
- "integrity": "sha512-GNgtXYSmA93Nuh3I9Dn30CsGqpomsfySp3gaqxiQ5Ih2AVH4js9GRxp+WdSJHjRY4dZ+UsQTLcv7ig7m6I+bGA==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@node-core/doc-kit/-/doc-kit-1.3.3.tgz",
+ "integrity": "sha512-atY8Opo1vdlJmT3d5u7511bjUZ1/hW4skMnwI/4Y33V4Wb/oPzAgFtpklSM9kf5nD4SCT0G7ddAvTUQISGpALA==",
"dev": true,
"dependencies": {
"@actions/core": "^3.0.0",
diff --git a/package.json b/package.json
index 1256ad4..e2ee3dc 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
- "@node-core/doc-kit": "1.3.1",
+ "@node-core/doc-kit": "1.3.3",
"@node-core/remark-lint": "^1.3.0",
"eslint": "^10.1.0",
"eslint-plugin-mdx": "^3.7.0",
diff --git a/site.json b/site.json
new file mode 100644
index 0000000..3283fea
--- /dev/null
+++ b/site.json
@@ -0,0 +1,386 @@
+{
+ "navigation": [
+ {
+ "link": "/learn",
+ "text": "Learn"
+ },
+ {
+ "link": "/about",
+ "text": "About"
+ },
+ {
+ "link": "/download",
+ "text": "Download"
+ },
+ {
+ "link": "/blog",
+ "text": "Blog"
+ },
+ {
+ "link": "/docs/latest/api/",
+ "text": "Docs"
+ },
+ {
+ "link": "https://github.com/nodejs/node/blob/main/CONTRIBUTING.md",
+ "text": "Contibute",
+ "target": "_blank"
+ },
+ {
+ "link": "https://training.linuxfoundation.org/openjs/",
+ "text": "Courses",
+ "target": "_blank"
+ }
+ ],
+ "footer": {
+ "links": [
+ {
+ "link": "https://openjsf.org/",
+ "text": "OpenJS Foundation"
+ },
+ {
+ "link": "https://terms-of-use.openjsf.org/",
+ "text": "Terms of Use"
+ },
+ {
+ "link": "https://privacy-policy.openjsf.org/",
+ "text": "Privacy Policy"
+ },
+ {
+ "link": "https://bylaws.openjsf.org/",
+ "text": "Bylaws"
+ },
+ {
+ "link": "https://github.com/openjs-foundation/cross-project-council/blob/main/CODE_OF_CONDUCT.md",
+ "text": "Code of Conduct"
+ },
+ {
+ "link": "https://trademark-policy.openjsf.org/",
+ "text": "Trademark Policy"
+ },
+ {
+ "link": "https://trademark-list.openjsf.org/",
+ "text": "Trademark List"
+ },
+ {
+ "link": "https://www.linuxfoundation.org/cookies/",
+ "text": "Cookie Policy"
+ },
+ {
+ "link": "https://github.com/nodejs/node/security/policy",
+ "text": "Security Policy"
+ }
+ ],
+ "social": [
+ {
+ "icon": "github",
+ "link": "https://github.com/nodejs/node",
+ "alt": "GitHub"
+ },
+ {
+ "icon": "discord",
+ "link": "https://discord.gg/nodejs",
+ "alt": "Discord"
+ },
+ {
+ "icon": "mastodon",
+ "link": "https://social.lfx.dev/@nodejs",
+ "alt": "Mastodon"
+ },
+ {
+ "icon": "bluesky",
+ "link": "https://bsky.app/profile/nodejs.org",
+ "alt": "Bluesky"
+ },
+ {
+ "icon": "twitter",
+ "link": "https://twitter.com/nodejs",
+ "alt": "Twitter"
+ },
+ {
+ "icon": "slack",
+ "link": "https://slack-invite.openjsf.org/",
+ "alt": "Slack"
+ },
+ {
+ "icon": "linkedin",
+ "link": "https://www.linkedin.com/company/node-js",
+ "alt": "LinkedIn"
+ }
+ ]
+ },
+ "sidebar": [
+ {
+ "groupName": "Getting Started",
+ "items": [
+ {
+ "link": "/learn/getting-started/introduction-to-nodejs",
+ "label": "Introduction to Node.js"
+ },
+ {
+ "link": "/learn/getting-started/how-much-javascript-do-you-need-to-know-to-use-nodejs",
+ "label": "How much JavaScript do you need to know to use Node.js?"
+ },
+ {
+ "link": "/learn/getting-started/differences-between-nodejs-and-the-browser",
+ "label": "Differences between Node.js and the Browser"
+ },
+ {
+ "link": "/learn/getting-started/the-v8-javascript-engine",
+ "label": "The V8 JavaScript Engine"
+ },
+ {
+ "link": "/learn/getting-started/an-introduction-to-the-npm-package-manager",
+ "label": "An introduction to the npm package manager"
+ },
+ {
+ "link": "/learn/getting-started/ecmascript-2015-es6-and-beyond",
+ "label": "ECMAScript 2015 (ES6) and beyond"
+ },
+ {
+ "link": "/learn/getting-started/debugging",
+ "label": "Debugging Node.js"
+ },
+ {
+ "link": "/learn/getting-started/fetch",
+ "label": "Fetching data with Node.js"
+ },
+ {
+ "link": "/learn/getting-started/websocket",
+ "label": "WebSocket client with Node.js"
+ },
+ {
+ "link": "/learn/getting-started/nodejs-the-difference-between-development-and-production",
+ "label": "Node.js, the difference between development and production"
+ },
+ {
+ "link": "/learn/getting-started/profiling",
+ "label": "Profiling Node.js Applications"
+ },
+ {
+ "link": "/learn/getting-started/nodejs-with-webassembly",
+ "label": "Node.js with WebAssembly"
+ },
+ {
+ "link": "/learn/getting-started/security-best-practices",
+ "label": "Security Best Practices"
+ },
+ {
+ "link": "/learn/getting-started/userland-migrations",
+ "label": "Introduction to Userland Migrations"
+ }
+ ]
+ },
+ {
+ "groupName": "Command Line",
+ "items": [
+ {
+ "link": "/learn/command-line/run-nodejs-scripts-from-the-command-line",
+ "label": "Run Node.js scripts from the command line"
+ },
+ {
+ "link": "/learn/command-line/how-to-use-the-nodejs-repl",
+ "label": "How to use the Node.js REPL"
+ },
+ {
+ "link": "/learn/command-line/output-to-the-command-line-using-nodejs",
+ "label": "Output to the command line using Node.js"
+ },
+ {
+ "link": "/learn/command-line/accept-input-from-the-command-line-in-nodejs",
+ "label": "Accept input from the command line in Node.js"
+ },
+ {
+ "link": "/learn/command-line/how-to-read-environment-variables-from-nodejs",
+ "label": "How to read environment variables from Node.js"
+ }
+ ]
+ },
+ {
+ "groupName": "HTTP",
+ "items": [
+ {
+ "link": "/learn/http/anatomy-of-an-http-transaction",
+ "label": "Anatomy of an HTTP Transaction"
+ },
+ {
+ "link": "/learn/http/enterprise-network-configuration",
+ "label": "Enterprise Network Configuration"
+ }
+ ]
+ },
+ {
+ "groupName": "Manipulating Files",
+ "items": [
+ {
+ "link": "/learn/manipulating-files/nodejs-file-stats",
+ "label": "Node.js file stats"
+ },
+ {
+ "link": "/learn/manipulating-files/nodejs-file-paths",
+ "label": "Node.js File Paths"
+ },
+ {
+ "link": "/learn/manipulating-files/reading-files-with-nodejs",
+ "label": "Reading files with Node.js"
+ },
+ {
+ "link": "/learn/manipulating-files/writing-files-with-nodejs",
+ "label": "Writing files with Node.js"
+ },
+ {
+ "link": "/learn/manipulating-files/working-with-file-descriptors-in-nodejs",
+ "label": "Working with file descriptors in Node.js"
+ },
+ {
+ "link": "/learn/manipulating-files/working-with-folders-in-nodejs",
+ "label": "Working with folders in Node.js"
+ },
+ {
+ "link": "/learn/manipulating-files/working-with-different-filesystems",
+ "label": "How to work with Different Filesystems"
+ }
+ ]
+ },
+ {
+ "groupName": "Asynchronous Work",
+ "items": [
+ {
+ "link": "/learn/asynchronous-work/javascript-asynchronous-programming-and-callbacks",
+ "label": "JavaScript Asynchronous Programming and Callbacks"
+ },
+ {
+ "link": "/learn/asynchronous-work/asynchronous-flow-control",
+ "label": "Asynchronous flow control"
+ },
+ {
+ "link": "/learn/asynchronous-work/discover-promises-in-nodejs",
+ "label": "Discover Promises in Node.js"
+ },
+ {
+ "link": "/learn/asynchronous-work/discover-javascript-timers",
+ "label": "Discover JavaScript Timers"
+ },
+ {
+ "link": "/learn/asynchronous-work/overview-of-blocking-vs-non-blocking",
+ "label": "Overview of Blocking vs Non-Blocking"
+ },
+ {
+ "link": "/learn/asynchronous-work/event-loop-timers-and-nexttick",
+ "label": "The Node.js Event Loop"
+ },
+ {
+ "link": "/learn/asynchronous-work/the-nodejs-event-emitter",
+ "label": "The Node.js Event Emitter"
+ },
+ {
+ "link": "/learn/asynchronous-work/understanding-processnexttick",
+ "label": "Understanding process.nextTick()"
+ },
+ {
+ "link": "/learn/asynchronous-work/understanding-setimmediate",
+ "label": "Understanding setImmediate()"
+ },
+ {
+ "link": "/learn/asynchronous-work/dont-block-the-event-loop",
+ "label": "Don't Block the Event Loop"
+ }
+ ]
+ },
+ {
+ "groupName": "TypeScript",
+ "items": [
+ {
+ "link": "/learn/typescript/introduction",
+ "label": "Introduction to TypeScript"
+ },
+ {
+ "link": "/learn/typescript/run-natively",
+ "label": "Running TypeScript Natively"
+ },
+ {
+ "link": "/learn/typescript/transpile",
+ "label": "Running TypeScript code using transpilation"
+ },
+ {
+ "link": "/learn/typescript/run",
+ "label": "Running TypeScript with a runner"
+ },
+ {
+ "link": "/learn/typescript/publishing-a-ts-package",
+ "label": "Publishing a TypeScript package"
+ }
+ ]
+ },
+ {
+ "groupName": "Modules",
+ "items": [
+ {
+ "link": "/learn/modules/how-to-use-streams",
+ "label": "How to use streams"
+ },
+ {
+ "link": "/learn/modules/backpressuring-in-streams",
+ "label": "Backpressuring in Streams"
+ },
+ {
+ "link": "/learn/modules/publishing-a-package",
+ "label": "Publishing a package"
+ },
+ {
+ "link": "/learn/modules/publishing-node-api-modules",
+ "label": "How to publish a Node-API package"
+ },
+ {
+ "link": "/learn/modules/abi-stability",
+ "label": "ABI Stability"
+ }
+ ]
+ },
+ {
+ "groupName": "Diagnostics",
+ "items": [
+ {
+ "link": "/learn/diagnostics/user-journey",
+ "label": "User Journey"
+ },
+ {
+ "link": "/learn/diagnostics/memory",
+ "label": "Memory"
+ },
+ {
+ "link": "/learn/diagnostics/live-debugging",
+ "label": "Live Debugging"
+ },
+ {
+ "link": "/learn/diagnostics/poor-performance",
+ "label": "Poor Performance"
+ },
+ {
+ "link": "/learn/diagnostics/flame-graphs",
+ "label": "Flame Graphs"
+ }
+ ]
+ },
+ {
+ "groupName": "Test Runner",
+ "items": [
+ {
+ "link": "/learn/test-runner/introduction",
+ "label": "Discovering Node.js's test runner"
+ },
+ {
+ "link": "/learn/test-runner/using-test-runner",
+ "label": "Using Node.js's test runner"
+ },
+ {
+ "link": "/learn/test-runner/mocking",
+ "label": "Mocking in tests"
+ },
+ {
+ "link": "/learn/test-runner/collecting-code-coverage",
+ "label": "Collecting code coverage in Node.js"
+ }
+ ]
+ }
+ ]
+}
diff --git a/template.html b/template.html
new file mode 100644
index 0000000..d3d7f6a
--- /dev/null
+++ b/template.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+ ${title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${dehydrated}
+
+
+
diff --git a/vercel.json b/vercel.json
index 588d5e0..491aecb 100644
--- a/vercel.json
+++ b/vercel.json
@@ -2,20 +2,11 @@
"$schema": "https://openapi.vercel.sh/vercel.json",
"outputDirectory": "out",
"cleanUrls": true,
+ "trailingSlash": false,
"redirects": [
{
"source": "/",
- "destination": "/learn/",
- "permanent": true
- },
- {
- "source": "/learn",
- "destination": "/learn/",
- "permanent": true
- },
- {
- "source": "/learn/(.*)/",
- "destination": "/learn/$1",
+ "destination": "/learn",
"permanent": true
}
],
From e01aca4e53737407a790fe680e0c621be125d440 Mon Sep 17 00:00:00 2001
From: Aviv Keller
Date: Mon, 6 Apr 2026 10:40:26 -0400
Subject: [PATCH 2/2] fixup!
---
doc-kit.config.mjs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/doc-kit.config.mjs b/doc-kit.config.mjs
index de0bb3d..06272c1 100644
--- a/doc-kit.config.mjs
+++ b/doc-kit.config.mjs
@@ -1,6 +1,9 @@
import web from '@node-core/doc-kit/src/generators/web/index.mjs';
import { join } from 'node:path';
+const origin =
+ process.env.VERCEL_ENV === 'preview' ? process.env.VERCEL_URL : 'nodejs.org';
+
/** @type {import('@node-core/doc-kit/src/utils/configuration/types.d.ts').Configuration} */
export default {
global: {
@@ -11,7 +14,7 @@ export default {
// Important Configuration
project: 'Node.js',
title: '{project} Learn',
- baseURL: 'https://nodejs.org/learn',
+ baseURL: `https://${origin}/learn`,
pageURL: '{baseURL}{path}.html',
editURL: 'https://github.com/nodejs/learn/edit/main/pages{path}.md',
useAbsoluteURLs: true,