@solidjs/babel-plugin 2.0.0-rc.11 → 2.0.0-rc.13
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/index.js +151 -33
- package/package.json +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -7,4 +7,4 @@ src/shared/transform.ts -> src/dom/element.ts -> src/universal/element.ts -> src
|
|
|
7
7
|
src/shared/transform.ts -> src/ssr/element.ts -> src/shared/transform.ts
|
|
8
8
|
src/shared/transform.ts -> src/shared/component.ts -> src/shared/transform.ts
|
|
9
9
|
src/shared/transform.ts -> src/shared/fragment.ts -> src/shared/transform.ts
|
|
10
|
-
[32mcreated [1mindex.js[22m in [
|
|
10
|
+
[32mcreated [1mindex.js[22m in [1m3.4s[22m[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @solidjs/babel-plugin
|
|
2
2
|
|
|
3
|
+
## 2.0.0-rc.13
|
|
4
|
+
|
|
5
|
+
## 2.0.0-rc.12
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- ad1ecc5: `serverComponents` (SSR): keep attribute-slot positions bindable on intrinsic elements. A dynamic `class` or `style` now compiles to a whole-attribute `_$ssrElementAttribute` hole instead of a value inside the template's quotes, so a server component's `class={{ selected: filters.all }}` can mark the class name a client attribute slot owns; `ref`/`on*` positions compile, as before, to one guarded `_$ssrClaim` hole per element, which now emits `_s:on:*`/`_s:ref` markers for slot reads (the `_bnd` behavior-claim marker is gone). A spread element compiles its named `ref`/`on*` (`<button {...rest} onClick={row.go} ref={row.el} />`, wherever they sit relative to the spreads) to the same claim map — `{ click: expr, ref: [a, b] }`, duplicate refs merged, a duplicate handler last-wins as on the template path — keyed by the index of the source each attribute sits before (`<b {...a} onClick={go} {...b}>` → `{ 1: { click: go } }`) and handed to `_$ssrElement` as a seventh argument, a thunk the runtime reads only inside a server component's render, so plain SSR never evaluates the expressions (they used to drop with no marker and no finding); the runtime settles each handler position in source order, as the client's `spread`/`merge()` do (the last source that has the key owns it, `undefined` included). A `ref` beside a lone spread goes through the sources loop under the option (a dynamic spread call is thunked there, as it is beside any other attribute). The event name is always the lowercased `on*` remainder (`onClick` → `click`); `on:`/`oncapture:` are not 2.0 syntaxes and are no longer special-cased. Both compilers share the `attributeSlots` server-components fixture; plain SSR output is unchanged.
|
|
10
|
+
- 077c787: A `<textarea>` with a dynamic `value`/`defaultValue` no longer desyncs hydration ids for the siblings after it (#3691). The server folds the value into the textarea's text content, and that fold was taking the `_$scope` hydration-id reservation a dynamic child hole gets — but the client writes the value as a plain property effect that never allocates an id, so every component after the textarea hydrated one id off and its updates targeted detached DOM. The fold is now treated like the `innerHTML`/`textContent` redirects (#3015): opaque content, no id reservation, in both the Babel plugin and the native compiler.
|
|
11
|
+
- c71486d: DOM output: an element's `ref` now runs after its attributes and spread are applied, and still before its children. A ref's own attribute writes now survive client creation the way they already survived hydration (#3685).
|
|
12
|
+
|
|
3
13
|
## 2.0.0-rc.11
|
|
4
14
|
|
|
5
15
|
## 2.0.0-rc.10
|
package/README.md
CHANGED
|
@@ -253,7 +253,7 @@ Inline style attributes in templates when the value is a string or `Record<strin
|
|
|
253
253
|
- Type: `boolean`
|
|
254
254
|
- Default: `false`
|
|
255
255
|
|
|
256
|
-
SSR-only:
|
|
256
|
+
SSR-only: keep attribute-slot positions on intrinsic elements bindable — `ref` / `on*` compile to a guarded `ssrClaim` hole instead of dropping, and a dynamic `class` / `style` compiles to a whole-attribute `ssrElementAttribute` hole instead of a value inside template quotes.
|
|
257
257
|
|
|
258
258
|
### hoistProps
|
|
259
259
|
|
package/index.js
CHANGED
|
@@ -1047,6 +1047,13 @@ isSSR)
|
|
|
1047
1047
|
child.extra = { raw: text, rawValue: text };
|
|
1048
1048
|
} else {
|
|
1049
1049
|
child = t__namespace.jsxExpressionContainer(value);
|
|
1050
|
+
// The folded value is the textarea's text content on the server, but
|
|
1051
|
+
// the client writes it as a plain `value` property effect that never
|
|
1052
|
+
// allocates a hydration id. Flag it like the innerHTML/textContent
|
|
1053
|
+
// redirects (#3015) so the SSR child pipeline skips the `_$scope` id
|
|
1054
|
+
// reservation — otherwise the reservation shifts every keyed sibling
|
|
1055
|
+
// after the textarea by one id (#3691).
|
|
1056
|
+
child._childProperty = true;
|
|
1050
1057
|
}
|
|
1051
1058
|
path.node.children = [child];
|
|
1052
1059
|
attr.remove();
|
|
@@ -2230,6 +2237,7 @@ results)
|
|
|
2230
2237
|
hasChildren = path.node.children.length > 0,
|
|
2231
2238
|
hasSpread = attributes.some((attribute) => t$1.isJSXSpreadAttribute(attribute.node)),
|
|
2232
2239
|
config = getConfig(path);
|
|
2240
|
+
const refs = [];
|
|
2233
2241
|
|
|
2234
2242
|
// preprocess spreads
|
|
2235
2243
|
if (hasSpread) {
|
|
@@ -2709,7 +2717,7 @@ results)
|
|
|
2709
2717
|
binding.kind === "const" || binding.kind === "module");
|
|
2710
2718
|
if (!isConstant && t$1.isLVal(value.expression)) {
|
|
2711
2719
|
const refIdentifier = path.scope.generateUidIdentifier("_ref$");
|
|
2712
|
-
|
|
2720
|
+
refs.push(
|
|
2713
2721
|
t$1.variableDeclaration("var", [t$1.variableDeclarator(refIdentifier, value.expression)]),
|
|
2714
2722
|
t$1.expressionStatement(
|
|
2715
2723
|
t$1.conditionalExpression(
|
|
@@ -2738,7 +2746,7 @@ results)
|
|
|
2738
2746
|
t$1.isFunction(value.expression) ||
|
|
2739
2747
|
t$1.isArrayExpression(value.expression))
|
|
2740
2748
|
{
|
|
2741
|
-
|
|
2749
|
+
refs.push(
|
|
2742
2750
|
t$1.expressionStatement(
|
|
2743
2751
|
t$1.callExpression(
|
|
2744
2752
|
registerImportMethod(path, "ref", getRendererConfig(path, "dom").moduleName),
|
|
@@ -2748,7 +2756,7 @@ results)
|
|
|
2748
2756
|
);
|
|
2749
2757
|
} else {
|
|
2750
2758
|
const refIdentifier = path.scope.generateUidIdentifier("_ref$");
|
|
2751
|
-
|
|
2759
|
+
refs.push(
|
|
2752
2760
|
t$1.variableDeclaration("var", [t$1.variableDeclarator(refIdentifier, value.expression)]),
|
|
2753
2761
|
t$1.expressionStatement(
|
|
2754
2762
|
t$1.logicalExpression(
|
|
@@ -2978,6 +2986,9 @@ results)
|
|
|
2978
2986
|
path.node.children.push(children);
|
|
2979
2987
|
}
|
|
2980
2988
|
if (spreadExpr) results.exprs.push(...(Array.isArray(spreadExpr) ? spreadExpr : [spreadExpr]));
|
|
2989
|
+
// Refs run after the element's attributes and spread are applied, so a ref's
|
|
2990
|
+
// own writes survive client creation the way they survive hydration.
|
|
2991
|
+
results.exprs.push(...refs);
|
|
2981
2992
|
|
|
2982
2993
|
results.hasHydratableEvent = results.hasHydratableEvent || hasHydratableEvent;
|
|
2983
2994
|
}
|
|
@@ -4852,10 +4863,12 @@ info)
|
|
|
4852
4863
|
const hasChildren = path.node.children.length > 0,
|
|
4853
4864
|
attributes = normalizeAttributes(path);
|
|
4854
4865
|
let children;
|
|
4855
|
-
// Server-components
|
|
4856
|
-
// intrinsics collect here and emit as one guarded
|
|
4857
|
-
//
|
|
4858
|
-
//
|
|
4866
|
+
// Server-components handler positions: ref/on* expressions on
|
|
4867
|
+
// server-rendered intrinsics collect here and emit as one guarded
|
|
4868
|
+
// whole-attribute hole after the loop, where `ssrClaim` turns attribute-slot
|
|
4869
|
+
// reads into `_s:on:*` / `_s:ref` markers (and drops server-local
|
|
4870
|
+
// functions). Evaluation is gated on the render context's claims flag so
|
|
4871
|
+
// plain SSR never runs the expressions.
|
|
4859
4872
|
const claims = [];
|
|
4860
4873
|
|
|
4861
4874
|
attributes.forEach((attribute) => {
|
|
@@ -4898,11 +4911,10 @@ info)
|
|
|
4898
4911
|
}
|
|
4899
4912
|
if (key.startsWith("prop:")) return;
|
|
4900
4913
|
if (key.startsWith("on")) {
|
|
4901
|
-
//
|
|
4902
|
-
//
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
const pos = key.startsWith("on:") ? key.slice(3) : key.slice(2).toLowerCase();
|
|
4914
|
+
// `onXxx` lowercases to the event name — the client runtime's own
|
|
4915
|
+
// derivation (`onClick` -> `click`); the position is bound under it.
|
|
4916
|
+
if (info.serverComponents) {
|
|
4917
|
+
const pos = key.slice(2).toLowerCase();
|
|
4906
4918
|
if (pos) claims.push([pos, value.expression]);
|
|
4907
4919
|
}
|
|
4908
4920
|
return;
|
|
@@ -4938,6 +4950,30 @@ info)
|
|
|
4938
4950
|
checkMember: true,
|
|
4939
4951
|
checkTags: true
|
|
4940
4952
|
});
|
|
4953
|
+
// Server components (principles §9.2.3): a dynamic `class`/`style`
|
|
4954
|
+
// is the one attribute shape the plain SSR output serializes INSIDE
|
|
4955
|
+
// template quotes (`class="${ssrClassName(x)}"`), where an attribute-slot
|
|
4956
|
+
// value read at that position — the whole value, or a name's
|
|
4957
|
+
// condition in object form — would be stringified instead of
|
|
4958
|
+
// bound. Under the option the whole attribute is a runtime hole,
|
|
4959
|
+
// `ssrElementAttribute("class", x)`, whose helper emits the same
|
|
4960
|
+
// bytes for a plain value and the position marker for a stand-in.
|
|
4961
|
+
// Object literals stay objects (no inlining) for the same reason.
|
|
4962
|
+
if (info.serverComponents && (key === "class" || key === "style")) {
|
|
4963
|
+
const attr = t.callExpression(registerImportMethod(path, "ssrElementAttribute"), [
|
|
4964
|
+
t.stringLiteral(key),
|
|
4965
|
+
value.expression]
|
|
4966
|
+
);
|
|
4967
|
+
results.template.push("");
|
|
4968
|
+
results.templateValues.push(
|
|
4969
|
+
isDynamicValue ?
|
|
4970
|
+
hoistExpression(path, results, t.arrowFunctionExpression([], attr), {
|
|
4971
|
+
group: true
|
|
4972
|
+
}) :
|
|
4973
|
+
attr
|
|
4974
|
+
);
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4941
4977
|
let doEscape = true;
|
|
4942
4978
|
let isBoolean =
|
|
4943
4979
|
t.isBooleanLiteral(value) ||
|
|
@@ -5081,23 +5117,7 @@ info)
|
|
|
5081
5117
|
}
|
|
5082
5118
|
});
|
|
5083
5119
|
if (claims.length) {
|
|
5084
|
-
|
|
5085
|
-
// exempt from that pass (client semantics fire every ref), so multiple
|
|
5086
|
-
// refs merge into an array value.
|
|
5087
|
-
const byPos = new Map();
|
|
5088
|
-
for (const [pos, expr] of claims) {
|
|
5089
|
-
let list = byPos.get(pos);
|
|
5090
|
-
if (!list) byPos.set(pos, list = []);
|
|
5091
|
-
list.push(expr);
|
|
5092
|
-
}
|
|
5093
|
-
const map = t.objectExpression(
|
|
5094
|
-
[...byPos].map(([pos, exprs]) =>
|
|
5095
|
-
t.objectProperty(
|
|
5096
|
-
t.stringLiteral(pos),
|
|
5097
|
-
exprs.length === 1 ? exprs[0] : t.arrayExpression(exprs)
|
|
5098
|
-
)
|
|
5099
|
-
)
|
|
5100
|
-
);
|
|
5120
|
+
const map = claimMap(claims);
|
|
5101
5121
|
// `_$sharedConfig.context && _$sharedConfig.context.claims
|
|
5102
5122
|
// ? _$ssrClaim({...}) : ""`
|
|
5103
5123
|
// — the claims flag is only set inside a server component's render
|
|
@@ -5243,7 +5263,7 @@ results,
|
|
|
5243
5263
|
|
|
5244
5264
|
function createElement(
|
|
5245
5265
|
path,
|
|
5246
|
-
{ topLevel, hydratable })
|
|
5266
|
+
{ topLevel, hydratable, serverComponents })
|
|
5247
5267
|
{
|
|
5248
5268
|
const tagName = getTagName(path.node),
|
|
5249
5269
|
config = getConfig(path),
|
|
@@ -5296,6 +5316,21 @@ path,
|
|
|
5296
5316
|
const node = attribute.node;
|
|
5297
5317
|
return !(t.isJSXAttribute(node) && t.isJSXIdentifier(node.name) && node.name.name === "ref");
|
|
5298
5318
|
});
|
|
5319
|
+
// Server components (principles §9.2.3): the named `ref`/`on*` attributes
|
|
5320
|
+
// of a spread element are handler positions like a template element's,
|
|
5321
|
+
// and compile to the same claim map — `{ click: expr, ref: [a, b] }`,
|
|
5322
|
+
// duplicate refs merged, a duplicate handler last-wins as the template
|
|
5323
|
+
// path strips it — keyed by the index of the source each attribute sits
|
|
5324
|
+
// before (`<b {...a} onClick={go} {...b}>` → `{ 1: { click: go } }`, the
|
|
5325
|
+
// spreads being sources 0 and 2), and handed to `ssrElement` as a thunk
|
|
5326
|
+
// it reads only inside a server component's render (the gate the template
|
|
5327
|
+
// path's `ssrClaim` guard reads), so plain SSR never evaluates the
|
|
5328
|
+
// expressions. The runtime settles each handler position in source order,
|
|
5329
|
+
// as the client's `spread(el, [a, { onClick: go }, b])` does — a spread at
|
|
5330
|
+
// that index or later that HAS the key owns it — and merges every ref.
|
|
5331
|
+
// Plain SSR output is unchanged (dropped, as a server element has no
|
|
5332
|
+
// handlers to run).
|
|
5333
|
+
const claims = [];
|
|
5299
5334
|
|
|
5300
5335
|
let props;
|
|
5301
5336
|
// Attributes written AFTER the last spread are markup, not a source: no
|
|
@@ -5316,7 +5351,13 @@ path,
|
|
|
5316
5351
|
// keys.
|
|
5317
5352
|
const tail = [];
|
|
5318
5353
|
const skipKeys = [];
|
|
5319
|
-
if (
|
|
5354
|
+
if (
|
|
5355
|
+
propAttributes.length === 1 &&
|
|
5356
|
+
t.isJSXSpreadAttribute(propAttributes[0].node) &&
|
|
5357
|
+
// A `ref` beside the lone spread is a claim under `serverComponents`;
|
|
5358
|
+
// the loop below places it.
|
|
5359
|
+
!(serverComponents && attributes.length > 1))
|
|
5360
|
+
{
|
|
5320
5361
|
props = [propAttributes[0].node.argument];
|
|
5321
5362
|
} else {
|
|
5322
5363
|
props = [];
|
|
@@ -5362,8 +5403,30 @@ path,
|
|
|
5362
5403
|
node.name.name;
|
|
5363
5404
|
|
|
5364
5405
|
if (hasChildren && key === "children") return;
|
|
5365
|
-
if (key === "ref")
|
|
5366
|
-
|
|
5406
|
+
if (key === "ref" || key.startsWith("on")) {
|
|
5407
|
+
if (serverComponents && t.isJSXExpressionContainer(value)) {
|
|
5408
|
+
const expression = value.expression;
|
|
5409
|
+
const pos = key === "ref" ? "ref" : key.slice(2).toLowerCase();
|
|
5410
|
+
if (
|
|
5411
|
+
pos &&
|
|
5412
|
+
!(
|
|
5413
|
+
t.isJSXEmptyExpression(expression) ||
|
|
5414
|
+
t.isStringLiteral(expression) ||
|
|
5415
|
+
t.isNumericLiteral(expression) ||
|
|
5416
|
+
t.isBooleanLiteral(expression)))
|
|
5417
|
+
|
|
5418
|
+
{
|
|
5419
|
+
// The index of the source this attribute sits before: the
|
|
5420
|
+
// sources pushed so far, plus the running literal if it will
|
|
5421
|
+
// be pushed ahead of the next spread. The literal never
|
|
5422
|
+
// carries a handler key, so a spread's index is either below
|
|
5423
|
+
// this or at/after it — never ambiguous.
|
|
5424
|
+
claims.push([props.length + (runningObject.length ? 1 : 0), pos, expression]);
|
|
5425
|
+
}
|
|
5426
|
+
}
|
|
5427
|
+
return;
|
|
5428
|
+
}
|
|
5429
|
+
if (key.startsWith("prop:")) return;
|
|
5367
5430
|
if (i > lastSpread) {
|
|
5368
5431
|
const part = tailAttribute(path, tagName, key, node);
|
|
5369
5432
|
if (part !== undefined) {
|
|
@@ -5440,10 +5503,65 @@ path,
|
|
|
5440
5503
|
}
|
|
5441
5504
|
args.push(registerSkip(path, skipKeys), markup);
|
|
5442
5505
|
}
|
|
5506
|
+
if (claims.length) {
|
|
5507
|
+
if (!skipKeys.length) args.push(t.identifier("undefined"), t.identifier("undefined"));
|
|
5508
|
+
args.push(t.arrowFunctionExpression([], claimSegments(claims)));
|
|
5509
|
+
}
|
|
5443
5510
|
const exprs = [t.callExpression(registerImportMethod(path, "ssrElement"), args)];
|
|
5444
5511
|
return { exprs, template: "", declarations: [], dynamics: [], spreadElement: true };
|
|
5445
5512
|
}
|
|
5446
5513
|
|
|
5514
|
+
/**
|
|
5515
|
+
* A spread element's claim map by source index (`createElement`): `{ 1: {
|
|
5516
|
+
* click: go }, 3: { ref: el } }`. A handler position keeps its LAST named
|
|
5517
|
+
* attribute only — the template path's duplicate strip, applied here to the
|
|
5518
|
+
* claims (a later attribute wins whatever sits between, so the earlier one
|
|
5519
|
+
* is never read on either side); refs merge within a segment as `claimMap`
|
|
5520
|
+
* merges them, and across segments at the runtime.
|
|
5521
|
+
*/
|
|
5522
|
+
function claimSegments(
|
|
5523
|
+
claims)
|
|
5524
|
+
{
|
|
5525
|
+
const lastHandler = new Map();
|
|
5526
|
+
claims.forEach(([, pos], i) => {
|
|
5527
|
+
if (pos !== "ref") lastHandler.set(pos, i);
|
|
5528
|
+
});
|
|
5529
|
+
const segments = new Map();
|
|
5530
|
+
claims.forEach(([index, pos, expr], i) => {
|
|
5531
|
+
if (pos !== "ref" && lastHandler.get(pos) !== i) return;
|
|
5532
|
+
let list = segments.get(index);
|
|
5533
|
+
if (!list) segments.set(index, list = []);
|
|
5534
|
+
list.push([pos, expr]);
|
|
5535
|
+
});
|
|
5536
|
+
return t.objectExpression(
|
|
5537
|
+
[...segments].map(([index, list]) => t.objectProperty(t.numericLiteral(index), claimMap(list)))
|
|
5538
|
+
);
|
|
5539
|
+
}
|
|
5540
|
+
|
|
5541
|
+
/**
|
|
5542
|
+
* The compiled claim map of an element's handler positions, `{ click: expr,
|
|
5543
|
+
* ref: [a, b] }`: duplicate event keys were last-wins-stripped by the
|
|
5544
|
+
* template path's duplicate strip (or `claimSegments`); `ref` is exempt from
|
|
5545
|
+
* that pass (client semantics fire every ref), so multiple refs merge into
|
|
5546
|
+
* an array value.
|
|
5547
|
+
*/
|
|
5548
|
+
function claimMap(claims) {
|
|
5549
|
+
const byPos = new Map();
|
|
5550
|
+
for (const [pos, expr] of claims) {
|
|
5551
|
+
let list = byPos.get(pos);
|
|
5552
|
+
if (!list) byPos.set(pos, list = []);
|
|
5553
|
+
list.push(expr);
|
|
5554
|
+
}
|
|
5555
|
+
return t.objectExpression(
|
|
5556
|
+
[...byPos].map(([pos, exprs]) =>
|
|
5557
|
+
t.objectProperty(
|
|
5558
|
+
t.stringLiteral(pos),
|
|
5559
|
+
exprs.length === 1 ? exprs[0] : t.arrayExpression(exprs)
|
|
5560
|
+
)
|
|
5561
|
+
)
|
|
5562
|
+
);
|
|
5563
|
+
}
|
|
5564
|
+
|
|
5447
5565
|
/**
|
|
5448
5566
|
* What an attribute after an element's last spread contributes to
|
|
5449
5567
|
* `ssrElement`'s attribute string. A static is written as the template path
|