Repository navigation
json-simple: stop removing headings from the metadata it shares with other generators #1182
Description
json-simple strips heading and stability nodes so its api-docs.json is simpler, but it does that in place, on the metadata entries it receives. Those aren't its own copy: every generator that depends on @doc-kit/core/metadata (legacy-json, legacy-html, json, man-page, llms-txt, jsx-ast, ...) gets the same array of the same objects. So building json-simple together with another target breaks the other one:
doc-kit generate -t json-simple legacy-jsoncrashes withTypeError: Cannot read properties of undefined (reading 'name'), becauselegacy-jsonexpects each entry's content to start with its heading.doc-kit generate -t json-simple legacy-htmlfinishes, but the HTML has no section headings and no stability banners.
The target order and thread count don't matter: json-simple has no worker step, so it changes the shared objects on the main thread before anything else copies them.
A bit of context if you're new here: each generator is one build target, and dependsOn says whose output it reads as input. Results are cached and handed to every dependent as-is, so a generator must treat its input as read-only. section-pages already does this by copying each entry before changing it.
Where to look
packages/core/src/generators/json-simple/generate.mjs:19-21:input.forEach(node => remove(node.content, [UNIST.isStabilityNode, UNIST.isHeading]))changes the shared entries.- The same file, lines 24 and 27: it writes and returns that same
input. packages/node-legacy/src/legacy-json/utils/buildSection.mjs:172and:85: wherelegacy-jsonexpects the heading and crashes without it.packages/react/src/section-pages/generate.mjs:85: the existing pattern,structuredClone(original)before changing an entry.
How to reproduce
Save this as foo.md:
# Foo
<!--introduced_in=v1.0.0-->
> Stability: 2 - Stable
The foo module.
## `foo.bar()`
<!-- YAML
added: v1.0.0
-->
* Returns: {string}
Returns bar.Then, from the repo root:
node packages/cli/bin/cli.mjs generate -i foo.md -t legacy-json -o out-a -v v24.0.0
# works
node packages/cli/bin/cli.mjs generate -i foo.md -t json-simple legacy-json -o out-b -v v24.0.0
# TypeError: Cannot read properties of undefined (reading 'name')Suggested approach
Have json-simple work on its own copies and leave input alone. Copying content is enough, since that's all remove changes:
const output = input.map(entry => {
const copy = { ...entry, content: structuredClone(entry.content) };
remove(copy.content, [UNIST.isStabilityNode, UNIST.isHeading]);
return copy;
});Then write and return output instead of input.
How to test
json-simple has no tests yet. Create packages/core/src/generators/json-simple/__tests__/generate.test.mjs, setting up the config like json-all/__tests__/generate.test.mjs does (setConfig({ target: ['json-simple'] }), then set output to undefined so nothing is written). Build an entry where the same heading object is both entry.heading and the first node of entry.content, as the metadata generator makes it, plus a stability blockquote and a paragraph. Then test that:
- The input is unchanged:
const snapshot = structuredClone(input); await generate(input); assert.deepEqual(input, snapshot);. Same idea assection-pages' "does not mutate the original entries" test. It fails today. - The output still drops them: the returned entry's
content.childrenonly has the paragraph.
Run everything with node --run test, and optionally repeat the CLI check above.
Things to watch out for
- Write and return the new array. If either line still uses
input,api-docs.jsongets its headings back. - Don't swap
removefor a shallowchildren.filter(...):removeis deep and also drops parents that end up empty, so the output would change. - Leave the cache alone. Sharing one result between generators is deliberate, to save memory; the rule is that generators don't change their
input. - The order of entries in
api-docs.jsoncan change from build to build, which is a separate issue. Compare contents, not order, when checking by hand.
Getting started
New to doc-kit? CONTRIBUTING.md covers setup, running the tests and adding a changeset (node --run changeset, a patch for @doc-kit/core). Leave a comment here if you'd like to take it on.
Activity
dxnnystyles commentedon Oct 26, 2026
i can take this one. i'll make json-simple copy the content it modifies, then add regression tests for input immutability + filtered output.