Repository navigation
Description
The legacy HTML generators (legacy-html, one <api>.html per module, and legacy-html-all, which writes all.html) fill their template with a chain of String.prototype.replace(placeholder, value) calls. When the second argument is a string, JavaScript treats some $ sequences in it as replacement patterns:
| In the value | Becomes |
|---|---|
$$ |
$ |
$& |
the matched placeholder (e.g. __CONTENT__) |
$` |
everything in the template before the placeholder |
$' |
everything in the template after it |
The whole rendered page body is passed that way, so any doc containing those sequences comes out wrong. This already shows on nodejs.org: the Makefile example in C++ addons (doc/api/addons.md, line 95 in nodejs/node) has $$(command -v node), and the page renders it as $(command -v node). $$ is how Make writes a literal $, so copying the snippet gives a broken Makefile.
Where to look
packages/node-legacy/src/legacy-html/utils/replaceTemplateValues.mjs:34:.replace('__CONTENT__', content), the main culprit.- The same file, lines 27-43: the rest of the chain does the same with
__SECTION__,__TOC__,__GTOC__,__TOC_PICKER__,__GTOC_PICKER__,__ALTDOCS__and__EDIT_ON_GITHUB__. - It's called from
legacy-html/generate.mjs:127(every page) andlegacy-html-all/generate.mjs:59(all.html), so one fix covers both. - Same pattern, lower risk, worth fixing in the same PR:
packages/node/src/man-page/generate.mjs:71-72andpackages/react/src/sitemap/generate.mjs:53-56,60.
How to reproduce
const template = '<pre>__CONTENT__</pre>';
template.replace('__CONTENT__', 'dirname "$$(command -v node)"');
// '<pre>dirname "$(command -v node)"</pre>' <- one `$` is gone
template.replace('__CONTENT__', "x $' y");
// '<pre>x </pre> y</pre>' <- the rest of the template got pasted in
template.replace('__CONTENT__', () => 'dirname "$$(command -v node)"');
// '<pre>dirname "$$(command -v node)"</pre>' <- a function replacer keeps it as-isSuggested approach
Pass each value through a replacer function. Its return value is inserted as-is, without looking at $:
.replace('__CONTENT__', () => content)
.replace(/__GTOC__/g, () => nav)
// ...and the same for every other line in the chainUsing () => on every line keeps it consistent. Keep the /g regexes where they are, since the template uses some placeholders more than once. Then do the same in man-page and sitemap.
How to test
There's no test for replaceTemplateValues yet. Create packages/node-legacy/src/legacy-html/utils/__tests__/replaceTemplateValues.test.mjs, following buildContent.test.mjs next to it (node:test and node:assert/strict):
- Call
setConfig({})in abeforehook, becausebuildVersionsreads thelegacy-htmlconfig. - Pass a small template like
'<main>__CONTENT__</main><nav>__GTOC__</nav>', the config{ version: { version: '1.0.0' }, changelog: [] }and{ skipGitHub: true }. - Assert that content containing
$$,$&,$'and$`comes out unchanged, and add one case fornavorsection.
Run the suite with node --run test, then node --run lint and node --run format:check.
Things to watch out for
- Only
$$,$&,$`and$'are special here, so the test needs those exact sequences. Shiki often splits code into<span>s, which is why most$in the docs survive and this was easy to miss. legacy-html-allreuses the same function, so don't duplicate the fix there.- In tests, pass
skipGitHub: true(or seteditURLin the config), because the "Edit on GitHub" link needs one.
Getting started
New to doc-kit? CONTRIBUTING.md covers setup, running the tests and adding a changeset (node --run changeset, a patch for @node-core/doc-kit-legacy). Leave a comment here if you'd like to take it on.
Activity
fix(legacy-html,man-page,sitemap): treat $$, $&, $` and $' in values …