Skip to content

Commit 353202a

Browse files
refactor(javascript): read the minifier's comments and tokens off arrays, not terser's objects (#23039)
* refactor(javascript): type the printer's nodes as the numbers they are Drop the ESTree-shaped `Print*` node typedefs, which every use cast a `SyntaxTree` node number to, and the mangling recording only the deleted `patchRecording` read. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC * refactor(javascript): read the minifier's comments and tokens off arrays Comments are numbers into per-parse arrays, and a gap's or parenthesis's comments a list number, instead of terser's `ParsedToken` and `SourceToken` objects; the stream marks what it printed in two byte arrays. A `comments` callback is still handed terser's comment shape. Output is byte-identical; a parse of lodash keeping every comment retains about 45% less heap. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC * refactor(javascript): drop terser's node shapes and module bag from the printer The printer's types no longer describe terser's AST: `NodeShape`, `NodeFields`, `Node`, `CompressorShape`, `PureCallView` and the generic `NodeAccess<N>`/`OutputStream<N>` over ESTree nodes are gone, and the `TerserModules` bag the phases installed into is built from explicit pieces, its unread `quotesKept` dropped. Claude-Session: https://claude.ai/code/session_01Tgxgyb4N35ndP6cC4KcrXC
1 parent 59ac091 commit 353202a

5 files changed

Lines changed: 809 additions & 1397 deletions

File tree

‎lib/javascript/ScopeAnalyzer.js‎

Lines changed: 24 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,10 +1120,7 @@ const analyzeScope = (ast, recordEveryReference = false) => {
11201120
};
11211121
};
11221122

1123-
/**
1124-
* @typedef {import("./syntax-printer").PrintNode} PrintNode
1125-
* @typedef {import("./syntax-printer").PrintIdentifier} PrintIdentifier
1126-
*/
1123+
/** @typedef {{ name: string, definition: TreeDefinition }} NamedIdentifier a name as the mangler reads it: as written, and its definition */
11271124

11281125
// How a minifier definition's name was declared, one code per kind of
11291126
// terser's declaring symbol; a global is a name nothing declares.
@@ -1219,13 +1216,19 @@ const encloseUnique = (scope, definition) => {
12191216
class MinifierScope extends Scope {
12201217
/**
12211218
* @param {ScopeType} type what opened this scope
1222-
* @param {PrintNode} node the node opening it
1219+
* @param {number} node the node opening it
12231220
* @param {MinifierScope | null} upper the enclosing scope
12241221
* @param {boolean} isVarScope whether `var` and function declarations hoist to here
12251222
*/
12261223
constructor(type, node, upper, isVarScope) {
1227-
super(type, /** @type {Node} */ (node), upper, isVarScope, false);
1228-
/** @type {PrintNode} */
1224+
super(
1225+
type,
1226+
/** @type {Node} */ (/** @type {Node | number} */ (node)),
1227+
upper,
1228+
isVarScope,
1229+
false
1230+
);
1231+
/** @type {number} */
12291232
this.node = node;
12301233
/** @type {MinifierVariable[]} the definitions it or a scope inside it reads, in the order first read */
12311234
this.enclosed = [];
@@ -1333,9 +1336,9 @@ class MinifierLabel {
13331336
* @property {MinifierLabel[]} labels every label, in the same order
13341337
* @property {Map<string, MinifierVariable>} globals the names nothing declares, in the order first read
13351338
* @property {Set<MinifierScope>} blockDefunScopes the function scopes holding a function declared in a block
1336-
* @property {PrintIdentifier[] | null} functionDeclarators under `keep_fnames`, the names declarators give anonymous functions, in the order the mangler asks `keep_fnames` of them
1337-
* @property {PrintIdentifier[] | null} classDeclarators under `classDeclarators` and `keep_classnames`, the names declarators give anonymous classes
1338-
* @property {PrintIdentifier[]} catchParameters unless `ie8` or `safari10` move them, the catch parameters, each named where the mangler's walk meets its scope
1339+
* @property {NamedIdentifier[] | null} functionDeclarators under `keep_fnames`, the names declarators give anonymous functions, in the order the mangler asks `keep_fnames` of them
1340+
* @property {NamedIdentifier[] | null} classDeclarators under `classDeclarators` and `keep_classnames`, the names declarators give anonymous classes
1341+
* @property {NamedIdentifier[]} catchParameters unless `ie8` or `safari10` move them, the catch parameters, each named where the mangler's walk meets its scope
13391342
* @property {boolean} namesPrivate whether the program names a private member, which renaming walks it for
13401343
* @property {SpelledNames | null} spelledNames under `spelledNames`, the names a key or an import or export spells for a name beside it
13411344
* @property {(number[] | null)=} aliases under `substitutesAliases`, the declarators whose name no read reads any more
@@ -1346,7 +1349,7 @@ class MinifierLabel {
13461349
* Where a name stands beside one spelled for it: a property's key, which the
13471350
* name as its value lets print alone, or an import's or export's other name.
13481351
* @typedef {object} SpelledNames
1349-
* @property {PrintIdentifier[]} identifiers each name, its `definition` read once the analysis resolves
1352+
* @property {NamedIdentifier[]} identifiers each name, its `definition` read once the analysis resolves
13501353
* @property {string[]} spellings the name spelled beside each
13511354
* @property {number[]} savings the characters each saves, its own name one long, where it takes the name spelled
13521355
*/
@@ -1578,12 +1581,7 @@ class MinifierTreeScopeAnalyzer {
15781581
/** @type {MinifierScope[]} */
15791582
this.parameterRanges = [];
15801583
/** @type {MinifierScope} */
1581-
this.toplevel = new MinifierScope(
1582-
"global",
1583-
/** @type {PrintNode} */ (/** @type {PrintNode | number} */ (program)),
1584-
null,
1585-
true
1586-
);
1584+
this.toplevel = new MinifierScope("global", program, null, true);
15871585
/** @type {MinifierScope} */
15881586
this.scope = this.toplevel;
15891587
/** @type {MinifierLabel[]} */
@@ -1786,12 +1784,7 @@ class MinifierTreeScopeAnalyzer {
17861784
* @returns {MinifierScope} the scope, now current
17871785
*/
17881786
_open(type, node, isVarScope) {
1789-
const scope = new MinifierScope(
1790-
type,
1791-
/** @type {PrintNode} */ (/** @type {PrintNode | number} */ (node)),
1792-
this.scope,
1793-
isVarScope
1794-
);
1787+
const scope = new MinifierScope(type, node, this.scope, isVarScope);
17951788
this.scope = scope;
17961789
return scope;
17971790
}
@@ -2171,9 +2164,7 @@ class MinifierTreeScopeAnalyzer {
21712164
_keepsInBlock(scope, name) {
21722165
const { tree } = this;
21732166
while (scope !== scope.variableScope) {
2174-
const node = /** @type {number} */ (
2175-
/** @type {number | PrintNode} */ (scope.node)
2176-
);
2167+
const { node } = scope;
21772168
switch (this.types[node]) {
21782169
case NodeType.BlockStatement:
21792170
if (this._listDeclaresLexically(node, Field.body, name)) return true;
@@ -2218,9 +2209,7 @@ class MinifierTreeScopeAnalyzer {
22182209
}
22192210
scope = /** @type {MinifierScope} */ (scope.upper);
22202211
}
2221-
const node = /** @type {number} */ (
2222-
/** @type {number | PrintNode} */ (scope.node)
2223-
);
2212+
const { node } = scope;
22242213
if (this.types[node] === NodeType.Program) {
22252214
return this._listDeclaresLexically(node, Field.body, name);
22262215
}
@@ -3210,9 +3199,7 @@ class MinifierTreeScopeAnalyzer {
32103199
if (
32113200
isMinifierVariable(read) &&
32123201
read.kind === DECLARES_FUNARG &&
3213-
/** @type {number | PrintNode} */ (
3214-
/** @type {MinifierScope} */ (read.scope).node
3215-
) === init &&
3202+
/** @type {MinifierScope} */ (read.scope).node === init &&
32163203
tree.name(referenceNodes[byStart[j]]) === "arguments"
32173204
) {
32183205
readsArguments = true;
@@ -3317,18 +3304,13 @@ class MinifierTreeScopeAnalyzer {
33173304

33183305
/**
33193306
* @param {number[]} identifiers names
3320-
* @returns {PrintIdentifier[]} each as the mangler reads it: its name and its definition
3307+
* @returns {NamedIdentifier[]} each as the mangler reads it: its name and its definition
33213308
*/
33223309
_asNames(identifiers) {
3323-
return identifiers.map(
3324-
(identifier) =>
3325-
/** @type {PrintIdentifier} */ (
3326-
/** @type {PrintIdentifier | { name: string, definition: TreeDefinition }} */ ({
3327-
name: this.tree.name(identifier),
3328-
definition: this.definitions[identifier]
3329-
})
3330-
)
3331-
);
3310+
return identifiers.map((identifier) => ({
3311+
name: this.tree.name(identifier),
3312+
definition: this.definitions[identifier]
3313+
}));
33323314
}
33333315
}
33343316

‎lib/javascript/jsMinify.js‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
/** @typedef {import("./syntax-printer").MinifyOptions} MinifyOptions */
99
/** @typedef {import("../util/SourceProcessor").SourceMap} SourceMap */
10-
/** @typedef {import("./syntax-printer").PrintNode} PrintNode */
1110

1211
/** @typedef {import("./syntax-parser").JavascriptSourceType} JavascriptSourceType */
1312

@@ -62,21 +61,21 @@ const jsMinify = async (
6261
}
6362
if (typeof condition === "function") return condition;
6463
if (condition instanceof RegExp) {
65-
return (/** @type {PrintNode} */ node, comment) =>
64+
return (/** @type {number} */ node, comment) =>
6665
condition.test(comment.value);
6766
}
6867
if (condition === "all") return () => true;
6968
if (condition === "some") {
7069
const pattern = /@preserve|@lic|@cc_on|^\**!/i;
71-
return (/** @type {PrintNode} */ node, comment) =>
70+
return (/** @type {number} */ node, comment) =>
7271
(comment.type === "comment2" || comment.type === "comment1") &&
7372
pattern.test(comment.value);
7473
}
7574
// Compile once, but only when asked: a malformed pattern must still be
7675
// harmless when there are no comments to test against it.
7776
/** @type {RegExp | undefined} */
7877
let pattern;
79-
return (/** @type {PrintNode} */ node, comment) => {
78+
return (/** @type {number} */ node, comment) => {
8079
if (pattern === undefined) pattern = new RegExp(condition);
8180
return pattern.test(comment.value);
8281
};
@@ -126,7 +125,7 @@ const jsMinify = async (
126125
const preserve = asCondition(kept);
127126
const seen = new Set();
128127
format.comments = (
129-
/** @type {PrintNode} */ node,
128+
/** @type {number} */ node,
130129
/** @type {Parameters<CommentCondition>[1]} */ comment
131130
) => {
132131
if (extract(node, comment)) {

0 commit comments

Comments
 (0)