Skip to content

feat(scripts): set up#2

Open
avivkeller wants to merge 1 commit intomainfrom
scripts
Open

feat(scripts): set up#2
avivkeller wants to merge 1 commit intomainfrom
scripts

Conversation

@avivkeller
Copy link
Copy Markdown
Member

@avivkeller avivkeller commented Mar 25, 2026

Adds some logic to get this basically set up. cc @nodejs/web-admins for deployment previews

Copilot AI review requested due to automatic review settings March 25, 2026 17:15
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Sets up the repository’s initial build/tooling for generating a Doc Kit static site, including a prebuild step that generates a shared navigation/authors config consumed by custom theme components.

Changes:

  • Add a config-generation script (prebuild) that fetches nav/i18n/authors and derives sidebar + page author mappings from pages/.
  • Add Doc Kit configuration plus custom theme components (Navigation/Sidebar/Metabar) wired via #theme/* imports.
  • Add baseline repo tooling/docs: package.json scripts/deps, ESLint flat config, README/CONTRIBUTING.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
scripts/generate-config.mjs Generates components/config.json from remote JSON + local pages/ content.
scripts/constants.mjs Centralizes paths and remote source URLs used by the generator.
package.json Defines build/lint/format scripts and dependencies (including prebuild).
eslint.config.mjs Adds ESLint flat config with TypeScript + MDX support.
doc-kit.config.mjs Configures Doc Kit generators and maps #theme/* to local components.
components/Sidebar/index.jsx Sidebar component consuming generated sideNav.
components/Navigation/index.jsx Top navigation/search/theme toggle consuming generated topNav.
components/Metabar/index.jsx Metabar/TOC + “Authors” + “Edit this page” consuming generated authors.
README.md Documents repo purpose and local build workflow.
CONTRIBUTING.md Documents content structure and contribution workflow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@avivkeller avivkeller requested a review from a team March 25, 2026 20:59
headings={{ items: headings }}
items={{
'Reading Time': readingTime,
...(CLIENT && pageAuthors?.length
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can... we extract this to a const, to avoid doing this ternary right here?

import { join } from 'node:path';

// Inputs & Outputs
export const PAGES_DIR = join(import.meta.dirname, '..', 'pages');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we separate constants by a new line?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this generating the doc-kit config? Can then you rename as generate-doc-kit-config.mjs?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And can you move all the utils to like scripts/utils/[..] to make the entrypoint cleaner?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this generating the doc-kit config?

Not quite. It's generating a configuration to be used by the UI components, since we don't currently pass additional frontmatter to custom layouts. The doc-kit config is seperate, but I can make it clearer

Copy link
Copy Markdown
Member

@ovflowd ovflowd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM! Left tiny comments and the PR title should probably match that it adds the scripts and initial packages and components :)

Comment on lines +45 to +47
* @property {string} group - Top-level directory slug (first path segment).
* @property {string} pathname - URL pathname derived from the file path.
* @property {string} label - Page title extracted from the first H1.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For JSDoc, I think we should leave just a single space between parameter names and their descriptions. In some places they're aligned, in others they're not instead of adding extra spaces to force alignment, it's better to keep them as-is

* @property {string} group - Top-level directory slug (first path segment).
* @property {string} pathname - URL pathname derived from the file path.
* @property {string} label - Page title extracted from the first H1.
* @property {string[]} authorIds - GitHub usernames listed in the YAML front-matter.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using Array<string> instead of string[] makes the code more readable. It would be nice to keep the same standards we use in website here as well

* @returns {string[]} An array of trimmed, non-empty author IDs.
*/
const extractAuthorIds = content => {
const yaml = content.match(/<!--\s*YAML\s+([\s\S]*?)-->/)?.[1] ?? '';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

II think it would be better to define the regexes either in constants.mjs or at the top of the file and reference them from there. That way, we can manage changes from a single place and make the code easier to read

* @returns {string[]} Ordered, deduplicated group slugs.
*/
const extractGroupOrder = content => [
...new Set([...content.matchAll(/\]\(\/([\w-]+)\//g)].map(m => m[1])),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
...new Set([...content.matchAll(/\]\(\/([\w-]+)\//g)].map(m => m[1])),
...new Set([...content.matchAll(/\]\(\/([\w-]+)\//g)].map(match => match[1])),

const line = yaml.match(/^authors:\s*(.+)$/m)?.[1] ?? '';
return line
.split(',')
.map(s => s.trim())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(s => s.trim())
.map(authorId => authorId.trim())

Since we wouldn't be trimming anything other than a s/string anyway, we should use more meaningful variable names

* @param {string} file - Relative file path (e.g. `"guides/intro.md"`).
* @returns {string} URL pathname (e.g. `"/guides/intro"`).
*/
const toPathname = file => '/' + file.replace(sep, '/').replace(/\.mdx?$/, '');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are also cases where there isn't just a single /, and it could be made more readable something like;

Suggested change
const toPathname = file => '/' + file.replace(sep, '/').replace(/\.mdx?$/, '');
const toPathname = file => {
const path = file.split(sep).join('/');
const withoutExtension = path.replace(MARKDOWN_EXTENSION, '');
return '/' + withoutExtension;
};

diagnostics/poor-performance/using-linux-perf.md


const sideNav = sortedEntries.map(([key, items]) => ({
groupName: slugToTitle(key),
items: items.map(p => ({ label: p.label, link: p.pathname + '.html' })),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
items: items.map(p => ({ label: p.label, link: p.pathname + '.html' })),
items: items.map(({ label, pathname }) => ({ label, link: pathname + '.html' })),

Lets destructure to make the properties self-documenting


const authors = Object.fromEntries(
pages
.filter(p => p.authorIds.length > 0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.filter(p => p.authorIds.length > 0)
.filter(({authorIds}) => authorIds.length > 0)

Comment on lines +230 to +233
.map(p => [
toEditUrl(p.pathname),
p.authorIds.map(id => resolveAuthor(id, authorsById)),
])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(p => [
toEditUrl(p.pathname),
p.authorIds.map(id => resolveAuthor(id, authorsById)),
])
.map(({pathname, authorIds}) => [
toEditUrl(pathname),
authorIds.map(id => resolveAuthor(id, authorsById)),
])

* Reads every markdown page under {@link PAGES_DIR} and produces the sidebar
* navigation groups and a per-page authors mapping.
* @param {Map<string, AuthorEntry>} authorsById - Lookup map of known authors.
* @returns {Promise<{ sideNav: SideNavGroup[], authors: Record<string, ResolvedAuthor[]> }>}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we define this as a typedef as well? Honestly, it looks a bit complex here 🤔

@canerakdas
Copy link
Copy Markdown
Member

adding .editorconfig would be great before merging any script 🙏

Ref: https://github.com/nodejs/doc-kit/blob/main/.editorconfig

Comment on lines +51 to +56
/**
* @typedef {Object} BuildOutput
* @property {NavItem[]} topNav - Top-level navigation items.
* @property {SideNavGroup[]} sideNav - Grouped sidebar navigation.
* @property {Record<string, ResolvedAuthor[]>} authors - Authors keyed by edit URL.
*/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This typedef is not being used 👀

"eslint": "^10.1.0",
"eslint-plugin-mdx": "^3.7.0",
"globals": "^17.4.0",
"husky": "^9.1.7",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're not going to add the pre-commit hook, we should remove the husky dependency

@canerakdas
Copy link
Copy Markdown
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants