@tsrx/core 0.1.17 → 0.1.18
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 +1 -1
- package/src/index.js +2 -0
- package/src/transform/jsx/index.js +85 -20
- package/src/transform/scoping.js +5 -16
- package/src/transform/style-ref.js +92 -1
- package/types/index.d.ts +2 -0
- package/types/jsx-platform.d.ts +7 -2
package/package.json
CHANGED
package/src/analyze/prune.js
CHANGED
|
@@ -1095,7 +1095,7 @@ export function prune_css(css, element, styleClasses, topScopedClasses) {
|
|
|
1095
1095
|
// A class is standalone only when the entire effective selector chain (after resolving
|
|
1096
1096
|
// nesting and stripping :global) is a single RelativeSelector with a single ClassSelector.
|
|
1097
1097
|
// This prevents classes from compound selectors like `.wrapper .nested` or selectors
|
|
1098
|
-
// inside :global() from being exported through style
|
|
1098
|
+
// inside :global() from being exported through style expression maps.
|
|
1099
1099
|
if (selectors.length === 1) {
|
|
1100
1100
|
const sole_selector = selectors[0];
|
|
1101
1101
|
if (
|
package/src/index.js
CHANGED
|
@@ -170,7 +170,9 @@ export {
|
|
|
170
170
|
export {
|
|
171
171
|
collect_style_ref_attributes as collectStyleRefAttributes,
|
|
172
172
|
create_style_class_map as createStyleClassMap,
|
|
173
|
+
create_style_class_map_from_stylesheet as createStyleClassMapFromStylesheet,
|
|
173
174
|
create_style_ref_setup_statements as createStyleRefSetupStatements,
|
|
175
|
+
get_style_element_stylesheet as getStyleElementStylesheet,
|
|
174
176
|
} from './transform/style-ref.js';
|
|
175
177
|
export {
|
|
176
178
|
clone_expression_node,
|
|
@@ -45,7 +45,9 @@ import { prepare_stylesheet_for_render, annotate_with_hash, is_style_element } f
|
|
|
45
45
|
import {
|
|
46
46
|
collect_style_ref_attributes,
|
|
47
47
|
create_style_class_map,
|
|
48
|
+
create_style_class_map_from_stylesheet,
|
|
48
49
|
create_style_ref_setup_statements,
|
|
50
|
+
get_style_element_stylesheet,
|
|
49
51
|
} from '../style-ref.js';
|
|
50
52
|
import { is_function_or_component_node } from '../../utils/ast.js';
|
|
51
53
|
import {
|
|
@@ -251,7 +253,16 @@ export function createJsxTransform(platform) {
|
|
|
251
253
|
);
|
|
252
254
|
},
|
|
253
255
|
|
|
254
|
-
Element(node, { next, state }) {
|
|
256
|
+
Element(node, { next, path, state }) {
|
|
257
|
+
if (is_style_element(node) && is_style_expression_position(path)) {
|
|
258
|
+
const stylesheet = get_style_element_stylesheet(node);
|
|
259
|
+
if (stylesheet) {
|
|
260
|
+
analyze_css(stylesheet);
|
|
261
|
+
state.stylesheets.push(stylesheet);
|
|
262
|
+
return /** @type {any} */ (create_style_class_map_from_stylesheet(stylesheet));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
255
266
|
// Capture raw children BEFORE the walker transforms them so a
|
|
256
267
|
// platform hook (e.g. Solid's textContent optimization) can
|
|
257
268
|
// inspect the original Text / TSRXExpression nodes rather than
|
|
@@ -1420,7 +1431,8 @@ function prepare_tsrx_fragment_styles(node, transform_context) {
|
|
|
1420
1431
|
annotate_tsrx_with_hash(
|
|
1421
1432
|
node,
|
|
1422
1433
|
css.hash,
|
|
1423
|
-
transform_context.platform.jsx.
|
|
1434
|
+
transform_context.platform.jsx.classAttrName ??
|
|
1435
|
+
(transform_context.platform.jsx.rewriteClassAttr ? 'className' : 'class'),
|
|
1424
1436
|
transform_context.typeOnly,
|
|
1425
1437
|
);
|
|
1426
1438
|
return { css, style_refs };
|
|
@@ -1502,11 +1514,33 @@ function collect_style_elements(node, styles) {
|
|
|
1502
1514
|
return;
|
|
1503
1515
|
}
|
|
1504
1516
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1517
|
+
if (node.type === 'Element') {
|
|
1518
|
+
collect_style_elements(node.children || [], styles);
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
if (node.type === 'BlockStatement') {
|
|
1523
|
+
collect_style_elements(node.body || [], styles);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
if (node.type === 'IfStatement') {
|
|
1528
|
+
collect_style_elements(node.consequent, styles);
|
|
1529
|
+
collect_style_elements(node.alternate, styles);
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (node.type === 'SwitchStatement') {
|
|
1534
|
+
for (const switch_case of node.cases || []) {
|
|
1535
|
+
collect_style_elements(switch_case.consequent || [], styles);
|
|
1508
1536
|
}
|
|
1509
|
-
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
if (node.type === 'TryStatement') {
|
|
1541
|
+
collect_style_elements(node.block, styles);
|
|
1542
|
+
collect_style_elements(node.handler?.body, styles);
|
|
1543
|
+
collect_style_elements(node.finalizer, styles);
|
|
1510
1544
|
}
|
|
1511
1545
|
}
|
|
1512
1546
|
|
|
@@ -1548,24 +1582,55 @@ function strip_style_elements(node) {
|
|
|
1548
1582
|
return node;
|
|
1549
1583
|
}
|
|
1550
1584
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1585
|
+
if (node.type === 'Element') {
|
|
1586
|
+
node.children = strip_style_elements(node.children || []);
|
|
1587
|
+
return node;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (node.type === 'BlockStatement') {
|
|
1591
|
+
node.body = strip_style_elements(node.body || []);
|
|
1592
|
+
return node;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
if (node.type === 'IfStatement') {
|
|
1596
|
+
node.consequent = strip_style_elements(node.consequent);
|
|
1597
|
+
if (node.alternate) node.alternate = strip_style_elements(node.alternate);
|
|
1598
|
+
return node;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
if (node.type === 'SwitchStatement') {
|
|
1602
|
+
for (const switch_case of node.cases || []) {
|
|
1603
|
+
switch_case.consequent = strip_style_elements(switch_case.consequent || []);
|
|
1563
1604
|
}
|
|
1605
|
+
return node;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
if (node.type === 'TryStatement') {
|
|
1609
|
+
node.block = strip_style_elements(node.block);
|
|
1610
|
+
if (node.handler?.body) node.handler.body = strip_style_elements(node.handler.body);
|
|
1611
|
+
if (node.finalizer) node.finalizer = strip_style_elements(node.finalizer);
|
|
1564
1612
|
}
|
|
1565
1613
|
|
|
1566
1614
|
return node;
|
|
1567
1615
|
}
|
|
1568
1616
|
|
|
1617
|
+
/**
|
|
1618
|
+
* @param {any[]} path
|
|
1619
|
+
* @returns {boolean}
|
|
1620
|
+
*/
|
|
1621
|
+
function is_style_expression_position(path) {
|
|
1622
|
+
const parent = path.at(-1);
|
|
1623
|
+
return !(
|
|
1624
|
+
parent?.type === 'Element' ||
|
|
1625
|
+
parent?.type === 'Tsrx' ||
|
|
1626
|
+
parent?.type === 'Tsx' ||
|
|
1627
|
+
parent?.type === 'TsxCompat' ||
|
|
1628
|
+
parent?.type === 'BlockStatement' ||
|
|
1629
|
+
parent?.type === 'Program' ||
|
|
1630
|
+
parent?.type === 'SwitchCase'
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1569
1634
|
/**
|
|
1570
1635
|
* @param {any} node
|
|
1571
1636
|
* @param {TransformContext} transform_context
|
|
@@ -5605,8 +5670,8 @@ export function to_jsx_attribute(attr, transform_context) {
|
|
|
5605
5670
|
attr,
|
|
5606
5671
|
);
|
|
5607
5672
|
}
|
|
5608
|
-
//
|
|
5609
|
-
//
|
|
5673
|
+
// Keep this legacy hook for targets that need React-style DOM attrs. The
|
|
5674
|
+
// current first-party targets preserve authored `class`.
|
|
5610
5675
|
let attr_name = attr.name;
|
|
5611
5676
|
if (
|
|
5612
5677
|
transform_context.platform.jsx.rewriteClassAttr &&
|
package/src/transform/scoping.js
CHANGED
|
@@ -119,7 +119,7 @@ export function annotate_with_hash(
|
|
|
119
119
|
return node;
|
|
120
120
|
}
|
|
121
121
|
if (!is_style_element(node) && !is_composite_element(node)) {
|
|
122
|
-
add_hash_class(node, hash);
|
|
122
|
+
add_hash_class(node, hash, jsx_class_attr_name);
|
|
123
123
|
}
|
|
124
124
|
if (Array.isArray(node.children)) {
|
|
125
125
|
node.children = node.children
|
|
@@ -184,13 +184,14 @@ export function annotate_component_with_hash(
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/**
|
|
187
|
-
* Ensure the element carries a
|
|
187
|
+
* Ensure the element carries a class attribute containing the scoping hash.
|
|
188
188
|
*
|
|
189
189
|
* @param {any} element
|
|
190
190
|
* @param {string} hash
|
|
191
|
+
* @param {'class' | 'className'} [class_attr_name='class']
|
|
191
192
|
* @returns {void}
|
|
192
193
|
*/
|
|
193
|
-
export function add_hash_class(element, hash) {
|
|
194
|
+
export function add_hash_class(element, hash, class_attr_name = 'class') {
|
|
194
195
|
const attrs = element.attributes || (element.attributes = []);
|
|
195
196
|
const existing = attrs.find(
|
|
196
197
|
(/** @type {any} */ a) =>
|
|
@@ -203,7 +204,7 @@ export function add_hash_class(element, hash) {
|
|
|
203
204
|
if (!existing) {
|
|
204
205
|
attrs.push({
|
|
205
206
|
type: 'Attribute',
|
|
206
|
-
name: b.id(
|
|
207
|
+
name: b.id(class_attr_name),
|
|
207
208
|
value: { type: 'Literal', value: hash, raw: JSON.stringify(hash) },
|
|
208
209
|
});
|
|
209
210
|
return;
|
|
@@ -249,18 +250,6 @@ function add_hash_class_to_jsx_element(element, hash, jsx_class_attr_name) {
|
|
|
249
250
|
return;
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
if (existing.name.name !== jsx_class_attr_name) {
|
|
253
|
-
existing.name = {
|
|
254
|
-
type: 'JSXIdentifier',
|
|
255
|
-
name: jsx_class_attr_name,
|
|
256
|
-
metadata: {
|
|
257
|
-
...(existing.name.metadata || { path: [] }),
|
|
258
|
-
source_name: existing.name.name,
|
|
259
|
-
source_length: existing.name.name.length,
|
|
260
|
-
},
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
253
|
const value = existing.value;
|
|
265
254
|
if (!value) {
|
|
266
255
|
existing.value = { type: 'Literal', value: hash, raw: JSON.stringify(hash) };
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import * as b from '../utils/builders.js';
|
|
4
4
|
import { clone_expression_node, clone_identifier } from './jsx/ast-builders.js';
|
|
5
5
|
|
|
6
|
+
const regex_backslash_and_following_character = /\\(.)/g;
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* @typedef {{
|
|
8
10
|
* allowMutableRefTarget?: boolean;
|
|
@@ -19,7 +21,7 @@ import { clone_expression_node, clone_identifier } from './jsx/ast-builders.js';
|
|
|
19
21
|
export function create_style_class_map(component, css) {
|
|
20
22
|
const hash = css?.hash ?? null;
|
|
21
23
|
const top_scoped_classes = /** @type {Map<string, any>} */ (
|
|
22
|
-
component?.metadata?.topScopedClasses ??
|
|
24
|
+
component?.metadata?.topScopedClasses ?? collect_style_class_map_entries(css)
|
|
23
25
|
);
|
|
24
26
|
const class_names = [...top_scoped_classes.keys()].sort();
|
|
25
27
|
|
|
@@ -30,6 +32,28 @@ export function create_style_class_map(component, css) {
|
|
|
30
32
|
);
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @param {any} css
|
|
37
|
+
* @returns {AST.ObjectExpression}
|
|
38
|
+
*/
|
|
39
|
+
export function create_style_class_map_from_stylesheet(css) {
|
|
40
|
+
return create_style_class_map(
|
|
41
|
+
{ metadata: { topScopedClasses: collect_style_class_map_entries(css) } },
|
|
42
|
+
css,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {any} style_element
|
|
48
|
+
* @returns {any | null}
|
|
49
|
+
*/
|
|
50
|
+
export function get_style_element_stylesheet(style_element) {
|
|
51
|
+
return (
|
|
52
|
+
style_element?.children?.find?.((/** @type {any} */ child) => child.type === 'StyleSheet') ??
|
|
53
|
+
null
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
33
57
|
/**
|
|
34
58
|
* @param {any} node
|
|
35
59
|
* @param {any[]} [refs]
|
|
@@ -233,3 +257,70 @@ function is_function_or_class_boundary(node) {
|
|
|
233
257
|
node?.type === 'ClassExpression'
|
|
234
258
|
);
|
|
235
259
|
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* @param {any} css
|
|
263
|
+
* @returns {Map<string, any>}
|
|
264
|
+
*/
|
|
265
|
+
function collect_style_class_map_entries(css) {
|
|
266
|
+
const entries = new Map();
|
|
267
|
+
collect_rule_class_map_entries(css, entries);
|
|
268
|
+
return entries;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @param {any} node
|
|
273
|
+
* @param {Map<string, any>} entries
|
|
274
|
+
* @returns {void}
|
|
275
|
+
*/
|
|
276
|
+
function collect_rule_class_map_entries(node, entries) {
|
|
277
|
+
if (!node || typeof node !== 'object') return;
|
|
278
|
+
|
|
279
|
+
if (Array.isArray(node)) {
|
|
280
|
+
for (const child of node) collect_rule_class_map_entries(child, entries);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (node.type === 'ComplexSelector') {
|
|
285
|
+
const class_selector = get_standalone_class_selector(node);
|
|
286
|
+
if (class_selector) {
|
|
287
|
+
const name = class_selector.name.replace(regex_backslash_and_following_character, '$1');
|
|
288
|
+
if (!entries.has(name)) {
|
|
289
|
+
entries.set(name, {
|
|
290
|
+
start: class_selector.start,
|
|
291
|
+
end: class_selector.end,
|
|
292
|
+
selector: class_selector,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (is_function_or_class_boundary(node)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
for (const key of Object.keys(node)) {
|
|
303
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
collect_rule_class_map_entries(node[key], entries);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* @param {any} complex_selector
|
|
312
|
+
* @returns {any | null}
|
|
313
|
+
*/
|
|
314
|
+
function get_standalone_class_selector(complex_selector) {
|
|
315
|
+
if (complex_selector?.children?.length !== 1) return null;
|
|
316
|
+
const relative_selector = complex_selector.children[0];
|
|
317
|
+
if (
|
|
318
|
+
relative_selector?.metadata?.is_global ||
|
|
319
|
+
relative_selector?.metadata?.is_global_like ||
|
|
320
|
+
relative_selector?.selectors?.length !== 1
|
|
321
|
+
) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
const selector = relative_selector.selectors[0];
|
|
325
|
+
return selector?.type === 'ClassSelector' ? selector : null;
|
|
326
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export { createJsxTransform };
|
|
|
26
26
|
|
|
27
27
|
export function collectStyleRefAttributes(node: any, refs?: any[]): any[];
|
|
28
28
|
export function createStyleClassMap(component: any, css: any): AST.ObjectExpression;
|
|
29
|
+
export function createStyleClassMapFromStylesheet(css: any): AST.ObjectExpression;
|
|
29
30
|
export function createStyleRefSetupStatements(
|
|
30
31
|
refAttributes: any[],
|
|
31
32
|
styleMap: AST.Expression,
|
|
@@ -35,6 +36,7 @@ export function createStyleRefSetupStatements(
|
|
|
35
36
|
visitExpression?: (expression: AST.Expression) => AST.Expression;
|
|
36
37
|
},
|
|
37
38
|
): AST.Statement[];
|
|
39
|
+
export function getStyleElementStylesheet(styleElement: any): any | null;
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
42
|
* Compile error interface
|
package/types/jsx-platform.d.ts
CHANGED
|
@@ -354,10 +354,15 @@ export interface JsxPlatform {
|
|
|
354
354
|
|
|
355
355
|
jsx: {
|
|
356
356
|
/**
|
|
357
|
-
* Rewrite Ripple's `class` attribute to
|
|
358
|
-
*
|
|
357
|
+
* Rewrite Ripple's `class` attribute to `className` for legacy targets
|
|
358
|
+
* that require it. First-party targets keep authored `class`.
|
|
359
359
|
*/
|
|
360
360
|
rewriteClassAttr: boolean;
|
|
361
|
+
/**
|
|
362
|
+
* Attribute name to use when TSRX injects scoped CSS classes. This does
|
|
363
|
+
* not rewrite authored attributes.
|
|
364
|
+
*/
|
|
365
|
+
classAttrName?: 'class' | 'className';
|
|
361
366
|
/**
|
|
362
367
|
* Accepted values of `kind` in `<tsx:kind>` compat blocks. React accepts
|
|
363
368
|
* only `'react'`. Preact accepts both `'preact'` and `'react'`.
|