@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
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic CSS scoping utilities shared between the `@tsrx/react`
|
|
3
|
+
* and `@tsrx/solid` transforms. These walk the template AST and annotate
|
|
4
|
+
* `Element` nodes with a hash class so scope-qualified selectors
|
|
5
|
+
* (e.g. `.foo.hash`) match after rendering.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { walk } from 'zimmerframe';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Mark every selector inside the stylesheet as "used" so `renderStylesheets`
|
|
12
|
+
* does not comment it out. We skip selector-pruning because component
|
|
13
|
+
* boundaries can be dynamic — any selector authored inside the component's
|
|
14
|
+
* `<style>` block is considered intentional.
|
|
15
|
+
*
|
|
16
|
+
* @param {any} stylesheet
|
|
17
|
+
* @returns {any}
|
|
18
|
+
*/
|
|
19
|
+
export function prepare_stylesheet_for_render(stylesheet) {
|
|
20
|
+
walk(stylesheet, null, {
|
|
21
|
+
_(node, { next }) {
|
|
22
|
+
if (node && node.metadata && typeof node.metadata === 'object') {
|
|
23
|
+
node.metadata.used = true;
|
|
24
|
+
if (node.type === 'RelativeSelector' && !node.metadata.is_global) {
|
|
25
|
+
node.metadata.scoped = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return next();
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
return stylesheet;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {any} node
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
export function is_style_element(node) {
|
|
39
|
+
return (
|
|
40
|
+
node &&
|
|
41
|
+
node.type === 'Element' &&
|
|
42
|
+
node.id &&
|
|
43
|
+
node.id.type === 'Identifier' &&
|
|
44
|
+
node.id.name === 'style'
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {any} node
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
export function is_composite_element(node) {
|
|
53
|
+
if (!node || node.type !== 'Element' || !node.id) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (node.id.type === 'Identifier') {
|
|
58
|
+
return /^[A-Z]/.test(node.id.name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return node.id.type === 'MemberExpression';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Recursively walk `Element` nodes within a component body and add the hash
|
|
66
|
+
* class name so scope-qualified selectors (e.g. `.foo.hash`) match.
|
|
67
|
+
*
|
|
68
|
+
* @param {any} node
|
|
69
|
+
* @param {string} hash
|
|
70
|
+
* @returns {any}
|
|
71
|
+
*/
|
|
72
|
+
export function annotate_with_hash(node, hash) {
|
|
73
|
+
if (!node || typeof node !== 'object') return node;
|
|
74
|
+
if (
|
|
75
|
+
node.type === 'Component' ||
|
|
76
|
+
node.type === 'FunctionDeclaration' ||
|
|
77
|
+
node.type === 'FunctionExpression' ||
|
|
78
|
+
node.type === 'ArrowFunctionExpression'
|
|
79
|
+
) {
|
|
80
|
+
return node;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (node.type === 'Element') {
|
|
84
|
+
if (!is_style_element(node) && !is_composite_element(node)) {
|
|
85
|
+
add_hash_class(node, hash);
|
|
86
|
+
}
|
|
87
|
+
if (Array.isArray(node.children)) {
|
|
88
|
+
node.children = node.children
|
|
89
|
+
.filter((/** @type {any} */ child) => !is_style_element(child))
|
|
90
|
+
.map((/** @type {any} */ child) => annotate_with_hash(child, hash));
|
|
91
|
+
}
|
|
92
|
+
return node;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const key of Object.keys(node)) {
|
|
96
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata' || key === 'css') {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const value = node[key];
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
node[key] = value.map((/** @type {any} */ child) => annotate_with_hash(child, hash));
|
|
103
|
+
} else if (value && typeof value === 'object') {
|
|
104
|
+
node[key] = annotate_with_hash(value, hash);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return node;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {any} component
|
|
113
|
+
* @param {string} hash
|
|
114
|
+
* @returns {void}
|
|
115
|
+
*/
|
|
116
|
+
export function annotate_component_with_hash(component, hash) {
|
|
117
|
+
/** @type {any[]} */
|
|
118
|
+
const body = component.body;
|
|
119
|
+
component.body = body
|
|
120
|
+
.filter((/** @type {any} */ child) => !is_style_element(child))
|
|
121
|
+
.map((/** @type {any} */ child) => annotate_with_hash(child, hash));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Ensure the element carries a `class` attribute containing the scoping hash.
|
|
126
|
+
*
|
|
127
|
+
* @param {any} element
|
|
128
|
+
* @param {string} hash
|
|
129
|
+
* @returns {void}
|
|
130
|
+
*/
|
|
131
|
+
export function add_hash_class(element, hash) {
|
|
132
|
+
const attrs = element.attributes || (element.attributes = []);
|
|
133
|
+
const existing = attrs.find(
|
|
134
|
+
(/** @type {any} */ a) =>
|
|
135
|
+
a.type === 'Attribute' &&
|
|
136
|
+
a.name &&
|
|
137
|
+
a.name.type === 'Identifier' &&
|
|
138
|
+
(a.name.name === 'class' || a.name.name === 'className'),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (!existing) {
|
|
142
|
+
attrs.push({
|
|
143
|
+
type: 'Attribute',
|
|
144
|
+
name: { type: 'Identifier', name: 'class' },
|
|
145
|
+
value: { type: 'Literal', value: hash, raw: JSON.stringify(hash) },
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const value = existing.value;
|
|
151
|
+
if (!value) {
|
|
152
|
+
existing.value = { type: 'Literal', value: hash, raw: JSON.stringify(hash) };
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (value.type === 'Literal' && typeof value.value === 'string') {
|
|
157
|
+
const merged = `${value.value} ${hash}`;
|
|
158
|
+
existing.value = { type: 'Literal', value: merged, raw: JSON.stringify(merged) };
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Dynamic expression. Concatenate at runtime via template literal.
|
|
163
|
+
const expression = value.type === 'JSXExpressionContainer' ? value.expression : value;
|
|
164
|
+
existing.value = {
|
|
165
|
+
type: 'TemplateLiteral',
|
|
166
|
+
expressions: [expression],
|
|
167
|
+
quasis: [
|
|
168
|
+
{
|
|
169
|
+
type: 'TemplateElement',
|
|
170
|
+
value: { raw: '', cooked: '' },
|
|
171
|
+
tail: false,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'TemplateElement',
|
|
175
|
+
value: { raw: ` ${hash}`, cooked: ` ${hash}` },
|
|
176
|
+
tail: true,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
CodeMapping,
|
|
10
10
|
VolarMappingsResult,
|
|
11
11
|
PostProcessingChanges,
|
|
12
|
+
LineOffsets,
|
|
12
13
|
} from '../../types/index';
|
|
13
14
|
@import { CodeMapping as VolarCodeMapping } from '@volar/language-core';
|
|
14
15
|
*/
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
mapping_data_verify_complete,
|
|
52
53
|
build_line_offsets,
|
|
53
54
|
get_mapping_from_node,
|
|
55
|
+
maybe_get_mapping_from_node,
|
|
54
56
|
} from '../source-map-utils.js';
|
|
55
57
|
|
|
56
58
|
const LABEL_TO_COMPONENT_REPLACE_REGEX = /(function|\((property|method)\))/;
|
|
@@ -668,9 +670,21 @@ export function convert_source_map_to_mappings(
|
|
|
668
670
|
return;
|
|
669
671
|
} else if (node.type === 'JSXExpressionContainer') {
|
|
670
672
|
if (node.loc) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
+
// Use maybe_get_mapping_from_node because a transform may set the
|
|
674
|
+
// container's loc to the source range of the original `{...}`
|
|
675
|
+
// construct (e.g. a Ripple TSRXExpression or Text node), while
|
|
676
|
+
// esrap only emits a segment for the inner expression. In that
|
|
677
|
+
// case the container's start/end won't resolve — skip rather
|
|
678
|
+
// than hard-failing, and rely on the inner expression's mapping.
|
|
679
|
+
const mapping = maybe_get_mapping_from_node(
|
|
680
|
+
node,
|
|
681
|
+
src_to_gen_map,
|
|
682
|
+
gen_line_offsets,
|
|
683
|
+
mapping_data_verify_only,
|
|
673
684
|
);
|
|
685
|
+
if (!(mapping instanceof Error)) {
|
|
686
|
+
mappings.push(mapping);
|
|
687
|
+
}
|
|
674
688
|
}
|
|
675
689
|
// Visit the expression inside {}
|
|
676
690
|
if (node.expression) {
|
|
@@ -726,24 +740,38 @@ export function convert_source_map_to_mappings(
|
|
|
726
740
|
}
|
|
727
741
|
}
|
|
728
742
|
|
|
729
|
-
if (closing || opening.selfClosing) {
|
|
730
|
-
// Add the whole closing tag or the self-closing
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
743
|
+
if ((closing?.loc || opening.loc) && (closing || opening.selfClosing)) {
|
|
744
|
+
// Add the whole closing tag or the self-closing.
|
|
745
|
+
// For self-closing elements, use maybe_get_mapping_from_node because
|
|
746
|
+
// attribute transforms (e.g. class→className, {ref fn}→ref={fn}) can shift
|
|
747
|
+
// the position of `/>` in the generated output, making the source map
|
|
748
|
+
// entry for the opening element's end position unresolvable.
|
|
749
|
+
const target_node = closing ? closing : opening;
|
|
750
|
+
const mapping = closing
|
|
751
|
+
? get_mapping_from_node(
|
|
752
|
+
target_node,
|
|
753
|
+
src_to_gen_map,
|
|
754
|
+
gen_line_offsets,
|
|
755
|
+
mapping_data_verify_only,
|
|
756
|
+
)
|
|
757
|
+
: maybe_get_mapping_from_node(
|
|
758
|
+
target_node,
|
|
759
|
+
src_to_gen_map,
|
|
760
|
+
gen_line_offsets,
|
|
761
|
+
mapping_data_verify_only,
|
|
762
|
+
);
|
|
737
763
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
764
|
+
if (!(mapping instanceof Error)) {
|
|
765
|
+
// The generated code includes a semicolon after the closing or self-closed tag
|
|
766
|
+
// We're extending the mapping to include the semicolon
|
|
767
|
+
// because the diagnostics errors can include the whole element
|
|
768
|
+
// and we need to account for the semicolon as it's a part of the diagnostic
|
|
769
|
+
// At the same time, we could've instead applied this logic to the whole `node` element
|
|
770
|
+
// but since we already map the opening - start, we just need the proper end
|
|
771
|
+
// and it was causing some issues with mappings
|
|
772
|
+
mapping.generatedLengths = [mapping.generatedLengths[0] + 1];
|
|
773
|
+
mappings.push(mapping);
|
|
774
|
+
}
|
|
747
775
|
}
|
|
748
776
|
|
|
749
777
|
if (closing) {
|
|
@@ -769,37 +797,52 @@ export function convert_source_map_to_mappings(
|
|
|
769
797
|
let start = node_fn.start;
|
|
770
798
|
const async_keyword = 'async';
|
|
771
799
|
|
|
772
|
-
if (node_fn.
|
|
773
|
-
|
|
800
|
+
if (is_component && node_fn.id?.loc) {
|
|
801
|
+
const mapping = get_mapping_from_node(node_fn.id, src_to_gen_map, gen_line_offsets);
|
|
802
|
+
const generated_id_start = mapping.generatedOffsets[0];
|
|
803
|
+
const generated_keyword_start = find_component_keyword_offset(
|
|
804
|
+
generated_code,
|
|
805
|
+
generated_id_start,
|
|
806
|
+
);
|
|
807
|
+
mapping.sourceOffsets = [start];
|
|
808
|
+
mapping.lengths = [source_func_keyword.length];
|
|
809
|
+
mapping.generatedOffsets = [generated_keyword_start];
|
|
810
|
+
mapping.generatedLengths = ['function'.length];
|
|
811
|
+
mapping.data.customData.hover = replace_label_to_component;
|
|
812
|
+
mappings.push(mapping);
|
|
813
|
+
} else {
|
|
814
|
+
if (node_fn.async) {
|
|
815
|
+
// We explicitly mapped async and function in esrap
|
|
816
|
+
tokens.push({
|
|
817
|
+
source: async_keyword,
|
|
818
|
+
generated: async_keyword,
|
|
819
|
+
loc: {
|
|
820
|
+
start: { line: node_fn.loc.start.line, column: start_col },
|
|
821
|
+
end: {
|
|
822
|
+
line: node_fn.loc.start.line,
|
|
823
|
+
column: start_col + async_keyword.length,
|
|
824
|
+
},
|
|
825
|
+
},
|
|
826
|
+
metadata: {},
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
start_col += async_keyword.length + 1; // +1 for space
|
|
830
|
+
start += async_keyword.length + 1;
|
|
831
|
+
}
|
|
832
|
+
|
|
774
833
|
tokens.push({
|
|
775
|
-
source:
|
|
776
|
-
generated:
|
|
834
|
+
source: source_func_keyword,
|
|
835
|
+
generated: 'function',
|
|
777
836
|
loc: {
|
|
778
837
|
start: { line: node_fn.loc.start.line, column: start_col },
|
|
779
838
|
end: {
|
|
780
839
|
line: node_fn.loc.start.line,
|
|
781
|
-
column: start_col +
|
|
840
|
+
column: start_col + source_func_keyword.length,
|
|
782
841
|
},
|
|
783
842
|
},
|
|
784
|
-
metadata: {},
|
|
843
|
+
metadata: is_component ? { hover: replace_label_to_component } : {},
|
|
785
844
|
});
|
|
786
|
-
|
|
787
|
-
start_col += async_keyword.length + 1; // +1 for space
|
|
788
|
-
start += async_keyword.length + 1;
|
|
789
845
|
}
|
|
790
|
-
|
|
791
|
-
tokens.push({
|
|
792
|
-
source: source_func_keyword,
|
|
793
|
-
generated: 'function',
|
|
794
|
-
loc: {
|
|
795
|
-
start: { line: node_fn.loc.start.line, column: start_col },
|
|
796
|
-
end: {
|
|
797
|
-
line: node_fn.loc.start.line,
|
|
798
|
-
column: start_col + source_func_keyword.length,
|
|
799
|
-
},
|
|
800
|
-
},
|
|
801
|
-
metadata: is_component ? { hover: replace_label_to_component } : {},
|
|
802
|
-
});
|
|
803
846
|
}
|
|
804
847
|
|
|
805
848
|
// Visit in source order: id, params, body
|
|
@@ -982,9 +1025,11 @@ export function convert_source_map_to_mappings(
|
|
|
982
1025
|
visit(node.body);
|
|
983
1026
|
}
|
|
984
1027
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1028
|
+
if (node.loc) {
|
|
1029
|
+
mappings.push(
|
|
1030
|
+
get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
988
1033
|
|
|
989
1034
|
return;
|
|
990
1035
|
} else if (node.type === 'WhileStatement' || node.type === 'DoWhileStatement') {
|
|
@@ -1321,9 +1366,11 @@ export function convert_source_map_to_mappings(
|
|
|
1321
1366
|
}
|
|
1322
1367
|
}
|
|
1323
1368
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1369
|
+
if (node.loc) {
|
|
1370
|
+
mappings.push(
|
|
1371
|
+
get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1327
1374
|
|
|
1328
1375
|
return;
|
|
1329
1376
|
} else if (node.type === 'SwitchCase') {
|
|
@@ -2030,11 +2077,16 @@ export function convert_source_map_to_mappings(
|
|
|
2030
2077
|
);
|
|
2031
2078
|
const source_length = source_text.length;
|
|
2032
2079
|
const gen_length = gen_text.length;
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2080
|
+
let gen_line_col;
|
|
2081
|
+
try {
|
|
2082
|
+
gen_line_col = get_generated_position(
|
|
2083
|
+
token.loc.start.line,
|
|
2084
|
+
token.loc.start.column,
|
|
2085
|
+
src_to_gen_map,
|
|
2086
|
+
);
|
|
2087
|
+
} catch {
|
|
2088
|
+
continue;
|
|
2089
|
+
}
|
|
2038
2090
|
const gen_start = loc_to_offset(gen_line_col.line, gen_line_col.column, gen_line_offsets);
|
|
2039
2091
|
|
|
2040
2092
|
/** @type {CustomMappingData} */
|
|
@@ -2138,3 +2190,117 @@ export function convert_source_map_to_mappings(
|
|
|
2138
2190
|
cssMappings,
|
|
2139
2191
|
};
|
|
2140
2192
|
}
|
|
2193
|
+
|
|
2194
|
+
/**
|
|
2195
|
+
* @param {string} generated_code
|
|
2196
|
+
* @param {number} generated_id_start
|
|
2197
|
+
* @returns {number}
|
|
2198
|
+
*/
|
|
2199
|
+
function find_component_keyword_offset(generated_code, generated_id_start) {
|
|
2200
|
+
const function_keyword_index = generated_code.lastIndexOf('function', generated_id_start);
|
|
2201
|
+
|
|
2202
|
+
if (function_keyword_index === -1) {
|
|
2203
|
+
return generated_id_start;
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
return function_keyword_index;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
/**
|
|
2210
|
+
* Build a `VolarMappingsResult` from generated code plus source-map metadata.
|
|
2211
|
+
*
|
|
2212
|
+
* Framework packages are responsible for producing the generated AST/code/map.
|
|
2213
|
+
* Core owns the generic mapping conversion and result envelope so the editor
|
|
2214
|
+
* integration is not coupled to any specific framework package.
|
|
2215
|
+
*
|
|
2216
|
+
* @param {{
|
|
2217
|
+
* ast: AST.Program,
|
|
2218
|
+
* ast_from_source: AST.Program,
|
|
2219
|
+
* source: string,
|
|
2220
|
+
* generated_code: string,
|
|
2221
|
+
* source_map: RawSourceMap,
|
|
2222
|
+
* errors?: import('../../types/index').CompileError[],
|
|
2223
|
+
* post_processing_changes?: PostProcessingChanges,
|
|
2224
|
+
* line_offsets?: LineOffsets,
|
|
2225
|
+
* }} params
|
|
2226
|
+
* @returns {VolarMappingsResult}
|
|
2227
|
+
*/
|
|
2228
|
+
export function create_volar_mappings_result({
|
|
2229
|
+
ast,
|
|
2230
|
+
ast_from_source,
|
|
2231
|
+
source,
|
|
2232
|
+
generated_code,
|
|
2233
|
+
source_map,
|
|
2234
|
+
errors = [],
|
|
2235
|
+
post_processing_changes,
|
|
2236
|
+
line_offsets,
|
|
2237
|
+
}) {
|
|
2238
|
+
return {
|
|
2239
|
+
...convert_source_map_to_mappings(
|
|
2240
|
+
ast,
|
|
2241
|
+
ast_from_source,
|
|
2242
|
+
source,
|
|
2243
|
+
generated_code,
|
|
2244
|
+
source_map,
|
|
2245
|
+
/** @type {PostProcessingChanges} */ (post_processing_changes),
|
|
2246
|
+
line_offsets ?? build_line_offsets(generated_code),
|
|
2247
|
+
),
|
|
2248
|
+
errors,
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
/**
|
|
2253
|
+
* Remove byte-for-byte duplicate mappings. Framework compilers that extract
|
|
2254
|
+
* shared helpers or replay JSX can emit identical mapping entries for the
|
|
2255
|
+
* same source and generated span; Volar merges duplicates into a single
|
|
2256
|
+
* hover/navigation result, so deduping upstream avoids a stutter.
|
|
2257
|
+
*
|
|
2258
|
+
* @param {CodeMapping[]} mappings
|
|
2259
|
+
* @returns {CodeMapping[]}
|
|
2260
|
+
*/
|
|
2261
|
+
export function dedupe_mappings(mappings) {
|
|
2262
|
+
const deduped = [];
|
|
2263
|
+
const seen = new Set();
|
|
2264
|
+
|
|
2265
|
+
for (const mapping of mappings) {
|
|
2266
|
+
const key = JSON.stringify(serialize_mapping_value(mapping));
|
|
2267
|
+
|
|
2268
|
+
if (seen.has(key)) {
|
|
2269
|
+
continue;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
seen.add(key);
|
|
2273
|
+
deduped.push(mapping);
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
return deduped;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
/**
|
|
2280
|
+
* Serialize a mapping (or any nested value) into a stable JSON-friendly
|
|
2281
|
+
* shape so {@link dedupe_mappings} can compare two entries by content.
|
|
2282
|
+
* Object keys are sorted and functions are reduced to their source so
|
|
2283
|
+
* structurally-identical entries produce the same string.
|
|
2284
|
+
*
|
|
2285
|
+
* @param {unknown} value
|
|
2286
|
+
* @returns {unknown}
|
|
2287
|
+
*/
|
|
2288
|
+
export function serialize_mapping_value(value) {
|
|
2289
|
+
if (typeof value === 'function') {
|
|
2290
|
+
return value.toString();
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
if (Array.isArray(value)) {
|
|
2294
|
+
return value.map(serialize_mapping_value);
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
if (value && typeof value === 'object') {
|
|
2298
|
+
return Object.fromEntries(
|
|
2299
|
+
Object.entries(value)
|
|
2300
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
2301
|
+
.map(([key, nested_value]) => [key, serialize_mapping_value(nested_value)]),
|
|
2302
|
+
);
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
return value;
|
|
2306
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -1387,7 +1387,7 @@ export interface TransformClientState extends BaseState {
|
|
|
1387
1387
|
applyParentCssScope?: AST.CSS.StyleSheet['hash'];
|
|
1388
1388
|
skip_children_traversal: boolean;
|
|
1389
1389
|
return_flags?: Map<AST.ReturnStatement, { name: string; tracked: boolean }>;
|
|
1390
|
-
|
|
1390
|
+
is_tsrx_element?: boolean;
|
|
1391
1391
|
}
|
|
1392
1392
|
|
|
1393
1393
|
/** Override zimmerframe types and provide our own */
|
package/types/parse.d.ts
CHANGED
|
@@ -182,7 +182,12 @@ export namespace Parse {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
export interface Options extends Omit<acorn.Options, 'onComment' | 'ecmaVersion'> {
|
|
185
|
-
|
|
185
|
+
tsrxOptions?: {
|
|
186
|
+
loose: boolean;
|
|
187
|
+
errors: CoreCompiler.CompileError[];
|
|
188
|
+
filename: string | undefined;
|
|
189
|
+
};
|
|
190
|
+
rippleOptions?: {
|
|
186
191
|
loose: boolean;
|
|
187
192
|
errors: CoreCompiler.CompileError[];
|
|
188
193
|
filename: string | undefined;
|