babel-plugin-jsx-dom-expressions 0.40.11 → 0.40.14

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.
Files changed (2) hide show
  1. package/index.js +151 -37
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -321,6 +321,49 @@ const SVGElements = /*#__PURE__*/new Set([
321
321
  "vkern"]);
322
322
 
323
323
 
324
+ const MathMLElements = /*#__PURE__*/new Set([
325
+ "annotation",
326
+ "annotation-xml",
327
+ "maction",
328
+ "math",
329
+ "menclose",
330
+ "merror",
331
+ "mfenced",
332
+ "mfrac",
333
+ "mi",
334
+ "mmultiscripts",
335
+ "mn",
336
+ "mo",
337
+ "mover",
338
+ "mpadded",
339
+ "mphantom",
340
+ "mprescripts",
341
+ "mroot",
342
+ "mrow",
343
+ "ms",
344
+ "mspace",
345
+ "msqrt",
346
+ "mstyle",
347
+ "msub",
348
+ "msubsup",
349
+ "msup",
350
+ "mtable",
351
+ "mtd",
352
+ "mtext",
353
+ "mtr",
354
+ "munder",
355
+ "munderover",
356
+ "semantics"]);
357
+
358
+
359
+ const MathMLTextIntegrationPoints = /*#__PURE__*/new Set([
360
+ "mi",
361
+ "mo",
362
+ "mn",
363
+ "ms",
364
+ "mtext"]);
365
+
366
+
324
367
  const SVGNamespace = {
325
368
  xlink: "http://www.w3.org/1999/xlink",
326
369
  xml: "http://www.w3.org/XML/1998/namespace"
@@ -409,23 +452,56 @@ function isComponent(tagName) {
409
452
 
410
453
  }
411
454
 
412
- // <script> and <style> are only raw text in HTML. Inside SVG the parser decodes
413
- // entities and treats `<` as markup, so their children still need escaping.
455
+ // <annotation-xml> is a MathML HTML integration point only when its encoding
456
+ // is text/html or application/xhtml+xml
457
+ function isHTMLEncodingAnnotationXML(node) {
458
+ const encoding = node.openingElement.attributes.find(
459
+ (attr) => t__namespace.isJSXAttribute(attr) && t__namespace.isJSXIdentifier(attr.name) && attr.name.name === "encoding");
460
+
461
+ return (
462
+ t__namespace.isStringLiteral(encoding?.value) &&
463
+ /^(?:text\/html|application\/xhtml\+xml)$/i.test(encoding.value.value));
464
+
465
+ }
466
+
467
+ // <script> and <style> are only raw text in HTML. Inside SVG or MathML the
468
+ // parser decodes entities and treats `<` as markup, so their children still
469
+ // need escaping. Integration points (<foreignObject> in SVG; the MathML text
470
+ // integration points and <annotation-xml> with an HTML encoding in MathML)
471
+ // switch their content back to HTML parsing.
414
472
  function isRawTextElement(path) {
415
473
  const tagName = getTagName(path.node);
416
474
  if (tagName !== "script" && tagName !== "style") return false;
417
- let parent = path.parentPath,
418
- rootTag;
475
+ const ancestors = [];
476
+ let parent = path.parentPath;
419
477
  while (t__namespace.isJSXElement(parent.node)) {
420
- const parentTag = getTagName(parent.node);
421
- if (isComponent(parentTag)) break;
422
- if (parentTag === "foreignObject") return true;
423
- if (parentTag === "svg") return false;
424
- rootTag = parentTag;
478
+ if (isComponent(getTagName(parent.node))) break;
479
+ ancestors.push(parent.node);
425
480
  parent = parent.parentPath;
426
481
  }
427
- // a top-level SVG element is parsed inside an <svg> wrapper
428
- return !rootTag || !SVGElements.has(rootTag);
482
+ const rootTag = ancestors.length ? getTagName(ancestors[ancestors.length - 1]) : undefined;
483
+ // a top-level SVG element is parsed inside an <svg> wrapper and a top-level
484
+ // MathML element inside a MathML template
485
+ let namespace = SVGElements.has(rootTag) ?
486
+ "svg" :
487
+ MathMLElements.has(rootTag) ?
488
+ "math" :
489
+ "html";
490
+ for (let i = ancestors.length - 1; i >= 0; i--) {
491
+ const parentTag = getTagName(ancestors[i]);
492
+ if (namespace === "html") {
493
+ if (parentTag === "svg") namespace = "svg";else
494
+ if (parentTag === "math") namespace = "math";
495
+ } else if (namespace === "svg") {
496
+ if (parentTag === "foreignObject") namespace = "html";
497
+ } else if (
498
+ MathMLTextIntegrationPoints.has(parentTag) ||
499
+ parentTag === "annotation-xml" && isHTMLEncodingAnnotationXML(ancestors[i]))
500
+ {
501
+ namespace = "html";
502
+ }
503
+ }
504
+ return namespace === "html";
429
505
  }
430
506
 
431
507
  function hasStaticMarker(object, path) {
@@ -2646,26 +2722,38 @@ function setAttr$1(attribute, results, name, value, isSVG) {
2646
2722
  }
2647
2723
  }
2648
2724
 
2649
- function escapeExpression(path, expression, attr, escapeLiterals) {
2725
+ /**
2726
+ * How `escapeExpression` treats a string literal it reaches.
2727
+ * - `undefined`: the hole itself. The caller escapes a literal when it
2728
+ * inlines it into the template, so it stays as written here.
2729
+ * - `"escape"`: a position whose value the runtime writes as is.
2730
+ * - `"keep"`: inside a function value in an attribute. `ssrAttribute`
2731
+ * stringifies the function's source, so literals stay as written.
2732
+ */
2733
+ function escapeExpression(path, expression, attr, literals) {
2734
+ if (!expression) return expression;
2735
+ // A literal in a branch is the hole's value, which `ssr` and
2736
+ // `ssrAttribute` write as is, so branches escape literals.
2737
+ const branch = literals === "keep" ? "keep" : "escape";
2738
+ // A function's returns escape like branches, except for a function value in
2739
+ // an attribute: `ssrAttribute` stringifies its source, so its literals stay
2740
+ // as written at every depth.
2741
+ const returns = literals === undefined && attr ? "keep" : branch;
2650
2742
  if (
2651
2743
  t__namespace.isStringLiteral(expression) ||
2652
2744
  t__namespace.isNumericLiteral(expression) ||
2653
2745
  t__namespace.isTemplateLiteral(expression) && expression.expressions.length === 0)
2654
2746
  {
2655
- if (escapeLiterals) {
2747
+ if (literals === "escape") {
2656
2748
  if (t__namespace.isStringLiteral(expression)) return t__namespace.stringLiteral(escapeHTML(expression.value, attr));else
2657
2749
  if (t__namespace.isTemplateLiteral(expression))
2658
- return t__namespace.stringLiteral(escapeHTML(expression.quasis[0].value.raw, attr));
2750
+ return t__namespace.stringLiteral(
2751
+ escapeHTML(expression.quasis[0].value.cooked ?? expression.quasis[0].value.raw, attr));
2752
+
2659
2753
  }
2660
2754
  return expression;
2661
2755
  } else if (t__namespace.isFunction(expression)) {
2662
- if (t__namespace.isBlockStatement(expression.body)) {
2663
- expression.body.body = expression.body.body.map((e) => {
2664
- if (t__namespace.isReturnStatement(e))
2665
- e.argument = escapeExpression(path, e.argument, attr, escapeLiterals);
2666
- return e;
2667
- });
2668
- } else expression.body = escapeExpression(path, expression.body, attr, escapeLiterals);
2756
+ escapeFunctionResult(path, expression, attr, returns);
2669
2757
  return expression;
2670
2758
  } else if (t__namespace.isTemplateLiteral(expression)) {
2671
2759
  // Interpolations are escaped recursively. The static quasis are not —
@@ -2673,35 +2761,30 @@ function escapeExpression(path, expression, attr, escapeLiterals) {
2673
2761
  // attr="...", which HTML parses as attribute injection.
2674
2762
  if (attr) escapeTemplateQuasis(expression, true);
2675
2763
  expression.expressions = expression.expressions.map((e) =>
2676
- escapeExpression(path, e, attr, escapeLiterals));
2764
+ escapeExpression(path, e, attr, literals));
2677
2765
 
2678
2766
  return expression;
2679
2767
  } else if (t__namespace.isUnaryExpression(expression)) {
2680
2768
  return expression;
2681
2769
  } else if (t__namespace.isBinaryExpression(expression)) {
2682
- expression.left = escapeExpression(path, expression.left, attr, escapeLiterals);
2683
- expression.right = escapeExpression(path, expression.right, attr, escapeLiterals);
2770
+ expression.left = escapeExpression(path, expression.left, attr, literals);
2771
+ expression.right = escapeExpression(path, expression.right, attr, literals);
2684
2772
  return expression;
2685
2773
  } else if (t__namespace.isConditionalExpression(expression)) {
2686
- expression.consequent = escapeExpression(path, expression.consequent, attr, escapeLiterals);
2687
- expression.alternate = escapeExpression(path, expression.alternate, attr, escapeLiterals);
2774
+ expression.consequent = escapeExpression(path, expression.consequent, attr, branch);
2775
+ expression.alternate = escapeExpression(path, expression.alternate, attr, branch);
2688
2776
  return expression;
2689
2777
  } else if (t__namespace.isLogicalExpression(expression)) {
2690
2778
  // Preserve the old short-circuit shape for && while still escaping the
2691
2779
  // selected result of || and ?? as a whole.
2692
2780
  if (expression.operator === "&&") {
2693
- expression.right = escapeExpression(path, expression.right, attr, escapeLiterals);
2781
+ expression.right = escapeExpression(path, expression.right, attr, branch);
2694
2782
  return expression;
2695
2783
  }
2696
2784
  } else if (t__namespace.isCallExpression(expression) && t__namespace.isFunction(expression.callee)) {
2697
- if (t__namespace.isBlockStatement(expression.callee.body)) {
2698
- expression.callee.body.body = expression.callee.body.body.map((e) => {
2699
- if (t__namespace.isReturnStatement(e))
2700
- e.argument = escapeExpression(path, e.argument, attr, escapeLiterals);
2701
- return e;
2702
- });
2703
- } else
2704
- expression.callee.body = escapeExpression(path, expression.callee.body, attr, escapeLiterals);
2785
+ // The call's result is the hole's value, so its returns escape like
2786
+ // branches.
2787
+ escapeFunctionResult(path, expression.callee, attr, branch);
2705
2788
  return expression;
2706
2789
  } else if (t__namespace.isJSXElement(expression) && !isComponent(getTagName(expression))) {
2707
2790
  expression.wontEscape = true;
@@ -2714,6 +2797,37 @@ function escapeExpression(path, expression, attr, escapeLiterals) {
2714
2797
 
2715
2798
  }
2716
2799
 
2800
+ /** Escapes a function's expression body or its `return` arguments. */
2801
+ function escapeFunctionResult(path, fn, attr, literals) {
2802
+ if (t__namespace.isBlockStatement(fn.body)) escapeReturns(path, fn.body, attr, literals);else
2803
+ fn.body = escapeExpression(path, fn.body, attr, literals);
2804
+ }
2805
+
2806
+ /**
2807
+ * Escapes the `return` arguments in a statement at any depth, without
2808
+ * entering nested functions or classes.
2809
+ */
2810
+ function escapeReturns(path, statement, attr, literals) {
2811
+ if (!statement) return;
2812
+ if (t__namespace.isReturnStatement(statement)) {
2813
+ statement.argument = escapeExpression(path, statement.argument, attr, literals);
2814
+ } else if (t__namespace.isBlockStatement(statement)) {
2815
+ for (const child of statement.body) escapeReturns(path, child, attr, literals);
2816
+ } else if (t__namespace.isIfStatement(statement)) {
2817
+ escapeReturns(path, statement.consequent, attr, literals);
2818
+ escapeReturns(path, statement.alternate, attr, literals);
2819
+ } else if (t__namespace.isSwitchStatement(statement)) {
2820
+ for (const switchCase of statement.cases)
2821
+ for (const child of switchCase.consequent) escapeReturns(path, child, attr, literals);
2822
+ } else if (t__namespace.isTryStatement(statement)) {
2823
+ escapeReturns(path, statement.block, attr, literals);
2824
+ escapeReturns(path, statement.handler && statement.handler.body, attr, literals);
2825
+ escapeReturns(path, statement.finalizer, attr, literals);
2826
+ } else if (t__namespace.isLoop(statement) || t__namespace.isLabeledStatement(statement) || t__namespace.isWithStatement(statement)) {
2827
+ escapeReturns(path, statement.body, attr, literals);
2828
+ }
2829
+ }
2830
+
2717
2831
  function escapeTemplateQuasis(expression, attr) {
2718
2832
  for (const quasi of expression.quasis) {
2719
2833
  const src = quasi.value.cooked != null ? quasi.value.cooked : quasi.value.raw;
@@ -2889,14 +3003,14 @@ function transformAttributes$1(path, results, info) {
2889
3003
  if (p.computed) {
2890
3004
  return t__namespace.callExpression(registerImportMethod(path, "ssrStyleProperty"), [
2891
3005
  t__namespace.binaryExpression("+", p.key, t__namespace.stringLiteral(":")),
2892
- escapeExpression(path, p.value, true, true)]);
3006
+ escapeExpression(path, p.value, true, "escape")]);
2893
3007
 
2894
3008
  }
2895
3009
  return t__namespace.callExpression(registerImportMethod(path, "ssrStyleProperty"), [
2896
3010
  t__namespace.stringLiteral(
2897
3011
  (i ? ";" : "") + (t__namespace.isIdentifier(p.key) ? p.key.name : p.key.value) + ":"),
2898
3012
 
2899
- escapeExpression(path, p.value, true, true)]);
3013
+ escapeExpression(path, p.value, true, "escape")]);
2900
3014
 
2901
3015
  });
2902
3016
 
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.11",
4
+ "version": "0.40.14",
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": "3eba17b37754abaae2346c0237a4a65de802a316"
36
+ "gitHead": "f6fecdf9e455d03cfa90aaba0323ea3d4686ada6"
37
37
  }