@dom-expressions/babel-plugin-jsx 0.50.0-next.15 → 0.50.0-next.16
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/CHANGELOG.md +30 -0
- package/index.js +166 -61
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# babel-plugin-jsx-dom-expressions
|
|
2
2
|
|
|
3
|
+
## 0.50.0-next.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 04849df: Preserve JS value semantics for wrapped `&&` conditions (#532). The dom generate's condition wrap used to emit `memo(() => !!left)() && right`, collapsing every falsy left value to `false` — visibly wrong for component props (`undefined` became `false`, breaking `== null` checks) and a hydration mismatch against the untransformed server output (`{0 && <div/>}` rendered "0" on the server, nothing on the client). `left && right` is exactly `left ? right : left`, so the wrap now emits `memo(() => !!left)() ? right : left`: branching still keys off the memoized truthiness (truthy-value churn never re-creates the right side) while the alternate returns the raw left, matching the server for free. Statically boolean lefts (comparisons, `!x`) keep the plain `memo(() => left)() && right` form — the memo's value is the expression's value, so it's already exact with no second evaluation. Ported identically to the Rust jsx-compiler.
|
|
8
|
+
- 248f784: fix(compiler): key the hole `scope()` wrap off the transform's `dynamic` flag instead of the transformed expression shape. The dom generate simplifies `{sig()}` to the bare getter `sig`, which the old `isDeferredChildSlotExpression` predicate didn't count as deferred while the matching ssr arrow was — so the server scope-wrapped the hole and the client didn't, shifting every sibling hydration id after it. Bare getters are re-wrapped as `() => sig()` on the dom side so `scope()` doesn't tag the user's function.
|
|
9
|
+
- c2a542b: Fix hydration key mismatches when async holes defer past eager siblings
|
|
10
|
+
(solidjs/solid#2801 bug 2). Dynamic element children that can allocate
|
|
11
|
+
hydration ids (conditionals, component-children access, call expressions)
|
|
12
|
+
are now compiled with their own id scope on both generates: the dom and ssr
|
|
13
|
+
generates wrap the hole expression in a new `scope()` runtime helper using a
|
|
14
|
+
shared predicate, so marking cannot desync.
|
|
15
|
+
|
|
16
|
+
On the client, `scope(fn)` tags the accessor and `insert()` makes the outer
|
|
17
|
+
render effect non-transparent (its own id scope) for tagged accessors; the
|
|
18
|
+
inner unwrapping effect stays transparent so content ids keep a fixed depth.
|
|
19
|
+
On the server, `scope` (framework-provided via rxcore as `ssrScope`) reserves
|
|
20
|
+
one id slot at registration and evaluates the hole — including async retries
|
|
21
|
+
— under that reserved id with a zeroed child counter, so retry timing can no
|
|
22
|
+
longer shift sibling ids. The ssr generate's `orderedInsert` sibling
|
|
23
|
+
thunk-wrapping is removed; it is superseded by hole scopes.
|
|
24
|
+
|
|
25
|
+
Hole content ids gain one nesting level (e.g. `_hk=10` instead of `_hk=1`)
|
|
26
|
+
identically on both sides. rxcore implementations must provide an `ssrScope`
|
|
27
|
+
export and honor a `scope: true` effect option (mapped to a non-transparent
|
|
28
|
+
render effect).
|
|
29
|
+
|
|
30
|
+
- bb7470e: Give every dynamic child slot its own insertion marker when a parent hosts more than one (solidjs/solid#2830). Adjacent expression slots used to share a marker (`null` at the tail, a shared following sibling, or one reused `<!>` between text), which collapsed them into a single `$$SLOT` ownership region: a node migrating between adjacent slots was destroyed by the slot it left, arrays exchanging members could throw `NotFoundError`, and a slot emptied via `[]` refilled at the wrong position. Slots in multi-slot parents now ride the immediately following static sibling or get a dedicated `<!>` placeholder — the same per-slot geometry hydratable output has always produced, which is why these shapes already worked after hydration. Zero runtime changes; single-slot parents compile byte-identically to before.
|
|
31
|
+
- 668264f: Universal JSX now passes compile-time static host props to `createElement(tag, staticProps)` so custom renderers can configure nodes before children are inserted. Dynamic props and elements with spreads continue to use the existing `setProp` / `spread` paths.
|
|
32
|
+
|
|
3
33
|
## 0.50.0-next.15
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
package/index.js
CHANGED
|
@@ -444,6 +444,34 @@ function toEventName(name) {
|
|
|
444
444
|
return name.slice(2).toLowerCase();
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
+
// === Hole-scope predicates (shared by dom + ssr generates) ===
|
|
448
|
+
//
|
|
449
|
+
// A dynamic element child ("hole") whose evaluation can allocate hydration
|
|
450
|
+
// ids (create hydratable elements, run components, register memos) gets its
|
|
451
|
+
// own owner scope so server and client allocate identical ids regardless of
|
|
452
|
+
// WHEN the hole evaluates (eager, deferred by async, or re-run). Both
|
|
453
|
+
// generates must agree exactly on which holes qualify, so the predicates
|
|
454
|
+
// live here.
|
|
455
|
+
|
|
456
|
+
function canReturnHydratableChild(node) {
|
|
457
|
+
if (t__namespace.isTSNonNullExpression(node) || t__namespace.isTSAsExpression(node) || t__namespace.isTSSatisfiesExpression(node))
|
|
458
|
+
return canReturnHydratableChild(node.expression);
|
|
459
|
+
if (t__namespace.isJSXElement(node) || t__namespace.isJSXFragment(node) || t__namespace.isCallExpression(node)) return true;
|
|
460
|
+
if (t__namespace.isMemberExpression(node) || t__namespace.isOptionalMemberExpression(node)) {
|
|
461
|
+
return !node.computed && t__namespace.isIdentifier(node.property, { name: "children" });
|
|
462
|
+
}
|
|
463
|
+
if (t__namespace.isConditionalExpression(node)) {
|
|
464
|
+
return canReturnHydratableChild(node.consequent) || canReturnHydratableChild(node.alternate);
|
|
465
|
+
}
|
|
466
|
+
return t__namespace.isLogicalExpression(node) && canReturnHydratableChild(node.right);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function canChildSlotAllocateIds(node) {
|
|
470
|
+
if (node.isJSXElement() || node.isJSXFragment()) return true;
|
|
471
|
+
if (node.isJSXSpreadChild()) return true;
|
|
472
|
+
return node.isJSXExpressionContainer() && canReturnHydratableChild(node.node.expression);
|
|
473
|
+
}
|
|
474
|
+
|
|
447
475
|
function wrappedByText(list, startIndex) {
|
|
448
476
|
let index = startIndex,
|
|
449
477
|
wrapped;
|
|
@@ -467,6 +495,30 @@ function wrappedByText(list, startIndex) {
|
|
|
467
495
|
return false;
|
|
468
496
|
}
|
|
469
497
|
|
|
498
|
+
const BOOLEAN_BINARY_OPS = new Set([
|
|
499
|
+
"==",
|
|
500
|
+
"!=",
|
|
501
|
+
"===",
|
|
502
|
+
"!==",
|
|
503
|
+
"<",
|
|
504
|
+
">",
|
|
505
|
+
"<=",
|
|
506
|
+
">=",
|
|
507
|
+
"instanceof",
|
|
508
|
+
"in"]
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
// Statically guaranteed to evaluate to a boolean: the memoized value IS the
|
|
512
|
+
// expression's value (`false` is the only falsy boolean), so no `!!` coercion
|
|
513
|
+
// is needed and the `&&` wrap can keep the logical form instead of the
|
|
514
|
+
// value-preserving ternary (no second evaluation of the left).
|
|
515
|
+
function isBooleanExpression(node) {
|
|
516
|
+
return (
|
|
517
|
+
t__namespace.isBinaryExpression(node) && BOOLEAN_BINARY_OPS.has(node.operator) ||
|
|
518
|
+
t__namespace.isUnaryExpression(node) && node.operator === "!");
|
|
519
|
+
|
|
520
|
+
}
|
|
521
|
+
|
|
470
522
|
|
|
471
523
|
|
|
472
524
|
|
|
@@ -481,7 +533,7 @@ path,
|
|
|
481
533
|
inline)
|
|
482
534
|
{
|
|
483
535
|
const config = getConfig(path);
|
|
484
|
-
|
|
536
|
+
let expr = path.node;
|
|
485
537
|
const memo = registerImportMethod(path, config.memoWrapper, undefined);
|
|
486
538
|
let dTest,
|
|
487
539
|
cond,
|
|
@@ -497,7 +549,7 @@ inline)
|
|
|
497
549
|
dTest = isDynamic(path.get("test"), { checkMember: true });
|
|
498
550
|
if (dTest) {
|
|
499
551
|
cond = expr.test;
|
|
500
|
-
if (!
|
|
552
|
+
if (!isBooleanExpression(cond))
|
|
501
553
|
cond = t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", cond, true), true);
|
|
502
554
|
id = inline ?
|
|
503
555
|
t__namespace.callExpression(memo, [t__namespace.arrowFunctionExpression([], cond)]) :
|
|
@@ -523,12 +575,30 @@ inline)
|
|
|
523
575
|
}));
|
|
524
576
|
if (dTest) {
|
|
525
577
|
cond = nextPath.node.left;
|
|
526
|
-
|
|
527
|
-
|
|
578
|
+
// `left && right` is exactly `left ? right : left`. Branch on the
|
|
579
|
+
// memoized truthiness (so truthy-value churn never re-creates the right
|
|
580
|
+
// side) but return the raw left in the alternate so the expression keeps
|
|
581
|
+
// JS value semantics — `0`/`""`/`undefined` flow through instead of
|
|
582
|
+
// collapsing to `false`, matching the untransformed ssr output (#532).
|
|
583
|
+
// Statically boolean lefts skip the ternary: the memo's value is the
|
|
584
|
+
// expression's value, so the logical form is already exact and the left
|
|
585
|
+
// never evaluates twice.
|
|
586
|
+
const boolLeft = isBooleanExpression(cond);
|
|
587
|
+
if (!boolLeft) cond = t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", cond, true), true);
|
|
528
588
|
id = inline ?
|
|
529
589
|
t__namespace.callExpression(memo, [t__namespace.arrowFunctionExpression([], cond)]) :
|
|
530
590
|
path.scope.generateUidIdentifier("_c$");
|
|
531
|
-
|
|
591
|
+
if (boolLeft) {
|
|
592
|
+
nextPath.node.left = t__namespace.callExpression(id, []);
|
|
593
|
+
} else {
|
|
594
|
+
const alternate = t__namespace.cloneNode(nextPath.node.left, true);
|
|
595
|
+
nextPath.replaceWith(
|
|
596
|
+
t__namespace.conditionalExpression(t__namespace.callExpression(id, []), nextPath.node.right, alternate)
|
|
597
|
+
);
|
|
598
|
+
// replaceWith swaps the node out of the tree; when the `&&` was the
|
|
599
|
+
// top-level expression the local reference is stale.
|
|
600
|
+
expr = path.node;
|
|
601
|
+
}
|
|
532
602
|
}
|
|
533
603
|
}
|
|
534
604
|
if (dTest && !inline && cond && id) {
|
|
@@ -2163,7 +2233,6 @@ config)
|
|
|
2163
2233
|
{
|
|
2164
2234
|
let tempPath = results.id && results.id.name || "",
|
|
2165
2235
|
tagName = getTagName(path.node),
|
|
2166
|
-
nextPlaceholder,
|
|
2167
2236
|
childPostExprs = [],
|
|
2168
2237
|
i = 0;
|
|
2169
2238
|
const filteredChildren = filterChildren(path.get("children")),
|
|
@@ -2181,6 +2250,8 @@ config)
|
|
|
2181
2250
|
skipId: !results.id || !detectExpressions(filteredChildren, index, config)
|
|
2182
2251
|
});
|
|
2183
2252
|
if (!transformed) return memo;
|
|
2253
|
+
transformed.allocatesIds =
|
|
2254
|
+
config.hydratable && canChildSlotAllocateIds(child);
|
|
2184
2255
|
const i = memo.length;
|
|
2185
2256
|
if (transformed.text && i && memo[i - 1].text) {
|
|
2186
2257
|
memo[i - 1].template =
|
|
@@ -2193,6 +2264,13 @@ config)
|
|
|
2193
2264
|
[]
|
|
2194
2265
|
);
|
|
2195
2266
|
|
|
2267
|
+
// Dynamic slots under this parent (children compiled to `insert()` calls).
|
|
2268
|
+
// With two or more, slots may not share an insertion marker: the marker is
|
|
2269
|
+
// also the runtime's ownership tag ($$SLOT), and adoption only re-tags when
|
|
2270
|
+
// the marker is truthy — shared or null markers let one slot's cleanup
|
|
2271
|
+
// destroy a node that migrated to its neighbor (solidjs/solid#2830).
|
|
2272
|
+
const dynamicSlots = childNodes.reduce((n, c) => c && !c.id && c.exprs.length ? n + 1 : n, 0);
|
|
2273
|
+
|
|
2196
2274
|
childNodes.forEach((child, index) => {
|
|
2197
2275
|
if (!child) return;
|
|
2198
2276
|
if (child.tagName && child.renderer !== "dom") {
|
|
@@ -2245,23 +2323,47 @@ config)
|
|
|
2245
2323
|
results.isImportNode = results.isImportNode || child.isImportNode;
|
|
2246
2324
|
results.isWrapped = results.isWrapped || child.isWrapped;
|
|
2247
2325
|
tempPath = child.id.name;
|
|
2248
|
-
nextPlaceholder = null;
|
|
2249
2326
|
i++;
|
|
2250
2327
|
} else if (child.exprs.length) {
|
|
2251
2328
|
let insert = registerImportMethod(path, "insert", getRendererConfig(path, "dom").moduleName);
|
|
2252
2329
|
const multi = checkLength(filteredChildren),
|
|
2253
|
-
markers = config.hydratable && multi
|
|
2330
|
+
markers = config.hydratable && multi,
|
|
2331
|
+
// CSR counterpart of the hydratable per-slot markers: when this parent
|
|
2332
|
+
// hosts multiple dynamic slots, each gets its own truthy marker — the
|
|
2333
|
+
// immediately following sibling when it has a reference, otherwise a
|
|
2334
|
+
// dedicated `<!>` placeholder.
|
|
2335
|
+
perSlot = !markers && dynamicSlots > 1;
|
|
2336
|
+
// Mirror of the ssr generate's `scope()` wrap: deferred holes that can
|
|
2337
|
+
// allocate hydration ids get their own owner scope (insert makes the
|
|
2338
|
+
// outer render effect non-transparent for tagged accessors). Keyed off
|
|
2339
|
+
// `dynamic` so both generates decide identically for the same source.
|
|
2340
|
+
if (child.allocatesIds && child.dynamic) {
|
|
2341
|
+
let expr = child.exprs[0];
|
|
2342
|
+
// The shared transform simplifies `{sig()}` to the bare getter `sig`;
|
|
2343
|
+
// rewrap so tagging the scope doesn't mutate the user's function.
|
|
2344
|
+
if (!t$1.isFunction(expr) && !(t$1.isCallExpression(expr) && t$1.isFunction(expr.callee))) {
|
|
2345
|
+
expr = t$1.arrowFunctionExpression([], t$1.callExpression(expr, []));
|
|
2346
|
+
}
|
|
2347
|
+
child.exprs[0] = t$1.callExpression(
|
|
2348
|
+
registerImportMethod(path, "scope", getRendererConfig(path, "dom").moduleName),
|
|
2349
|
+
[expr]
|
|
2350
|
+
);
|
|
2351
|
+
}
|
|
2254
2352
|
// boxed by textNodes
|
|
2255
|
-
if (markers || wrappedByText(childNodes, index)) {
|
|
2353
|
+
if (markers || perSlot || wrappedByText(childNodes, index)) {
|
|
2256
2354
|
let exprId;
|
|
2257
2355
|
let contentId;
|
|
2258
2356
|
if (markers) tempPath = createPlaceholder(path, results, tempPath, i++, "$")[0].name;
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2357
|
+
// Ride the immediately following sibling's reference when it exists —
|
|
2358
|
+
// unless the slot is boxed by text, where a placeholder is structurally
|
|
2359
|
+
// required to keep the surrounding template text nodes from merging.
|
|
2360
|
+
if (perSlot && !wrappedByText(childNodes, index)) {
|
|
2361
|
+
exprId = childNodes[index + 1] && childNodes[index + 1].id || undefined;
|
|
2362
|
+
}
|
|
2363
|
+
if (!exprId) {
|
|
2262
2364
|
[exprId, contentId] = createPlaceholder(path, results, tempPath, i++, markers ? "/" : "");
|
|
2365
|
+
tempPath = exprId.name;
|
|
2263
2366
|
}
|
|
2264
|
-
if (!markers) nextPlaceholder = exprId;
|
|
2265
2367
|
const args = contentId ?
|
|
2266
2368
|
[
|
|
2267
2369
|
results.id,
|
|
@@ -2275,7 +2377,6 @@ config)
|
|
|
2275
2377
|
exprId];
|
|
2276
2378
|
|
|
2277
2379
|
results.exprs.push(t$1.expressionStatement(t$1.callExpression(insert, args)));
|
|
2278
|
-
tempPath = exprId.name;
|
|
2279
2380
|
} else if (multi) {
|
|
2280
2381
|
results.exprs.push(
|
|
2281
2382
|
t$1.expressionStatement(
|
|
@@ -2293,7 +2394,7 @@ config)
|
|
|
2293
2394
|
)
|
|
2294
2395
|
);
|
|
2295
2396
|
}
|
|
2296
|
-
}
|
|
2397
|
+
}
|
|
2297
2398
|
});
|
|
2298
2399
|
results.postExprs.unshift(...childPostExprs);
|
|
2299
2400
|
}
|
|
@@ -2782,7 +2883,6 @@ const t = t__namespace;
|
|
|
2782
2883
|
|
|
2783
2884
|
|
|
2784
2885
|
|
|
2785
|
-
|
|
2786
2886
|
|
|
2787
2887
|
|
|
2788
2888
|
function appendToTemplate(template, value) {
|
|
@@ -2961,8 +3061,7 @@ info = {})
|
|
|
2961
3061
|
hoistExpression(
|
|
2962
3062
|
path,
|
|
2963
3063
|
results,
|
|
2964
|
-
t.callExpression(registerImportMethod(path, "ssrHydrationKey"), [])
|
|
2965
|
-
{ }
|
|
3064
|
+
t.callExpression(registerImportMethod(path, "ssrHydrationKey"), [])
|
|
2966
3065
|
)
|
|
2967
3066
|
);
|
|
2968
3067
|
}
|
|
@@ -3505,29 +3604,6 @@ quasis)
|
|
|
3505
3604
|
});
|
|
3506
3605
|
}
|
|
3507
3606
|
|
|
3508
|
-
function canReturnHydratableChild(node) {
|
|
3509
|
-
if (t.isTSNonNullExpression(node) || t.isTSAsExpression(node) || t.isTSSatisfiesExpression(node))
|
|
3510
|
-
return canReturnHydratableChild(node.expression);
|
|
3511
|
-
if (t.isJSXElement(node) || t.isJSXFragment(node) || t.isCallExpression(node)) return true;
|
|
3512
|
-
if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
|
|
3513
|
-
return !node.computed && t.isIdentifier(node.property, { name: "children" });
|
|
3514
|
-
}
|
|
3515
|
-
if (t.isConditionalExpression(node)) {
|
|
3516
|
-
return canReturnHydratableChild(node.consequent) || canReturnHydratableChild(node.alternate);
|
|
3517
|
-
}
|
|
3518
|
-
return t.isLogicalExpression(node) && canReturnHydratableChild(node.right);
|
|
3519
|
-
}
|
|
3520
|
-
|
|
3521
|
-
function canChildSlotAllocateIds(node) {
|
|
3522
|
-
if (node.isJSXElement() || node.isJSXFragment()) return true;
|
|
3523
|
-
if (node.isJSXSpreadChild()) return true;
|
|
3524
|
-
return node.isJSXExpressionContainer() && canReturnHydratableChild(node.node.expression);
|
|
3525
|
-
}
|
|
3526
|
-
|
|
3527
|
-
function isDeferredChildSlotExpression(node) {
|
|
3528
|
-
return t.isFunction(node) || t.isCallExpression(node) && t.isFunction(node.callee);
|
|
3529
|
-
}
|
|
3530
|
-
|
|
3531
3607
|
function transformChildren$1(
|
|
3532
3608
|
path,
|
|
3533
3609
|
results,
|
|
@@ -3538,14 +3614,13 @@ results,
|
|
|
3538
3614
|
const filteredChildren = filterChildren(path.get("children"));
|
|
3539
3615
|
const multi = checkLength(filteredChildren),
|
|
3540
3616
|
markers = hydratable && multi;
|
|
3541
|
-
let orderedInsert = false;
|
|
3542
3617
|
filteredChildren.forEach((node) => {
|
|
3543
3618
|
if (node.isJSXFragment()) {
|
|
3544
3619
|
throw new Error(
|
|
3545
3620
|
`Fragments can only be used top level in JSX. Not used under a <${tagName}>.`
|
|
3546
3621
|
);
|
|
3547
3622
|
}
|
|
3548
|
-
const allocatesIds = canChildSlotAllocateIds(node);
|
|
3623
|
+
const allocatesIds = hydratable && canChildSlotAllocateIds(node);
|
|
3549
3624
|
const child = transformNode(node, { doNotEscape, parentResults: results });
|
|
3550
3625
|
if (!child) return;
|
|
3551
3626
|
appendToTemplate(results.template, child.template);
|
|
@@ -3570,14 +3645,14 @@ results,
|
|
|
3570
3645
|
_groupableTextContent ?
|
|
3571
3646
|
{ group: true } :
|
|
3572
3647
|
undefined;
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3648
|
+
// Deferred holes that can allocate hydration ids evaluate under their
|
|
3649
|
+
// own owner scope so retry timing can't skew sibling ids (mirrors the
|
|
3650
|
+
// dom generate's `scope()` wrap around the matching insert accessor).
|
|
3651
|
+
// Keyed off `dynamic` so both generates decide identically.
|
|
3652
|
+
let expr = child.exprs[0];
|
|
3653
|
+
if (allocatesIds && child.dynamic) {
|
|
3654
|
+
expr = t.callExpression(registerImportMethod(path, "scope"), [expr]);
|
|
3655
|
+
}
|
|
3581
3656
|
|
|
3582
3657
|
// boxed by textNodes
|
|
3583
3658
|
if (markers && !child.spreadElement) {
|
|
@@ -3905,6 +3980,9 @@ info = {})
|
|
|
3905
3980
|
renderer: "universal"
|
|
3906
3981
|
};
|
|
3907
3982
|
|
|
3983
|
+
const initProps = transformAttributes(path, results);
|
|
3984
|
+
const createElementArgs = [t__namespace.stringLiteral(tagName)];
|
|
3985
|
+
if (initProps.length) createElementArgs.push(t__namespace.objectExpression(initProps));
|
|
3908
3986
|
results.declarations.push(
|
|
3909
3987
|
t__namespace.variableDeclarator(
|
|
3910
3988
|
results.id,
|
|
@@ -3914,12 +3992,10 @@ info = {})
|
|
|
3914
3992
|
"createElement",
|
|
3915
3993
|
getRendererConfig(path, "universal").moduleName
|
|
3916
3994
|
),
|
|
3917
|
-
|
|
3995
|
+
createElementArgs
|
|
3918
3996
|
)
|
|
3919
3997
|
)
|
|
3920
3998
|
);
|
|
3921
|
-
|
|
3922
|
-
transformAttributes(path, results);
|
|
3923
3999
|
transformChildren(path, results);
|
|
3924
4000
|
|
|
3925
4001
|
return results;
|
|
@@ -3931,12 +4007,14 @@ results)
|
|
|
3931
4007
|
{
|
|
3932
4008
|
let children, spreadExpr;
|
|
3933
4009
|
let attributes = path.get("openingElement").get("attributes");
|
|
4010
|
+
const initProps = [];
|
|
3934
4011
|
const elem = results.id,
|
|
3935
4012
|
hasChildren = path.node.children.length > 0,
|
|
3936
|
-
config = getConfig(path)
|
|
4013
|
+
config = getConfig(path),
|
|
4014
|
+
hasSpread = attributes.some((attribute) => t__namespace.isJSXSpreadAttribute(attribute.node));
|
|
3937
4015
|
|
|
3938
4016
|
// preprocess spreads
|
|
3939
|
-
if (
|
|
4017
|
+
if (hasSpread) {
|
|
3940
4018
|
[attributes, spreadExpr] = processSpreads(path, attributes, {
|
|
3941
4019
|
elem,
|
|
3942
4020
|
hasChildren,
|
|
@@ -4065,20 +4143,47 @@ results)
|
|
|
4065
4143
|
{
|
|
4066
4144
|
results.dynamics.push({ elem, key, value: value.expression });
|
|
4067
4145
|
} else {
|
|
4068
|
-
|
|
4069
|
-
|
|
4146
|
+
addStaticAttr(
|
|
4147
|
+
attribute,
|
|
4148
|
+
results,
|
|
4149
|
+
initProps,
|
|
4150
|
+
elem,
|
|
4151
|
+
key,
|
|
4152
|
+
value.expression,
|
|
4153
|
+
hasSpread
|
|
4070
4154
|
);
|
|
4071
4155
|
}
|
|
4072
4156
|
} else {
|
|
4073
|
-
results
|
|
4074
|
-
t__namespace.expressionStatement(setAttr(attribute, elem, key, value))
|
|
4075
|
-
);
|
|
4157
|
+
addStaticAttr(attribute, results, initProps, elem, key, value, hasSpread);
|
|
4076
4158
|
}
|
|
4077
4159
|
});
|
|
4078
4160
|
if (spreadExpr) results.exprs.push(spreadExpr);
|
|
4079
4161
|
if (!hasChildren && children) {
|
|
4080
4162
|
path.node.children.push(children);
|
|
4081
4163
|
}
|
|
4164
|
+
return initProps;
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
function addStaticAttr(
|
|
4168
|
+
path,
|
|
4169
|
+
results,
|
|
4170
|
+
initProps,
|
|
4171
|
+
elem,
|
|
4172
|
+
key,
|
|
4173
|
+
value,
|
|
4174
|
+
hasSpread)
|
|
4175
|
+
{
|
|
4176
|
+
if (!value) value = t__namespace.booleanLiteral(true);
|
|
4177
|
+
if (hasSpread) {
|
|
4178
|
+
results.exprs.push(t__namespace.expressionStatement(setAttr(path, elem, key, value)));
|
|
4179
|
+
} else {
|
|
4180
|
+
initProps.push(
|
|
4181
|
+
t__namespace.objectProperty(
|
|
4182
|
+
t__namespace.isValidIdentifier(key) ? t__namespace.identifier(key) : t__namespace.stringLiteral(key),
|
|
4183
|
+
value
|
|
4184
|
+
)
|
|
4185
|
+
);
|
|
4186
|
+
}
|
|
4082
4187
|
}
|
|
4083
4188
|
|
|
4084
4189
|
function setAttr(
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dom-expressions/babel-plugin-jsx",
|
|
3
3
|
"description": "A JSX to DOM plugin that wraps expressions for fine grained change detection",
|
|
4
|
-
"version": "0.50.0-next.
|
|
4
|
+
"version": "0.50.0-next.16",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@rollup/plugin-babel": "7.0.0",
|
|
29
29
|
"@types/babel__core": "^7.20.5",
|
|
30
30
|
"@types/babel__traverse": "^7.28.0",
|
|
31
|
-
"@dom-expressions/runtime": "0.50.0-next.
|
|
31
|
+
"@dom-expressions/runtime": "0.50.0-next.16"
|
|
32
32
|
},
|
|
33
33
|
"gitHead": "eb463c653325e24824422cff6bfed0d35113ef33",
|
|
34
34
|
"scripts": {
|