@tsrx/core 0.1.16 → 0.1.17

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.
@@ -438,14 +438,14 @@ function replace_lazy_in_pattern(pattern, is_top = true) {
438
438
 
439
439
  /**
440
440
  * Walk the AST and pre-allocate `lazy_id` metadata on every lazy destructuring
441
- * pattern: function/component params, variable declarator ids, and statement-level
442
- * assignment LHS. Walks into non-lazy outer patterns to find nested lazy ones,
443
- * e.g. `{ pair: &[a, b] }` allocates an id for the inner `&[a, b]`. Idempotent:
444
- * skips patterns that already have a `lazy_id`.
441
+ * pattern: function params, variable declarator ids, and statement-level
442
+ * assignment LHS. Walks into non-lazy outer patterns to
443
+ * find nested lazy ones, e.g. `{ pair: &[a, b] }` allocates an id for the inner
444
+ * `&[a, b]`. Idempotent: skips patterns that already have a `lazy_id`.
445
445
  *
446
446
  * Also stamps `metadata.has_lazy_descendants = true` on every function-like
447
447
  * node whose subtree contains any lazy pattern, so `apply_lazy_transforms`
448
- * can take a constant-time early-return path for purely non-lazy functions.
448
+ * can take a constant-time fast path for purely non-lazy functions.
449
449
  *
450
450
  * @param {any} root
451
451
  * @param {LazyContext} context
@@ -570,7 +570,7 @@ export function apply_lazy_transforms(node, lazy_bindings) {
570
570
  return node;
571
571
  }
572
572
 
573
- // Past the early-return: either we have active lazy bindings, lazy
573
+ // Past the fast path: either we have active lazy bindings, lazy
574
574
  // params to replace, defaults referencing outer lazy, or the body
575
575
  // contains lazy descendants the BlockStatement handler will collect.
576
576
  // In every case the body needs to be walked.
@@ -89,7 +89,7 @@ function is_composite_jsx_element(node) {
89
89
  }
90
90
 
91
91
  /**
92
- * Recursively walk `Element` nodes within a component body and add the hash
92
+ * Recursively walk `Element` nodes within a TSRX fragment and add the hash
93
93
  * class name so scope-qualified selectors (e.g. `.foo.hash`) match.
94
94
  *
95
95
  * @param {any} node
@@ -106,7 +106,6 @@ export function annotate_with_hash(
106
106
  ) {
107
107
  if (!node || typeof node !== 'object') return node;
108
108
  if (
109
- node.type === 'Component' ||
110
109
  node.type === 'FunctionDeclaration' ||
111
110
  node.type === 'FunctionExpression' ||
112
111
  node.type === 'ArrowFunctionExpression'
@@ -56,7 +56,6 @@ import {
56
56
  get_mapping_from_node,
57
57
  } from '../source-map-utils.js';
58
58
 
59
- const LABEL_TO_COMPONENT_REPLACE_REGEX = /(function|\((property|method)\))/;
60
59
  const LAZY_PARAM_IDENTIFIER_REGEX = /^__lazy\d+$/;
61
60
 
62
61
  /**
@@ -67,17 +66,6 @@ function escape_regex(value) {
67
66
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
67
  }
69
68
 
70
- /**
71
- * @param {string} content
72
- * @returns {string}
73
- */
74
- function replace_label_to_component(content) {
75
- return content.replace(LABEL_TO_COMPONENT_REPLACE_REGEX, (_, fn, kind) => {
76
- if (fn === 'function') return 'component';
77
- return `(component ${kind})`;
78
- });
79
- }
80
-
81
69
  /**
82
70
  * @param {string} lazy_id
83
71
  * @param {(content: string) => string} [base_hover]
@@ -442,53 +430,12 @@ export function convert_source_map_to_mappings(
442
430
  }
443
431
  }
444
432
 
445
- /**
446
- * @typedef {AST.Property & {value: AST.FunctionExpression, method: true} & {value: {metadata: {is_component: true}}}} PropertyIsComponent
447
- */
448
-
449
- /**
450
- * Maps `component` to the identifier's location
451
- * e.g. const obj = { component something() { } }
452
- * since there is no function keyword in source maps
453
- * @param {PropertyIsComponent} node
454
- * @returns {void}
455
- */
456
- function set_component_mapping_to_name(node) {
457
- if (node.key.loc) {
458
- /** @type {CodeMapping} */
459
- let mapping;
460
- let start = /** @type {AST.NodeWithLocation} */ (node).start;
461
- let length = 'component'.length;
462
-
463
- if (node.value.type === 'FunctionExpression' && node.value.id) {
464
- const id = /** @type {AST.Identifier & AST.NodeWithLocation} */ (node.value.id);
465
- mapping = get_mapping_from_node(id, src_to_gen_map, gen_line_offsets);
466
- } else {
467
- // e.g. key is computed or literal
468
- mapping = get_mapping_from_node(node.key, src_to_gen_map, gen_line_offsets);
469
- }
470
-
471
- // overwrite source start and length to point to 'component' keyword
472
- mapping.sourceOffsets = [start];
473
- mapping.lengths = [length];
474
- mapping.data.customData.hover = replace_label_to_component;
475
-
476
- mappings.push(mapping);
477
- }
478
- }
479
-
480
433
  /**
481
434
  * @param {AST.Literal} node
482
- * @param {boolean} [is_component]
483
435
  */
484
- function handle_literal(node, is_component = false) {
436
+ function handle_literal(node) {
485
437
  if (node.loc) {
486
438
  const mapping = get_mapping_from_node(node, src_to_gen_map, gen_line_offsets);
487
-
488
- if (is_component) {
489
- mapping.data.customData.hover = replace_label_to_component;
490
- }
491
-
492
439
  mappings.push(mapping);
493
440
  }
494
441
  }
@@ -524,15 +471,8 @@ export function convert_source_map_to_mappings(
524
471
  };
525
472
  }
526
473
 
527
- if (node.metadata?.is_component) {
528
- // only if the node has a component as the parent
529
- token.metadata.hover = replace_label_to_component;
530
- }
531
474
  if (node.metadata?.source_length != null && LAZY_PARAM_IDENTIFIER_REGEX.test(node.name)) {
532
- token.metadata.hover = create_lazy_param_hover_replacement(
533
- node.name,
534
- node.metadata?.lazy_param_is_component ? replace_label_to_component : undefined,
535
- );
475
+ token.metadata.hover = create_lazy_param_hover_replacement(node.name);
536
476
  }
537
477
  if ('hover' in (node.metadata || {})) {
538
478
  token.metadata.hover = /** @type {any} */ (node.metadata).hover;
@@ -772,25 +712,6 @@ export function convert_source_map_to_mappings(
772
712
  visit(node.name);
773
713
  }
774
714
 
775
- if (
776
- node.name.type === 'JSXIdentifier' &&
777
- node.name.metadata?.is_component &&
778
- node.name.loc
779
- ) {
780
- const mapping = get_mapping_from_node(
781
- node.name,
782
- src_to_gen_map,
783
- gen_line_offsets,
784
- mapping_data,
785
- );
786
- mapping.sourceOffsets = [
787
- /** @type {AST.NodeWithLocation} */ (node.name).start - 'component '.length,
788
- ];
789
- mapping.lengths = ['component'.length];
790
-
791
- mapping.data.customData.hover = replace_label_to_component;
792
- mappings.push(mapping);
793
- }
794
715
  if (node.value) {
795
716
  visit(node.value);
796
717
  }
@@ -920,69 +841,51 @@ export function convert_source_map_to_mappings(
920
841
  }
921
842
  }
922
843
 
923
- // Add function/component keyword token
844
+ // Add the function keyword token.
924
845
  if (
925
846
  (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') &&
926
847
  !is_method &&
927
848
  node.loc
928
849
  ) {
929
850
  const node_fn = /** @type (typeof node) & AST.NodeWithLocation */ (node);
930
- const is_component = node_fn.metadata?.is_component;
931
- const source_func_keyword = is_component ? 'component' : 'function';
932
851
  const function_hover = create_function_hover_replacement(
933
852
  /** @type {AST.Parameter[]} */ (node.params),
934
- is_component ? replace_label_to_component : undefined,
935
853
  );
936
854
  let start_col = node_fn.loc.start.column;
937
855
  let start = node_fn.start;
938
856
  const async_keyword = 'async';
939
857
 
940
- if (is_component && node_fn.id?.loc) {
941
- const mapping = get_mapping_from_node(node_fn.id, src_to_gen_map, gen_line_offsets);
942
- const generated_id_start = mapping.generatedOffsets[0];
943
- const generated_keyword_start = find_component_keyword_offset(
944
- generated_code,
945
- generated_id_start,
946
- );
947
- mapping.sourceOffsets = [start];
948
- mapping.lengths = [source_func_keyword.length];
949
- mapping.generatedOffsets = [generated_keyword_start];
950
- mapping.generatedLengths = ['function'.length];
951
- if (function_hover) mapping.data.customData.hover = function_hover;
952
- mappings.push(mapping);
953
- } else {
954
- if (node_fn.async) {
955
- // We explicitly mapped async and function in esrap
956
- tokens.push({
957
- source: async_keyword,
958
- generated: async_keyword,
959
- loc: {
960
- start: { line: node_fn.loc.start.line, column: start_col },
961
- end: {
962
- line: node_fn.loc.start.line,
963
- column: start_col + async_keyword.length,
964
- },
965
- },
966
- metadata: {},
967
- });
968
-
969
- start_col += async_keyword.length + 1; // +1 for space
970
- start += async_keyword.length + 1;
971
- }
972
-
858
+ if (node_fn.async) {
859
+ // We explicitly mapped async and function in esrap
973
860
  tokens.push({
974
- source: source_func_keyword,
975
- generated: 'function',
861
+ source: async_keyword,
862
+ generated: async_keyword,
976
863
  loc: {
977
864
  start: { line: node_fn.loc.start.line, column: start_col },
978
865
  end: {
979
866
  line: node_fn.loc.start.line,
980
- column: start_col + source_func_keyword.length,
867
+ column: start_col + async_keyword.length,
981
868
  },
982
869
  },
983
- metadata: function_hover ? { hover: function_hover } : {},
870
+ metadata: {},
984
871
  });
872
+
873
+ start_col += async_keyword.length + 1; // +1 for space
874
+ start += async_keyword.length + 1;
985
875
  }
876
+
877
+ tokens.push({
878
+ source: 'function',
879
+ generated: 'function',
880
+ loc: {
881
+ start: { line: node_fn.loc.start.line, column: start_col },
882
+ end: {
883
+ line: node_fn.loc.start.line,
884
+ column: start_col + 'function'.length,
885
+ },
886
+ },
887
+ metadata: function_hover ? { hover: function_hover } : {},
888
+ });
986
889
  }
987
890
 
988
891
  // Visit in source order: id, params, body
@@ -997,7 +900,6 @@ export function convert_source_map_to_mappings(
997
900
  );
998
901
  const function_hover = create_function_hover_replacement(
999
902
  /** @type {AST.Parameter[]} */ (node.params),
1000
- node.metadata?.is_component ? replace_label_to_component : undefined,
1001
903
  );
1002
904
  if (function_hover && id.loc) {
1003
905
  tokens.push({
@@ -1018,13 +920,6 @@ export function convert_source_map_to_mappings(
1018
920
 
1019
921
  if (node.params) {
1020
922
  for (const param of node.params) {
1021
- if (
1022
- param.type === 'Identifier' &&
1023
- param.metadata?.source_length != null &&
1024
- LAZY_PARAM_IDENTIFIER_REGEX.test(param.name)
1025
- ) {
1026
- param.metadata.lazy_param_is_component = node.metadata?.is_component === true;
1027
- }
1028
923
  visit(param);
1029
924
  if (param.typeAnnotation) {
1030
925
  visit(param.typeAnnotation);
@@ -1406,19 +1301,8 @@ export function convert_source_map_to_mappings(
1406
1301
  set_bracket_computed_mapping(node, mappings);
1407
1302
  }
1408
1303
 
1409
- if (
1410
- node.value.type === 'FunctionExpression' &&
1411
- node.method &&
1412
- node.value.metadata.is_component
1413
- ) {
1414
- set_component_mapping_to_name(/** @type {PropertyIsComponent} */ (node));
1415
- }
1416
-
1417
1304
  if (node.key.type === 'Literal') {
1418
- handle_literal(
1419
- node.key,
1420
- /** @type {AST.FunctionExpression} */ (node.value).metadata.is_component,
1421
- );
1305
+ handle_literal(node.key);
1422
1306
  } else {
1423
1307
  visit(node.key);
1424
1308
  }
@@ -2374,21 +2258,6 @@ export function convert_source_map_to_mappings(
2374
2258
  };
2375
2259
  }
2376
2260
 
2377
- /**
2378
- * @param {string} generated_code
2379
- * @param {number} generated_id_start
2380
- * @returns {number}
2381
- */
2382
- function find_component_keyword_offset(generated_code, generated_id_start) {
2383
- const function_keyword_index = generated_code.lastIndexOf('function', generated_id_start);
2384
-
2385
- if (function_keyword_index === -1) {
2386
- return generated_id_start;
2387
- }
2388
-
2389
- return function_keyword_index;
2390
- }
2391
-
2392
2261
  /**
2393
2262
  * Build a `VolarMappingsResult` from generated code plus source-map metadata.
2394
2263
  *
@@ -0,0 +1,235 @@
1
+ /** @import * as AST from 'estree' */
2
+
3
+ import * as b from '../utils/builders.js';
4
+ import { clone_expression_node, clone_identifier } from './jsx/ast-builders.js';
5
+
6
+ /**
7
+ * @typedef {{
8
+ * allowMutableRefTarget?: boolean;
9
+ * createTempIdentifier?: () => AST.Identifier;
10
+ * visitExpression?: (expression: AST.Expression) => AST.Expression;
11
+ * }} StyleRefOptions
12
+ */
13
+
14
+ /**
15
+ * @param {any} component
16
+ * @param {any} css
17
+ * @returns {AST.ObjectExpression}
18
+ */
19
+ export function create_style_class_map(component, css) {
20
+ const hash = css?.hash ?? null;
21
+ const top_scoped_classes = /** @type {Map<string, any>} */ (
22
+ component?.metadata?.topScopedClasses ?? new Map()
23
+ );
24
+ const class_names = [...top_scoped_classes.keys()].sort();
25
+
26
+ return b.object(
27
+ class_names.map((class_name) =>
28
+ b.prop('init', b.literal(class_name), b.literal(hash ? `${hash} ${class_name}` : class_name)),
29
+ ),
30
+ );
31
+ }
32
+
33
+ /**
34
+ * @param {any} node
35
+ * @param {any[]} [refs]
36
+ * @returns {any[]}
37
+ */
38
+ export function collect_style_ref_attributes(node, refs = []) {
39
+ if (!node || typeof node !== 'object') return refs;
40
+
41
+ if (Array.isArray(node)) {
42
+ for (const child of node) collect_style_ref_attributes(child, refs);
43
+ return refs;
44
+ }
45
+
46
+ if (is_style_element(node)) {
47
+ for (const attr of node.attributes || []) {
48
+ if (is_ref_attribute(attr) && attr.value) {
49
+ refs.push(attr);
50
+ }
51
+ }
52
+ return refs;
53
+ }
54
+
55
+ if (is_function_or_class_boundary(node)) {
56
+ return refs;
57
+ }
58
+
59
+ for (const key of Object.keys(node)) {
60
+ if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata' || key === 'css') {
61
+ continue;
62
+ }
63
+ collect_style_ref_attributes(node[key], refs);
64
+ }
65
+
66
+ return refs;
67
+ }
68
+
69
+ /**
70
+ * @param {any[]} ref_attributes
71
+ * @param {AST.Expression} style_map
72
+ * @param {StyleRefOptions} [options]
73
+ * @returns {AST.Statement[]}
74
+ */
75
+ export function create_style_ref_setup_statements(ref_attributes, style_map, options = {}) {
76
+ /** @type {AST.Statement[]} */
77
+ const statements = [];
78
+ for (const attr of ref_attributes) {
79
+ const source = get_ref_attribute_expression(attr);
80
+ if (!source) continue;
81
+ statements.push(...create_style_ref_expression_statements(source, style_map, options));
82
+ }
83
+ return statements;
84
+ }
85
+
86
+ /**
87
+ * @param {AST.Expression} source
88
+ * @param {AST.Expression} style_map
89
+ * @param {StyleRefOptions} options
90
+ * @returns {AST.Statement[]}
91
+ */
92
+ function create_style_ref_expression_statements(source, style_map, options) {
93
+ if (source.type === 'ArrayExpression') {
94
+ return source.elements.flatMap((element) => {
95
+ if (!element) return [];
96
+ const expression = element.type === 'SpreadElement' ? element.argument : element;
97
+ return create_style_ref_expression_statements(
98
+ /** @type {AST.Expression} */ (expression),
99
+ style_map,
100
+ options,
101
+ );
102
+ });
103
+ }
104
+
105
+ if (
106
+ options.allowMutableRefTarget !== false &&
107
+ (source.type === 'Identifier' || source.type === 'MemberExpression')
108
+ ) {
109
+ const target = clone_expression_node(source, false);
110
+ return [
111
+ b.stmt(
112
+ b.assignment(
113
+ '=',
114
+ /** @type {AST.Pattern} */ (target),
115
+ clone_expression_node(style_map, false),
116
+ ),
117
+ ),
118
+ ];
119
+ }
120
+
121
+ if (source.type === 'ArrowFunctionExpression' || source.type === 'FunctionExpression') {
122
+ return [
123
+ b.stmt(
124
+ b.call(
125
+ visit_expression(clone_expression_node(source, false), options),
126
+ clone_expression_node(style_map, false),
127
+ ),
128
+ ),
129
+ ];
130
+ }
131
+
132
+ return create_dynamic_style_ref_statement(source, style_map, options);
133
+ }
134
+
135
+ /**
136
+ * @param {AST.Expression} source
137
+ * @param {AST.Expression} style_map
138
+ * @param {StyleRefOptions} options
139
+ * @returns {AST.Statement[]}
140
+ */
141
+ function create_dynamic_style_ref_statement(source, style_map, options) {
142
+ const ref_id = options.createTempIdentifier?.() ?? b.id('__tsrx_style_ref');
143
+ const ref_read = () => clone_identifier(ref_id);
144
+ const current_write = b.stmt(
145
+ b.assignment('=', b.member(ref_read(), 'current'), clone_expression_node(style_map, false)),
146
+ );
147
+ const value_write = b.stmt(
148
+ b.assignment('=', b.member(ref_read(), 'value'), clone_expression_node(style_map, false)),
149
+ );
150
+
151
+ return [
152
+ b.let(ref_id, visit_expression(clone_expression_node(source, false), options)),
153
+ b.if(
154
+ b.binary('===', b.unary('typeof', ref_read()), b.literal('function')),
155
+ b.block([b.stmt(b.call(ref_read(), clone_expression_node(style_map, false)))]),
156
+ b.if(
157
+ b.logical(
158
+ '&&',
159
+ ref_read(),
160
+ b.binary('===', b.unary('typeof', ref_read()), b.literal('object')),
161
+ ),
162
+ b.block([
163
+ b.if(
164
+ b.binary('in', b.literal('current'), ref_read()),
165
+ b.block([current_write]),
166
+ b.if(b.binary('in', b.literal('value'), ref_read()), b.block([value_write]), null),
167
+ ),
168
+ ]),
169
+ null,
170
+ ),
171
+ ),
172
+ ];
173
+ }
174
+
175
+ /**
176
+ * @param {AST.Expression} expression
177
+ * @param {StyleRefOptions} options
178
+ * @returns {AST.Expression}
179
+ */
180
+ function visit_expression(expression, options) {
181
+ return options.visitExpression ? options.visitExpression(expression) : expression;
182
+ }
183
+
184
+ /**
185
+ * @param {any} attr
186
+ * @returns {AST.Expression | null}
187
+ */
188
+ function get_ref_attribute_expression(attr) {
189
+ const value = attr.value;
190
+ if (!value) return null;
191
+ if (value.type === 'JSXExpressionContainer') {
192
+ return value.expression.type === 'JSXEmptyExpression' ? null : value.expression;
193
+ }
194
+ return value;
195
+ }
196
+
197
+ /**
198
+ * @param {any} attr
199
+ * @returns {boolean}
200
+ */
201
+ function is_ref_attribute(attr) {
202
+ return (
203
+ (attr?.type === 'Attribute' && attr.name?.type === 'Identifier' && attr.name.name === 'ref') ||
204
+ (attr?.type === 'JSXAttribute' &&
205
+ attr.name?.type === 'JSXIdentifier' &&
206
+ attr.name.name === 'ref')
207
+ );
208
+ }
209
+
210
+ /**
211
+ * @param {any} node
212
+ * @returns {boolean}
213
+ */
214
+ function is_style_element(node) {
215
+ return !!(
216
+ node &&
217
+ node.type === 'Element' &&
218
+ node.id?.type === 'Identifier' &&
219
+ node.id.name === 'style'
220
+ );
221
+ }
222
+
223
+ /**
224
+ * @param {any} node
225
+ * @returns {boolean}
226
+ */
227
+ function is_function_or_class_boundary(node) {
228
+ return (
229
+ node?.type === 'FunctionDeclaration' ||
230
+ node?.type === 'FunctionExpression' ||
231
+ node?.type === 'ArrowFunctionExpression' ||
232
+ node?.type === 'ClassDeclaration' ||
233
+ node?.type === 'ClassExpression'
234
+ );
235
+ }
package/src/utils/ast.js CHANGED
@@ -37,12 +37,7 @@ export function is_function_node(node) {
37
37
  * @returns {boolean}
38
38
  */
39
39
  export function is_function_or_component_node(node) {
40
- return (
41
- node.type === 'FunctionDeclaration' ||
42
- node.type === 'FunctionExpression' ||
43
- node.type === 'ArrowFunctionExpression' ||
44
- node.type === 'Component'
45
- );
40
+ return is_function_node(node);
46
41
  }
47
42
 
48
43
  /**
@@ -54,32 +49,29 @@ export function is_class_node(node) {
54
49
  }
55
50
 
56
51
  /**
57
- * @param {AST.Node} node
58
- * @returns {boolean}
59
- */
60
- export function is_component_node(node) {
61
- return node.type === 'Component';
62
- }
63
-
64
- /**
65
- * Returns the closest component in an ancestry path. By default, function and
66
- * class boundaries stop the search so callers only match direct component
67
- * body/control-flow nodes.
52
+ * Returns the closest native TSRX function in an ancestry path. By default,
53
+ * function and class boundaries stop the search so callers only match direct
54
+ * native TSRX function body/control-flow nodes.
68
55
  *
69
56
  * @param {AST.Node[]} path
70
57
  * @param {boolean} [includes_functions=false]
71
- * @returns {AST.Component | undefined}
58
+ * @returns {AST.Function | undefined}
72
59
  */
73
60
  export function get_component_from_path(path, includes_functions = false) {
74
61
  for (let i = path.length - 1; i >= 0; i -= 1) {
75
62
  const node = path[i];
76
63
 
77
- if (!includes_functions && (is_function_node(node) || is_class_node(node))) {
78
- return;
64
+ if (is_function_node(node)) {
65
+ if (/** @type {any} */ (node).metadata?.native_tsrx_function) {
66
+ return /** @type {AST.Function} */ (node);
67
+ }
68
+ if (!includes_functions) {
69
+ return;
70
+ }
79
71
  }
80
72
 
81
- if (is_component_node(node)) {
82
- return /** @type {AST.Component} */ (node);
73
+ if (!includes_functions && is_class_node(node)) {
74
+ return;
83
75
  }
84
76
  }
85
77
  }
@@ -87,7 +79,7 @@ export function get_component_from_path(path, includes_functions = false) {
87
79
  /**
88
80
  * @param {AST.Node[] | { path: AST.Node[] }} context_or_path
89
81
  * @param {boolean} [includes_functions=false]
90
- * @returns {AST.Component | undefined}
82
+ * @returns {AST.Function | undefined}
91
83
  */
92
84
  export function is_inside_component(context_or_path, includes_functions = false) {
93
85
  const path = Array.isArray(context_or_path) ? context_or_path : context_or_path.path;
@@ -77,24 +77,6 @@ export function arrow(params, body, async = false, type_parameters, loc_info) {
77
77
  return set_location(node, loc_info);
78
78
  }
79
79
 
80
- /**
81
- * @param {AST.Identifier} id
82
- * @param {AST.Pattern[]} params
83
- * @param {AST.Node[]} body
84
- * @returns {AST.Component}
85
- */
86
- export function component(id, params, body) {
87
- return {
88
- type: 'Component',
89
- id,
90
- params,
91
- body,
92
- css: null,
93
- metadata: { path: [] },
94
- default: false,
95
- };
96
- }
97
-
98
80
  /**
99
81
  * @param {AST.AssignmentOperator} operator
100
82
  * @param {AST.Pattern} left