Skip to content

Commit cf97e07

Browse files
perf(javascript): parse into a SyntaxTree and print from it when the minifier neither compresses nor mangles (#22643)
* perf(javascript): write a parsed tree into a SyntaxTree's columns Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC * perf(javascript): print a source neither compressed nor mangled from its SyntaxTree minify with compress: false and mangle: false parses the source into a SyntaxTree and prints it with a tree printer reading only the tree and its tokens, skipping adoptEstree and the phases; any option the tree printer cannot match yet falls back to printing terser's tree. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC * perf(javascript): map a source printed from its SyntaxTree The tree printer adds the source mappings printEstree adds, so a minify asking for a source map prints from the tree too. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC * perf(javascript): write each function body into the SyntaxTree as it is read addEstree takes a child already written as its id, so the tree parse writes each function body once the parser finishes it and its ESTree nodes go before the rest of the source is read: the live heap holds the tree, not the tree and the whole ESTree. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC * chore(types): declare SyntaxTree#addEstree Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC * fix(javascript): write an import or export an older edition parsed without attributes Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC
1 parent b76f39a commit cf97e07

5 files changed

Lines changed: 3470 additions & 108 deletions

File tree

‎lib/javascript/syntax-parser.js‎

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18079,23 +18079,112 @@ class SyntaxTree {
1807918079
}
1808018080
}
1808118081

18082+
/**
18083+
* Writes a parsed node and everything under it, its children taking ids
18084+
* after it in source order; a child already written stands as its id.
18085+
* @param {import("estree").Node} root the node the parser made
18086+
* @returns {number} its id
18087+
*/
18088+
addEstree(root) {
18089+
const types = getTreeNodeTypes();
18090+
// Pairs of a node and where its id goes: a slot from 0, a list item as
18091+
// `-2 - index`, `-1` for the root.
18092+
/** @type {EXPECTED_ANY[]} */
18093+
const work = [root, -1];
18094+
let rootNode = 0;
18095+
while (work.length > 0) {
18096+
const target = /** @type {number} */ (work.pop());
18097+
const object = /** @type {Record<string, EXPECTED_ANY>} */ (work.pop());
18098+
const type = /** @type {Record<string, number>} */ (NodeType)[
18099+
object.type
18100+
];
18101+
const descriptor = types[type];
18102+
if (descriptor === undefined) {
18103+
throw new Error(`Unknown node type ${object.type}`);
18104+
}
18105+
const node = this.add(type, object.start, object.end);
18106+
if (target === -1) rootNode = node;
18107+
else if (target >= 0) this._slots[target] = node;
18108+
else this._listItems[-2 - target] = node;
18109+
const base = this._bases[node];
18110+
const fields = descriptor.fields;
18111+
// Pushed last field first, so the first comes off the stack first.
18112+
for (let f = fields.length - 1; f >= 0; f--) {
18113+
const field = fields[f];
18114+
const value = object[field.name];
18115+
switch (field.kind) {
18116+
case FIELD_NODE:
18117+
if (typeof value === "number") {
18118+
this._slots[base + field.slot] = value;
18119+
} else if (value) {
18120+
work.push(value, base + field.slot);
18121+
}
18122+
break;
18123+
case FIELD_LIST: {
18124+
// An edition before attributes leaves them off an import or export.
18125+
const length = value === undefined ? 0 : value.length;
18126+
const list = this._reserveList(length);
18127+
this._slots[base + field.slot] = list;
18128+
const start = this._listStarts[list];
18129+
for (let i = length - 1; i >= 0; i--) {
18130+
const item = value[i];
18131+
if (typeof item === "number") {
18132+
this._listItems[start + i] = item;
18133+
} else if (item !== null) {
18134+
work.push(item, -2 - (start + i));
18135+
}
18136+
}
18137+
break;
18138+
}
18139+
case FIELD_FLAG:
18140+
if (value) this._flags[node] |= field.slot;
18141+
break;
18142+
case FIELD_OPTIONAL:
18143+
if (value !== undefined) {
18144+
this._slots[base + field.slot] = this._values.push(value);
18145+
}
18146+
break;
18147+
case FIELD_VALUE:
18148+
this._slots[base + field.slot] = this._values.push(value);
18149+
break;
18150+
default:
18151+
if (value !== null) {
18152+
this._slots[base + field.slot] = this._values.push(value);
18153+
}
18154+
}
18155+
}
18156+
}
18157+
return rootNode;
18158+
}
18159+
1808218160
/**
1808318161
* @param {(number | null)[]} nodes the list's items, null for a hole
1808418162
* @returns {number} the list, from 1
1808518163
*/
1808618164
_addList(nodes) {
18165+
const list = this._reserveList(nodes.length);
18166+
const start = this._listStarts[list];
18167+
for (let i = 0; i < nodes.length; i++) {
18168+
const item = nodes[i];
18169+
this._listItems[start + i] = item === null ? 0 : item;
18170+
}
18171+
return list;
18172+
}
18173+
18174+
/**
18175+
* @param {number} length how many items the list holds, each `0` until set
18176+
* @returns {number} the list, from 1
18177+
*/
18178+
_reserveList(length) {
1808718179
const list = ++this._listCount;
1808818180
this._listStarts = growColumn(this._listStarts, list + 1);
1808918181
this._listLengths = growColumn(this._listLengths, list + 1);
1809018182
const start = this._listItemTop;
18091-
this._listItemTop += nodes.length;
18183+
this._listItemTop += length;
1809218184
this._listItems = growColumn(this._listItems, this._listItemTop);
18093-
for (let i = 0; i < nodes.length; i++) {
18094-
const item = nodes[i];
18095-
this._listItems[start + i] = item === null ? 0 : item;
18096-
}
18185+
this._listItems.fill(0, start, this._listItemTop);
1809718186
this._listStarts[list] = start;
18098-
this._listLengths[list] = nodes.length;
18187+
this._listLengths[list] = length;
1809918188
return list;
1810018189
}
1810118190

0 commit comments

Comments
 (0)