@tsrx/core 0.0.4 → 0.0.6
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/index.js +32 -1
- package/src/parse/index.js +64 -1
- package/src/parse/style.js +1 -1
- package/src/plugin.js +41 -25
- package/src/source-map-utils.js +1 -1
- package/src/transform/await.js +59 -0
- package/src/transform/jsx-interleave.js +99 -0
- package/src/transform/lazy.js +664 -0
- package/src/transform/scoping.js +180 -0
- package/src/transform/segments.js +218 -52
- package/types/index.d.ts +1 -1
- package/types/parse.d.ts +6 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -132,7 +132,38 @@ export { escape } from './utils/escaping.js';
|
|
|
132
132
|
|
|
133
133
|
// Transform
|
|
134
134
|
export { render_stylesheets as renderStylesheets } from './transform/stylesheet.js';
|
|
135
|
-
export {
|
|
135
|
+
export {
|
|
136
|
+
prepare_stylesheet_for_render as prepareStylesheetForRender,
|
|
137
|
+
is_style_element as isStyleElement,
|
|
138
|
+
is_composite_element as isCompositeElement,
|
|
139
|
+
annotate_with_hash as annotateWithHash,
|
|
140
|
+
annotate_component_with_hash as annotateComponentWithHash,
|
|
141
|
+
add_hash_class as addHashClass,
|
|
142
|
+
} from './transform/scoping.js';
|
|
143
|
+
export {
|
|
144
|
+
convert_source_map_to_mappings as convertSourceMapToMappings,
|
|
145
|
+
create_volar_mappings_result as createVolarMappingsResult,
|
|
146
|
+
dedupe_mappings as dedupeMappings,
|
|
147
|
+
serialize_mapping_value as serializeMappingValue,
|
|
148
|
+
} from './transform/segments.js';
|
|
149
|
+
export {
|
|
150
|
+
create_lazy_context as createLazyContext,
|
|
151
|
+
collect_lazy_bindings as collectLazyBindings,
|
|
152
|
+
collect_lazy_bindings_from_component as collectLazyBindingsFromComponent,
|
|
153
|
+
collect_lazy_bindings_from_statements as collectLazyBindingsFromStatements,
|
|
154
|
+
preallocate_lazy_ids as preallocateLazyIds,
|
|
155
|
+
apply_lazy_transforms as applyLazyTransforms,
|
|
156
|
+
replace_lazy_params as replaceLazyParams,
|
|
157
|
+
} from './transform/lazy.js';
|
|
158
|
+
export {
|
|
159
|
+
find_first_top_level_await as findFirstTopLevelAwait,
|
|
160
|
+
find_first_top_level_await_in_component_body as findFirstTopLevelAwaitInComponentBody,
|
|
161
|
+
} from './transform/await.js';
|
|
162
|
+
export {
|
|
163
|
+
is_interleaved_body as isInterleavedBody,
|
|
164
|
+
is_capturable_jsx_child as isCapturableJsxChild,
|
|
165
|
+
capture_jsx_child as captureJsxChild,
|
|
166
|
+
} from './transform/jsx-interleave.js';
|
|
136
167
|
|
|
137
168
|
// Analyze
|
|
138
169
|
export { analyze_css as analyzeCss } from './analyze/css-analyze.js';
|
package/src/parse/index.js
CHANGED
|
@@ -110,6 +110,68 @@ export function isWhitespaceTextNode(node) {
|
|
|
110
110
|
return false;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* @type {AcornPlugin}
|
|
115
|
+
*/
|
|
116
|
+
function elementTemplateClosingTagPlugin(Base) {
|
|
117
|
+
const jsxTagStart = /** @type {any} */ (Base).acornTypeScript?.tokTypes?.jsxTagStart;
|
|
118
|
+
if (!jsxTagStart) return Base;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {any} parser
|
|
122
|
+
*/
|
|
123
|
+
function inElementTemplateBodyDirect(parser) {
|
|
124
|
+
const stack = parser.context;
|
|
125
|
+
const top = stack[stack.length - 1];
|
|
126
|
+
const below = stack[stack.length - 2];
|
|
127
|
+
return top && top.token === '{' && below && below.token === '<tag>...</tag>';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {any} parser
|
|
132
|
+
*/
|
|
133
|
+
function inElementTemplateBodyAnywhere(parser) {
|
|
134
|
+
const stack = parser.context;
|
|
135
|
+
for (let i = 1; i < stack.length; i++) {
|
|
136
|
+
if (
|
|
137
|
+
stack[i] &&
|
|
138
|
+
stack[i].token === '{' &&
|
|
139
|
+
stack[i - 1] &&
|
|
140
|
+
stack[i - 1].token === '<tag>...</tag>'
|
|
141
|
+
) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return class extends Base {
|
|
149
|
+
/** @param {number} code */
|
|
150
|
+
// @ts-ignore — extending acorn's Parser with internal hooks
|
|
151
|
+
getTokenFromCode(code) {
|
|
152
|
+
if (code === 60 /* '<' */ && !(/** @type {any} */ (this).inType)) {
|
|
153
|
+
const self = /** @type {any} */ (this);
|
|
154
|
+
const nextChar =
|
|
155
|
+
self.pos + 1 < self.input.length ? self.input.charCodeAt(self.pos + 1) : -1;
|
|
156
|
+
if (nextChar === 47 /* '/' */ && inElementTemplateBodyDirect(self)) {
|
|
157
|
+
++self.pos;
|
|
158
|
+
return self.finishToken(jsxTagStart);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// @ts-ignore — super dispatches to next layer in the plugin chain
|
|
162
|
+
return super.getTokenFromCode(code);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// @ts-ignore — extending acorn's Parser with internal hooks
|
|
166
|
+
canInsertSemicolon() {
|
|
167
|
+
const self = /** @type {any} */ (this);
|
|
168
|
+
if (self.type === jsxTagStart && inElementTemplateBodyAnywhere(self)) return true;
|
|
169
|
+
// @ts-ignore
|
|
170
|
+
return super.canInsertSemicolon();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
113
175
|
/**
|
|
114
176
|
* Create a parser by composing Acorn with TypeScript/JSX support and optional framework plugins.
|
|
115
177
|
*
|
|
@@ -125,6 +187,7 @@ export function createParser(...plugins) {
|
|
|
125
187
|
acorn.Parser.extend(
|
|
126
188
|
tsPlugin({ jsx: true }),
|
|
127
189
|
...plugins.map((p) => /** @type {AcornPlugin} */ (/** @type {unknown} */ (p))),
|
|
190
|
+
elementTemplateClosingTagPlugin,
|
|
128
191
|
)
|
|
129
192
|
)
|
|
130
193
|
);
|
|
@@ -151,7 +214,7 @@ export function createParser(...plugins) {
|
|
|
151
214
|
allowReturnOutsideFunction: true,
|
|
152
215
|
locations: true,
|
|
153
216
|
onComment,
|
|
154
|
-
|
|
217
|
+
tsrxOptions: {
|
|
155
218
|
filename,
|
|
156
219
|
errors: options?.errors ?? [],
|
|
157
220
|
loose: options?.loose || false,
|
package/src/parse/style.js
CHANGED
package/src/plugin.js
CHANGED
|
@@ -51,9 +51,10 @@ export function TSRXPlugin(config) {
|
|
|
51
51
|
*/
|
|
52
52
|
constructor(options, input) {
|
|
53
53
|
super(options, input);
|
|
54
|
-
|
|
55
|
-
this.#
|
|
56
|
-
this.#
|
|
54
|
+
const tsrx_options = options?.tsrxOptions ?? options?.rippleOptions;
|
|
55
|
+
this.#loose = tsrx_options?.loose === true;
|
|
56
|
+
this.#errors = tsrx_options?.errors;
|
|
57
|
+
this.#filename = tsrx_options?.filename || null;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/**
|
|
@@ -438,6 +439,10 @@ export function TSRXPlugin(config) {
|
|
|
438
439
|
const isTagLikeAfterLt =
|
|
439
440
|
!isWhitespaceAfterLt &&
|
|
440
441
|
(nextChar === 47 || // '/'
|
|
442
|
+
nextChar === 62 || // '>' (fragments: <>)
|
|
443
|
+
nextChar === 64 || // '@'
|
|
444
|
+
nextChar === 36 || // '$'
|
|
445
|
+
nextChar === 95 || // '_'
|
|
441
446
|
(nextChar >= 65 && nextChar <= 90) || // A-Z
|
|
442
447
|
(nextChar >= 97 && nextChar <= 122)); // a-z
|
|
443
448
|
const prevAllowsTagStart =
|
|
@@ -502,13 +507,11 @@ export function TSRXPlugin(config) {
|
|
|
502
507
|
}
|
|
503
508
|
}
|
|
504
509
|
|
|
505
|
-
//
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
return this.finishToken(tstt.jsxTagStart);
|
|
511
|
-
}
|
|
510
|
+
// At the start of a line inside template bodies, only treat `<` as
|
|
511
|
+
// a tag start when the following character can actually begin a tag.
|
|
512
|
+
if (allWhitespace && isTagLikeAfterLt) {
|
|
513
|
+
++this.pos;
|
|
514
|
+
return this.finishToken(tstt.jsxTagStart);
|
|
512
515
|
}
|
|
513
516
|
}
|
|
514
517
|
}
|
|
@@ -1472,7 +1475,7 @@ export function TSRXPlugin(config) {
|
|
|
1472
1475
|
*/
|
|
1473
1476
|
parseElement() {
|
|
1474
1477
|
const inside_head = this.#path.findLast(
|
|
1475
|
-
(n) => n.type === 'Element' && n.id.type === 'Identifier' && n.id.name === 'head',
|
|
1478
|
+
(n) => n.type === 'Element' && n.id && n.id.type === 'Identifier' && n.id.name === 'head',
|
|
1476
1479
|
);
|
|
1477
1480
|
// Adjust the start so we capture the `<` as part of the element
|
|
1478
1481
|
const start = this.start - 1;
|
|
@@ -1491,10 +1494,14 @@ export function TSRXPlugin(config) {
|
|
|
1491
1494
|
// Always attach the concrete opening element node for accurate source mapping
|
|
1492
1495
|
element.openingElement = open;
|
|
1493
1496
|
|
|
1494
|
-
//
|
|
1495
|
-
const
|
|
1497
|
+
// Fragments (<>) produce JSXOpeningFragment with no `name` property
|
|
1498
|
+
const is_fragment = !open.name;
|
|
1499
|
+
const is_tsx_compat = !is_fragment && open.name.type === 'JSXNamespacedName';
|
|
1496
1500
|
const is_tsx =
|
|
1497
|
-
!
|
|
1501
|
+
!is_fragment &&
|
|
1502
|
+
!is_tsx_compat &&
|
|
1503
|
+
open.name.type === 'JSXIdentifier' &&
|
|
1504
|
+
open.name.name === 'tsx';
|
|
1498
1505
|
|
|
1499
1506
|
if (is_tsx_compat) {
|
|
1500
1507
|
const namespace_node = /** @type {ESTreeJSX.JSXNamespacedName} */ (open.name);
|
|
@@ -1544,11 +1551,13 @@ export function TSRXPlugin(config) {
|
|
|
1544
1551
|
}
|
|
1545
1552
|
}
|
|
1546
1553
|
|
|
1547
|
-
if (!is_tsx_compat && !is_tsx) {
|
|
1554
|
+
if (!is_tsx_compat && !is_tsx && !is_fragment) {
|
|
1548
1555
|
/** @type {AST.Element} */ (element).id = /** @type {AST.Identifier} */ (
|
|
1549
1556
|
convert_from_jsx(/** @type {ESTreeJSX.JSXIdentifier} */ (open.name))
|
|
1550
1557
|
);
|
|
1551
1558
|
element.selfClosing = open.selfClosing;
|
|
1559
|
+
} else if (is_fragment) {
|
|
1560
|
+
element.selfClosing = false;
|
|
1552
1561
|
}
|
|
1553
1562
|
|
|
1554
1563
|
element.attributes = open.attributes;
|
|
@@ -1562,6 +1571,10 @@ export function TSRXPlugin(config) {
|
|
|
1562
1571
|
this.pos--;
|
|
1563
1572
|
this.next();
|
|
1564
1573
|
}
|
|
1574
|
+
} else if (is_fragment) {
|
|
1575
|
+
this.enterScope(0);
|
|
1576
|
+
this.parseTemplateBody(/** @type {AST.Element} */ (element).children);
|
|
1577
|
+
this.exitScope();
|
|
1565
1578
|
} else {
|
|
1566
1579
|
if (/** @type {ESTreeJSX.JSXIdentifier} */ (open.name).name === 'script') {
|
|
1567
1580
|
let content = '';
|
|
@@ -1814,7 +1827,7 @@ export function TSRXPlugin(config) {
|
|
|
1814
1827
|
}
|
|
1815
1828
|
}
|
|
1816
1829
|
|
|
1817
|
-
if (element.closingElement && !is_tsx_compat && !is_tsx) {
|
|
1830
|
+
if (element.closingElement && !is_tsx_compat && !is_tsx && element.closingElement.name) {
|
|
1818
1831
|
/** @type {unknown} */ (element.closingElement.name) = convert_from_jsx(
|
|
1819
1832
|
element.closingElement.name,
|
|
1820
1833
|
);
|
|
@@ -2039,12 +2052,13 @@ export function TSRXPlugin(config) {
|
|
|
2039
2052
|
? closingElement.name.namespace.name + ':' + closingElement.name.name.name
|
|
2040
2053
|
: this.getElementName(closingElement.name);
|
|
2041
2054
|
} else {
|
|
2042
|
-
// Regular Element node
|
|
2043
|
-
openingTagName = this.getElementName(currentElement.id);
|
|
2044
|
-
closingTagName =
|
|
2045
|
-
closingElement.name?.type === 'JSXNamespacedName'
|
|
2055
|
+
// Regular Element node (or fragment)
|
|
2056
|
+
openingTagName = currentElement.id ? this.getElementName(currentElement.id) : null;
|
|
2057
|
+
closingTagName = closingElement.name
|
|
2058
|
+
? closingElement.name?.type === 'JSXNamespacedName'
|
|
2046
2059
|
? closingElement.name.namespace.name + ':' + closingElement.name.name.name
|
|
2047
|
-
: this.getElementName(closingElement.name)
|
|
2060
|
+
: this.getElementName(closingElement.name)
|
|
2061
|
+
: null;
|
|
2048
2062
|
}
|
|
2049
2063
|
|
|
2050
2064
|
if (openingTagName !== closingTagName) {
|
|
@@ -2068,7 +2082,9 @@ export function TSRXPlugin(config) {
|
|
|
2068
2082
|
? 'tsx:' + elem.kind
|
|
2069
2083
|
: elem.type === 'Tsx'
|
|
2070
2084
|
? 'tsx'
|
|
2071
|
-
:
|
|
2085
|
+
: elem.id
|
|
2086
|
+
? this.getElementName(elem.id)
|
|
2087
|
+
: null;
|
|
2072
2088
|
|
|
2073
2089
|
// Found matching opening tag
|
|
2074
2090
|
if (elemName === closingTagName) {
|
|
@@ -2089,9 +2105,9 @@ export function TSRXPlugin(config) {
|
|
|
2089
2105
|
|
|
2090
2106
|
const elementToClose = this.#path[this.#path.length - 1];
|
|
2091
2107
|
if (elementToClose && elementToClose.type === 'Element') {
|
|
2092
|
-
const elementToCloseName =
|
|
2093
|
-
/** @type {AST.Element} */ (elementToClose).id
|
|
2094
|
-
|
|
2108
|
+
const elementToCloseName = /** @type {AST.Element} */ (elementToClose).id
|
|
2109
|
+
? this.getElementName(/** @type {AST.Element} */ (elementToClose).id)
|
|
2110
|
+
: null;
|
|
2095
2111
|
if (elementToCloseName === closingTagName) {
|
|
2096
2112
|
/** @type {AST.Element} */ (elementToClose).closingElement = closingElement;
|
|
2097
2113
|
}
|
package/src/source-map-utils.js
CHANGED
|
@@ -286,7 +286,7 @@ export function build_line_offsets(text) {
|
|
|
286
286
|
* @param {number} [gen_max_len]
|
|
287
287
|
* @returns {CodeMapping | Error}
|
|
288
288
|
*/
|
|
289
|
-
function maybe_get_mapping_from_node(
|
|
289
|
+
export function maybe_get_mapping_from_node(
|
|
290
290
|
node,
|
|
291
291
|
src_to_gen_map,
|
|
292
292
|
gen_line_offsets,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {any[]} body_nodes
|
|
3
|
+
* @returns {any | null}
|
|
4
|
+
*/
|
|
5
|
+
export function find_first_top_level_await_in_component_body(body_nodes) {
|
|
6
|
+
for (const node of body_nodes) {
|
|
7
|
+
const found = find_first_top_level_await(node, false);
|
|
8
|
+
if (found) return found;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {any} node
|
|
16
|
+
* @param {boolean} inside_nested_function
|
|
17
|
+
* @returns {any | null}
|
|
18
|
+
*/
|
|
19
|
+
export function find_first_top_level_await(node, inside_nested_function) {
|
|
20
|
+
if (!node || typeof node !== 'object') {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (Array.isArray(node)) {
|
|
25
|
+
for (const child of node) {
|
|
26
|
+
const found = find_first_top_level_await(child, inside_nested_function);
|
|
27
|
+
if (found) return found;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
node.type === 'FunctionDeclaration' ||
|
|
35
|
+
node.type === 'FunctionExpression' ||
|
|
36
|
+
node.type === 'ArrowFunctionExpression'
|
|
37
|
+
) {
|
|
38
|
+
return inside_nested_function ? null : find_first_top_level_await(node.body, true);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (inside_nested_function) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (node.type === 'AwaitExpression' || (node.type === 'ForOfStatement' && node.await === true)) {
|
|
46
|
+
return node;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const key of Object.keys(node)) {
|
|
50
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const found = find_first_top_level_await(node[key], false);
|
|
55
|
+
if (found) return found;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for preserving source-order semantics when non-JSX statements are
|
|
3
|
+
* interleaved with JSX children inside a component or element body.
|
|
4
|
+
*
|
|
5
|
+
* Without these, targets like React and Solid would hoist all statements
|
|
6
|
+
* before any JSX is constructed, so mutations between sibling JSX children
|
|
7
|
+
* would be observed by every sibling instead of only the ones that appear
|
|
8
|
+
* textually after the mutation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Returns true when the body contains a non-JSX statement that appears
|
|
13
|
+
* after a JSX child. In that case JSX children must be captured at their
|
|
14
|
+
* source position so mutations in following statements do not retroactively
|
|
15
|
+
* change what earlier children rendered.
|
|
16
|
+
*
|
|
17
|
+
* The `is_jsx_child` predicate is target-specific — each target recognizes
|
|
18
|
+
* a different set of JSX-bearing node types (Ripple `Element`, `Text`,
|
|
19
|
+
* `TSRXExpression`, etc. plus plain JSX nodes).
|
|
20
|
+
*
|
|
21
|
+
* @param {any[]} body_nodes
|
|
22
|
+
* @param {(node: any) => boolean} is_jsx_child
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function is_interleaved_body(body_nodes, is_jsx_child) {
|
|
26
|
+
let seen_jsx = false;
|
|
27
|
+
for (const child of body_nodes) {
|
|
28
|
+
if (is_jsx_child(child)) {
|
|
29
|
+
seen_jsx = true;
|
|
30
|
+
} else if (seen_jsx) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Only JSX nodes that evaluate to a single expression can be hoisted into a
|
|
39
|
+
* `const`. Static text children (`JSXText`) are inert and don't need
|
|
40
|
+
* capturing — their position relative to mutations doesn't change output.
|
|
41
|
+
*
|
|
42
|
+
* @param {any} jsx
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
export function is_capturable_jsx_child(jsx) {
|
|
46
|
+
if (!jsx) return false;
|
|
47
|
+
const t = jsx.type;
|
|
48
|
+
return t === 'JSXElement' || t === 'JSXFragment' || t === 'JSXExpressionContainer';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build a `VariableDeclaration` that captures a JSX child into a const at
|
|
53
|
+
* its source position, along with a JSXExpressionContainer referencing the
|
|
54
|
+
* capture. The caller inserts the declaration into the enclosing block's
|
|
55
|
+
* statements in source order and uses the reference in place of the JSX
|
|
56
|
+
* child inside the returned fragment.
|
|
57
|
+
*
|
|
58
|
+
* @param {any} jsx
|
|
59
|
+
* @param {number} capture_index
|
|
60
|
+
* @returns {{ declaration: any, reference: any }}
|
|
61
|
+
*/
|
|
62
|
+
export function capture_jsx_child(jsx, capture_index) {
|
|
63
|
+
const name = `_tsrx_child_${capture_index}`;
|
|
64
|
+
const init = jsx.type === 'JSXExpressionContainer' ? jsx.expression : jsx;
|
|
65
|
+
|
|
66
|
+
const declaration = /** @type {any} */ ({
|
|
67
|
+
type: 'VariableDeclaration',
|
|
68
|
+
kind: 'const',
|
|
69
|
+
declarations: [
|
|
70
|
+
/** @type {any} */ ({
|
|
71
|
+
type: 'VariableDeclarator',
|
|
72
|
+
id: /** @type {any} */ ({
|
|
73
|
+
type: 'Identifier',
|
|
74
|
+
name,
|
|
75
|
+
metadata: { path: [] },
|
|
76
|
+
}),
|
|
77
|
+
init,
|
|
78
|
+
metadata: { path: [] },
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
metadata: { path: [] },
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// NOTE: JSXExpressionContainer nodes are intentionally created without
|
|
85
|
+
// loc — they're synthetic wrappers whose source positions don't
|
|
86
|
+
// correspond to source-map entries and adding loc causes Volar mapping
|
|
87
|
+
// failures.
|
|
88
|
+
const reference = /** @type {any} */ ({
|
|
89
|
+
type: 'JSXExpressionContainer',
|
|
90
|
+
expression: /** @type {any} */ ({
|
|
91
|
+
type: 'Identifier',
|
|
92
|
+
name,
|
|
93
|
+
metadata: { path: [] },
|
|
94
|
+
}),
|
|
95
|
+
metadata: { path: [] },
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return { declaration, reference };
|
|
99
|
+
}
|