@tsrx/core 0.1.59 → 0.1.60
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/analyze/prune.js +7 -1
- package/src/transform/jsx/index.js +76 -16
- package/src/transform/segments.js +1 -1
- package/types/index.d.ts +4 -0
package/package.json
CHANGED
package/src/analyze/prune.js
CHANGED
|
@@ -15,6 +15,8 @@ const BACKWARD = 1;
|
|
|
15
15
|
// since the code is synchronous, this is safe
|
|
16
16
|
/** @type {string} */
|
|
17
17
|
let css_hash;
|
|
18
|
+
/** @type {string} */
|
|
19
|
+
let css_region_hash;
|
|
18
20
|
/** @type {StyleClasses} */
|
|
19
21
|
let style_identifier_classes;
|
|
20
22
|
/** @type {TopScopedClasses} */
|
|
@@ -300,6 +302,7 @@ function apply_selector(relative_selectors, rule, element, direction) {
|
|
|
300
302
|
start: selector.start,
|
|
301
303
|
end: selector.end,
|
|
302
304
|
selector: selector,
|
|
305
|
+
regionHash: css_region_hash,
|
|
303
306
|
});
|
|
304
307
|
}
|
|
305
308
|
}
|
|
@@ -1107,10 +1110,12 @@ function rule_has_animation(rule) {
|
|
|
1107
1110
|
* @param {AST.TSRXElementNode} element
|
|
1108
1111
|
* @param {StyleClasses} styleClasses
|
|
1109
1112
|
* @param {TopScopedClasses} topScopedClasses
|
|
1113
|
+
* @param {string} [regionHash]
|
|
1110
1114
|
* @return {void}
|
|
1111
1115
|
*/
|
|
1112
|
-
export function prune_css(css, element, styleClasses, topScopedClasses) {
|
|
1116
|
+
export function prune_css(css, element, styleClasses, topScopedClasses, regionHash = css.hash) {
|
|
1113
1117
|
css_hash = css.hash;
|
|
1118
|
+
css_region_hash = regionHash;
|
|
1114
1119
|
style_identifier_classes = styleClasses;
|
|
1115
1120
|
top_scoped_classes = topScopedClasses;
|
|
1116
1121
|
|
|
@@ -1152,6 +1157,7 @@ export function prune_css(css, element, styleClasses, topScopedClasses) {
|
|
|
1152
1157
|
start: class_selector.start,
|
|
1153
1158
|
end: class_selector.end,
|
|
1154
1159
|
selector: class_selector,
|
|
1160
|
+
regionHash: css_region_hash,
|
|
1155
1161
|
});
|
|
1156
1162
|
}
|
|
1157
1163
|
}
|
|
@@ -728,9 +728,24 @@ export function createJsxTransform(platform) {
|
|
|
728
728
|
// hooks can inspect the original JSX child shape.
|
|
729
729
|
const raw_children = node_children(node).map((child) => ({ ...child }));
|
|
730
730
|
const inner = /** @type {AST.TSRXJSXElement} */ (next() ?? node);
|
|
731
|
+
const in_jsx_child = in_jsx_child_context(path);
|
|
731
732
|
const hook = platform.hooks?.transformElement;
|
|
732
|
-
|
|
733
|
-
|
|
733
|
+
const produced = hook
|
|
734
|
+
? hook(inner, state, raw_children)
|
|
735
|
+
: to_jsx_element(inner, state, raw_children, in_jsx_child);
|
|
736
|
+
// A host element carrying `ref` plus a spread lowers to a generated
|
|
737
|
+
// `let X = __normalize_spread_props_for_ref_attr(…)` that rides on the
|
|
738
|
+
// element's metadata for a later pass to hoist. Only the render-block
|
|
739
|
+
// statement builder and the native-directive path hoist it, so an
|
|
740
|
+
// element in plain-JS expression position — a ternary arm, a concise
|
|
741
|
+
// arrow body, a declarator init, a callback body, an attribute value, an
|
|
742
|
+
// array element — reaches neither: the declaration is dropped while the
|
|
743
|
+
// rewritten attributes still reference the name, and the type-only print
|
|
744
|
+
// carries an undefined identifier (TS2304). Wrap it in the same IIFE the
|
|
745
|
+
// native-directive path already uses.
|
|
746
|
+
return state.typeOnly && produced.type !== 'JSXSpreadChild' && produced.type !== 'JSXText'
|
|
747
|
+
? wrap_jsx_setup_declarations(produced, in_jsx_child)
|
|
748
|
+
: produced;
|
|
734
749
|
},
|
|
735
750
|
|
|
736
751
|
JSXExpressionContainer(node, { next, state }) {
|
|
@@ -791,7 +806,7 @@ export function createJsxTransform(platform) {
|
|
|
791
806
|
return visited;
|
|
792
807
|
}
|
|
793
808
|
const is_component = is_component_like_jsx_name(visited.name);
|
|
794
|
-
|
|
809
|
+
const lowered = b.jsx_opening_element(
|
|
795
810
|
visited.name,
|
|
796
811
|
merge_duplicate_refs(
|
|
797
812
|
normalize_host_ref_spreads(visited.attributes || [], !is_component, transform_context),
|
|
@@ -801,6 +816,17 @@ export function createJsxTransform(platform) {
|
|
|
801
816
|
visited.typeArguments,
|
|
802
817
|
has_location(visited) ? visited : undefined,
|
|
803
818
|
);
|
|
819
|
+
// `normalize_host_ref_spreads` is NOT idempotent: run twice it reads the
|
|
820
|
+
// `ref={[authored, __spread_props1.ref]}` array it just produced as an
|
|
821
|
+
// AUTHORED ref and lowers again, emitting a second helper binding and a
|
|
822
|
+
// nested ref array. The attribute list cannot answer "already lowered"
|
|
823
|
+
// on its own — `merge_duplicate_refs` rebuilds the merged `ref` without
|
|
824
|
+
// the `synthetic_ref` marker — so record it on the element instead.
|
|
825
|
+
lowered.metadata = {
|
|
826
|
+
...(lowered.metadata || {}),
|
|
827
|
+
host_ref_spread_lowered: true,
|
|
828
|
+
};
|
|
829
|
+
return lowered;
|
|
804
830
|
},
|
|
805
831
|
});
|
|
806
832
|
|
|
@@ -1065,6 +1091,7 @@ function inject_dynamic_import(program, transform_context) {
|
|
|
1065
1091
|
* @param {AST.CSS.StyleSheet} css
|
|
1066
1092
|
* @param {TransformContext} transform_context
|
|
1067
1093
|
* @param {boolean} [export_top_scoped_classes]
|
|
1094
|
+
* @param {string} [region_hash]
|
|
1068
1095
|
* @returns {void}
|
|
1069
1096
|
*/
|
|
1070
1097
|
function apply_css_definition_metadata(
|
|
@@ -1072,6 +1099,7 @@ function apply_css_definition_metadata(
|
|
|
1072
1099
|
css,
|
|
1073
1100
|
transform_context,
|
|
1074
1101
|
export_top_scoped_classes = false,
|
|
1102
|
+
region_hash = css.hash,
|
|
1075
1103
|
) {
|
|
1076
1104
|
analyze_css(css);
|
|
1077
1105
|
|
|
@@ -1082,7 +1110,7 @@ function apply_css_definition_metadata(
|
|
|
1082
1110
|
|
|
1083
1111
|
const prune = () => {
|
|
1084
1112
|
for (const element of elements) {
|
|
1085
|
-
prune_css(css, element, style_classes, top_scoped_classes);
|
|
1113
|
+
prune_css(css, element, style_classes, top_scoped_classes, region_hash);
|
|
1086
1114
|
}
|
|
1087
1115
|
};
|
|
1088
1116
|
|
|
@@ -2291,19 +2319,20 @@ function node_contains_native_tsrx_template(node) {
|
|
|
2291
2319
|
|
|
2292
2320
|
/**
|
|
2293
2321
|
* @param {AST.NativeTSRXNode} node
|
|
2294
|
-
* @
|
|
2322
|
+
* @param {boolean} allow_multiple
|
|
2323
|
+
* @returns {AST.CSS.StyleSheet[] | null}
|
|
2295
2324
|
*/
|
|
2296
|
-
function
|
|
2325
|
+
function collect_tsrx_stylesheets(node, allow_multiple) {
|
|
2297
2326
|
/** @type {AST.CSS.StyleSheet[]} */
|
|
2298
2327
|
const styles = [];
|
|
2299
2328
|
collect_style_elements(node_children(node), styles);
|
|
2300
2329
|
|
|
2301
2330
|
if (styles.length === 0) return null;
|
|
2302
|
-
if (styles.length > 1) {
|
|
2331
|
+
if (styles.length > 1 && !allow_multiple) {
|
|
2303
2332
|
throw new Error('TSRX fragments can only have one style tag');
|
|
2304
2333
|
}
|
|
2305
2334
|
|
|
2306
|
-
return styles
|
|
2335
|
+
return styles;
|
|
2307
2336
|
}
|
|
2308
2337
|
|
|
2309
2338
|
/**
|
|
@@ -2312,14 +2341,37 @@ function collect_tsrx_stylesheet(node) {
|
|
|
2312
2341
|
* @returns {JsxStyleContext | null}
|
|
2313
2342
|
*/
|
|
2314
2343
|
function prepare_tsrx_fragment_styles(node, transform_context) {
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2344
|
+
// Type-only output must stay analyzable: a throw here makes language hosts
|
|
2345
|
+
// (tsrx-tsc, editors) fall back to presenting the RAW source as the virtual
|
|
2346
|
+
// TSX, so every CSS brace in the file becomes a TSX parse error. Platforms
|
|
2347
|
+
// whose runtime dialect allows one scope to split its CSS across several
|
|
2348
|
+
// `<style>` tags are valid input here, and platforms that forbid it still
|
|
2349
|
+
// report the rule through their own runtime compiler.
|
|
2350
|
+
const sheets = collect_tsrx_stylesheets(node, transform_context.typeOnly);
|
|
2351
|
+
if (!sheets) return null;
|
|
2352
|
+
|
|
2353
|
+
const css = sheets[0];
|
|
2318
2354
|
const style_refs = collect_style_ref_attributes(node);
|
|
2319
|
-
//
|
|
2320
|
-
//
|
|
2321
|
-
|
|
2322
|
-
|
|
2355
|
+
// A component scope keeps ONE hash even when its CSS is split across
|
|
2356
|
+
// several `<style>` tags: rebase every sheet onto the first sheet's hash
|
|
2357
|
+
// before scoping, so selector rewriting and the DOM hash annotation below
|
|
2358
|
+
// agree. `apply_css_definition_metadata` accumulates into the component's
|
|
2359
|
+
// `styleClasses`/`topScopedClasses` metadata, so per-sheet calls compose
|
|
2360
|
+
// into one class map for style refs.
|
|
2361
|
+
for (const sheet of sheets) {
|
|
2362
|
+
const region_hash = sheet.hash;
|
|
2363
|
+
sheet.hash = css.hash;
|
|
2364
|
+
// `prune_css` inside marks the matching selectors as used/scoped; selectors
|
|
2365
|
+
// that match no element render commented out, like the Ripple target.
|
|
2366
|
+
apply_css_definition_metadata(
|
|
2367
|
+
node,
|
|
2368
|
+
sheet,
|
|
2369
|
+
transform_context,
|
|
2370
|
+
style_refs.length > 0,
|
|
2371
|
+
region_hash,
|
|
2372
|
+
);
|
|
2373
|
+
transform_context.stylesheets.push(sheet);
|
|
2374
|
+
}
|
|
2323
2375
|
const fragment = annotate_tsrx_with_hash(
|
|
2324
2376
|
node,
|
|
2325
2377
|
css.hash,
|
|
@@ -6120,8 +6172,16 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
|
|
|
6120
6172
|
}
|
|
6121
6173
|
const hook = transform_context.platform.hooks?.transformElementAttributes;
|
|
6122
6174
|
const result = hook ? hook(attrs, transform_context, element) : attrs;
|
|
6175
|
+
// An element in plain-JS expression position reaches BOTH lowering sites —
|
|
6176
|
+
// the JSXOpeningElement visitor above and this dispatch — so without the
|
|
6177
|
+
// marker its host ref/spread is lowered twice. Scoped to the type-only
|
|
6178
|
+
// print: runtime emit for the other platforms sharing this transform keeps
|
|
6179
|
+
// its existing output.
|
|
6180
|
+
const already_lowered =
|
|
6181
|
+
transform_context.typeOnly &&
|
|
6182
|
+
element?.openingElement?.metadata?.host_ref_spread_lowered === true;
|
|
6123
6183
|
return merge_duplicate_refs(
|
|
6124
|
-
normalize_host_ref_spreads(result, !is_component, transform_context),
|
|
6184
|
+
already_lowered ? result : normalize_host_ref_spreads(result, !is_component, transform_context),
|
|
6125
6185
|
transform_context,
|
|
6126
6186
|
);
|
|
6127
6187
|
}
|
|
@@ -885,7 +885,7 @@ export function convert_source_map_to_mappings(
|
|
|
885
885
|
definition: {
|
|
886
886
|
description: `CSS class selector for '.${name}'`,
|
|
887
887
|
location: {
|
|
888
|
-
embeddedId: get_style_region_id(css.hash),
|
|
888
|
+
embeddedId: get_style_region_id(cssLocation.regionHash ?? css.hash),
|
|
889
889
|
start: cssLocation.start,
|
|
890
890
|
end: cssLocation.end,
|
|
891
891
|
},
|
package/types/index.d.ts
CHANGED
|
@@ -989,6 +989,8 @@ declare module 'estree-jsx' {
|
|
|
989
989
|
interface JSXOpeningElement {
|
|
990
990
|
metadata: BaseNodeMetaData & {
|
|
991
991
|
native_tsrx_pretransformed?: boolean;
|
|
992
|
+
/** Type-only host ref/spread normalization has already run for this element. */
|
|
993
|
+
host_ref_spread_lowered?: boolean;
|
|
992
994
|
};
|
|
993
995
|
}
|
|
994
996
|
|
|
@@ -2090,6 +2092,8 @@ export type TopScopedClasses = Map<
|
|
|
2090
2092
|
start: number;
|
|
2091
2093
|
end: number;
|
|
2092
2094
|
selector: AST.CSS.ClassSelector;
|
|
2095
|
+
/** Source `<style>` region for editor definition navigation. */
|
|
2096
|
+
regionHash?: string;
|
|
2093
2097
|
}
|
|
2094
2098
|
>;
|
|
2095
2099
|
|