@tsrx/core 0.0.24 → 0.0.25
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 +4 -1
- package/src/plugin.js +36 -2
- package/src/transform/jsx/index.js +105 -38
- package/src/utils/builders.js +7 -4
- package/src/utils/escaping.js +11 -0
- package/src/utils/events.js +1 -1
- package/types/index.d.ts +12 -6
- package/types/jsx-platform.d.ts +1 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -133,8 +133,11 @@ export {
|
|
|
133
133
|
// Sanitize
|
|
134
134
|
export { sanitize_template_string as sanitizeTemplateString } from './utils/sanitize_template_string.js';
|
|
135
135
|
|
|
136
|
+
// CSS Property Name
|
|
137
|
+
export { normalize_css_property_name as normalizeCssPropertyName } from './utils/normalize_css_property_name.js';
|
|
138
|
+
|
|
136
139
|
// Escaping
|
|
137
|
-
export { escape } from './utils/escaping.js';
|
|
140
|
+
export { escape, escape_script as escapeScript } from './utils/escaping.js';
|
|
138
141
|
|
|
139
142
|
// Transform
|
|
140
143
|
export {
|
package/src/plugin.js
CHANGED
|
@@ -313,6 +313,12 @@ export function TSRXPlugin(config) {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
#popTemplateLiteralTokenContext() {
|
|
317
|
+
while (this.curContext()?.token === '`') {
|
|
318
|
+
this.context.pop();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
316
322
|
#isDoubleQuotedTextChildStart() {
|
|
317
323
|
if (this.#path.findLast((n) => n.type === 'TsxCompat' || n.type === 'Tsx')) {
|
|
318
324
|
return false;
|
|
@@ -1097,6 +1103,19 @@ export function TSRXPlugin(config) {
|
|
|
1097
1103
|
this.parseFunctionParams(node);
|
|
1098
1104
|
this.checkComponentParams(node.params);
|
|
1099
1105
|
|
|
1106
|
+
const is_arrow_component = this.type === tt.arrow;
|
|
1107
|
+
if (is_arrow_component) {
|
|
1108
|
+
if (node.id || requireName || skipName) {
|
|
1109
|
+
this.raise(
|
|
1110
|
+
this.start,
|
|
1111
|
+
'Arrow component syntax is only supported for anonymous component expressions.',
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
node.metadata ??= { path: [] };
|
|
1115
|
+
node.metadata.arrow = true;
|
|
1116
|
+
this.next();
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1100
1119
|
// Reset before `eat(braceL)` so the lookahead `next()` it triggers reads
|
|
1101
1120
|
// the component body's first token as if we'd entered fresh — no
|
|
1102
1121
|
// surrounding function body should affect our parseStatement/parseBlock
|
|
@@ -2370,7 +2389,7 @@ export function TSRXPlugin(config) {
|
|
|
2370
2389
|
body.push(node);
|
|
2371
2390
|
} else if (this.type === tstt.jsxTagStart) {
|
|
2372
2391
|
// Parse JSX element
|
|
2373
|
-
const node = super.
|
|
2392
|
+
const node = super.jsx_parseElement();
|
|
2374
2393
|
body.push(node);
|
|
2375
2394
|
} else {
|
|
2376
2395
|
const start = this.start;
|
|
@@ -2401,6 +2420,7 @@ export function TSRXPlugin(config) {
|
|
|
2401
2420
|
body.push(node);
|
|
2402
2421
|
}
|
|
2403
2422
|
|
|
2423
|
+
this.#popTemplateLiteralTokenContext();
|
|
2404
2424
|
// Always call next() to ensure parser makes progress
|
|
2405
2425
|
this.next();
|
|
2406
2426
|
}
|
|
@@ -2433,7 +2453,7 @@ export function TSRXPlugin(config) {
|
|
|
2433
2453
|
body.push(node);
|
|
2434
2454
|
} else if (this.type === tstt.jsxTagStart) {
|
|
2435
2455
|
// Parse JSX element
|
|
2436
|
-
const node = super.
|
|
2456
|
+
const node = super.jsx_parseElement();
|
|
2437
2457
|
body.push(node);
|
|
2438
2458
|
} else {
|
|
2439
2459
|
const start = this.start;
|
|
@@ -2464,6 +2484,7 @@ export function TSRXPlugin(config) {
|
|
|
2464
2484
|
body.push(node);
|
|
2465
2485
|
}
|
|
2466
2486
|
|
|
2487
|
+
this.#popTemplateLiteralTokenContext();
|
|
2467
2488
|
this.next();
|
|
2468
2489
|
}
|
|
2469
2490
|
}
|
|
@@ -2748,6 +2769,19 @@ export function TSRXPlugin(config) {
|
|
|
2748
2769
|
return node;
|
|
2749
2770
|
}
|
|
2750
2771
|
|
|
2772
|
+
if (
|
|
2773
|
+
this.#functionBodyDepth === 0 &&
|
|
2774
|
+
this.type === tt.string &&
|
|
2775
|
+
this.input.charCodeAt(this.start) === 34 &&
|
|
2776
|
+
(this.#path.at(-1)?.type === 'Component' || this.#path.at(-1)?.type === 'Element')
|
|
2777
|
+
) {
|
|
2778
|
+
this.pos = this.start;
|
|
2779
|
+
this.#readDoubleQuotedTextChildToken();
|
|
2780
|
+
const node = this.parseDoubleQuotedTextChild();
|
|
2781
|
+
this.semicolon();
|
|
2782
|
+
return node;
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2751
2785
|
// &[ or &{ at statement level — lazy destructuring assignment
|
|
2752
2786
|
// e.g., &[data] = track(0); or &{x, y} = obj;
|
|
2753
2787
|
if (this.type === tt.bitwiseAND) {
|
|
@@ -487,7 +487,7 @@ function has_use_server_directive(program) {
|
|
|
487
487
|
* @param {any} component
|
|
488
488
|
* @param {TransformContext} transform_context
|
|
489
489
|
* @param {{ base_name: string, next_id: number, helpers: AST.FunctionDeclaration[], statics: any[] }} [walk_helper_state]
|
|
490
|
-
* @returns {AST.FunctionDeclaration}
|
|
490
|
+
* @returns {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression}
|
|
491
491
|
*/
|
|
492
492
|
export function component_to_function_declaration(component, transform_context, walk_helper_state) {
|
|
493
493
|
const helper_state = walk_helper_state || create_helper_state(component.id?.name || 'Component');
|
|
@@ -527,28 +527,62 @@ export function component_to_function_declaration(component, transform_context,
|
|
|
527
527
|
const final_body =
|
|
528
528
|
lazy_bindings.size > 0 ? apply_lazy_transforms(body_block, lazy_bindings) : body_block;
|
|
529
529
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
530
|
+
/** @type {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression} */
|
|
531
|
+
let fn;
|
|
532
|
+
|
|
533
|
+
if (component.id) {
|
|
534
|
+
fn = /** @type {any} */ ({
|
|
535
|
+
type: 'FunctionDeclaration',
|
|
536
|
+
id: component.id,
|
|
537
|
+
typeParameters: component.typeParameters,
|
|
538
|
+
params: final_params,
|
|
539
|
+
body: final_body,
|
|
540
|
+
async: is_async_component,
|
|
541
|
+
generator: false,
|
|
542
|
+
metadata: {
|
|
543
|
+
path: [],
|
|
544
|
+
is_component: true,
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
} else if (component.metadata?.arrow) {
|
|
548
|
+
fn = /** @type {any} */ ({
|
|
549
|
+
type: 'ArrowFunctionExpression',
|
|
550
|
+
typeParameters: component.typeParameters,
|
|
551
|
+
params: final_params,
|
|
552
|
+
body: final_body,
|
|
553
|
+
async: is_async_component,
|
|
554
|
+
generator: false,
|
|
555
|
+
expression: false,
|
|
556
|
+
metadata: {
|
|
557
|
+
path: [],
|
|
558
|
+
is_component: true,
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
} else {
|
|
562
|
+
fn = /** @type {any} */ ({
|
|
563
|
+
type: 'FunctionExpression',
|
|
564
|
+
id: null,
|
|
565
|
+
typeParameters: component.typeParameters,
|
|
566
|
+
params: final_params,
|
|
567
|
+
body: final_body,
|
|
568
|
+
async: is_async_component,
|
|
569
|
+
generator: false,
|
|
570
|
+
metadata: {
|
|
571
|
+
path: [],
|
|
572
|
+
is_component: true,
|
|
573
|
+
},
|
|
574
|
+
});
|
|
575
|
+
}
|
|
543
576
|
|
|
544
577
|
// Restore context
|
|
545
578
|
transform_context.helper_state = saved_helper_state;
|
|
546
579
|
transform_context.available_bindings = saved_bindings;
|
|
547
580
|
|
|
548
|
-
|
|
549
|
-
|
|
581
|
+
const fn_metadata = /** @type {any} */ (fn.metadata);
|
|
582
|
+
fn_metadata.generated_helpers = helper_state.helpers;
|
|
583
|
+
fn_metadata.generated_statics = helper_state.statics;
|
|
550
584
|
|
|
551
|
-
if (fn.id) {
|
|
585
|
+
if (fn.type === 'FunctionDeclaration' && fn.id) {
|
|
552
586
|
fn.id.metadata = /** @type {AST.Identifier['metadata']} */ ({
|
|
553
587
|
...fn.id.metadata,
|
|
554
588
|
is_component: true,
|
|
@@ -1297,9 +1331,9 @@ function hoist_static_render_nodes(render_nodes, transform_context) {
|
|
|
1297
1331
|
*/
|
|
1298
1332
|
function expand_component_helpers(program) {
|
|
1299
1333
|
program.body = program.body.flatMap((statement) => {
|
|
1300
|
-
const
|
|
1301
|
-
const statics = meta
|
|
1302
|
-
const helpers = meta
|
|
1334
|
+
const metas = get_generated_component_metadata_list(statement);
|
|
1335
|
+
const statics = metas.flatMap((meta) => meta.generated_statics || []);
|
|
1336
|
+
const helpers = metas.flatMap((meta) => meta.generated_helpers || []);
|
|
1303
1337
|
if (statics.length || helpers.length) {
|
|
1304
1338
|
return [...statics, ...helpers, statement];
|
|
1305
1339
|
}
|
|
@@ -1312,30 +1346,63 @@ function expand_component_helpers(program) {
|
|
|
1312
1346
|
|
|
1313
1347
|
/**
|
|
1314
1348
|
* Component hooks may replace a `Component` node with a function declaration,
|
|
1315
|
-
* variable declaration, or export-safe expression.
|
|
1316
|
-
* metadata is carried on whichever replacement node
|
|
1317
|
-
* helper expansion must read metadata from that broader
|
|
1349
|
+
* variable declaration, object literal member, or export-safe expression.
|
|
1350
|
+
* Generated helper/statics metadata is carried on whichever replacement node
|
|
1351
|
+
* the hook returns, so helper expansion must read metadata from that broader
|
|
1352
|
+
* set.
|
|
1318
1353
|
*
|
|
1319
1354
|
* @param {any} node
|
|
1320
|
-
* @returns {{ generated_helpers?: any[], generated_statics?: any[] }
|
|
1355
|
+
* @returns {{ generated_helpers?: any[], generated_statics?: any[] }[]}
|
|
1321
1356
|
*/
|
|
1322
|
-
function
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1357
|
+
function get_generated_component_metadata_list(node) {
|
|
1358
|
+
/** @type {{ generated_helpers?: any[], generated_statics?: any[] }[]} */
|
|
1359
|
+
const metas = [];
|
|
1360
|
+
const seen_nodes = new Set();
|
|
1361
|
+
const seen_metas = new Set();
|
|
1362
|
+
|
|
1363
|
+
/** @param {any} current */
|
|
1364
|
+
const visit = (current) => {
|
|
1365
|
+
if (!current || typeof current !== 'object' || seen_nodes.has(current)) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1326
1368
|
|
|
1327
|
-
|
|
1328
|
-
return node.metadata;
|
|
1329
|
-
}
|
|
1369
|
+
seen_nodes.add(current);
|
|
1330
1370
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1371
|
+
if (current.metadata?.generated_helpers || current.metadata?.generated_statics) {
|
|
1372
|
+
if (!seen_metas.has(current.metadata)) {
|
|
1373
|
+
seen_metas.add(current.metadata);
|
|
1374
|
+
metas.push(current.metadata);
|
|
1375
|
+
}
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
if (
|
|
1380
|
+
current.type === 'FunctionDeclaration' ||
|
|
1381
|
+
current.type === 'FunctionExpression' ||
|
|
1382
|
+
current.type === 'ArrowFunctionExpression'
|
|
1383
|
+
) {
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
for (const key of Object.keys(current)) {
|
|
1388
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
const value = current[key];
|
|
1393
|
+
if (Array.isArray(value)) {
|
|
1394
|
+
for (const child of value) {
|
|
1395
|
+
visit(child);
|
|
1396
|
+
}
|
|
1397
|
+
} else {
|
|
1398
|
+
visit(value);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
visit(node);
|
|
1337
1404
|
|
|
1338
|
-
return
|
|
1405
|
+
return metas;
|
|
1339
1406
|
}
|
|
1340
1407
|
|
|
1341
1408
|
/**
|
package/src/utils/builders.js
CHANGED
|
@@ -57,18 +57,21 @@ export function assignment_pattern(left, right) {
|
|
|
57
57
|
/**
|
|
58
58
|
* @param {Array<AST.Pattern>} params
|
|
59
59
|
* @param {AST.BlockStatement | AST.Expression} body
|
|
60
|
+
* @param {AST.NodeWithLocation} [loc_info]
|
|
60
61
|
* @returns {AST.ArrowFunctionExpression}
|
|
61
62
|
*/
|
|
62
|
-
export function arrow(params, body, async = false) {
|
|
63
|
-
|
|
63
|
+
export function arrow(params, body, async = false, loc_info) {
|
|
64
|
+
const node = /** @type {AST.ArrowFunctionExpression} */ ({
|
|
64
65
|
type: 'ArrowFunctionExpression',
|
|
65
66
|
params,
|
|
66
67
|
body,
|
|
67
68
|
expression: body.type !== 'BlockStatement',
|
|
68
69
|
generator: false,
|
|
69
70
|
async,
|
|
70
|
-
metadata:
|
|
71
|
-
};
|
|
71
|
+
metadata: { path: [] },
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return set_location(node, loc_info);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
/**
|
package/src/utils/escaping.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const ATTR_REGEX = /[&"<]/g;
|
|
2
2
|
const CONTENT_REGEX = /[&<]/g;
|
|
3
|
+
const OPEN_TAG_REGEX = /</g;
|
|
4
|
+
const CLOSE_TAG_REGEX = />/g;
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* @template V
|
|
@@ -24,3 +26,12 @@ export function escape(value, is_attr) {
|
|
|
24
26
|
|
|
25
27
|
return escaped + str.substring(last);
|
|
26
28
|
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Escapes characters that can prematurely terminate inline script tags.
|
|
32
|
+
* @param {string} str
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
export function escape_script(str) {
|
|
36
|
+
return str.replace(OPEN_TAG_REGEX, '\\u003c').replace(CLOSE_TAG_REGEX, '\\u003e');
|
|
37
|
+
}
|
package/src/utils/events.js
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -320,12 +320,13 @@ declare module 'estree' {
|
|
|
320
320
|
*/
|
|
321
321
|
interface Component extends AST.BaseNode {
|
|
322
322
|
type: 'Component';
|
|
323
|
-
// null is for anonymous components
|
|
323
|
+
// null is for anonymous components, e.g. `component(props) => {}`
|
|
324
324
|
id: AST.Identifier | null;
|
|
325
325
|
params: AST.Pattern[];
|
|
326
326
|
body: AST.Node[];
|
|
327
327
|
css: CSS.StyleSheet | null;
|
|
328
328
|
metadata: BaseNodeMetaData & {
|
|
329
|
+
arrow?: boolean;
|
|
329
330
|
topScopedClasses?: TopScopedClasses;
|
|
330
331
|
styleClasses?: StyleClasses;
|
|
331
332
|
};
|
|
@@ -1499,15 +1500,20 @@ export type StyleClasses = Map<string, AST.MemberExpression['property']>;
|
|
|
1499
1500
|
/**
|
|
1500
1501
|
* Event handling types
|
|
1501
1502
|
*/
|
|
1502
|
-
export interface
|
|
1503
|
+
export interface AddEventOptions extends ExtendedEventOptions {
|
|
1503
1504
|
customName?: string;
|
|
1504
|
-
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
export interface AddEventObject extends AddEventOptions {
|
|
1508
|
+
handleEvent(object: Event): void;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
export interface ExtendedEventOptions {
|
|
1512
|
+
capture?: boolean;
|
|
1505
1513
|
once?: boolean;
|
|
1506
1514
|
passive?: boolean;
|
|
1507
1515
|
signal?: AbortSignal;
|
|
1508
|
-
|
|
1509
|
-
// from EventListenerObject
|
|
1510
|
-
handleEvent?(object: Event): void;
|
|
1516
|
+
delegated?: boolean;
|
|
1511
1517
|
}
|
|
1512
1518
|
|
|
1513
1519
|
/**
|
package/types/jsx-platform.d.ts
CHANGED