@tsrx/core 0.1.63 → 0.1.64
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 +2 -2
- package/src/analyze/index.js +65 -18
- package/src/analyze/validation.js +19 -0
- package/src/diagnostics.js +1 -0
- package/src/index.js +3 -0
- package/src/plugin.js +100 -37
- package/src/source-map-utils.js +21 -66
- package/src/transform/jsx/index.js +48 -24
- package/src/transform/lazy.js +527 -72
- package/src/transform/segments.js +17 -4
- package/src/utils/ast.js +90 -7
- package/types/index.d.ts +2 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core compiler infrastructure for TSRX syntax",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.64",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"is-reference": "^3.0.3",
|
|
80
80
|
"magic-string": "^0.30.18",
|
|
81
81
|
"zimmerframe": "^1.1.2",
|
|
82
|
-
"@tsrx/runtime": "0.1.
|
|
82
|
+
"@tsrx/runtime": "0.1.3"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/node": "^24.3.0",
|
package/src/analyze/index.js
CHANGED
|
@@ -7,30 +7,75 @@ import { walk } from 'zimmerframe';
|
|
|
7
7
|
import {
|
|
8
8
|
is_code_block_function_body,
|
|
9
9
|
is_statement_position,
|
|
10
|
+
is_supported_lazy_assignment_position,
|
|
11
|
+
is_transparent_expression_wrapper,
|
|
10
12
|
is_tsrx_render_output_node,
|
|
11
13
|
} from '../utils/ast.js';
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
validate_forgotten_statement_container,
|
|
16
|
+
validate_unsupported_lazy_assignment_position,
|
|
17
|
+
} from './validation.js';
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* Find the first authored lazy pattern along an assignment target's binding
|
|
21
|
+
* edges. Default-value expressions and computed keys are evaluated
|
|
22
|
+
* expressions, so nested assignments there own their own diagnostics.
|
|
18
23
|
*
|
|
19
|
-
* @param {AST.Node}
|
|
20
|
-
* @
|
|
21
|
-
* @returns {boolean}
|
|
24
|
+
* @param {AST.Node} node
|
|
25
|
+
* @returns {import('../../types/index').LazyPattern | null}
|
|
22
26
|
*/
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
function find_first_lazy_pattern(node) {
|
|
28
|
+
if ((node.type === 'ObjectPattern' || node.type === 'ArrayPattern') && node.lazy) {
|
|
29
|
+
return node;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
switch (node.type) {
|
|
33
|
+
case 'AssignmentPattern':
|
|
34
|
+
return find_first_lazy_pattern(node.left);
|
|
35
|
+
case 'RestElement':
|
|
36
|
+
return find_first_lazy_pattern(node.argument);
|
|
37
|
+
case 'ObjectPattern':
|
|
38
|
+
for (const property of node.properties) {
|
|
39
|
+
const lazy =
|
|
40
|
+
property.type === 'Property'
|
|
41
|
+
? find_first_lazy_pattern(property.value)
|
|
42
|
+
: find_first_lazy_pattern(property.argument);
|
|
43
|
+
if (lazy) return lazy;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
case 'ArrayPattern':
|
|
47
|
+
for (const element of node.elements) {
|
|
48
|
+
if (!element) continue;
|
|
49
|
+
const lazy = find_first_lazy_pattern(element);
|
|
50
|
+
if (lazy) return lazy;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
default: {
|
|
54
|
+
const expression = /** @type {{ expression?: AST.Node }} */ (node).expression;
|
|
55
|
+
return expression && is_transparent_expression_wrapper(node, expression)
|
|
56
|
+
? find_first_lazy_pattern(expression)
|
|
57
|
+
: null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {AST.AssignmentExpression} node
|
|
64
|
+
* @param {{ next: () => unknown, path: AST.Node[], state: TSRXAnalysisState }} context
|
|
65
|
+
*/
|
|
66
|
+
function visit_assignment_expression(node, { next, path, state }) {
|
|
67
|
+
const lazy = find_first_lazy_pattern(node.left);
|
|
68
|
+
|
|
69
|
+
if (lazy && !is_supported_lazy_assignment_position(node, path)) {
|
|
70
|
+
validate_unsupported_lazy_assignment_position(
|
|
71
|
+
lazy,
|
|
72
|
+
state.filename,
|
|
73
|
+
state.collect ? state.errors : undefined,
|
|
74
|
+
state.comments,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
next();
|
|
34
79
|
}
|
|
35
80
|
|
|
36
81
|
/**
|
|
@@ -126,6 +171,8 @@ function visit_class(_node, { next, state }) {
|
|
|
126
171
|
}
|
|
127
172
|
|
|
128
173
|
const visitors = {
|
|
174
|
+
AssignmentExpression: visit_assignment_expression,
|
|
175
|
+
|
|
129
176
|
FunctionDeclaration: visit_function,
|
|
130
177
|
FunctionExpression: visit_function,
|
|
131
178
|
ArrowFunctionExpression: visit_function,
|
|
@@ -30,6 +30,8 @@ export const TSRX_DO_WHILE_STATEMENT_ERROR =
|
|
|
30
30
|
'Do...while loops are not supported in TSRX templates. Move the do...while loop into a function.';
|
|
31
31
|
export const TSRX_FORGOTTEN_STATEMENT_CONTAINER_ERROR =
|
|
32
32
|
"This TSRX template output is unused. Return it, assign it to a value that is rendered, or make it part of the rendered output of a function '@{...}' body.";
|
|
33
|
+
export const TSRX_UNSUPPORTED_LAZY_ASSIGNMENT_POSITION_ERROR =
|
|
34
|
+
'Lazy destructuring assignments require a directly lazy target as a standalone statement inside a program, block, TSRX code block, or switch case.';
|
|
33
35
|
|
|
34
36
|
const invalid_nestings = {
|
|
35
37
|
// <p> cannot contain block-level elements
|
|
@@ -220,6 +222,23 @@ export function validate_forgotten_statement_container(node, filename, errors, c
|
|
|
220
222
|
);
|
|
221
223
|
}
|
|
222
224
|
|
|
225
|
+
/**
|
|
226
|
+
* @param {import('../../types/index').LazyPattern} node
|
|
227
|
+
* @param {string | null | undefined} filename
|
|
228
|
+
* @param {CompileError[]} [errors]
|
|
229
|
+
* @param {AST.CommentWithLocation[]} [comments]
|
|
230
|
+
*/
|
|
231
|
+
export function validate_unsupported_lazy_assignment_position(node, filename, errors, comments) {
|
|
232
|
+
error(
|
|
233
|
+
TSRX_UNSUPPORTED_LAZY_ASSIGNMENT_POSITION_ERROR,
|
|
234
|
+
filename ?? null,
|
|
235
|
+
node,
|
|
236
|
+
errors,
|
|
237
|
+
comments,
|
|
238
|
+
DIAGNOSTIC_CODES.UNSUPPORTED_LAZY_ASSIGNMENT_POSITION,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
223
242
|
/**
|
|
224
243
|
* @param {AST.ReturnStatement} node
|
|
225
244
|
* @param {string | null | undefined} filename
|
package/src/diagnostics.js
CHANGED
|
@@ -5,4 +5,5 @@ export const DIAGNOSTIC_CODES = {
|
|
|
5
5
|
TEMPLATE_EXPRESSION_TRAILING_SEMICOLON: 'tsrx-template-expression-trailing-semicolon',
|
|
6
6
|
TEMPLATE_RETURN_STATEMENT: 'tsrx-template-return-statement',
|
|
7
7
|
FORGOTTEN_STATEMENT_CONTAINER: 'tsrx-forgotten-statement-container',
|
|
8
|
+
UNSUPPORTED_LAZY_ASSIGNMENT_POSITION: 'tsrx-unsupported-lazy-assignment-position',
|
|
8
9
|
};
|
package/src/index.js
CHANGED
|
@@ -94,6 +94,7 @@ export {
|
|
|
94
94
|
is_template_directive as isTemplateDirective,
|
|
95
95
|
is_tsrx_render_output_node as isTsrxRenderOutputNode,
|
|
96
96
|
is_code_block_function_body as isCodeBlockFunctionBody,
|
|
97
|
+
is_statement_list_item as isStatementListItem,
|
|
97
98
|
is_statement_position as isStatementPosition,
|
|
98
99
|
} from './utils/ast.js';
|
|
99
100
|
|
|
@@ -262,6 +263,7 @@ export {
|
|
|
262
263
|
TSRX_LOOP_CONTINUE_ERROR,
|
|
263
264
|
TSRX_LOOP_RETURN_ERROR,
|
|
264
265
|
TSRX_RETURN_STATEMENT_ERROR,
|
|
266
|
+
TSRX_UNSUPPORTED_LAZY_ASSIGNMENT_POSITION_ERROR,
|
|
265
267
|
TSRX_WHILE_STATEMENT_ERROR,
|
|
266
268
|
get_return_keyword_node as getReturnKeywordNode,
|
|
267
269
|
get_statement_keyword_node as getStatementKeywordNode,
|
|
@@ -272,6 +274,7 @@ export {
|
|
|
272
274
|
validate_tsrx_loop_continue_statement as validateTsrxLoopContinueStatement,
|
|
273
275
|
validate_tsrx_loop_return_statement as validateTsrxLoopReturnStatement,
|
|
274
276
|
validate_tsrx_return_statement as validateTsrxReturnStatement,
|
|
277
|
+
validate_unsupported_lazy_assignment_position as validateUnsupportedLazyAssignmentPosition,
|
|
275
278
|
validate_tsrx_unsupported_loop_statement as validateTsrxUnsupportedLoopStatement,
|
|
276
279
|
validate_forgotten_statement_container as validateForgottenStatementContainer,
|
|
277
280
|
validate_nesting as validateNesting,
|
package/src/plugin.js
CHANGED
|
@@ -49,6 +49,60 @@ const CharCode = Object.freeze({
|
|
|
49
49
|
closeBrace: 125,
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
/** @type {WeakMap<Parse.Parser, number[]>} */
|
|
53
|
+
const parser_line_starts = new WeakMap();
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolve an offset without rescanning the source from the beginning on every
|
|
57
|
+
* TSRX tokenizer rewind. Acorn's `getLineInfo` is linear in the offset; the
|
|
58
|
+
* parser calls this path often enough that large modules otherwise pay a
|
|
59
|
+
* quadratic location-tracking cost.
|
|
60
|
+
*
|
|
61
|
+
* @param {Parse.Parser} parser
|
|
62
|
+
* @param {number} offset
|
|
63
|
+
* @returns {acorn.Position}
|
|
64
|
+
*/
|
|
65
|
+
function get_line_info(parser, offset) {
|
|
66
|
+
let starts = parser_line_starts.get(parser);
|
|
67
|
+
if (starts === undefined) {
|
|
68
|
+
starts = [0];
|
|
69
|
+
for (let i = 0; i < parser.input.length; i++) {
|
|
70
|
+
const ch = parser.input.charCodeAt(i);
|
|
71
|
+
if (ch === CharCode.carriageReturn && parser.input.charCodeAt(i + 1) === CharCode.lineFeed) {
|
|
72
|
+
i++;
|
|
73
|
+
starts.push(i + 1);
|
|
74
|
+
} else if (
|
|
75
|
+
ch === CharCode.lineFeed ||
|
|
76
|
+
ch === CharCode.carriageReturn ||
|
|
77
|
+
ch === 0x2028 ||
|
|
78
|
+
ch === 0x2029
|
|
79
|
+
) {
|
|
80
|
+
starts.push(i + 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
parser_line_starts.set(parser, starts);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let low = 0;
|
|
87
|
+
let high = starts.length;
|
|
88
|
+
while (low + 1 < high) {
|
|
89
|
+
const middle = (low + high) >>> 1;
|
|
90
|
+
if (starts[middle] <= offset) low = middle;
|
|
91
|
+
else high = middle;
|
|
92
|
+
}
|
|
93
|
+
// `getLineInfo(input, offset)` treats a CR as a complete line break when
|
|
94
|
+
// `offset` points at the LF of a CRLF pair, even though later offsets treat
|
|
95
|
+
// the pair as one terminator. Preserve that boundary behavior exactly.
|
|
96
|
+
if (
|
|
97
|
+
offset > 0 &&
|
|
98
|
+
parser.input.charCodeAt(offset) === CharCode.lineFeed &&
|
|
99
|
+
parser.input.charCodeAt(offset - 1) === CharCode.carriageReturn
|
|
100
|
+
) {
|
|
101
|
+
return new acorn.Position(low + 2, 0);
|
|
102
|
+
}
|
|
103
|
+
return new acorn.Position(low + 1, offset - starts[low]);
|
|
104
|
+
}
|
|
105
|
+
|
|
52
106
|
// Transparent wrappers to look through when validating a dynamic tag
|
|
53
107
|
// expression (`<{expr}>`), and syntax that disqualifies one outright.
|
|
54
108
|
const DYNAMIC_TAG_WRAPPER_TYPES = new Set([
|
|
@@ -709,12 +763,12 @@ export function TSRXPlugin(config) {
|
|
|
709
763
|
}
|
|
710
764
|
if (this.#isTemplateBlockCommentStart(index)) {
|
|
711
765
|
const comment_start = index;
|
|
712
|
-
const comment_start_loc =
|
|
766
|
+
const comment_start_loc = get_line_info(this, comment_start);
|
|
713
767
|
const close = this.input.indexOf('*/', index + 2);
|
|
714
768
|
const value_end = close === -1 ? this.input.length : close;
|
|
715
769
|
index = close === -1 ? this.input.length : close + 2;
|
|
716
770
|
if (this.options.onComment && comment_start >= token_end) {
|
|
717
|
-
const comment_end_loc =
|
|
771
|
+
const comment_end_loc = get_line_info(this, index);
|
|
718
772
|
this.options.onComment(
|
|
719
773
|
true,
|
|
720
774
|
this.input.slice(comment_start + 2, value_end),
|
|
@@ -741,7 +795,7 @@ export function TSRXPlugin(config) {
|
|
|
741
795
|
index++;
|
|
742
796
|
}
|
|
743
797
|
|
|
744
|
-
const endLoc =
|
|
798
|
+
const endLoc = get_line_info(this, index);
|
|
745
799
|
const node = /** @type {ESTreeJSX.JSXText} */ (this.startNodeAt(start, this.startLoc));
|
|
746
800
|
node.value = value;
|
|
747
801
|
node.raw = this.input.slice(start, index);
|
|
@@ -793,7 +847,7 @@ export function TSRXPlugin(config) {
|
|
|
793
847
|
}
|
|
794
848
|
}
|
|
795
849
|
if (!has_newline) return;
|
|
796
|
-
const loc =
|
|
850
|
+
const loc = get_line_info(this, index);
|
|
797
851
|
this.start = index;
|
|
798
852
|
this.startLoc = new acorn.Position(loc.line, loc.column);
|
|
799
853
|
if (this.pos <= index) {
|
|
@@ -822,8 +876,8 @@ export function TSRXPlugin(config) {
|
|
|
822
876
|
this.input.slice(start + 2, end),
|
|
823
877
|
start,
|
|
824
878
|
end,
|
|
825
|
-
|
|
826
|
-
|
|
879
|
+
get_line_info(this, start),
|
|
880
|
+
get_line_info(this, end),
|
|
827
881
|
metadata,
|
|
828
882
|
);
|
|
829
883
|
}
|
|
@@ -936,7 +990,7 @@ export function TSRXPlugin(config) {
|
|
|
936
990
|
}
|
|
937
991
|
this.pos = start;
|
|
938
992
|
this.start = start;
|
|
939
|
-
this.startLoc =
|
|
993
|
+
this.startLoc = get_line_info(this, start);
|
|
940
994
|
this.exprAllowed = true;
|
|
941
995
|
this.#suppressTemplateRawTextToken = true;
|
|
942
996
|
this.next();
|
|
@@ -1031,7 +1085,7 @@ export function TSRXPlugin(config) {
|
|
|
1031
1085
|
index++;
|
|
1032
1086
|
}
|
|
1033
1087
|
|
|
1034
|
-
const endLoc =
|
|
1088
|
+
const endLoc = get_line_info(this, index);
|
|
1035
1089
|
const node = /** @type {ESTreeJSX.JSXText} */ (this.startNodeAt(start, this.startLoc));
|
|
1036
1090
|
node.value = this.input.slice(start, index);
|
|
1037
1091
|
node.raw = node.value;
|
|
@@ -1195,7 +1249,7 @@ export function TSRXPlugin(config) {
|
|
|
1195
1249
|
const start = this.pos;
|
|
1196
1250
|
const index = this.#templateRawTextEnd(start);
|
|
1197
1251
|
|
|
1198
|
-
const endLoc =
|
|
1252
|
+
const endLoc = get_line_info(this, index);
|
|
1199
1253
|
const value = this.input.slice(start, index);
|
|
1200
1254
|
if (value.match(regex_newline_characters)) {
|
|
1201
1255
|
this.curLine = endLoc.line;
|
|
@@ -1394,7 +1448,7 @@ export function TSRXPlugin(config) {
|
|
|
1394
1448
|
let node;
|
|
1395
1449
|
try {
|
|
1396
1450
|
if (this.type === tstt.jsxText || this.type === tstt.jsxName) {
|
|
1397
|
-
const loc =
|
|
1451
|
+
const loc = get_line_info(this, this.start);
|
|
1398
1452
|
this.pos = this.start;
|
|
1399
1453
|
this.curLine = loc.line;
|
|
1400
1454
|
this.lineStart = this.start - loc.column;
|
|
@@ -1429,7 +1483,7 @@ export function TSRXPlugin(config) {
|
|
|
1429
1483
|
// (a preceding setup statement's context restore can strip the JSX tag
|
|
1430
1484
|
// contexts the trailing `<`/`@` token first pushed).
|
|
1431
1485
|
if (this.start !== at_index) {
|
|
1432
|
-
const loc =
|
|
1486
|
+
const loc = get_line_info(this, at_index);
|
|
1433
1487
|
this.pos = at_index;
|
|
1434
1488
|
this.start = at_index;
|
|
1435
1489
|
this.startLoc = new acorn.Position(loc.line, loc.column);
|
|
@@ -1541,7 +1595,7 @@ export function TSRXPlugin(config) {
|
|
|
1541
1595
|
if (this.curContext() !== b_stat) {
|
|
1542
1596
|
this.context.push(b_stat);
|
|
1543
1597
|
}
|
|
1544
|
-
const braceLoc =
|
|
1598
|
+
const braceLoc = get_line_info(this, braceStart);
|
|
1545
1599
|
this.pos = braceStart;
|
|
1546
1600
|
this.start = braceStart;
|
|
1547
1601
|
this.startLoc = new acorn.Position(braceLoc.line, braceLoc.column);
|
|
@@ -1681,7 +1735,7 @@ export function TSRXPlugin(config) {
|
|
|
1681
1735
|
const keywordStart = start + 1;
|
|
1682
1736
|
this.pos = keywordStart;
|
|
1683
1737
|
this.start = keywordStart;
|
|
1684
|
-
this.startLoc =
|
|
1738
|
+
this.startLoc = get_line_info(this, keywordStart);
|
|
1685
1739
|
this.curLine = this.startLoc.line;
|
|
1686
1740
|
this.lineStart = keywordStart - this.startLoc.column;
|
|
1687
1741
|
this.#filterTemplateScriptContexts();
|
|
@@ -1814,13 +1868,13 @@ export function TSRXPlugin(config) {
|
|
|
1814
1868
|
start: keywordStart,
|
|
1815
1869
|
end: keywordEnd,
|
|
1816
1870
|
loc: {
|
|
1817
|
-
start:
|
|
1818
|
-
end:
|
|
1871
|
+
start: get_line_info(this, keywordStart),
|
|
1872
|
+
end: get_line_info(this, keywordEnd),
|
|
1819
1873
|
},
|
|
1820
1874
|
};
|
|
1821
1875
|
this.pos = wordStart;
|
|
1822
1876
|
this.start = wordStart;
|
|
1823
|
-
this.startLoc =
|
|
1877
|
+
this.startLoc = get_line_info(this, wordStart);
|
|
1824
1878
|
this.curLine = this.startLoc.line;
|
|
1825
1879
|
this.lineStart = wordStart - this.startLoc.column;
|
|
1826
1880
|
this.#filterTemplateScriptContexts();
|
|
@@ -1856,7 +1910,7 @@ export function TSRXPlugin(config) {
|
|
|
1856
1910
|
|
|
1857
1911
|
this.pos = wordStart;
|
|
1858
1912
|
this.start = wordStart;
|
|
1859
|
-
this.startLoc =
|
|
1913
|
+
this.startLoc = get_line_info(this, wordStart);
|
|
1860
1914
|
this.curLine = this.startLoc.line;
|
|
1861
1915
|
this.lineStart = wordStart - this.startLoc.column;
|
|
1862
1916
|
this.#filterTemplateScriptContexts();
|
|
@@ -2211,12 +2265,12 @@ export function TSRXPlugin(config) {
|
|
|
2211
2265
|
|
|
2212
2266
|
if (relativeCloseStart !== -1) {
|
|
2213
2267
|
const closingStart = contentStart + content.length;
|
|
2214
|
-
const closingLineInfo =
|
|
2268
|
+
const closingLineInfo = get_line_info(this, closingStart);
|
|
2215
2269
|
const closingStartLoc = new acorn.Position(closingLineInfo.line, closingLineInfo.column);
|
|
2216
2270
|
const nameStart = closingStart + 2;
|
|
2217
2271
|
const nameEnd = nameStart + tagName.length;
|
|
2218
|
-
const nameStartInfo =
|
|
2219
|
-
const nameEndInfo =
|
|
2272
|
+
const nameStartInfo = get_line_info(this, nameStart);
|
|
2273
|
+
const nameEndInfo = get_line_info(this, nameEnd);
|
|
2220
2274
|
const name = /** @type {ESTreeJSX.JSXIdentifier} */ (
|
|
2221
2275
|
this.startNodeAt(
|
|
2222
2276
|
nameStart,
|
|
@@ -2231,7 +2285,7 @@ export function TSRXPlugin(config) {
|
|
|
2231
2285
|
new acorn.Position(nameEndInfo.line, nameEndInfo.column),
|
|
2232
2286
|
);
|
|
2233
2287
|
const closingEnd = closingStart + closeTag.length;
|
|
2234
|
-
const closingEndInfo =
|
|
2288
|
+
const closingEndInfo = get_line_info(this, closingEnd);
|
|
2235
2289
|
const closingElement =
|
|
2236
2290
|
/** @type {ESTreeJSX.TSRXJSXClosingElement & AST.NodeWithLocation} */ (
|
|
2237
2291
|
this.startNodeAt(closingStart, closingStartLoc)
|
|
@@ -2337,14 +2391,14 @@ export function TSRXPlugin(config) {
|
|
|
2337
2391
|
node.children = [];
|
|
2338
2392
|
|
|
2339
2393
|
if (content.length > 0) {
|
|
2340
|
-
const bodyStartInfo =
|
|
2394
|
+
const bodyStartInfo = get_line_info(this, open.end);
|
|
2341
2395
|
const text = /** @type {ESTreeJSX.JSXText} */ (
|
|
2342
2396
|
this.startNodeAt(open.end, new acorn.Position(bodyStartInfo.line, bodyStartInfo.column))
|
|
2343
2397
|
);
|
|
2344
2398
|
text.value = content;
|
|
2345
2399
|
text.raw = content;
|
|
2346
2400
|
const bodyEnd = open.end + content.length;
|
|
2347
|
-
const bodyEndInfo =
|
|
2401
|
+
const bodyEndInfo = get_line_info(this, bodyEnd);
|
|
2348
2402
|
this.finishNodeAt(
|
|
2349
2403
|
text,
|
|
2350
2404
|
'JSXText',
|
|
@@ -2625,8 +2679,8 @@ export function TSRXPlugin(config) {
|
|
|
2625
2679
|
#report_recoverable_error_range(position, end, message, code) {
|
|
2626
2680
|
const start = Math.max(0, Math.min(position, this.input.length));
|
|
2627
2681
|
const range_end = Math.max(start, Math.min(end, this.input.length));
|
|
2628
|
-
const start_loc =
|
|
2629
|
-
const end_loc =
|
|
2682
|
+
const start_loc = get_line_info(this, start);
|
|
2683
|
+
const end_loc = get_line_info(this, range_end);
|
|
2630
2684
|
|
|
2631
2685
|
error(
|
|
2632
2686
|
message,
|
|
@@ -3026,7 +3080,7 @@ export function TSRXPlugin(config) {
|
|
|
3026
3080
|
this.input.charCodeAt(this.pos - 1) === CharCode.equals
|
|
3027
3081
|
) {
|
|
3028
3082
|
const start = this.pos - 1;
|
|
3029
|
-
const loc =
|
|
3083
|
+
const loc = get_line_info(this, start);
|
|
3030
3084
|
this.start = start;
|
|
3031
3085
|
this.startLoc = loc;
|
|
3032
3086
|
this.pos++;
|
|
@@ -3078,7 +3132,7 @@ export function TSRXPlugin(config) {
|
|
|
3078
3132
|
this.input.charCodeAt(this.pos - 1) === CharCode.equals
|
|
3079
3133
|
) {
|
|
3080
3134
|
const start = this.pos - 1;
|
|
3081
|
-
const loc =
|
|
3135
|
+
const loc = get_line_info(this, start);
|
|
3082
3136
|
this.start = start;
|
|
3083
3137
|
this.startLoc = loc;
|
|
3084
3138
|
this.pos++;
|
|
@@ -3823,8 +3877,8 @@ export function TSRXPlugin(config) {
|
|
|
3823
3877
|
}
|
|
3824
3878
|
const brace_start = skip_whitespace_from(this.input, name_end);
|
|
3825
3879
|
if (this.input.charCodeAt(brace_start) === CharCode.closeBrace) {
|
|
3826
|
-
const name_start_loc =
|
|
3827
|
-
const name_end_loc =
|
|
3880
|
+
const name_start_loc = get_line_info(this, name_start);
|
|
3881
|
+
const name_end_loc = get_line_info(this, name_end);
|
|
3828
3882
|
const name_value = this.input.slice(name_start, name_end);
|
|
3829
3883
|
const id = /** @type {ESTreeJSX.JSXIdentifier} */ (
|
|
3830
3884
|
this.startNodeAt(name_start, name_start_loc)
|
|
@@ -3844,14 +3898,14 @@ export function TSRXPlugin(config) {
|
|
|
3844
3898
|
expression,
|
|
3845
3899
|
'JSXExpressionContainer',
|
|
3846
3900
|
brace_start + 1,
|
|
3847
|
-
|
|
3901
|
+
get_line_info(this, brace_start + 1),
|
|
3848
3902
|
);
|
|
3849
3903
|
/** @type {ESTreeJSX.JSXAttribute} */ (node).name = id;
|
|
3850
3904
|
/** @type {ESTreeJSX.JSXAttribute} */ (node).value = expression;
|
|
3851
3905
|
/** @type {ESTreeJSX.JSXAttribute} */ (node).shorthand = true;
|
|
3852
3906
|
|
|
3853
3907
|
const end = brace_start + 1;
|
|
3854
|
-
const endLoc =
|
|
3908
|
+
const endLoc = get_line_info(this, end);
|
|
3855
3909
|
this.pos = end;
|
|
3856
3910
|
this.curLine = endLoc.line;
|
|
3857
3911
|
this.lineStart = end - endLoc.column;
|
|
@@ -4104,7 +4158,7 @@ export function TSRXPlugin(config) {
|
|
|
4104
4158
|
if (this.input.charCodeAt(paramStart) === CharCode.openParen) {
|
|
4105
4159
|
this.pos = paramStart;
|
|
4106
4160
|
this.start = paramStart;
|
|
4107
|
-
this.startLoc =
|
|
4161
|
+
this.startLoc = get_line_info(this, paramStart);
|
|
4108
4162
|
this.curLine = this.startLoc.line;
|
|
4109
4163
|
this.lineStart = paramStart - this.startLoc.column;
|
|
4110
4164
|
this.#filterTemplateScriptContexts();
|
|
@@ -4274,7 +4328,7 @@ export function TSRXPlugin(config) {
|
|
|
4274
4328
|
this.input.charCodeAt(index + 1) === CharCode.greaterThan &&
|
|
4275
4329
|
this.context.includes(tstc.tc_expr)
|
|
4276
4330
|
) {
|
|
4277
|
-
const loc =
|
|
4331
|
+
const loc = get_line_info(this, index);
|
|
4278
4332
|
this.pos = index;
|
|
4279
4333
|
this.start = index;
|
|
4280
4334
|
this.startLoc = loc;
|
|
@@ -4467,7 +4521,7 @@ export function TSRXPlugin(config) {
|
|
|
4467
4521
|
!this.#shouldReadTemplateRawTextToken()
|
|
4468
4522
|
) {
|
|
4469
4523
|
const start = this.pos - 1;
|
|
4470
|
-
const loc =
|
|
4524
|
+
const loc = get_line_info(this, start);
|
|
4471
4525
|
this.start = start;
|
|
4472
4526
|
this.startLoc = loc;
|
|
4473
4527
|
this.pos++;
|
|
@@ -4818,7 +4872,7 @@ export function TSRXPlugin(config) {
|
|
|
4818
4872
|
);
|
|
4819
4873
|
text_node.value = ws_value;
|
|
4820
4874
|
text_node.raw = ws_value;
|
|
4821
|
-
const loc =
|
|
4875
|
+
const loc = get_line_info(this, at_index);
|
|
4822
4876
|
const at_position = new acorn.Position(loc.line, loc.column);
|
|
4823
4877
|
this.finishNodeAt(text_node, 'JSXText', at_index, at_position);
|
|
4824
4878
|
if (this.#shouldKeepTemplateTextNode(text_node)) {
|
|
@@ -4928,7 +4982,7 @@ export function TSRXPlugin(config) {
|
|
|
4928
4982
|
this.start > blockEnd &&
|
|
4929
4983
|
/^\s*$/.test(this.input.slice(blockEnd, this.start))
|
|
4930
4984
|
) {
|
|
4931
|
-
const loc =
|
|
4985
|
+
const loc = get_line_info(this, blockEnd);
|
|
4932
4986
|
this.pos = blockEnd;
|
|
4933
4987
|
this.start = blockEnd;
|
|
4934
4988
|
this.startLoc = new acorn.Position(loc.line, loc.column);
|
|
@@ -5373,7 +5427,16 @@ export function TSRXPlugin(config) {
|
|
|
5373
5427
|
parseBlock(createNewLexicalScope, node, exitStrict) {
|
|
5374
5428
|
const parent = this.#path.at(-1);
|
|
5375
5429
|
|
|
5376
|
-
|
|
5430
|
+
// `#templateControlFlowBlockDepth` alone decides here — don't also
|
|
5431
|
+
// require `#isNativeTemplateNode(parent)`: a directive body's own
|
|
5432
|
+
// parsing (`#parseTemplateControlFlowBlock`) empties `#path`, so for
|
|
5433
|
+
// a `@for` nested inside another directive's branch, `parent` is
|
|
5434
|
+
// `undefined` and that check would skip this redirect, dropping the
|
|
5435
|
+
// `@for`'s body into plain statement parsing (nested directives then
|
|
5436
|
+
// land in expression position instead of template position). The
|
|
5437
|
+
// depth counter is set only around a `@for`'s own header+body and
|
|
5438
|
+
// `@empty` clause, so plain JS `for` loops can't false-positive.
|
|
5439
|
+
if (this.#templateControlFlowBlockDepth > 0) {
|
|
5377
5440
|
this.#templateControlFlowBlockDepth--;
|
|
5378
5441
|
try {
|
|
5379
5442
|
return this.#parseTemplateControlFlowBlock(createNewLexicalScope, node, exitStrict);
|