@tsrx/core 0.0.12 → 0.0.14

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.
@@ -26,7 +26,7 @@
26
26
  */
27
27
 
28
28
  /**
29
- * @typedef {{ source_name: string, read: () => any }} LazyBinding
29
+ * @typedef {{ source_name: string, read: (reference?: any) => any }} LazyBinding
30
30
  */
31
31
 
32
32
  /**
@@ -46,12 +46,169 @@ function generate_lazy_id(context) {
46
46
  return `__lazy${context.lazy_next_id++}`;
47
47
  }
48
48
 
49
+ /**
50
+ * @param {any} node
51
+ * @param {any} [loc_info]
52
+ * @returns {any}
53
+ */
54
+ function set_source_location(node, loc_info) {
55
+ if (loc_info?.loc) {
56
+ node.start = loc_info.start;
57
+ node.end = loc_info.end;
58
+ node.loc = loc_info.loc;
59
+ }
60
+ return node;
61
+ }
62
+
49
63
  /**
50
64
  * @param {string} name
65
+ * @param {any} [loc_info]
66
+ * @param {string} [source_name]
67
+ * @param {number} [source_length]
51
68
  * @returns {any}
52
69
  */
53
- function create_generated_identifier(name) {
54
- return /** @type {any} */ ({ type: 'Identifier', name, metadata: { path: [] } });
70
+ function create_generated_identifier(name, loc_info, source_name, source_length) {
71
+ const id = /** @type {any} */ ({ type: 'Identifier', name, metadata: { path: [] } });
72
+ if (source_name && source_name !== name) id.metadata.source_name = source_name;
73
+ if (source_length != null) id.metadata.source_length = source_length;
74
+ return set_source_location(id, loc_info);
75
+ }
76
+
77
+ /**
78
+ * @param {any} pattern
79
+ * @returns {{ start: number, end: number, loc: any, source_length: number } | null}
80
+ */
81
+ function get_lazy_pattern_mapping_range(pattern) {
82
+ if (!pattern.loc) return null;
83
+
84
+ const end = pattern.typeAnnotation?.start ?? pattern.end;
85
+ const end_loc = pattern.typeAnnotation?.loc?.start ?? pattern.loc.end;
86
+ return {
87
+ start: pattern.start,
88
+ end,
89
+ loc: {
90
+ start: pattern.loc.start,
91
+ end: end_loc,
92
+ },
93
+ source_length: end - pattern.start,
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Synthesize an object-shaped annotation for untyped lazy object params so the
99
+ * virtual TSX can expose prop names to TypeScript completions.
100
+ *
101
+ * @param {any} pattern
102
+ * @returns {any | null}
103
+ */
104
+ function create_lazy_object_type_annotation(pattern) {
105
+ if (pattern.type !== 'ObjectPattern') return null;
106
+
107
+ const members = [];
108
+ for (const prop of pattern.properties || []) {
109
+ if (prop.type === 'RestElement' || prop.computed) continue;
110
+
111
+ const key = prop.key;
112
+ if (key.type !== 'Identifier' && key.type !== 'Literal') continue;
113
+
114
+ members.push({
115
+ type: 'TSPropertySignature',
116
+ key:
117
+ key.type === 'Identifier'
118
+ ? create_generated_identifier(key.name, key)
119
+ : set_source_location({ ...key, metadata: { path: [] } }, key),
120
+ computed: false,
121
+ optional: false,
122
+ readonly: false,
123
+ static: false,
124
+ kind: 'init',
125
+ typeAnnotation: {
126
+ type: 'TSTypeAnnotation',
127
+ typeAnnotation: {
128
+ type: 'TSAnyKeyword',
129
+ metadata: { path: [] },
130
+ },
131
+ metadata: { path: [] },
132
+ },
133
+ metadata: { path: [] },
134
+ });
135
+ }
136
+
137
+ if (members.length === 0) return null;
138
+
139
+ return {
140
+ type: 'TSTypeAnnotation',
141
+ typeAnnotation: {
142
+ type: 'TSTypeLiteral',
143
+ members,
144
+ metadata: { path: [] },
145
+ },
146
+ metadata: { path: [] },
147
+ };
148
+ }
149
+
150
+ /**
151
+ * @param {any} node
152
+ * @returns {string | null}
153
+ */
154
+ function get_static_property_name(node) {
155
+ if (node.type === 'Identifier') return node.name;
156
+ if (node.type === 'Literal') return String(node.value);
157
+ return null;
158
+ }
159
+
160
+ /**
161
+ * @param {any} type_annotation
162
+ * @returns {Map<string, any>}
163
+ */
164
+ function get_type_property_keys(type_annotation) {
165
+ const keys = new Map();
166
+ const members = type_annotation?.typeAnnotation?.members;
167
+ if (!Array.isArray(members)) return keys;
168
+
169
+ for (const member of members) {
170
+ if (member.type !== 'TSPropertySignature' || !member.key) continue;
171
+ const name = get_static_property_name(member.key);
172
+ if (name != null && !keys.has(name)) keys.set(name, member.key);
173
+ }
174
+
175
+ return keys;
176
+ }
177
+
178
+ /**
179
+ * Store extra mappings from lazy object binding identifiers to generated type
180
+ * property keys. Parser diagnostics for duplicate bindings point at the binding
181
+ * names (`&{ a: value, value }`), while the virtual param only exposes object
182
+ * properties (`__lazy0: { a: ...; value: ... }`).
183
+ *
184
+ * @param {any} lazy_id
185
+ * @param {any} pattern
186
+ */
187
+ function set_lazy_param_binding_mappings(lazy_id, pattern) {
188
+ if (pattern.type !== 'ObjectPattern') return;
189
+
190
+ const type_keys = get_type_property_keys(lazy_id.typeAnnotation);
191
+ if (type_keys.size === 0) return;
192
+
193
+ const mappings = [];
194
+ for (const prop of pattern.properties || []) {
195
+ if (prop.type === 'RestElement' || prop.computed) continue;
196
+
197
+ const value = prop.value;
198
+ const actual = value.type === 'AssignmentPattern' ? value.left : value;
199
+ if (actual.type !== 'Identifier' || !actual.loc) continue;
200
+
201
+ const key_name = get_static_property_name(prop.key);
202
+ const generated = key_name == null ? null : type_keys.get(key_name);
203
+ if (generated?.loc) {
204
+ generated.metadata = { ...generated.metadata, disable_verification: true };
205
+ mappings.push({ source: actual, generated });
206
+ }
207
+ }
208
+
209
+ if (mappings.length > 0) {
210
+ lazy_id.metadata.lazy_param_binding_mappings = mappings;
211
+ }
55
212
  }
56
213
 
57
214
  /**
@@ -76,12 +233,13 @@ export function collect_lazy_bindings(pattern, source_name, lazy_bindings) {
76
233
  const computed = prop.computed || key.type !== 'Identifier';
77
234
  lazy_bindings.set(actual.name, {
78
235
  source_name,
79
- read: () => ({
236
+ read: (reference) => ({
80
237
  type: 'MemberExpression',
81
238
  object: create_generated_identifier(source_name),
82
- property: computed
83
- ? { ...key }
84
- : { type: 'Identifier', name: key.name, metadata: { path: [] } },
239
+ property:
240
+ computed || key.type !== 'Identifier'
241
+ ? { ...key }
242
+ : create_generated_identifier(key.name, reference, reference?.name),
85
243
  computed,
86
244
  optional: false,
87
245
  metadata: { path: [] },
@@ -491,7 +649,7 @@ export function apply_lazy_transforms(node, lazy_bindings) {
491
649
  const binding = /** @type {LazyBinding} */ (lazy_bindings.get(node.left.name));
492
650
  return {
493
651
  ...node,
494
- left: binding.read(),
652
+ left: binding.read(node.left),
495
653
  right: apply_lazy_transforms(node.right, lazy_bindings),
496
654
  };
497
655
  }
@@ -502,7 +660,7 @@ export function apply_lazy_transforms(node, lazy_bindings) {
502
660
  lazy_bindings.has(node.argument.name)
503
661
  ) {
504
662
  const binding = /** @type {LazyBinding} */ (lazy_bindings.get(node.argument.name));
505
- return { ...node, argument: binding.read() };
663
+ return { ...node, argument: binding.read(node.argument) };
506
664
  }
507
665
 
508
666
  // Replace lazy variable declaration patterns with generated identifiers.
@@ -520,14 +678,14 @@ export function apply_lazy_transforms(node, lazy_bindings) {
520
678
  if (node.type === 'Property' && node.shorthand && node.value?.type === 'Identifier') {
521
679
  const binding = lazy_bindings.get(node.value.name);
522
680
  if (binding) {
523
- return { ...node, shorthand: false, value: binding.read() };
681
+ return { ...node, shorthand: false, value: binding.read(node.value) };
524
682
  }
525
683
  }
526
684
 
527
685
  // Bare identifier reference.
528
686
  if (node.type === 'Identifier' && lazy_bindings.has(node.name)) {
529
687
  const binding = /** @type {LazyBinding} */ (lazy_bindings.get(node.name));
530
- return binding.read();
688
+ return binding.read(node);
531
689
  }
532
690
 
533
691
  // JSXIdentifier is a label (component/element name), never a reference.
@@ -654,8 +812,22 @@ export function replace_lazy_params(params) {
654
812
  pattern.lazy &&
655
813
  pattern.metadata?.lazy_id
656
814
  ) {
657
- const lazy_id = create_generated_identifier(pattern.metadata.lazy_id);
658
- if (pattern.typeAnnotation) lazy_id.typeAnnotation = pattern.typeAnnotation;
815
+ const pattern_range = get_lazy_pattern_mapping_range(pattern);
816
+ const lazy_id = pattern_range
817
+ ? create_generated_identifier(
818
+ pattern.metadata.lazy_id,
819
+ pattern_range,
820
+ undefined,
821
+ pattern_range.source_length,
822
+ )
823
+ : create_generated_identifier(pattern.metadata.lazy_id);
824
+ if (pattern.typeAnnotation) {
825
+ lazy_id.typeAnnotation = pattern.typeAnnotation;
826
+ } else {
827
+ const type_annotation = create_lazy_object_type_annotation(pattern);
828
+ if (type_annotation) lazy_id.typeAnnotation = type_annotation;
829
+ }
830
+ set_lazy_param_binding_mappings(lazy_id, pattern);
659
831
  if (param.type === 'AssignmentPattern') return { ...param, left: lazy_id };
660
832
  return lazy_id;
661
833
  }
@@ -27,6 +27,7 @@
27
27
  loc: AST.SourceLocation;
28
28
  metadata: PluginActionOverrides;
29
29
  end_loc?: AST.SourceLocation;
30
+ sourceLength?: number;
30
31
  mappingData?: Partial<VolarCodeMapping['data']>;
31
32
  }} Token;
32
33
  @typedef {{
@@ -55,6 +56,15 @@ import {
55
56
  } from '../source-map-utils.js';
56
57
 
57
58
  const LABEL_TO_COMPONENT_REPLACE_REGEX = /(function|\((property|method)\))/;
59
+ const LAZY_PARAM_IDENTIFIER_REGEX = /^__lazy\d+$/;
60
+
61
+ /**
62
+ * @param {string} value
63
+ * @returns {string}
64
+ */
65
+ function escape_regex(value) {
66
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
67
+ }
58
68
 
59
69
  /**
60
70
  * @param {string} content
@@ -67,6 +77,51 @@ function replace_label_to_component(content) {
67
77
  });
68
78
  }
69
79
 
80
+ /**
81
+ * @param {string} lazy_id
82
+ * @param {(content: string) => string} [base_hover]
83
+ * @returns {(content: string) => string}
84
+ */
85
+ function create_lazy_param_hover_replacement(lazy_id, base_hover) {
86
+ const lazy_param_regex = new RegExp(`\\b${escape_regex(lazy_id)}\\s*:\\s*`, 'g');
87
+
88
+ return (content) => {
89
+ const next = base_hover ? base_hover(content) : content;
90
+ return next.replace(lazy_param_regex, '&');
91
+ };
92
+ }
93
+
94
+ /**
95
+ * @param {AST.Parameter[] | undefined} params
96
+ * @param {(content: string) => string} [base_hover]
97
+ * @returns {((content: string) => string) | undefined}
98
+ */
99
+ function create_function_hover_replacement(params, base_hover) {
100
+ const lazy_ids =
101
+ params
102
+ ?.filter(
103
+ (param) =>
104
+ param.type === 'Identifier' &&
105
+ param.metadata?.source_length != null &&
106
+ LAZY_PARAM_IDENTIFIER_REGEX.test(param.name),
107
+ )
108
+ .map((param) => /** @type {AST.Identifier} */ (param).name) ?? [];
109
+
110
+ if (lazy_ids.length === 0) return base_hover;
111
+
112
+ const lazy_param_regexes = lazy_ids.map(
113
+ (lazy_id) => new RegExp(`\\b${escape_regex(lazy_id)}\\s*:\\s*`, 'g'),
114
+ );
115
+
116
+ return (content) => {
117
+ let next = base_hover ? base_hover(content) : content;
118
+ for (const regex of lazy_param_regexes) {
119
+ next = next.replace(regex, '&');
120
+ }
121
+ return next;
122
+ };
123
+ }
124
+
70
125
  /**
71
126
  * @param {string} [hash]
72
127
  * @param {string} [fallback]
@@ -423,6 +478,7 @@ export function convert_source_map_to_mappings(
423
478
  generated: node.name,
424
479
  loc: node.loc,
425
480
  metadata: {},
481
+ sourceLength: node.metadata.source_length,
426
482
  };
427
483
  } else {
428
484
  token = {
@@ -430,6 +486,7 @@ export function convert_source_map_to_mappings(
430
486
  generated: node.name,
431
487
  loc: node.loc,
432
488
  metadata: {},
489
+ sourceLength: node.metadata?.source_length,
433
490
  };
434
491
  }
435
492
 
@@ -437,7 +494,36 @@ export function convert_source_map_to_mappings(
437
494
  // only if the node has a component as the parent
438
495
  token.metadata.hover = replace_label_to_component;
439
496
  }
497
+ if (node.metadata?.source_length != null && LAZY_PARAM_IDENTIFIER_REGEX.test(node.name)) {
498
+ token.metadata.hover = create_lazy_param_hover_replacement(
499
+ node.name,
500
+ node.metadata?.lazy_param_is_component ? replace_label_to_component : undefined,
501
+ );
502
+ }
503
+ if (node.metadata?.disable_verification) {
504
+ token.mappingData = { ...mapping_data, verification: false };
505
+ }
440
506
  tokens.push(token);
507
+
508
+ if (Array.isArray(node.metadata?.lazy_param_binding_mappings)) {
509
+ for (const binding_mapping of node.metadata.lazy_param_binding_mappings) {
510
+ const source_node = binding_mapping.source;
511
+ const generated_node = binding_mapping.generated;
512
+ if (!source_node?.loc || !generated_node?.loc) continue;
513
+
514
+ const mapping = get_mapping_from_node(
515
+ generated_node,
516
+ src_to_gen_map,
517
+ gen_line_offsets,
518
+ mapping_data_verify_only,
519
+ );
520
+ const source_start = /** @type {number} */ (source_node.start);
521
+ const source_end = /** @type {number} */ (source_node.end);
522
+ mapping.sourceOffsets = [source_start];
523
+ mapping.lengths = [source_end - source_start];
524
+ mappings.push(mapping);
525
+ }
526
+ }
441
527
  }
442
528
  return; // Leaf node, don't traverse further
443
529
  } else if (node.type === 'JSXIdentifier') {
@@ -761,11 +847,16 @@ export function convert_source_map_to_mappings(
761
847
  // Add function/component keyword token
762
848
  if (
763
849
  (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') &&
764
- !is_method
850
+ !is_method &&
851
+ node.loc
765
852
  ) {
766
853
  const node_fn = /** @type (typeof node) & AST.NodeWithLocation */ (node);
767
854
  const is_component = node_fn.metadata?.is_component;
768
855
  const source_func_keyword = is_component ? 'component' : 'function';
856
+ const function_hover = create_function_hover_replacement(
857
+ /** @type {AST.Parameter[]} */ (node.params),
858
+ is_component ? replace_label_to_component : undefined,
859
+ );
769
860
  let start_col = node_fn.loc.start.column;
770
861
  let start = node_fn.start;
771
862
  const async_keyword = 'async';
@@ -781,7 +872,7 @@ export function convert_source_map_to_mappings(
781
872
  mapping.lengths = [source_func_keyword.length];
782
873
  mapping.generatedOffsets = [generated_keyword_start];
783
874
  mapping.generatedLengths = ['function'.length];
784
- mapping.data.customData.hover = replace_label_to_component;
875
+ if (function_hover) mapping.data.customData.hover = function_hover;
785
876
  mappings.push(mapping);
786
877
  } else {
787
878
  if (node_fn.async) {
@@ -813,7 +904,7 @@ export function convert_source_map_to_mappings(
813
904
  column: start_col + source_func_keyword.length,
814
905
  },
815
906
  },
816
- metadata: is_component ? { hover: replace_label_to_component } : {},
907
+ metadata: function_hover ? { hover: function_hover } : {},
817
908
  });
818
909
  }
819
910
  }
@@ -825,11 +916,24 @@ export function convert_source_map_to_mappings(
825
916
  /** @type {AST.FunctionDeclaration | AST.FunctionExpression} */ (node).id &&
826
917
  !is_method
827
918
  ) {
828
- visit(
829
- /** @type {AST.Node} */ (
830
- /** @type {AST.FunctionDeclaration | AST.FunctionExpression} */ (node).id
831
- ),
919
+ const id = /** @type {AST.Identifier} */ (
920
+ /** @type {AST.FunctionDeclaration | AST.FunctionExpression} */ (node).id
832
921
  );
922
+ const function_hover = create_function_hover_replacement(
923
+ /** @type {AST.Parameter[]} */ (node.params),
924
+ node.metadata?.is_component ? replace_label_to_component : undefined,
925
+ );
926
+ if (function_hover && id.loc) {
927
+ tokens.push({
928
+ source: id.metadata?.source_name ?? id.name,
929
+ generated: id.name,
930
+ loc: id.loc,
931
+ metadata: { hover: function_hover },
932
+ sourceLength: id.metadata?.source_length,
933
+ });
934
+ } else {
935
+ visit(/** @type {AST.Node} */ (id));
936
+ }
833
937
  }
834
938
 
835
939
  if (node.typeParameters) {
@@ -838,6 +942,13 @@ export function convert_source_map_to_mappings(
838
942
 
839
943
  if (node.params) {
840
944
  for (const param of node.params) {
945
+ if (
946
+ param.type === 'Identifier' &&
947
+ param.metadata?.source_length != null &&
948
+ LAZY_PARAM_IDENTIFIER_REGEX.test(param.name)
949
+ ) {
950
+ param.metadata.lazy_param_is_component = node.metadata?.is_component === true;
951
+ }
841
952
  visit(param);
842
953
  if (param.typeAnnotation) {
843
954
  visit(param.typeAnnotation);
@@ -2066,7 +2177,7 @@ export function convert_source_map_to_mappings(
2066
2177
  token.loc.start.column,
2067
2178
  src_line_offsets,
2068
2179
  );
2069
- const source_length = source_text.length;
2180
+ const source_length = token.sourceLength ?? source_text.length;
2070
2181
  const gen_length = gen_text.length;
2071
2182
  let gen_line_col;
2072
2183
  try {
@@ -2250,21 +2361,25 @@ export function create_volar_mappings_result({
2250
2361
  * @returns {CodeMapping[]}
2251
2362
  */
2252
2363
  export function dedupe_mappings(mappings) {
2253
- const deduped = [];
2254
- const seen = new Set();
2364
+ // keep for now more for testing and maybe logging later.
2365
+ // We should not use deduping and instead should be
2366
+ // fixing source map generation or mapping generation
2367
+ return mappings;
2368
+ // const deduped = [];
2369
+ // const seen = new Set();
2255
2370
 
2256
- for (const mapping of mappings) {
2257
- const key = JSON.stringify(serialize_mapping_value(mapping));
2371
+ // for (const mapping of mappings) {
2372
+ // const key = JSON.stringify(serialize_mapping_value(mapping));
2258
2373
 
2259
- if (seen.has(key)) {
2260
- continue;
2261
- }
2374
+ // if (seen.has(key)) {
2375
+ // continue;
2376
+ // }
2262
2377
 
2263
- seen.add(key);
2264
- deduped.push(mapping);
2265
- }
2378
+ // seen.add(key);
2379
+ // deduped.push(mapping);
2380
+ // }
2266
2381
 
2267
- return deduped;
2382
+ // return deduped;
2268
2383
  }
2269
2384
 
2270
2385
  /**
package/src/utils/ast.js CHANGED
@@ -20,6 +20,67 @@
20
20
 
21
21
  import * as b from './builders.js';
22
22
 
23
+ /**
24
+ * @param {AST.Node} node
25
+ * @returns {boolean}
26
+ */
27
+ export function is_function_node(node) {
28
+ return (
29
+ node.type === 'FunctionExpression' ||
30
+ node.type === 'ArrowFunctionExpression' ||
31
+ node.type === 'FunctionDeclaration'
32
+ );
33
+ }
34
+
35
+ /**
36
+ * @param {AST.Node} node
37
+ * @returns {boolean}
38
+ */
39
+ export function is_class_node(node) {
40
+ return node.type === 'ClassExpression' || node.type === 'ClassDeclaration';
41
+ }
42
+
43
+ /**
44
+ * @param {AST.Node} node
45
+ * @returns {boolean}
46
+ */
47
+ export function is_component_node(node) {
48
+ return node.type === 'Component';
49
+ }
50
+
51
+ /**
52
+ * Returns the closest component in an ancestry path. By default, function and
53
+ * class boundaries stop the search so callers only match direct component
54
+ * body/control-flow nodes.
55
+ *
56
+ * @param {AST.Node[]} path
57
+ * @param {boolean} [includes_functions=false]
58
+ * @returns {AST.Component | undefined}
59
+ */
60
+ export function get_component_from_path(path, includes_functions = false) {
61
+ for (let i = path.length - 1; i >= 0; i -= 1) {
62
+ const node = path[i];
63
+
64
+ if (!includes_functions && (is_function_node(node) || is_class_node(node))) {
65
+ return;
66
+ }
67
+
68
+ if (is_component_node(node)) {
69
+ return /** @type {AST.Component} */ (node);
70
+ }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @param {AST.Node[] | { path: AST.Node[] }} context_or_path
76
+ * @param {boolean} [includes_functions=false]
77
+ * @returns {AST.Component | undefined}
78
+ */
79
+ export function is_inside_component(context_or_path, includes_functions = false) {
80
+ const path = Array.isArray(context_or_path) ? context_or_path : context_or_path.path;
81
+ return get_component_from_path(path, includes_functions);
82
+ }
83
+
23
84
  /**
24
85
  * Gets the left-most identifier of a member expression or identifier.
25
86
  * @param {AST.MemberExpression | AST.Identifier} expression
package/types/index.d.ts CHANGED
@@ -37,6 +37,11 @@ export interface CompileOptions {
37
37
  dev?: boolean;
38
38
  hmr?: boolean;
39
39
  compat_kinds?: string[];
40
+ /**
41
+ * When true, non-fatal errors are collected on the result's `errors`
42
+ * array instead of being thrown. Defaults to false (strict mode: throws).
43
+ */
44
+ loose?: boolean;
40
45
  }
41
46
 
42
47
  export type NameSpace = 'html' | 'svg' | 'mathml';
@@ -45,6 +50,7 @@ interface BaseNodeMetaData {
45
50
  path: AST.Node[];
46
51
  has_template?: boolean;
47
52
  source_name?: string | '#server' | '#style';
53
+ source_length?: number;
48
54
  is_capitalized?: boolean;
49
55
  commentContainerId?: number;
50
56
  parenthesized?: boolean;
@@ -56,6 +62,12 @@ interface BaseNodeMetaData {
56
62
  lone_return?: boolean;
57
63
  forceMapping?: boolean;
58
64
  lazy_id?: string;
65
+ disable_verification?: boolean;
66
+ lazy_param_is_component?: boolean;
67
+ lazy_param_binding_mappings?: Array<{
68
+ source: AST.Identifier;
69
+ generated: AST.Identifier | AST.Literal;
70
+ }>;
59
71
  }
60
72
 
61
73
  interface FunctionMetaData extends BaseNodeMetaData {
@@ -1558,6 +1570,11 @@ export interface CompileResult {
1558
1570
  };
1559
1571
  /** The generated CSS */
1560
1572
  css: string;
1573
+ /**
1574
+ * Non-fatal errors collected during compilation. Populated only when the
1575
+ * caller passes `loose: true`; empty otherwise.
1576
+ */
1577
+ errors: CompileError[];
1561
1578
  }
1562
1579
 
1563
1580
  /**
package/types/parse.d.ts CHANGED
@@ -1639,7 +1639,7 @@ export namespace Parse {
1639
1639
  * Parse JSX attribute (name="value" or {spread})
1640
1640
  * @returns JSXAttribute or JSXSpreadAttribute
1641
1641
  */
1642
- jsx_parseAttribute(): AST.TSRXAttribute | ESTreeJSX.JSXAttribute;
1642
+ jsx_parseAttribute(): AST.TSRXAttribute | ESTreeJSX.JSXAttribute | ESTreeJSX.JSXSpreadAttribute;
1643
1643
 
1644
1644
  /**
1645
1645
  * Parse JSX opening element at position