@tsrx/core 0.1.8 → 0.1.9
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 +5 -5
- package/src/diagnostics.js +1 -0
- package/src/index.js +7 -0
- package/src/plugin.js +56 -12
- package/src/transform/jsx/index.js +177 -9
- package/types/jsx-platform.d.ts +2 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core compiler infrastructure for TSRX syntax",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.9",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -82,10 +82,10 @@
|
|
|
82
82
|
"vscode-languageserver-types": "^3.17.5",
|
|
83
83
|
"vue": "3.6.0-beta.10",
|
|
84
84
|
"vue-jsx-vapor": "^3.2.12",
|
|
85
|
-
"@tsrx/preact": "0.1.
|
|
86
|
-
"@tsrx/react": "0.2.
|
|
87
|
-
"@tsrx/solid": "0.1.
|
|
88
|
-
"@tsrx/vue": "0.1.
|
|
85
|
+
"@tsrx/preact": "0.1.9",
|
|
86
|
+
"@tsrx/react": "0.2.9",
|
|
87
|
+
"@tsrx/solid": "0.1.9",
|
|
88
|
+
"@tsrx/vue": "0.1.9"
|
|
89
89
|
},
|
|
90
90
|
"files": [
|
|
91
91
|
"src",
|
package/src/diagnostics.js
CHANGED
|
@@ -5,4 +5,5 @@ export const DIAGNOSTIC_CODES = {
|
|
|
5
5
|
UNCLOSED_TAG: 'tsrx-unclosed-tag',
|
|
6
6
|
MISMATCHED_CLOSING_TAG: 'tsrx-mismatched-closing-tag',
|
|
7
7
|
TEMPLATE_EXPRESSION_TRAILING_SEMICOLON: 'tsrx-template-expression-trailing-semicolon',
|
|
8
|
+
HTML_DIRECTIVE_AS_ATTRIBUTE_VALUE: 'tsrx-html-directive-as-attribute-value',
|
|
8
9
|
};
|
package/src/index.js
CHANGED
|
@@ -146,14 +146,21 @@ export {
|
|
|
146
146
|
clone_switch_helper_invocation as cloneSwitchHelperInvocation,
|
|
147
147
|
collect_param_bindings as collectParamBindings,
|
|
148
148
|
collect_statement_bindings as collectStatementBindings,
|
|
149
|
+
create_host_html_attribute as createHostHtmlAttribute,
|
|
150
|
+
create_host_html_conflict_error as createHostHtmlConflictError,
|
|
149
151
|
createJsxTransform,
|
|
150
152
|
CREATE_REF_PROP_INTERNAL_NAME,
|
|
151
153
|
extract_jsx_setup_declarations as extractJsxSetupDeclarations,
|
|
154
|
+
get_host_html_conflicting_attribute as getHostHtmlConflictingAttribute,
|
|
155
|
+
get_invalid_html_child_error_message as getInvalidHtmlChildErrorMessage,
|
|
156
|
+
is_component_like_element,
|
|
152
157
|
is_ref_prop_expression as isRefPropExpression,
|
|
153
158
|
MERGE_REFS_INTERNAL_NAME,
|
|
154
159
|
merge_duplicate_refs as mergeDuplicateRefs,
|
|
155
160
|
NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
|
|
156
161
|
plan_switch_lift as planSwitchLift,
|
|
162
|
+
recover_invalid_html_child as recoverInvalidHtmlChild,
|
|
163
|
+
rewrite_host_html_children as rewriteHostHtmlChildren,
|
|
157
164
|
return_value_body_to_expression as returnValueBodyToExpression,
|
|
158
165
|
rewrite_loop_continues_to_bare_returns as rewriteLoopContinuesToBareReturns,
|
|
159
166
|
to_jsx_attribute as toJsxAttribute,
|
package/src/plugin.js
CHANGED
|
@@ -19,6 +19,8 @@ import { DIAGNOSTIC_CODES } from './diagnostics.js';
|
|
|
19
19
|
|
|
20
20
|
const JSX_EXPRESSION_VALUE_ERROR =
|
|
21
21
|
'JSX elements cannot be used as expressions. Wrap JSX with `<>...</>` or `<tsx>...</tsx>`, wrap TSRX templates with `<tsrx>...</tsrx>`, or use elements as statements within a component.';
|
|
22
|
+
const HTML_ATTRIBUTE_VALUE_ERROR =
|
|
23
|
+
'`{html ...}` is not supported as an attribute value. Use a string literal or expression without `html`.';
|
|
22
24
|
|
|
23
25
|
const CharCode = Object.freeze({
|
|
24
26
|
tab: 9,
|
|
@@ -1969,6 +1971,27 @@ export function TSRXPlugin(config) {
|
|
|
1969
1971
|
/** @type {AST.RefAttribute} */ (node).argument = this.parseMaybeAssign();
|
|
1970
1972
|
this.expect(tt.braceR);
|
|
1971
1973
|
return /** @type {AST.RefAttribute} */ (this.finishNode(node, 'RefAttribute'));
|
|
1974
|
+
} else if (this.type === tt.name && this.value === 'html') {
|
|
1975
|
+
// {html ...}
|
|
1976
|
+
// The support is purely for better error messages to avoid
|
|
1977
|
+
// the parser throw an unexpected token error
|
|
1978
|
+
const id = /** @type {AST.Identifier} */ (this.parseIdentNode());
|
|
1979
|
+
id.tracked = false;
|
|
1980
|
+
this.finishNode(id, 'Identifier');
|
|
1981
|
+
this.next();
|
|
1982
|
+
const value = this.type === tt.braceR ? id : this.parseMaybeAssign();
|
|
1983
|
+
const report_end = this.type === tt.braceR ? this.end : (value.end ?? this.end);
|
|
1984
|
+
this.#report_recoverable_error_range(
|
|
1985
|
+
node.start ?? id.start ?? this.start,
|
|
1986
|
+
report_end,
|
|
1987
|
+
HTML_ATTRIBUTE_VALUE_ERROR,
|
|
1988
|
+
DIAGNOSTIC_CODES.HTML_DIRECTIVE_AS_ATTRIBUTE_VALUE,
|
|
1989
|
+
);
|
|
1990
|
+
/** @type {AST.Attribute} */ (node).name = id;
|
|
1991
|
+
/** @type {AST.Attribute} */ (node).value = value;
|
|
1992
|
+
/** @type {AST.Attribute} */ (node).shorthand = false;
|
|
1993
|
+
this.expect(tt.braceR);
|
|
1994
|
+
return this.finishNode(node, 'Attribute');
|
|
1972
1995
|
} else if (this.type === tt.ellipsis) {
|
|
1973
1996
|
this.expect(tt.ellipsis);
|
|
1974
1997
|
/** @type {AST.SpreadAttribute} */ (node).argument = this.parseMaybeAssign();
|
|
@@ -1992,10 +2015,18 @@ export function TSRXPlugin(config) {
|
|
|
1992
2015
|
}
|
|
1993
2016
|
}
|
|
1994
2017
|
/** @type {ESTreeJSX.JSXAttribute} */ (node).name = this.jsx_parseNamespacedName();
|
|
1995
|
-
/** @type {ESTreeJSX.JSXAttribute} */ (
|
|
1996
|
-
|
|
1997
|
-
|
|
2018
|
+
const value = /** @type {ESTreeJSX.JSXAttribute['value'] | null} */ (
|
|
2019
|
+
this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null
|
|
2020
|
+
);
|
|
2021
|
+
if (value?.type === 'JSXExpressionContainer' && value.html) {
|
|
2022
|
+
this.#report_recoverable_error_range(
|
|
2023
|
+
value.start ?? node.start ?? this.start,
|
|
2024
|
+
value.end ?? node.end ?? this.end,
|
|
2025
|
+
HTML_ATTRIBUTE_VALUE_ERROR,
|
|
2026
|
+
DIAGNOSTIC_CODES.HTML_DIRECTIVE_AS_ATTRIBUTE_VALUE,
|
|
1998
2027
|
);
|
|
2028
|
+
}
|
|
2029
|
+
/** @type {ESTreeJSX.JSXAttribute} */ (node).value = value;
|
|
1999
2030
|
return this.finishNode(node, 'JSXAttribute');
|
|
2000
2031
|
}
|
|
2001
2032
|
|
|
@@ -2205,6 +2236,8 @@ export function TSRXPlugin(config) {
|
|
|
2205
2236
|
// In JSX text mode, '<' and '{' always start a tag/expression container.
|
|
2206
2237
|
// `exprAllowed` can be false here due to surrounding parser state, but
|
|
2207
2238
|
// throwing breaks valid templates (e.g. sibling tags after a close).
|
|
2239
|
+
this.start = this.pos;
|
|
2240
|
+
this.startLoc = this.curPosition();
|
|
2208
2241
|
if (ch === CharCode.lessThan) {
|
|
2209
2242
|
++this.pos;
|
|
2210
2243
|
return this.finishToken(tstt.jsxTagStart);
|
|
@@ -2434,6 +2467,8 @@ export function TSRXPlugin(config) {
|
|
|
2434
2467
|
/** @type {AST.NodeWithLocation} */ (element).loc.start = position;
|
|
2435
2468
|
element.metadata = { path: [] };
|
|
2436
2469
|
element.children = [];
|
|
2470
|
+
element.type = 'Element';
|
|
2471
|
+
this.#path.push(element);
|
|
2437
2472
|
|
|
2438
2473
|
const open = /** @type {ESTreeJSX.JSXOpeningElement & AST.NodeWithLocation} */ (
|
|
2439
2474
|
this.jsx_parseOpeningElementAt(start, position)
|
|
@@ -2492,8 +2527,6 @@ export function TSRXPlugin(config) {
|
|
|
2492
2527
|
element.type = 'Element';
|
|
2493
2528
|
}
|
|
2494
2529
|
|
|
2495
|
-
this.#path.push(element);
|
|
2496
|
-
|
|
2497
2530
|
for (const attr of open.attributes) {
|
|
2498
2531
|
if (attr.type === 'JSXAttribute') {
|
|
2499
2532
|
/** @type {AST.Attribute} */ (/** @type {unknown} */ (attr)).type = 'Attribute';
|
|
@@ -2534,7 +2567,12 @@ export function TSRXPlugin(config) {
|
|
|
2534
2567
|
|
|
2535
2568
|
element.attributes = open.attributes;
|
|
2536
2569
|
element.metadata ??= { path: [] };
|
|
2537
|
-
|
|
2570
|
+
// Opening-tag parsing can tokenize comments that appear before the first
|
|
2571
|
+
// child. Preserve that early container id so the comment stays associated
|
|
2572
|
+
// with this element during comment attachment/printing.
|
|
2573
|
+
if (element.metadata.commentContainerId === undefined) {
|
|
2574
|
+
element.metadata.commentContainerId = ++this.#commentContextId;
|
|
2575
|
+
}
|
|
2538
2576
|
|
|
2539
2577
|
if (element.selfClosing) {
|
|
2540
2578
|
this.#path.pop();
|
|
@@ -2548,7 +2586,7 @@ export function TSRXPlugin(config) {
|
|
|
2548
2586
|
enterScope: true,
|
|
2549
2587
|
});
|
|
2550
2588
|
|
|
2551
|
-
if (element.type === 'Tsx') {
|
|
2589
|
+
if (/** @type {AST.Tsx} */ (element).type === 'Tsx') {
|
|
2552
2590
|
this.#path.pop();
|
|
2553
2591
|
|
|
2554
2592
|
if (!element.unclosed) {
|
|
@@ -2725,7 +2763,7 @@ export function TSRXPlugin(config) {
|
|
|
2725
2763
|
enterScope: true,
|
|
2726
2764
|
});
|
|
2727
2765
|
|
|
2728
|
-
if (element.type === 'Tsx') {
|
|
2766
|
+
if (/** @type {AST.Tsx} */ (element).type === 'Tsx') {
|
|
2729
2767
|
this.#path.pop();
|
|
2730
2768
|
|
|
2731
2769
|
if (!element.unclosed) {
|
|
@@ -2749,12 +2787,15 @@ export function TSRXPlugin(config) {
|
|
|
2749
2787
|
this.#popTsxTokenContextBeforeTemplateExpressionChild();
|
|
2750
2788
|
this.next();
|
|
2751
2789
|
}
|
|
2752
|
-
} else if (element.type === 'TsxCompat') {
|
|
2790
|
+
} else if (/** @type {AST.TsxCompat} */ (element).type === 'TsxCompat') {
|
|
2753
2791
|
this.#path.pop();
|
|
2754
2792
|
|
|
2755
2793
|
if (!element.unclosed) {
|
|
2756
2794
|
const raise_error = () => {
|
|
2757
|
-
this.raise(
|
|
2795
|
+
this.raise(
|
|
2796
|
+
this.start,
|
|
2797
|
+
`Expected closing tag '</tsx:${/** @type {AST.TsxCompat} */ (element).kind}>'`,
|
|
2798
|
+
);
|
|
2758
2799
|
};
|
|
2759
2800
|
|
|
2760
2801
|
this.next();
|
|
@@ -2771,7 +2812,7 @@ export function TSRXPlugin(config) {
|
|
|
2771
2812
|
raise_error();
|
|
2772
2813
|
}
|
|
2773
2814
|
this.next();
|
|
2774
|
-
if (this.value !== element.kind) {
|
|
2815
|
+
if (this.value !== /** @type {AST.TsxCompat} */ (element).kind) {
|
|
2775
2816
|
raise_error();
|
|
2776
2817
|
}
|
|
2777
2818
|
this.next();
|
|
@@ -2781,7 +2822,10 @@ export function TSRXPlugin(config) {
|
|
|
2781
2822
|
this.#popTsxTokenContextBeforeTemplateExpressionChild();
|
|
2782
2823
|
this.next();
|
|
2783
2824
|
}
|
|
2784
|
-
} else if (
|
|
2825
|
+
} else if (
|
|
2826
|
+
/** @type {AST.Tsrx} */ (element).type === 'Tsrx' &&
|
|
2827
|
+
this.#path[this.#path.length - 1] === element
|
|
2828
|
+
) {
|
|
2785
2829
|
this.#report_broken_markup_error(
|
|
2786
2830
|
this.start,
|
|
2787
2831
|
"Unclosed tag '<tsrx>'. Expected '</tsrx>' before end of component.",
|
|
@@ -60,20 +60,43 @@ const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
|
|
|
60
60
|
const TEMPLATE_FRAGMENT_ERROR =
|
|
61
61
|
'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only within <tsx>...</tsx>.';
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* @param {TransformContext} transform_context
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
export function get_invalid_html_child_error_message(transform_context) {
|
|
68
|
+
return `\`{html ...}\` is only supported as the sole child of an element in ${transform_context.platform.name}.`;
|
|
69
|
+
}
|
|
70
|
+
|
|
63
71
|
/**
|
|
64
72
|
* @param {AST.Node} node
|
|
65
73
|
* @param {TransformContext} transform_context
|
|
66
74
|
*/
|
|
67
|
-
function
|
|
68
|
-
// this should be a fatal error so we don't pass the errors collection,
|
|
69
|
-
// since we don't have a transform for the Html node
|
|
75
|
+
function report_invalid_html_child_error(node, transform_context) {
|
|
70
76
|
error(
|
|
71
|
-
|
|
77
|
+
get_invalid_html_child_error_message(transform_context),
|
|
72
78
|
transform_context.filename,
|
|
73
79
|
node,
|
|
80
|
+
transform_context.errors,
|
|
81
|
+
transform_context.comments,
|
|
74
82
|
);
|
|
75
83
|
}
|
|
76
84
|
|
|
85
|
+
/**
|
|
86
|
+
* In loose/editor mode `error(...)` records the diagnostic and continues, so an
|
|
87
|
+
* invalid standalone `{html ...}` child still needs a valid expression node for
|
|
88
|
+
* the virtual TSX output.
|
|
89
|
+
*
|
|
90
|
+
* @param {any} node
|
|
91
|
+
* @param {TransformContext} transform_context
|
|
92
|
+
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
93
|
+
*/
|
|
94
|
+
export function recover_invalid_html_child(node, transform_context) {
|
|
95
|
+
report_invalid_html_child_error(node, transform_context);
|
|
96
|
+
const expression = set_loc(clone_expression_node(node.expression), node);
|
|
97
|
+
return to_jsx_expression_container(expression, node);
|
|
98
|
+
}
|
|
99
|
+
|
|
77
100
|
/**
|
|
78
101
|
* @param {AST.Node} node
|
|
79
102
|
* @param {TransformContext} transform_context
|
|
@@ -2310,10 +2333,19 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
|
|
|
2310
2333
|
selfClosing = child_transform.selfClosing;
|
|
2311
2334
|
}
|
|
2312
2335
|
} else {
|
|
2313
|
-
|
|
2314
|
-
|
|
2336
|
+
const html_child_transform = rewrite_host_html_children(
|
|
2337
|
+
node,
|
|
2338
|
+
walked_children,
|
|
2339
|
+
raw_children,
|
|
2340
|
+
attributes,
|
|
2341
|
+
transform_context,
|
|
2342
|
+
);
|
|
2343
|
+
if (html_child_transform) {
|
|
2344
|
+
children = html_child_transform.children;
|
|
2345
|
+
selfClosing = html_child_transform.selfClosing;
|
|
2346
|
+
} else {
|
|
2347
|
+
children = create_element_children(walked_children, transform_context);
|
|
2315
2348
|
}
|
|
2316
|
-
children = create_element_children(walked_children, transform_context);
|
|
2317
2349
|
}
|
|
2318
2350
|
const has_unmappable_attribute = attributes.some(
|
|
2319
2351
|
(/** @type {any} */ attribute) => attribute?.metadata?.has_unmappable_value,
|
|
@@ -2341,6 +2373,117 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
|
|
|
2341
2373
|
return set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
|
|
2342
2374
|
}
|
|
2343
2375
|
|
|
2376
|
+
/**
|
|
2377
|
+
* @param {any} node
|
|
2378
|
+
* @param {any[]} walked_children
|
|
2379
|
+
* @param {any[]} raw_children
|
|
2380
|
+
* @param {any[]} attributes
|
|
2381
|
+
* @param {TransformContext} transform_context
|
|
2382
|
+
* @returns {{ children: any[]; selfClosing: boolean } | null}
|
|
2383
|
+
*/
|
|
2384
|
+
export function rewrite_host_html_children(
|
|
2385
|
+
node,
|
|
2386
|
+
walked_children,
|
|
2387
|
+
raw_children,
|
|
2388
|
+
attributes,
|
|
2389
|
+
transform_context,
|
|
2390
|
+
) {
|
|
2391
|
+
const source_children = raw_children || walked_children;
|
|
2392
|
+
const source_html_index = source_children.findIndex((child) => child?.type === 'Html');
|
|
2393
|
+
if (source_html_index === -1) {
|
|
2394
|
+
return null;
|
|
2395
|
+
}
|
|
2396
|
+
const source_html = source_children[source_html_index];
|
|
2397
|
+
const walked_html =
|
|
2398
|
+
walked_children[source_html_index]?.type === 'Html'
|
|
2399
|
+
? walked_children[source_html_index]
|
|
2400
|
+
: source_html;
|
|
2401
|
+
|
|
2402
|
+
if (is_component_like_element(node) || source_children.length !== 1) {
|
|
2403
|
+
report_invalid_html_child_error(source_html, transform_context);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
const conflicting_attribute = get_host_html_conflicting_attribute(attributes, transform_context);
|
|
2407
|
+
if (conflicting_attribute !== null) {
|
|
2408
|
+
error(
|
|
2409
|
+
create_host_html_conflict_error(conflicting_attribute, transform_context),
|
|
2410
|
+
transform_context.filename,
|
|
2411
|
+
source_html,
|
|
2412
|
+
transform_context.errors,
|
|
2413
|
+
transform_context.comments,
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
attributes.push(create_host_html_attribute(walked_html, source_html, transform_context));
|
|
2418
|
+
|
|
2419
|
+
return { children: [], selfClosing: true };
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
/**
|
|
2423
|
+
* @param {any[]} attributes
|
|
2424
|
+
* @param {TransformContext} transform_context
|
|
2425
|
+
* @returns {{ kind: 'attribute'; name: string } | null}
|
|
2426
|
+
*/
|
|
2427
|
+
export function get_host_html_conflicting_attribute(attributes, transform_context) {
|
|
2428
|
+
const conflicting_attributes = get_host_html_conflicting_attribute_names(transform_context);
|
|
2429
|
+
for (const name of conflicting_attributes) {
|
|
2430
|
+
if (has_jsx_attribute(attributes, name)) {
|
|
2431
|
+
return { kind: 'attribute', name };
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
return null;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
/**
|
|
2439
|
+
* @param {{ kind: 'attribute'; name: string }} conflicting_attribute
|
|
2440
|
+
* @param {TransformContext} transform_context
|
|
2441
|
+
* @returns {string}
|
|
2442
|
+
*/
|
|
2443
|
+
export function create_host_html_conflict_error(conflicting_attribute, transform_context) {
|
|
2444
|
+
const html_attribute = get_host_html_attribute_name(transform_context);
|
|
2445
|
+
return `\`{html ...}\` lowers to \`${html_attribute}\` on the ${transform_context.platform.name} target and cannot be combined with an existing \`${conflicting_attribute.name}\` attribute.`;
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
/**
|
|
2449
|
+
* @param {TransformContext} transform_context
|
|
2450
|
+
* @returns {string[]}
|
|
2451
|
+
*/
|
|
2452
|
+
function get_host_html_conflicting_attribute_names(transform_context) {
|
|
2453
|
+
switch (transform_context.platform.name) {
|
|
2454
|
+
case 'Solid':
|
|
2455
|
+
return ['innerHTML', 'textContent'];
|
|
2456
|
+
case 'Vue':
|
|
2457
|
+
return ['innerHTML'];
|
|
2458
|
+
default:
|
|
2459
|
+
return [get_host_html_attribute_name(transform_context)];
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
/**
|
|
2464
|
+
* @param {TransformContext} transform_context
|
|
2465
|
+
* @returns {'dangerouslySetInnerHTML' | 'innerHTML'}
|
|
2466
|
+
*/
|
|
2467
|
+
function get_host_html_attribute_name(transform_context) {
|
|
2468
|
+
return transform_context.platform.jsx?.htmlProp === 'dangerouslySetInnerHTML'
|
|
2469
|
+
? 'dangerouslySetInnerHTML'
|
|
2470
|
+
: 'innerHTML';
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
/**
|
|
2474
|
+
* @param {any[]} attributes
|
|
2475
|
+
* @param {string} name
|
|
2476
|
+
* @returns {boolean}
|
|
2477
|
+
*/
|
|
2478
|
+
function has_jsx_attribute(attributes, name) {
|
|
2479
|
+
return attributes.some(
|
|
2480
|
+
(attr) =>
|
|
2481
|
+
attr?.type === 'JSXAttribute' &&
|
|
2482
|
+
attr.name?.type === 'JSXIdentifier' &&
|
|
2483
|
+
attr.name.name === name,
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2344
2487
|
/**
|
|
2345
2488
|
* @param {any[]} children
|
|
2346
2489
|
* @param {TransformContext} transform_context
|
|
@@ -3580,7 +3723,7 @@ function to_jsx_child(node, transform_context) {
|
|
|
3580
3723
|
case 'TSRXExpression':
|
|
3581
3724
|
return to_jsx_expression_container(node.expression, node);
|
|
3582
3725
|
case 'Html':
|
|
3583
|
-
return
|
|
3726
|
+
return recover_invalid_html_child(node, transform_context);
|
|
3584
3727
|
case 'IfStatement':
|
|
3585
3728
|
return (
|
|
3586
3729
|
transform_context.platform.hooks?.controlFlow?.ifStatement ?? if_statement_to_jsx_child
|
|
@@ -4918,7 +5061,7 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
|
|
|
4918
5061
|
* @param {any} element
|
|
4919
5062
|
* @returns {boolean}
|
|
4920
5063
|
*/
|
|
4921
|
-
function is_component_like_element(element) {
|
|
5064
|
+
export function is_component_like_element(element) {
|
|
4922
5065
|
const id = element?.id;
|
|
4923
5066
|
if (!id) return false;
|
|
4924
5067
|
if (id.type === 'Identifier') return /^[A-Z]/.test(id.name);
|
|
@@ -5127,6 +5270,31 @@ function is_named_ref_attribute(attr) {
|
|
|
5127
5270
|
);
|
|
5128
5271
|
}
|
|
5129
5272
|
|
|
5273
|
+
/**
|
|
5274
|
+
* @param {any} html_expression
|
|
5275
|
+
* @param {any} source_attr
|
|
5276
|
+
* @param {TransformContext} transform_context
|
|
5277
|
+
* @returns {any}
|
|
5278
|
+
*/
|
|
5279
|
+
export function create_host_html_attribute(html_expression, source_attr, transform_context) {
|
|
5280
|
+
const expression =
|
|
5281
|
+
html_expression?.type === 'Html' ? html_expression.expression : html_expression;
|
|
5282
|
+
const name = get_host_html_attribute_name(transform_context);
|
|
5283
|
+
const value =
|
|
5284
|
+
name === 'dangerouslySetInnerHTML'
|
|
5285
|
+
? set_loc(b.object([b.prop('init', b.id('__html'), expression)]), source_attr)
|
|
5286
|
+
: expression;
|
|
5287
|
+
const value_container = to_jsx_expression_container(value, source_attr);
|
|
5288
|
+
if (name !== 'dangerouslySetInnerHTML') {
|
|
5289
|
+
setLocation(value_container, source_attr, true);
|
|
5290
|
+
}
|
|
5291
|
+
|
|
5292
|
+
return set_loc(
|
|
5293
|
+
build_jsx_attribute(b.jsx_id(name), value_container, false, source_attr),
|
|
5294
|
+
source_attr,
|
|
5295
|
+
);
|
|
5296
|
+
}
|
|
5297
|
+
|
|
5130
5298
|
/**
|
|
5131
5299
|
* @param {any} expression
|
|
5132
5300
|
* @returns {boolean}
|
package/types/jsx-platform.d.ts
CHANGED
|
@@ -345,6 +345,8 @@ export interface JsxPlatform {
|
|
|
345
345
|
* explicit `ref={normalized.ref}` attribute.
|
|
346
346
|
*/
|
|
347
347
|
hostSpreadRefStrategy?: 'explicit-ref-attr';
|
|
348
|
+
/** Native host prop used when lowering a sole child `{html ...}`. */
|
|
349
|
+
htmlProp?: 'dangerouslySetInnerHTML' | 'innerHTML';
|
|
348
350
|
};
|
|
349
351
|
|
|
350
352
|
validation: {
|