@tsrx/core 0.1.62 → 0.1.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/plugin.js +213 -2
- package/types/parse.d.ts +7 -0
package/package.json
CHANGED
package/src/plugin.js
CHANGED
|
@@ -108,6 +108,88 @@ function get_argument_clash_reported_names(check_clashes) {
|
|
|
108
108
|
return reported_names;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The position of a pending `&{…}`/`&[…]` lazy binding pattern recorded on a
|
|
113
|
+
* DestructuringErrors context, or -1. Acorn's DestructuringErrors class knows
|
|
114
|
+
* nothing about the field, so absence means none was recorded.
|
|
115
|
+
*
|
|
116
|
+
* @param {Parse.DestructuringErrors | undefined | null} refDestructuringErrors
|
|
117
|
+
* @returns {number}
|
|
118
|
+
*/
|
|
119
|
+
function get_lazy_binding_pos(refDestructuringErrors) {
|
|
120
|
+
return refDestructuringErrors?.lazyBindingPos ?? -1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** @type {ReadonlySet<string>} */
|
|
124
|
+
const lazy_target_wrapper_types = new Set([
|
|
125
|
+
'ParenthesizedExpression',
|
|
126
|
+
'TSAsExpression',
|
|
127
|
+
'TSSatisfiesExpression',
|
|
128
|
+
'TSNonNullExpression',
|
|
129
|
+
'TSTypeAssertion',
|
|
130
|
+
'TSTypeCastExpression',
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Whether the lazy binding pattern recorded at `pos` (the position of its `&`,
|
|
135
|
+
* one character before the pattern node) sits in a pattern-forming position of
|
|
136
|
+
* `node` — a slot that toAssignable converts into a binding or assignment
|
|
137
|
+
* target. A lazy pattern reached only through an expression position (for
|
|
138
|
+
* example as a member expression's object in `&{ a }.b = x`) is an expression
|
|
139
|
+
* use and must keep its pending error. `node` is the pre-conversion tree, so
|
|
140
|
+
* both the expression and pattern spellings of each slot appear here.
|
|
141
|
+
*
|
|
142
|
+
* @param {AST.Node | null | undefined} node
|
|
143
|
+
* @param {number} pos
|
|
144
|
+
* @returns {boolean}
|
|
145
|
+
*/
|
|
146
|
+
function pattern_position_contains_lazy(node, pos) {
|
|
147
|
+
if (!node || typeof node !== 'object') return false;
|
|
148
|
+
if (
|
|
149
|
+
/** @type {AST.ObjectPattern | AST.ArrayPattern} */ (node).lazy &&
|
|
150
|
+
/** @type {number} */ (node.start) === pos + 1
|
|
151
|
+
) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
// Parentheses and the TypeScript expression wrappers acorn-typescript's
|
|
155
|
+
// toAssignable unwraps when converting a target (`[&{ a }!] = arr`) — the
|
|
156
|
+
// wrapped node stays in a pattern-forming position. TSTypeCastExpression is
|
|
157
|
+
// internal to acorn-typescript, hence the string set rather than switch
|
|
158
|
+
// cases over the ESTree union.
|
|
159
|
+
if (lazy_target_wrapper_types.has(node.type)) {
|
|
160
|
+
return pattern_position_contains_lazy(
|
|
161
|
+
/** @type {{ expression: AST.Node }} */ (/** @type {unknown} */ (node)).expression,
|
|
162
|
+
pos,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
switch (node.type) {
|
|
166
|
+
case 'ObjectExpression':
|
|
167
|
+
case 'ObjectPattern':
|
|
168
|
+
return node.properties.some((property) =>
|
|
169
|
+
pattern_position_contains_lazy(
|
|
170
|
+
property.type === 'Property'
|
|
171
|
+
? /** @type {AST.Node} */ (property.value)
|
|
172
|
+
: property.argument,
|
|
173
|
+
pos,
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
case 'ArrayExpression':
|
|
177
|
+
case 'ArrayPattern':
|
|
178
|
+
return node.elements.some(
|
|
179
|
+
(element) => element && pattern_position_contains_lazy(element, pos),
|
|
180
|
+
);
|
|
181
|
+
case 'AssignmentExpression':
|
|
182
|
+
return node.operator === '=' && pattern_position_contains_lazy(node.left, pos);
|
|
183
|
+
case 'AssignmentPattern':
|
|
184
|
+
return pattern_position_contains_lazy(node.left, pos);
|
|
185
|
+
case 'SpreadElement':
|
|
186
|
+
case 'RestElement':
|
|
187
|
+
return pattern_position_contains_lazy(node.argument, pos);
|
|
188
|
+
default:
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
111
193
|
/**
|
|
112
194
|
* A `<` opens a tag only when the character after it can begin one: `/` for a
|
|
113
195
|
* closing tag, `>` for a fragment, `{` for a dynamic tag, or an element or
|
|
@@ -276,6 +358,8 @@ export function TSRXPlugin(config) {
|
|
|
276
358
|
#errors = undefined;
|
|
277
359
|
/** @type {string | null} */
|
|
278
360
|
#filename = null;
|
|
361
|
+
/** @type {WeakMap<object, { names: Set<string>, lexicalLength: number, varLength: number }>} */
|
|
362
|
+
#localExportNamesByScope = new WeakMap();
|
|
279
363
|
#functionBodyDepth = 0;
|
|
280
364
|
#allowExpressionContainerTrailingSemicolon = false;
|
|
281
365
|
#jsxAttributeValueExpressionDepth = 0;
|
|
@@ -1514,6 +1598,25 @@ export function TSRXPlugin(config) {
|
|
|
1514
1598
|
* @type {Parse.Parser['parseExprAtom']}
|
|
1515
1599
|
*/
|
|
1516
1600
|
parseExprAtom(refDestructuringErrors, forInit, forNew) {
|
|
1601
|
+
// A `&{…}`/`&[…]` lazy binding pattern is only meaningful where the
|
|
1602
|
+
// expression may still turn out to be a binding or assignment target —
|
|
1603
|
+
// an arrow parameter list, a destructuring assignment target, or a
|
|
1604
|
+
// for–of/for–in loop target. Those are exactly the positions Acorn
|
|
1605
|
+
// parses with a DestructuringErrors context, so parse the pattern there
|
|
1606
|
+
// and record it as a pending error the same way Acorn treats `{a = b}`
|
|
1607
|
+
// shorthand: pattern conversion accepts the node as-is, while contexts
|
|
1608
|
+
// that remain expressions raise in checkExpressionErrors.
|
|
1609
|
+
if (
|
|
1610
|
+
refDestructuringErrors &&
|
|
1611
|
+
this.type === tt.bitwiseAND &&
|
|
1612
|
+
(this.input.charCodeAt(this.end) === CharCode.openBrace ||
|
|
1613
|
+
this.input.charCodeAt(this.end) === CharCode.openBracket)
|
|
1614
|
+
) {
|
|
1615
|
+
if (get_lazy_binding_pos(refDestructuringErrors) < 0) {
|
|
1616
|
+
refDestructuringErrors.lazyBindingPos = this.start;
|
|
1617
|
+
}
|
|
1618
|
+
return /** @type {AST.Expression} */ (/** @type {unknown} */ (this.parseBindingAtom()));
|
|
1619
|
+
}
|
|
1517
1620
|
// A token already consumed as JSX text (a script-mode element child) must
|
|
1518
1621
|
// stay text even when it happens to begin at an `@` — otherwise whether
|
|
1519
1622
|
// `@if` parses as a directive would depend on leading whitespace.
|
|
@@ -3236,6 +3339,89 @@ export function TSRXPlugin(config) {
|
|
|
3236
3339
|
return expr;
|
|
3237
3340
|
}
|
|
3238
3341
|
|
|
3342
|
+
/**
|
|
3343
|
+
* A recorded lazy binding pattern that never became a binding or
|
|
3344
|
+
* assignment target is a pending error, exactly like `{a = b}` shorthand
|
|
3345
|
+
* outside a destructuring pattern. Acorn calls the throwing form at every
|
|
3346
|
+
* boundary where a context definitively stayed an expression
|
|
3347
|
+
* (parenthesized expressions, call arguments, plain assignments'
|
|
3348
|
+
* right-hand sides, …) — that is where the record is enforced.
|
|
3349
|
+
*
|
|
3350
|
+
* The non-throwing form is deliberately left untouched: Acorn uses it in
|
|
3351
|
+
* parseExprOps/parseMaybeConditional to stop parsing operators early, and
|
|
3352
|
+
* returning true there would cut the pattern off from the `as` /
|
|
3353
|
+
* `satisfies` operator lane before toAssignable can accept the wrapped
|
|
3354
|
+
* target (`[&{ a } as T] = arr`). Every path that keeps a stale record
|
|
3355
|
+
* still ends in a throwing check.
|
|
3356
|
+
*
|
|
3357
|
+
* @type {Parse.Parser['checkExpressionErrors']}
|
|
3358
|
+
*/
|
|
3359
|
+
checkExpressionErrors(refDestructuringErrors, andThrow) {
|
|
3360
|
+
if (andThrow) {
|
|
3361
|
+
const lazy_binding_pos = get_lazy_binding_pos(refDestructuringErrors);
|
|
3362
|
+
if (lazy_binding_pos >= 0) {
|
|
3363
|
+
this.raise(
|
|
3364
|
+
lazy_binding_pos,
|
|
3365
|
+
'Lazy binding patterns are only valid as binding or assignment targets',
|
|
3366
|
+
);
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
return super.checkExpressionErrors(refDestructuringErrors, andThrow);
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
/**
|
|
3373
|
+
* acorn-typescript unwraps TS expression wrappers in checkLValSimple,
|
|
3374
|
+
* which is only right for simple targets (`[b as any] = arr`). A wrapped
|
|
3375
|
+
* destructuring pattern (`[&{ a } as T] = arr`) reaches checkLValPattern
|
|
3376
|
+
* still wrapped — toAssignableList ignores return values, so the wrapper
|
|
3377
|
+
* survives conversion — and would fall through to checkLValSimple's
|
|
3378
|
+
* "Assigning to rvalue". Unwrap here so wrapped patterns take the
|
|
3379
|
+
* pattern lane.
|
|
3380
|
+
*
|
|
3381
|
+
* @type {Parse.Parser['checkLValPattern']}
|
|
3382
|
+
*/
|
|
3383
|
+
checkLValPattern(expr, bindingType, checkClashes) {
|
|
3384
|
+
let node = expr;
|
|
3385
|
+
while (
|
|
3386
|
+
node.type === 'TSNonNullExpression' ||
|
|
3387
|
+
node.type === 'TSAsExpression' ||
|
|
3388
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
3389
|
+
node.type === 'TSTypeAssertion'
|
|
3390
|
+
) {
|
|
3391
|
+
node = /** @type {AST.Node} */ (
|
|
3392
|
+
/** @type {{ expression: AST.Node }} */ (/** @type {unknown} */ (node)).expression
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
return super.checkLValPattern(node, bindingType, checkClashes);
|
|
3396
|
+
}
|
|
3397
|
+
|
|
3398
|
+
/**
|
|
3399
|
+
* Converting a node into an assignment target resolves any lazy binding
|
|
3400
|
+
* pattern recorded inside it — the pattern landed in a valid position,
|
|
3401
|
+
* so its pending error must not outlive the conversion (e.g.
|
|
3402
|
+
* `({ pair: &{ a } } = obj)` would otherwise still raise when the
|
|
3403
|
+
* enclosing parenthesized expression runs checkExpressionErrors). This
|
|
3404
|
+
* mirrors how Acorn resets `shorthandAssign` once the shorthand ends up
|
|
3405
|
+
* inside a converted left-hand side.
|
|
3406
|
+
*
|
|
3407
|
+
* @type {Parse.Parser['toAssignable']}
|
|
3408
|
+
*/
|
|
3409
|
+
toAssignable(node, isBinding, refDestructuringErrors, preserveTypeScriptWrapper) {
|
|
3410
|
+
const lazy_binding_pos = get_lazy_binding_pos(refDestructuringErrors);
|
|
3411
|
+
// Only a pattern-forming position resolves the record: a lazy pattern
|
|
3412
|
+
// that is merely inside the target's span but reached through an
|
|
3413
|
+
// expression position (`&{ a }.b = x`) is still an expression use.
|
|
3414
|
+
if (lazy_binding_pos >= 0 && pattern_position_contains_lazy(node, lazy_binding_pos)) {
|
|
3415
|
+
/** @type {Parse.DestructuringErrors} */ (refDestructuringErrors).lazyBindingPos = -1;
|
|
3416
|
+
}
|
|
3417
|
+
return super.toAssignable(
|
|
3418
|
+
node,
|
|
3419
|
+
isBinding,
|
|
3420
|
+
refDestructuringErrors,
|
|
3421
|
+
preserveTypeScriptWrapper,
|
|
3422
|
+
);
|
|
3423
|
+
}
|
|
3424
|
+
|
|
3239
3425
|
/**
|
|
3240
3426
|
* Override checkLocalExport to check all scopes in the scope stack.
|
|
3241
3427
|
* This is needed because submodules create nested scopes, but exports
|
|
@@ -3248,8 +3434,7 @@ export function TSRXPlugin(config) {
|
|
|
3248
3434
|
if (this.hasImport(name)) return;
|
|
3249
3435
|
// Check all scopes in the scope stack, not just the top-level scope
|
|
3250
3436
|
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
|
|
3251
|
-
|
|
3252
|
-
if (scope.lexical.indexOf(name) !== -1 || scope.var.indexOf(name) !== -1) {
|
|
3437
|
+
if (this.#scopeDeclaredNames(this.scopeStack[i]).has(name)) {
|
|
3253
3438
|
// Found in a scope, remove from undefinedExports if it was added
|
|
3254
3439
|
delete this.undefinedExports[name];
|
|
3255
3440
|
return;
|
|
@@ -3259,6 +3444,32 @@ export function TSRXPlugin(config) {
|
|
|
3259
3444
|
this.undefinedExports[name] = id;
|
|
3260
3445
|
}
|
|
3261
3446
|
|
|
3447
|
+
/**
|
|
3448
|
+
* The names declared in `scope`, as a cached Set. Acorn only ever
|
|
3449
|
+
* appends to a scope's `lexical` and `var` arrays during the scope's
|
|
3450
|
+
* lifetime, so syncing from the last-seen lengths is enough to keep the
|
|
3451
|
+
* Set current.
|
|
3452
|
+
*
|
|
3453
|
+
* @param {{ lexical: string[], var: string[] }} scope
|
|
3454
|
+
* @returns {Set<string>}
|
|
3455
|
+
*/
|
|
3456
|
+
#scopeDeclaredNames(scope) {
|
|
3457
|
+
let cached = this.#localExportNamesByScope.get(scope);
|
|
3458
|
+
if (!cached) {
|
|
3459
|
+
cached = { names: new Set(), lexicalLength: 0, varLength: 0 };
|
|
3460
|
+
this.#localExportNamesByScope.set(scope, cached);
|
|
3461
|
+
}
|
|
3462
|
+
for (let i = cached.lexicalLength; i < scope.lexical.length; i++) {
|
|
3463
|
+
cached.names.add(scope.lexical[i]);
|
|
3464
|
+
}
|
|
3465
|
+
for (let i = cached.varLength; i < scope.var.length; i++) {
|
|
3466
|
+
cached.names.add(scope.var[i]);
|
|
3467
|
+
}
|
|
3468
|
+
cached.lexicalLength = scope.lexical.length;
|
|
3469
|
+
cached.varLength = scope.var.length;
|
|
3470
|
+
return cached.names;
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3262
3473
|
/** @type {Parse.Parser['parseForStatement']} */
|
|
3263
3474
|
parseForStatement(node) {
|
|
3264
3475
|
this.next();
|
package/types/parse.d.ts
CHANGED
|
@@ -86,6 +86,11 @@ export namespace Parse {
|
|
|
86
86
|
parenthesizedAssign: number;
|
|
87
87
|
parenthesizedBind: number;
|
|
88
88
|
doubleProto: number;
|
|
89
|
+
/**
|
|
90
|
+
* TSRX extension: position of a pending `&{…}`/`&[…]` lazy binding
|
|
91
|
+
* pattern parsed in expression position, set only by TSRXPlugin.
|
|
92
|
+
*/
|
|
93
|
+
lazyBindingPos?: number;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
/**
|
|
@@ -1605,11 +1610,13 @@ export namespace Parse {
|
|
|
1605
1610
|
* @param node Expression to convert
|
|
1606
1611
|
* @param isBinding Whether binding pattern
|
|
1607
1612
|
* @param refDestructuringErrors Error collector
|
|
1613
|
+
* @param preserveTypeScriptWrapper Keep TS wrapper nodes (acorn-typescript extension)
|
|
1608
1614
|
*/
|
|
1609
1615
|
toAssignable(
|
|
1610
1616
|
node: AST.Node,
|
|
1611
1617
|
isBinding?: boolean,
|
|
1612
1618
|
refDestructuringErrors?: DestructuringErrors,
|
|
1619
|
+
preserveTypeScriptWrapper?: boolean,
|
|
1613
1620
|
): AST.Pattern;
|
|
1614
1621
|
|
|
1615
1622
|
/**
|