babel-plugin-jsx-dom-expressions 0.40.6 → 0.40.10
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/README.md +31 -0
- package/index.js +21 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -175,6 +175,37 @@ Removes tags from the template output if they have no closing parents and are th
|
|
|
175
175
|
|
|
176
176
|
Removes quotes for html attributes when possible from the template output. This may not work in all browser-like environments the same. The solution has been tested again Chrome/Edge/Firefox/Safari.
|
|
177
177
|
|
|
178
|
+
### omitAttributeSpacing
|
|
179
|
+
|
|
180
|
+
- Type: `boolean`
|
|
181
|
+
- Default: `true`
|
|
182
|
+
|
|
183
|
+
When `true` (the default), the template output drops the whitespace between a quoted attribute and the next attribute (`<svg width="12"height="13">`). This mirrors the prior behavior and relies on the browser's HTML parser to tolerate the missing space. Some strict parsers (notably XML/SVG-only or game-engine browser-likes such as Coherent Gameface) reject the resulting markup.
|
|
184
|
+
|
|
185
|
+
Set this to `false` to emit a single space between attributes (`<svg width="12" height="13">`) so the generated HTML is strictly valid. This may also be desirable together with `omitQuotes: false` / `omitLastClosingTag: false` when targeting stricter parsers.
|
|
186
|
+
|
|
187
|
+
### omitServerOnlyTemplates
|
|
188
|
+
|
|
189
|
+
- Type: `boolean`
|
|
190
|
+
- Default: `true`
|
|
191
|
+
|
|
192
|
+
Only applies when `hydratable` is `true`. When `true` (the default), an element marked `$ServerOnly` has no template registered, so its markup is never shipped to the client and the generated code calls `getNextElement()` with no argument. That element can then only be obtained by hydrating against server-rendered DOM.
|
|
193
|
+
|
|
194
|
+
Because `getNextElement` falls back to `template()` whenever it is not hydrating, such an element throws `TypeError: template is not a function` if it is ever re-created on the client. Hot module replacement does exactly that: it disposes the old root and re-runs the component outside of hydration, so editing any file containing `$ServerOnly` breaks the page in development.
|
|
195
|
+
|
|
196
|
+
Set this to `false` to keep registering the template. The hydration path is unchanged, since `getNextElement` prefers the hydration registry and only calls `template()` when there is nothing to hydrate. The only cost is the template markup being present in the output, which makes this suitable for development builds:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
{
|
|
200
|
+
plugins: [
|
|
201
|
+
["jsx-dom-expressions", {
|
|
202
|
+
hydratable: true,
|
|
203
|
+
omitServerOnlyTemplates: process.env.NODE_ENV === "production"
|
|
204
|
+
}]
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
178
209
|
### requireImportSource
|
|
179
210
|
|
|
180
211
|
- Type: `string | false`
|
package/index.js
CHANGED
|
@@ -1477,7 +1477,7 @@ function transformAttributes$2(path, results) {
|
|
|
1477
1477
|
}
|
|
1478
1478
|
|
|
1479
1479
|
if (needsQuoting) {
|
|
1480
|
-
needsSpacing =
|
|
1480
|
+
needsSpacing = !config.omitAttributeSpacing;
|
|
1481
1481
|
results.template += `="${escapeHTML(text, true)}"`;
|
|
1482
1482
|
} else {
|
|
1483
1483
|
needsSpacing = true;
|
|
@@ -1856,7 +1856,7 @@ function transformAttributes$2(path, results) {
|
|
|
1856
1856
|
}
|
|
1857
1857
|
} else {
|
|
1858
1858
|
if (config.hydratable && key === "$ServerOnly") {
|
|
1859
|
-
results.skipTemplate = true;
|
|
1859
|
+
if (config.omitServerOnlyTemplates) results.skipTemplate = true;
|
|
1860
1860
|
return;
|
|
1861
1861
|
}
|
|
1862
1862
|
if (t__namespace.isJSXExpressionContainer(value)) value = value.expression;
|
|
@@ -2647,6 +2647,10 @@ function escapeExpression(path, expression, attr, escapeLiterals) {
|
|
|
2647
2647
|
} else expression.body = escapeExpression(path, expression.body, attr, escapeLiterals);
|
|
2648
2648
|
return expression;
|
|
2649
2649
|
} else if (t__namespace.isTemplateLiteral(expression)) {
|
|
2650
|
+
// Interpolations are escaped recursively. The static quasis are not —
|
|
2651
|
+
// `url("${x}")` would otherwise leave a raw `"` inside style="..." /
|
|
2652
|
+
// attr="...", which HTML parses as attribute injection.
|
|
2653
|
+
if (attr) escapeTemplateQuasis(expression, true);
|
|
2650
2654
|
expression.expressions = expression.expressions.map((e) =>
|
|
2651
2655
|
escapeExpression(path, e, attr, escapeLiterals));
|
|
2652
2656
|
|
|
@@ -2689,6 +2693,18 @@ function escapeExpression(path, expression, attr, escapeLiterals) {
|
|
|
2689
2693
|
|
|
2690
2694
|
}
|
|
2691
2695
|
|
|
2696
|
+
function escapeTemplateQuasis(expression, attr) {
|
|
2697
|
+
for (const quasi of expression.quasis) {
|
|
2698
|
+
const src = quasi.value.cooked != null ? quasi.value.cooked : quasi.value.raw;
|
|
2699
|
+
const escaped = escapeHTML(src, attr);
|
|
2700
|
+
if (typeof escaped !== "string" || escaped === src) continue;
|
|
2701
|
+
quasi.value.cooked = escaped;
|
|
2702
|
+
// Codegen prints `raw`. Re-escape template delimiters so `"` etc.
|
|
2703
|
+
// stay literal text.
|
|
2704
|
+
quasi.value.raw = escaped.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2692
2708
|
function transformToObject(attrName, attributes, selectedAttributes) {
|
|
2693
2709
|
const properties = [];
|
|
2694
2710
|
const existingAttribute = attributes.find((a) => a.node.name.name === attrName);
|
|
@@ -3785,8 +3801,7 @@ function transformComponent(path) {
|
|
|
3785
3801
|
}))
|
|
3786
3802
|
{
|
|
3787
3803
|
if (
|
|
3788
|
-
config.wrapConditionals &&
|
|
3789
|
-
config.generate !== "ssr" && (
|
|
3804
|
+
config.wrapConditionals && (
|
|
3790
3805
|
t__namespace.isLogicalExpression(value.expression) ||
|
|
3791
3806
|
t__namespace.isConditionalExpression(value.expression)))
|
|
3792
3807
|
{
|
|
@@ -4308,7 +4323,9 @@ var config = {
|
|
|
4308
4323
|
omitNestedClosingTags: false,
|
|
4309
4324
|
omitLastClosingTag: true,
|
|
4310
4325
|
omitQuotes: true,
|
|
4326
|
+
omitAttributeSpacing: true,
|
|
4311
4327
|
contextToCustomElements: false,
|
|
4328
|
+
omitServerOnlyTemplates: true,
|
|
4312
4329
|
staticMarker: "@once",
|
|
4313
4330
|
effectWrapper: "effect",
|
|
4314
4331
|
memoWrapper: "memo",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babel-plugin-jsx-dom-expressions",
|
|
3
3
|
"description": "A JSX to DOM plugin that wraps expressions for fine grained change detection",
|
|
4
|
-
"version": "0.40.
|
|
4
|
+
"version": "0.40.10",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
"@rollup/plugin-babel": "6.0.3",
|
|
34
34
|
"@types/babel__core": "^7.20.5"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "727298ff79a54ef5299d3649ab7f4105a326e9b3"
|
|
37
37
|
}
|