Files
react.dev/beta/scripts/headingIDHelpers/generateHeadingIDs.js
dan 54e6276f2f [Beta] Fix header IDs (#5128)
* Fix heading scripts

* [Beta] Fix header ID generation

* Fix headers

* Remove the Convention block breaking TOC

* Fix message
2022-09-28 19:05:10 +01:00

111 lines
2.8 KiB
JavaScript
Raw Permalink 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');
const walk = require('./walk');
let modules;
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;
}
async function main(paths) {
paths = paths.length === 0 ? ['src/content'] : paths;
const [unifiedMod, remarkParseMod, remarkSlugMod] = await Promise.all([
import('unified'),
import('remark-parse'),
import('remark-slug'),
]);
const unified = unifiedMod.unified;
const remarkParse = remarkParseMod.default;
const remarkSlug = remarkSlugMod.default;
modules = {unified, remarkParse, remarkSlug};
const files = paths.map((path) => [...walk(path)]).flat();
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'));
});
}
module.exports = main;