From 4277af908f330b1bb9933756a6ff704e67eefe2e Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Tue, 3 Nov 2026 19:07:09 +0100 Subject: [PATCH 1/2] perf: drop node positions after metadata Nothing after `metadata` reads where a node came from in the source: the type warnings that print positions run while it parses. Dropping them shrinks every entry the main thread holds and copies to and from the workers. On Node.js's docs that is ~480k position objects, about 80MB, and builds are ~5% faster. `json-simple`, which writes the metadata as it is, no longer has them either, and is about a third of its size. Assisted-by: Claude Opus 5.5 Assisted-by: Claude Sonnet 5.5 --- .changeset/drop-positions.md | 5 +++ .../metadata/utils/__tests__/parse.test.mjs | 31 +++++++++++++++++++ .../src/generators/metadata/utils/parse.mjs | 12 +++++++ 3 files changed, 48 insertions(+) create mode 100644 .changeset/drop-positions.md diff --git a/.changeset/drop-positions.md b/.changeset/drop-positions.md new file mode 100644 index 000000000..6d2e789e0 --- /dev/null +++ b/.changeset/drop-positions.md @@ -0,0 +1,5 @@ +--- +'@doc-kit/core': minor +--- + +The nodes of `metadata`'s entries no longer keep their `position` in the source once they are parsed, since nothing after `metadata` reads it. Builds hold less in memory and are faster, and the output of `json-simple` no longer has them either, now about a third of its size. diff --git a/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs b/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs index 4458f5d24..1b9e0c928 100644 --- a/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs +++ b/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs @@ -4,6 +4,7 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { u } from 'unist-builder'; +import { visit } from 'unist-util-visit'; import { parseApiDoc } from '../parse.mjs'; @@ -338,4 +339,34 @@ describe('parseApiDoc', () => { assert.strictEqual(secondContent[0].type, 'heading'); }); }); + + describe('source positions', () => { + it('drops where each node of an entry came from', () => { + const tree = u('root', [ + h('fs'), + stability('Stability: 2 - Stable'), + u('paragraph', [u('text', 'Content.')]), + ]); + const position = { + start: { line: 1, column: 1 }, + end: { line: 1, column: 1 }, + }; + + visit(tree, node => { + node.position = position; + }); + + const [entry] = parseApiDoc({ path, tree }, typeMap); + const positioned = []; + + visit(entry.content, node => { + if ('position' in node) { + positioned.push(node.type); + } + }); + + assert.strictEqual(entry.content.children.length, 3); + assert.deepStrictEqual(positioned, []); + }); + }); }); diff --git a/packages/core/src/generators/metadata/utils/parse.mjs b/packages/core/src/generators/metadata/utils/parse.mjs index c3eb41fb5..8a8050121 100644 --- a/packages/core/src/generators/metadata/utils/parse.mjs +++ b/packages/core/src/generators/metadata/utils/parse.mjs @@ -155,5 +155,17 @@ export const parseApiDoc = ({ path, tree, mdx = false }, typeMap) => { return SKIP; }); + // Nothing after this reads where a node came from: letting it go saves + // memory, and copying it to and from the worker threads + for (const { content, heading, stability } of metadataCollection) { + for (const root of [content, heading, stability]) { + if (root) { + visit(root, node => { + delete node.position; + }); + } + } + } + return metadataCollection; }; From b88b1d5b66f68ae1c74025f186ad36d4356d8b2a Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 4 Nov 2026 19:08:12 +0100 Subject: [PATCH 2/2] refactor: drop positions with one walk over each entry's content An entry's heading and stability are nodes of its content, so walking the content alone covers them, without a loop over the three of them and a check for a missing one. The test now checks the heading and the stability too. Assisted-by: Claude Opus 5.5 --- .../metadata/utils/__tests__/parse.test.mjs | 2 ++ .../core/src/generators/metadata/utils/parse.mjs | 15 ++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs b/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs index 1b9e0c928..17872567e 100644 --- a/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs +++ b/packages/core/src/generators/metadata/utils/__tests__/parse.test.mjs @@ -367,6 +367,8 @@ describe('parseApiDoc', () => { assert.strictEqual(entry.content.children.length, 3); assert.deepStrictEqual(positioned, []); + assert.ok(!('position' in entry.heading)); + assert.ok(entry.stability && !('position' in entry.stability)); }); }); }); diff --git a/packages/core/src/generators/metadata/utils/parse.mjs b/packages/core/src/generators/metadata/utils/parse.mjs index 8a8050121..df81dd243 100644 --- a/packages/core/src/generators/metadata/utils/parse.mjs +++ b/packages/core/src/generators/metadata/utils/parse.mjs @@ -156,15 +156,12 @@ export const parseApiDoc = ({ path, tree, mdx = false }, typeMap) => { }); // Nothing after this reads where a node came from: letting it go saves - // memory, and copying it to and from the worker threads - for (const { content, heading, stability } of metadataCollection) { - for (const root of [content, heading, stability]) { - if (root) { - visit(root, node => { - delete node.position; - }); - } - } + // memory, and copying it to and from the worker threads. An entry's heading + // and stability are nodes of its content + for (const { content } of metadataCollection) { + visit(content, node => { + delete node.position; + }); } return metadataCollection;