Files
react.dev/beta/scripts/generateHeadingIDs.js
Titus 4022f9774b Use custom ID format that works better with MDX 2 (#4105)
* Fix two unicode whitespaces which are invalid

* Use custom ID format that works better with MDX 2

* Fix sebastian

* remark-slug as dev dep
2021-11-15 04:20:32 +00:00

109 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*/
// To do: Make this ESM.
// To do: properly check heading numbers (headings with the same text get
// numbered, this script doesnt check that).
const assert = require('assert');
const fs = require('fs');
const GithubSlugger = require('github-slugger');
let modules
function walk(dir) {
let results = [];
const list = fs.readdirSync(dir);
list.forEach(function (file) {
file = dir + '/' + file;
const stat = fs.statSync(file);
if (stat && stat.isDirectory()) {
/* Recurse into a subdirectory */
results = results.concat(walk(file));
} else {
/* Is a file */
results.push(file);
}
});
return results;
}
function stripLinks(line) {
return line.replace(/\[([^\]]+)\]\([^)]+\)/, (match, p1) => p1);
}
function addHeaderID(line, slugger) {
// check if we're a header at all
if (!line.startsWith('#')) {
return line;
}
const match = /^(#+\s+)(.+?)(\s*\{(?:\/\*|#)([^\}\*\/]+)(?:\*\/)?\}\s*)?$/.exec(line);
const before = match[1] + match[2]
const proc = modules.unified().use(modules.remarkParse).use(modules.remarkSlug)
const tree = proc.runSync(proc.parse(before))
const head = tree.children[0]
assert(head && head.type === 'heading', 'expected `' + before + '` to be a heading, is it using a normal space after `#`?')
const autoId = head.data.id
const existingId = match[4]
const id = existingId || autoId
// Ignore numbers:
const cleanExisting = existingId ? existingId.replace(/-\d+$/, '') : undefined
const cleanAuto = autoId.replace(/-\d+$/, '')
if (cleanExisting && cleanExisting !== cleanAuto) {
console.log('Note: heading `%s` has a different ID (`%s`) than what GH generates for it: `%s`:', before, existingId, autoId)
}
return match[1] + match[2] + ' {/*' + id + '*/}';
}
function addHeaderIDs(lines) {
// Sluggers should be per file
const slugger = new GithubSlugger();
let inCode = false;
const results = [];
lines.forEach((line) => {
// Ignore code blocks
if (line.startsWith('```')) {
inCode = !inCode;
results.push(line);
return;
}
if (inCode) {
results.push(line);
return;
}
results.push(addHeaderID(line, slugger));
});
return results;
}
const [path] = process.argv.slice(2);
main()
async function main() {
const [unifiedMod, remarkParseMod, remarkSlugMod] = await Promise.all([import('unified'), import('remark-parse'), import('remark-slug')])
const unified = unifiedMod.default
const remarkParse = remarkParseMod.default
const remarkSlug = remarkSlugMod.default
modules = {unified, remarkParse, remarkSlug}
const files = walk(path);
files.forEach((file) => {
if (!(file.endsWith('.md') || file.endsWith('.mdx'))) {
return;
}
const content = fs.readFileSync(file, 'utf8');
const lines = content.split('\n');
const updatedLines = addHeaderIDs(lines);
fs.writeFileSync(file, updatedLines.join('\n'));
});
}