@reckona/mreact-compat 0.0.90 → 0.0.92

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 (104) hide show
  1. package/README.md +1 -0
  2. package/dist/class-component.d.ts +20 -6
  3. package/dist/class-component.d.ts.map +1 -1
  4. package/dist/class-component.js +94 -51
  5. package/dist/class-component.js.map +1 -1
  6. package/dist/context.d.ts +19 -3
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/context.js +55 -6
  9. package/dist/context.js.map +1 -1
  10. package/dist/dom-children.d.ts +2 -0
  11. package/dist/dom-children.d.ts.map +1 -1
  12. package/dist/dom-children.js +103 -1
  13. package/dist/dom-children.js.map +1 -1
  14. package/dist/dom-host-rules.d.ts +10 -0
  15. package/dist/dom-host-rules.d.ts.map +1 -0
  16. package/dist/dom-host-rules.js +86 -0
  17. package/dist/dom-host-rules.js.map +1 -0
  18. package/dist/dom-props.d.ts +3 -2
  19. package/dist/dom-props.d.ts.map +1 -1
  20. package/dist/dom-props.js +229 -33
  21. package/dist/dom-props.js.map +1 -1
  22. package/dist/element.d.ts +9 -4
  23. package/dist/element.d.ts.map +1 -1
  24. package/dist/element.js +101 -26
  25. package/dist/element.js.map +1 -1
  26. package/dist/event-listeners.d.ts +4 -4
  27. package/dist/event-listeners.d.ts.map +1 -1
  28. package/dist/event-listeners.js +1 -1
  29. package/dist/event-listeners.js.map +1 -1
  30. package/dist/event-types.d.ts +10 -0
  31. package/dist/event-types.d.ts.map +1 -1
  32. package/dist/event-types.js.map +1 -1
  33. package/dist/events.js +22 -1
  34. package/dist/events.js.map +1 -1
  35. package/dist/fiber-commit.d.ts +2 -1
  36. package/dist/fiber-commit.d.ts.map +1 -1
  37. package/dist/fiber-commit.js +13 -1
  38. package/dist/fiber-commit.js.map +1 -1
  39. package/dist/fiber-reconciler.d.ts.map +1 -1
  40. package/dist/fiber-reconciler.js +28 -7
  41. package/dist/fiber-reconciler.js.map +1 -1
  42. package/dist/fiber-work-loop.d.ts.map +1 -1
  43. package/dist/fiber-work-loop.js +4 -3
  44. package/dist/fiber-work-loop.js.map +1 -1
  45. package/dist/fiber.d.ts +5 -0
  46. package/dist/fiber.d.ts.map +1 -1
  47. package/dist/fiber.js +9 -0
  48. package/dist/fiber.js.map +1 -1
  49. package/dist/hooks-entry.d.ts +3 -0
  50. package/dist/hooks-entry.d.ts.map +1 -0
  51. package/dist/hooks-entry.js +2 -0
  52. package/dist/hooks-entry.js.map +1 -0
  53. package/dist/hooks.d.ts +39 -5
  54. package/dist/hooks.d.ts.map +1 -1
  55. package/dist/hooks.js +373 -326
  56. package/dist/hooks.js.map +1 -1
  57. package/dist/host-reconciler.d.ts +3 -0
  58. package/dist/host-reconciler.d.ts.map +1 -1
  59. package/dist/host-reconciler.js +1152 -64
  60. package/dist/host-reconciler.js.map +1 -1
  61. package/dist/hydration.d.ts +1 -1
  62. package/dist/hydration.d.ts.map +1 -1
  63. package/dist/hydration.js.map +1 -1
  64. package/dist/index.d.ts +2 -1
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +2 -1
  67. package/dist/index.js.map +1 -1
  68. package/dist/react-default.d.ts +4 -4
  69. package/dist/react-default.d.ts.map +1 -1
  70. package/dist/react-default.js +2 -1
  71. package/dist/react-default.js.map +1 -1
  72. package/dist/reconciler.d.ts.map +1 -1
  73. package/dist/reconciler.js +38 -22
  74. package/dist/reconciler.js.map +1 -1
  75. package/dist/root.d.ts.map +1 -1
  76. package/dist/root.js +48 -13
  77. package/dist/root.js.map +1 -1
  78. package/dist/server-render.d.ts +6 -0
  79. package/dist/server-render.d.ts.map +1 -0
  80. package/dist/server-render.js +307 -0
  81. package/dist/server-render.js.map +1 -0
  82. package/package.json +6 -2
  83. package/src/class-component.ts +216 -51
  84. package/src/context.ts +108 -9
  85. package/src/dom-children.ts +155 -1
  86. package/src/dom-host-rules.ts +115 -0
  87. package/src/dom-props.ts +297 -46
  88. package/src/element.ts +141 -31
  89. package/src/event-listeners.ts +6 -6
  90. package/src/event-types.ts +10 -0
  91. package/src/events.ts +32 -10
  92. package/src/fiber-commit.ts +16 -1
  93. package/src/fiber-reconciler.ts +39 -6
  94. package/src/fiber-work-loop.ts +4 -3
  95. package/src/fiber.ts +14 -0
  96. package/src/hooks-entry.ts +24 -0
  97. package/src/hooks.ts +482 -479
  98. package/src/host-reconciler.ts +1628 -94
  99. package/src/hydration.ts +1 -1
  100. package/src/index.ts +1 -1
  101. package/src/react-default.ts +1 -1
  102. package/src/reconciler.ts +61 -22
  103. package/src/root.ts +55 -12
  104. package/src/server-render.ts +478 -0
@@ -2,22 +2,124 @@ import { collectScopedNodes } from "./hydration.js";
2
2
  export function syncChildNodes(parent, nextNodes) {
3
3
  syncScopedChildNodes(parent, null, null, nextNodes);
4
4
  }
5
+ export function syncOwnedChildNodes(parent, previousNodes, nextNodes) {
6
+ const nextSet = new Set(nextNodes);
7
+ for (const node of nextNodes) {
8
+ if (node.parentNode !== parent || node.nextSibling !== null) {
9
+ parent.appendChild(node);
10
+ }
11
+ }
12
+ for (const child of previousNodes) {
13
+ if (!nextSet.has(child)) {
14
+ removeChildIfPresent(parent, child);
15
+ }
16
+ }
17
+ }
5
18
  export function syncScopedChildNodes(parent, before, after, nextNodes) {
19
+ if (replaceDisjointFullChildList(parent, before, after, nextNodes)) {
20
+ return;
21
+ }
22
+ if (nextNodes.length > 16 &&
23
+ hasSingleExtraScopedChild(parent, before, after, nextNodes.length) &&
24
+ removeSingleMissingChild(parent, before, after, nextNodes)) {
25
+ return;
26
+ }
6
27
  let cursor = parent.firstChild;
7
28
  if (before !== null) {
8
29
  cursor = before.nextSibling;
9
30
  }
31
+ let changed = false;
10
32
  for (const node of nextNodes) {
11
33
  if (node !== cursor) {
12
34
  parent.insertBefore(node, cursor === after ? after : cursor);
35
+ changed = true;
13
36
  }
14
37
  cursor = node.nextSibling;
15
38
  }
39
+ if (!changed && cursor === after) {
40
+ return;
41
+ }
42
+ if (!changed && after === null && cursor === null) {
43
+ return;
44
+ }
16
45
  const nextSet = new Set(nextNodes);
17
46
  for (const child of collectScopedNodes(parent, before, after)) {
18
47
  if (!nextSet.has(child)) {
19
- parent.removeChild(child);
48
+ removeChildIfPresent(parent, child);
20
49
  }
21
50
  }
22
51
  }
52
+ function replaceDisjointFullChildList(parent, before, after, nextNodes) {
53
+ if (before !== null || after !== null || nextNodes.length <= 16) {
54
+ return false;
55
+ }
56
+ const currentNodes = parent.childNodes;
57
+ if (currentNodes.length <= 16 || currentNodes.length !== nextNodes.length) {
58
+ return false;
59
+ }
60
+ const nextSet = new Set(nextNodes);
61
+ for (let index = 0; index < currentNodes.length; index += 1) {
62
+ if (nextSet.has(currentNodes[index])) {
63
+ return false;
64
+ }
65
+ }
66
+ parent.replaceChildren(...nextNodes);
67
+ return true;
68
+ }
69
+ function hasSingleExtraScopedChild(parent, before, after, nextCount) {
70
+ if (before === null && after === null) {
71
+ return parent.childNodes.length === nextCount + 1;
72
+ }
73
+ let cursor = before === null ? parent.firstChild : before.nextSibling;
74
+ let currentCount = 0;
75
+ while (cursor !== null && cursor !== after && currentCount <= nextCount + 1) {
76
+ currentCount += 1;
77
+ cursor = cursor.nextSibling;
78
+ }
79
+ return currentCount === nextCount + 1 && (cursor === null || cursor === after);
80
+ }
81
+ function removeSingleMissingChild(parent, before, after, nextNodes) {
82
+ let cursor = before === null ? parent.firstChild : before.nextSibling;
83
+ let removed = false;
84
+ for (const nextNode of nextNodes) {
85
+ if (cursor === nextNode) {
86
+ cursor = cursor.nextSibling;
87
+ continue;
88
+ }
89
+ if (removed || cursor === null || cursor === after || cursor.nextSibling !== nextNode) {
90
+ return false;
91
+ }
92
+ const missing = cursor;
93
+ cursor = nextNode.nextSibling;
94
+ removeChildIfPresent(parent, missing);
95
+ removed = true;
96
+ }
97
+ if (cursor === after || cursor === null) {
98
+ return removed;
99
+ }
100
+ if (removed || cursor.nextSibling !== after) {
101
+ return false;
102
+ }
103
+ removeChildIfPresent(parent, cursor);
104
+ return true;
105
+ }
106
+ export function removeChildIfPresent(parent, child) {
107
+ if (child.parentNode !== parent) {
108
+ return;
109
+ }
110
+ try {
111
+ parent.removeChild(child);
112
+ }
113
+ catch (error) {
114
+ if (child.parentNode !== parent && isNotFoundError(error)) {
115
+ return;
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+ function isNotFoundError(error) {
121
+ const maybeError = error;
122
+ return (maybeError.name === "NotFoundError" ||
123
+ (typeof maybeError.message === "string" && maybeError.message.includes("not a child")));
124
+ }
23
125
  //# sourceMappingURL=dom-children.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom-children.js","sourceRoot":"","sources":["../src/dom-children.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,UAAU,cAAc,CAAC,MAAkB,EAAE,SAA0B;IAC3E,oBAAoB,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,MAAkB,EAClB,MAAwB,EACxB,KAAuB,EACvB,SAA0B;IAE1B,IAAI,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;IAE/B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC;IAC9B,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAEnC,KAAK,MAAM,KAAK,IAAI,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;QAC9D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { collectScopedNodes } from \"./hydration.js\";\n\nexport function syncChildNodes(parent: ParentNode, nextNodes: readonly Node[]): void {\n syncScopedChildNodes(parent, null, null, nextNodes);\n}\n\nexport function syncScopedChildNodes(\n parent: ParentNode,\n before: ChildNode | null,\n after: ChildNode | null,\n nextNodes: readonly Node[],\n): void {\n let cursor = parent.firstChild;\n\n if (before !== null) {\n cursor = before.nextSibling;\n }\n\n for (const node of nextNodes) {\n if (node !== cursor) {\n parent.insertBefore(node, cursor === after ? after : cursor);\n }\n\n cursor = node.nextSibling;\n }\n\n const nextSet = new Set(nextNodes);\n\n for (const child of collectScopedNodes(parent, before, after)) {\n if (!nextSet.has(child)) {\n parent.removeChild(child);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"dom-children.js","sourceRoot":"","sources":["../src/dom-children.ts"],"names":[],"mappings":"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","sourcesContent":["import { collectScopedNodes } from \"./hydration.js\";\n\nexport function syncChildNodes(parent: ParentNode, nextNodes: readonly Node[]): void {\n syncScopedChildNodes(parent, null, null, nextNodes);\n}\n\nexport function syncOwnedChildNodes(\n parent: ParentNode,\n previousNodes: readonly Node[],\n nextNodes: readonly Node[],\n): void {\n const nextSet = new Set(nextNodes);\n\n for (const node of nextNodes) {\n if (node.parentNode !== parent || node.nextSibling !== null) {\n parent.appendChild(node);\n }\n }\n\n for (const child of previousNodes) {\n if (!nextSet.has(child)) {\n removeChildIfPresent(parent, child);\n }\n }\n}\n\nexport function syncScopedChildNodes(\n parent: ParentNode,\n before: ChildNode | null,\n after: ChildNode | null,\n nextNodes: readonly Node[],\n): void {\n if (replaceDisjointFullChildList(parent, before, after, nextNodes)) {\n return;\n }\n\n if (\n nextNodes.length > 16 &&\n hasSingleExtraScopedChild(parent, before, after, nextNodes.length) &&\n removeSingleMissingChild(parent, before, after, nextNodes)\n ) {\n return;\n }\n\n let cursor = parent.firstChild;\n\n if (before !== null) {\n cursor = before.nextSibling;\n }\n\n let changed = false;\n\n for (const node of nextNodes) {\n if (node !== cursor) {\n parent.insertBefore(node, cursor === after ? after : cursor);\n changed = true;\n }\n\n cursor = node.nextSibling;\n }\n\n if (!changed && cursor === after) {\n return;\n }\n\n if (!changed && after === null && cursor === null) {\n return;\n }\n\n const nextSet = new Set(nextNodes);\n\n for (const child of collectScopedNodes(parent, before, after)) {\n if (!nextSet.has(child)) {\n removeChildIfPresent(parent, child);\n }\n }\n}\n\nfunction replaceDisjointFullChildList(\n parent: ParentNode,\n before: ChildNode | null,\n after: ChildNode | null,\n nextNodes: readonly Node[],\n): boolean {\n if (before !== null || after !== null || nextNodes.length <= 16) {\n return false;\n }\n\n const currentNodes = parent.childNodes;\n\n if (currentNodes.length <= 16 || currentNodes.length !== nextNodes.length) {\n return false;\n }\n\n const nextSet = new Set(nextNodes);\n\n for (let index = 0; index < currentNodes.length; index += 1) {\n if (nextSet.has(currentNodes[index]!)) {\n return false;\n }\n }\n\n parent.replaceChildren(...nextNodes);\n return true;\n}\n\nfunction hasSingleExtraScopedChild(\n parent: ParentNode,\n before: ChildNode | null,\n after: ChildNode | null,\n nextCount: number,\n): boolean {\n if (before === null && after === null) {\n return parent.childNodes.length === nextCount + 1;\n }\n\n let cursor = before === null ? parent.firstChild : before.nextSibling;\n let currentCount = 0;\n\n while (cursor !== null && cursor !== after && currentCount <= nextCount + 1) {\n currentCount += 1;\n cursor = cursor.nextSibling;\n }\n\n return currentCount === nextCount + 1 && (cursor === null || cursor === after);\n}\n\nfunction removeSingleMissingChild(\n parent: ParentNode,\n before: ChildNode | null,\n after: ChildNode | null,\n nextNodes: readonly Node[],\n): boolean {\n let cursor = before === null ? parent.firstChild : before.nextSibling;\n let removed = false;\n\n for (const nextNode of nextNodes) {\n if (cursor === nextNode) {\n cursor = cursor.nextSibling;\n continue;\n }\n\n if (removed || cursor === null || cursor === after || cursor.nextSibling !== nextNode) {\n return false;\n }\n\n const missing = cursor;\n cursor = nextNode.nextSibling;\n removeChildIfPresent(parent, missing);\n removed = true;\n }\n\n if (cursor === after || cursor === null) {\n return removed;\n }\n\n if (removed || cursor.nextSibling !== after) {\n return false;\n }\n\n removeChildIfPresent(parent, cursor);\n return true;\n}\n\nexport function removeChildIfPresent(parent: ParentNode, child: Node): void {\n if (child.parentNode !== parent) {\n return;\n }\n\n try {\n parent.removeChild(child);\n } catch (error) {\n if (child.parentNode !== parent && isNotFoundError(error)) {\n return;\n }\n\n throw error;\n }\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n const maybeError = error as { message?: unknown; name?: unknown };\n\n return (\n maybeError.name === \"NotFoundError\" ||\n (typeof maybeError.message === \"string\" && maybeError.message.includes(\"not a child\"))\n );\n}\n"]}
@@ -0,0 +1,10 @@
1
+ export type HostElement = HTMLElement | SVGElement;
2
+ export type HostNamespace = "html" | "svg";
3
+ export declare function createHostElement(documentRef: Document, tagName: string, namespace: HostNamespace): HostElement;
4
+ export declare function isHostElement(value: unknown): value is HostElement;
5
+ export declare function namespaceForHostElement(parentNamespace: HostNamespace, tagName: string): HostNamespace;
6
+ export declare function namespaceForHostChildren(elementNamespace: HostNamespace, tagName: string): HostNamespace;
7
+ export declare function hostElementMatches(element: HostElement, tagName: string, namespace: HostNamespace): boolean;
8
+ export declare function serializeClientStyleValue(name: string, value: unknown): string;
9
+ export declare function styleNameToCssName(name: string): string;
10
+ //# sourceMappingURL=dom-host-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom-host-rules.d.ts","sourceRoot":"","sources":["../src/dom-host-rules.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;AACnD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,CAAC;AAoD3C,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,QAAQ,EACrB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,aAAa,GACvB,WAAW,CAMb;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,aAAa,EAC9B,OAAO,EAAE,MAAM,GACd,aAAa,CAMf;AAED,wBAAgB,wBAAwB,CACtC,gBAAgB,EAAE,aAAa,EAC/B,OAAO,EAAE,MAAM,GACd,aAAa,CAEf;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,aAAa,GACvB,OAAO,CAET;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAM9E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD"}
@@ -0,0 +1,86 @@
1
+ const htmlNamespace = "http://www.w3.org/1999/xhtml";
2
+ const svgNamespace = "http://www.w3.org/2000/svg";
3
+ const isUnitlessNumberStyle = new Set([
4
+ "animationIterationCount",
5
+ "aspectRatio",
6
+ "borderImageOutset",
7
+ "borderImageSlice",
8
+ "borderImageWidth",
9
+ "boxFlex",
10
+ "boxFlexGroup",
11
+ "boxOrdinalGroup",
12
+ "columnCount",
13
+ "columns",
14
+ "flex",
15
+ "flexGrow",
16
+ "flexPositive",
17
+ "flexShrink",
18
+ "flexNegative",
19
+ "flexOrder",
20
+ "gridArea",
21
+ "gridRow",
22
+ "gridRowEnd",
23
+ "gridRowSpan",
24
+ "gridRowStart",
25
+ "gridColumn",
26
+ "gridColumnEnd",
27
+ "gridColumnSpan",
28
+ "gridColumnStart",
29
+ "fontWeight",
30
+ "lineClamp",
31
+ "lineHeight",
32
+ "opacity",
33
+ "order",
34
+ "orphans",
35
+ "scale",
36
+ "tabSize",
37
+ "widows",
38
+ "zIndex",
39
+ "zoom",
40
+ "fillOpacity",
41
+ "floodOpacity",
42
+ "stopOpacity",
43
+ "strokeDasharray",
44
+ "strokeDashoffset",
45
+ "strokeMiterlimit",
46
+ "strokeOpacity",
47
+ "strokeWidth",
48
+ ]);
49
+ export function createHostElement(documentRef, tagName, namespace) {
50
+ if (namespaceForHostElement(namespace, tagName) === "svg") {
51
+ return documentRef.createElementNS(svgNamespace, tagName);
52
+ }
53
+ return documentRef.createElement(tagName);
54
+ }
55
+ export function isHostElement(value) {
56
+ return value instanceof HTMLElement || value instanceof SVGElement;
57
+ }
58
+ export function namespaceForHostElement(parentNamespace, tagName) {
59
+ if (tagName === "svg") {
60
+ return "svg";
61
+ }
62
+ return parentNamespace;
63
+ }
64
+ export function namespaceForHostChildren(elementNamespace, tagName) {
65
+ return elementNamespace === "svg" && tagName === "foreignObject" ? "html" : elementNamespace;
66
+ }
67
+ export function hostElementMatches(element, tagName, namespace) {
68
+ return element.tagName.toLowerCase() === tagName && element.namespaceURI === namespaceUri(namespace);
69
+ }
70
+ export function serializeClientStyleValue(name, value) {
71
+ if (typeof value === "number" && value !== 0 && !name.startsWith("--") && !isUnitlessNumberStyle.has(name)) {
72
+ return `${value}px`;
73
+ }
74
+ return String(value);
75
+ }
76
+ export function styleNameToCssName(name) {
77
+ if (name.startsWith("--")) {
78
+ return name;
79
+ }
80
+ const cssName = name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
81
+ return cssName.startsWith("ms-") ? `-${cssName}` : cssName;
82
+ }
83
+ function namespaceUri(namespace) {
84
+ return namespace === "svg" ? svgNamespace : htmlNamespace;
85
+ }
86
+ //# sourceMappingURL=dom-host-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom-host-rules.js","sourceRoot":"","sources":["../src/dom-host-rules.ts"],"names":[],"mappings":"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","sourcesContent":["export type HostElement = HTMLElement | SVGElement;\nexport type HostNamespace = \"html\" | \"svg\";\n\nconst htmlNamespace = \"http://www.w3.org/1999/xhtml\";\nconst svgNamespace = \"http://www.w3.org/2000/svg\";\n\nconst isUnitlessNumberStyle = new Set([\n \"animationIterationCount\",\n \"aspectRatio\",\n \"borderImageOutset\",\n \"borderImageSlice\",\n \"borderImageWidth\",\n \"boxFlex\",\n \"boxFlexGroup\",\n \"boxOrdinalGroup\",\n \"columnCount\",\n \"columns\",\n \"flex\",\n \"flexGrow\",\n \"flexPositive\",\n \"flexShrink\",\n \"flexNegative\",\n \"flexOrder\",\n \"gridArea\",\n \"gridRow\",\n \"gridRowEnd\",\n \"gridRowSpan\",\n \"gridRowStart\",\n \"gridColumn\",\n \"gridColumnEnd\",\n \"gridColumnSpan\",\n \"gridColumnStart\",\n \"fontWeight\",\n \"lineClamp\",\n \"lineHeight\",\n \"opacity\",\n \"order\",\n \"orphans\",\n \"scale\",\n \"tabSize\",\n \"widows\",\n \"zIndex\",\n \"zoom\",\n \"fillOpacity\",\n \"floodOpacity\",\n \"stopOpacity\",\n \"strokeDasharray\",\n \"strokeDashoffset\",\n \"strokeMiterlimit\",\n \"strokeOpacity\",\n \"strokeWidth\",\n]);\n\nexport function createHostElement(\n documentRef: Document,\n tagName: string,\n namespace: HostNamespace,\n): HostElement {\n if (namespaceForHostElement(namespace, tagName) === \"svg\") {\n return documentRef.createElementNS(svgNamespace, tagName);\n }\n\n return documentRef.createElement(tagName);\n}\n\nexport function isHostElement(value: unknown): value is HostElement {\n return value instanceof HTMLElement || value instanceof SVGElement;\n}\n\nexport function namespaceForHostElement(\n parentNamespace: HostNamespace,\n tagName: string,\n): HostNamespace {\n if (tagName === \"svg\") {\n return \"svg\";\n }\n\n return parentNamespace;\n}\n\nexport function namespaceForHostChildren(\n elementNamespace: HostNamespace,\n tagName: string,\n): HostNamespace {\n return elementNamespace === \"svg\" && tagName === \"foreignObject\" ? \"html\" : elementNamespace;\n}\n\nexport function hostElementMatches(\n element: HostElement,\n tagName: string,\n namespace: HostNamespace,\n): boolean {\n return element.tagName.toLowerCase() === tagName && element.namespaceURI === namespaceUri(namespace);\n}\n\nexport function serializeClientStyleValue(name: string, value: unknown): string {\n if (typeof value === \"number\" && value !== 0 && !name.startsWith(\"--\") && !isUnitlessNumberStyle.has(name)) {\n return `${value}px`;\n }\n\n return String(value);\n}\n\nexport function styleNameToCssName(name: string): string {\n if (name.startsWith(\"--\")) {\n return name;\n }\n\n const cssName = name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);\n return cssName.startsWith(\"ms-\") ? `-${cssName}` : cssName;\n}\n\nfunction namespaceUri(namespace: HostNamespace): string {\n return namespace === \"svg\" ? svgNamespace : htmlNamespace;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { type RenderOptions } from "./hydration.js";
2
- export declare function applyProps(element: HTMLElement, props: Record<string, unknown>, path: string, options: RenderOptions): void;
3
- export declare function applyPostChildFormProps(element: HTMLElement, props: Record<string, unknown>): void;
2
+ import { type HostElement } from "./dom-host-rules.js";
3
+ export declare function applyProps(element: HostElement, props: Record<string, unknown>, path: string, options: RenderOptions): void;
4
+ export declare function applyPostChildFormProps(element: Element, props: Record<string, unknown>): void;
4
5
  //# sourceMappingURL=dom-props.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom-props.d.ts","sourceRoot":"","sources":["../src/dom-props.ts"],"names":[],"mappings":"AAQA,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AASvE,wBAAgB,UAAU,CACxB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GACrB,IAAI,CAkGN;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAyBN"}
1
+ {"version":3,"file":"dom-props.d.ts","sourceRoot":"","sources":["../src/dom-props.ts"],"names":[],"mappings":"AASA,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAQvE,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,UAAU,CACxB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GACrB,IAAI,CAuIN;AA6GD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAyBN"}
package/dist/dom-props.js CHANGED
@@ -1,25 +1,46 @@
1
1
  import { getAppliedProps, setAppliedProps, ensureDelegatedEventListener, toEventNames, } from "./host-event-binder.js";
2
+ import { HOST_OWN_PROPS_META } from "./element.js";
2
3
  import { reportRecoverable } from "./hydration.js";
3
4
  import { isDangerousHtmlAttribute, isDangerousHtmlOptIn, isUnsafeUrlAttribute, isUrlAttribute, } from "./url-safety.js";
5
+ import { serializeClientStyleValue, styleNameToCssName, } from "./dom-host-rules.js";
4
6
  export function applyProps(element, props, path, options) {
5
- const previous = getAppliedProps(element) ?? {
6
- props: {},
7
- listeners: new Map(),
8
- };
9
- const nextAttributeNames = collectAttributeNames(props);
10
7
  const preserveHydrationAttributes = options.preserveHydrationAttributes === true;
8
+ const previous = getAppliedProps(element);
9
+ if (previous === undefined && !preserveHydrationAttributes) {
10
+ if (applyInitialRowProps(element, props)) {
11
+ setAppliedProps(element, { props });
12
+ return;
13
+ }
14
+ setAppliedProps(element, {
15
+ props,
16
+ ...applyInitialProps(element, props, path, options),
17
+ });
18
+ return;
19
+ }
20
+ const previousProps = previous?.props ?? {};
21
+ let listeners = previous?.listeners;
22
+ const previousAttributeNames = collectAttributeNames(previousProps);
23
+ const nextAttributeNames = collectAttributeNames(props);
11
24
  if (!preserveHydrationAttributes) {
12
- for (const attribute of Array.from(element.attributes)) {
13
- if (!nextAttributeNames.has(attribute.name)) {
14
- reportRecoverable(options, "attribute", path, new Error(`Hydration attribute mismatch: ${attribute.name}.`));
15
- element.removeAttribute(attribute.name);
25
+ for (const attributeName of previousAttributeNames) {
26
+ if (!nextAttributeNames.has(attributeName)) {
27
+ if (attributeName === "style") {
28
+ removePreviousStyle(element, previousProps.style, path, options);
29
+ continue;
30
+ }
31
+ if (element.hasAttribute(attributeName)) {
32
+ reportRecoverable(options, "attribute", path, new Error(`Hydration attribute mismatch: ${attributeName}.`));
33
+ element.removeAttribute(attributeName);
34
+ }
16
35
  }
17
36
  }
18
37
  }
19
- for (const [name, appliedListener] of previous.listeners) {
20
- const nextValue = props[name];
21
- if (nextValue !== appliedListener.handler) {
22
- previous.listeners.delete(name);
38
+ if (listeners !== undefined) {
39
+ for (const [name, appliedListener] of listeners) {
40
+ const nextValue = props[name];
41
+ if (nextValue !== appliedListener.handler) {
42
+ listeners.delete(name);
43
+ }
23
44
  }
24
45
  }
25
46
  for (const [name, value] of Object.entries(props)) {
@@ -38,22 +59,27 @@ export function applyProps(element, props, path, options) {
38
59
  continue;
39
60
  }
40
61
  if (name === "style") {
41
- applyStyle(element, previous.props[name], value, path, options);
62
+ applyStyle(element, previousProps[name], value, path, options);
42
63
  continue;
43
64
  }
44
65
  if (/^on[A-Z]/.test(name) && typeof value === "function") {
45
- if (previous.listeners.get(name)?.handler === value) {
66
+ if (listeners?.get(name)?.handler === value) {
46
67
  continue;
47
68
  }
48
69
  const handler = value;
49
70
  for (const eventName of toEventNames(name)) {
50
71
  ensureDelegatedEventListener(options.eventRoot ?? element, eventName);
51
72
  }
52
- previous.listeners.set(name, { handler });
73
+ listeners ??= new Map();
74
+ listeners.set(name, { handler });
75
+ continue;
76
+ }
77
+ const attributeName = toDomAttributeName(name);
78
+ if (typeof value === "boolean" && isBooleanishStringAttribute(attributeName)) {
79
+ applyAttribute(element, attributeName, value ? "true" : "false", path, options);
53
80
  continue;
54
81
  }
55
82
  if (typeof value === "boolean") {
56
- const attributeName = toDomAttributeName(name);
57
83
  if (element.hasAttribute(attributeName) !== value) {
58
84
  if (!preserveHydrationAttributes) {
59
85
  reportRecoverable(options, "attribute", path, new Error(`Hydration attribute mismatch: ${attributeName}.`));
@@ -62,18 +88,102 @@ export function applyProps(element, props, path, options) {
62
88
  if (preserveHydrationAttributes) {
63
89
  continue;
64
90
  }
65
- element[name] = value;
91
+ if (element[name] !== value) {
92
+ element[name] = value;
93
+ }
66
94
  if (value) {
67
- element.setAttribute(attributeName, "");
95
+ if (!element.hasAttribute(attributeName)) {
96
+ element.setAttribute(attributeName, "");
97
+ }
68
98
  }
69
- else {
99
+ else if (element.hasAttribute(attributeName)) {
70
100
  element.removeAttribute(attributeName);
71
101
  }
72
102
  continue;
73
103
  }
74
104
  applyAttribute(element, toDomAttributeName(name), value, path, options);
75
105
  }
76
- setAppliedProps(element, { props: { ...props }, listeners: previous.listeners });
106
+ setAppliedProps(element, {
107
+ props,
108
+ ...(listeners === undefined ? {} : { listeners }),
109
+ });
110
+ }
111
+ function applyInitialProps(element, props, path, options) {
112
+ let listeners;
113
+ for (const name in props) {
114
+ if (!Object.prototype.hasOwnProperty.call(props, name)) {
115
+ continue;
116
+ }
117
+ const value = props[name];
118
+ if (name === "children" || name === "ref" || name === "key") {
119
+ continue;
120
+ }
121
+ if (applyFormValueProp(element, name, value, path, options)) {
122
+ continue;
123
+ }
124
+ if (value === null || value === undefined) {
125
+ continue;
126
+ }
127
+ if (name === "className") {
128
+ applyAttribute(element, "class", value, path, options);
129
+ continue;
130
+ }
131
+ if (name === "htmlFor") {
132
+ applyAttribute(element, "for", value, path, options);
133
+ continue;
134
+ }
135
+ if (name === "style") {
136
+ applyStyle(element, undefined, value, path, options);
137
+ continue;
138
+ }
139
+ if (/^on[A-Z]/.test(name) && typeof value === "function") {
140
+ const handler = value;
141
+ for (const eventName of toEventNames(name)) {
142
+ ensureDelegatedEventListener(options.eventRoot ?? element, eventName);
143
+ }
144
+ listeners ??= new Map();
145
+ listeners.set(name, { handler });
146
+ continue;
147
+ }
148
+ const attributeName = toDomAttributeName(name);
149
+ if (typeof value === "boolean" && isBooleanishStringAttribute(attributeName)) {
150
+ applyAttribute(element, attributeName, value ? "true" : "false", path, options);
151
+ continue;
152
+ }
153
+ if (value === false) {
154
+ continue;
155
+ }
156
+ if (typeof value === "boolean") {
157
+ if (element[name] !== value) {
158
+ element[name] = value;
159
+ }
160
+ if (value) {
161
+ if (!element.hasAttribute(attributeName)) {
162
+ element.setAttribute(attributeName, "");
163
+ }
164
+ }
165
+ else if (element.hasAttribute(attributeName)) {
166
+ element.removeAttribute(attributeName);
167
+ }
168
+ continue;
169
+ }
170
+ applyAttribute(element, attributeName, value, path, options);
171
+ }
172
+ return listeners === undefined ? {} : { listeners };
173
+ }
174
+ function applyInitialRowProps(element, props) {
175
+ const meta = props[HOST_OWN_PROPS_META];
176
+ if (meta === undefined) {
177
+ return false;
178
+ }
179
+ element.setAttribute("data-key", String(props["data-key"]));
180
+ if ((meta & 1) !== 0) {
181
+ element.setAttribute("class", "selected");
182
+ }
183
+ if ((meta & 2) !== 0) {
184
+ element.setAttribute("data-selected", "true");
185
+ }
186
+ return true;
77
187
  }
78
188
  export function applyPostChildFormProps(element, props) {
79
189
  const value = props.value ?? props.defaultValue;
@@ -138,12 +248,16 @@ function applyAttribute(element, name, value, path, options) {
138
248
  }
139
249
  return;
140
250
  }
141
- if (element.getAttribute(name) !== stringValue && !preserveHydrationAttributes) {
251
+ const currentValue = element.getAttribute(name);
252
+ if (currentValue !== stringValue && !preserveHydrationAttributes) {
142
253
  reportRecoverable(options, "attribute", path, new Error(`Hydration attribute mismatch: ${name}.`));
143
254
  }
144
255
  if (preserveHydrationAttributes) {
145
256
  return;
146
257
  }
258
+ if (currentValue === stringValue) {
259
+ return;
260
+ }
147
261
  element.setAttribute(name, stringValue);
148
262
  }
149
263
  function applyFormValueProp(element, name, value, path, options) {
@@ -188,23 +302,37 @@ function applyFormValueProp(element, name, value, path, options) {
188
302
  return false;
189
303
  }
190
304
  function applyStyle(element, previousStyle, nextStyle, path, options) {
305
+ if (options.preserveHydrationAttributes === true) {
306
+ return;
307
+ }
308
+ removePreviousStyle(element, previousStyle, path, options);
309
+ if (isStyleObject(nextStyle)) {
310
+ for (const [name, value] of Object.entries(nextStyle)) {
311
+ if (value === null || value === undefined || value === false) {
312
+ continue;
313
+ }
314
+ element.style.setProperty(styleNameToCssName(name), serializeClientStyleValue(name, value));
315
+ }
316
+ return;
317
+ }
318
+ if (nextStyle !== undefined && nextStyle !== null && nextStyle !== false) {
319
+ element.removeAttribute("style");
320
+ }
321
+ }
322
+ function removePreviousStyle(element, previousStyle, path, options) {
191
323
  if (options.preserveHydrationAttributes === true) {
192
324
  return;
193
325
  }
194
326
  if (isStyleObject(previousStyle)) {
195
327
  for (const name of Object.keys(previousStyle)) {
196
- element.style.removeProperty(name);
328
+ element.style.removeProperty(styleNameToCssName(name));
197
329
  }
330
+ return;
198
331
  }
199
- else if (element.hasAttribute("style")) {
332
+ if (previousStyle !== undefined && element.hasAttribute("style")) {
200
333
  reportRecoverable(options, "attribute", path, new Error("Hydration attribute mismatch: style."));
201
334
  element.removeAttribute("style");
202
335
  }
203
- if (isStyleObject(nextStyle)) {
204
- Object.assign(element.style, nextStyle);
205
- return;
206
- }
207
- element.removeAttribute("style");
208
336
  }
209
337
  function collectAttributeNames(props) {
210
338
  const names = new Set();
@@ -213,11 +341,14 @@ function collectAttributeNames(props) {
213
341
  name === "ref" ||
214
342
  name === "key" ||
215
343
  /^on[A-Z]/.test(name) ||
216
- value === false ||
217
344
  value === null ||
218
345
  value === undefined) {
219
346
  continue;
220
347
  }
348
+ const attributeName = toDomAttributeName(name);
349
+ if (value === false && !isBooleanishStringAttribute(attributeName)) {
350
+ continue;
351
+ }
221
352
  if (name === "defaultValue") {
222
353
  names.add("value");
223
354
  continue;
@@ -226,14 +357,23 @@ function collectAttributeNames(props) {
226
357
  names.add("checked");
227
358
  continue;
228
359
  }
229
- names.add(toDomAttributeName(name));
360
+ names.add(attributeName);
230
361
  }
231
362
  return names;
232
363
  }
364
+ function isBooleanishStringAttribute(name) {
365
+ const attributeName = toDomAttributeName(name).toLowerCase();
366
+ return attributeName.startsWith("aria-") || BOOLEANISH_STRING_ATTRIBUTES.has(attributeName);
367
+ }
368
+ const BOOLEANISH_STRING_ATTRIBUTES = new Set([
369
+ "contenteditable",
370
+ "draggable",
371
+ "spellcheck",
372
+ ]);
233
373
  function toDomAttributeName(name) {
234
- return HTML_ATTRIBUTE_ALIASES[name] ?? name;
374
+ return DOM_ATTRIBUTE_ALIASES[name] ?? name;
235
375
  }
236
- const HTML_ATTRIBUTE_ALIASES = {
376
+ const DOM_ATTRIBUTE_ALIASES = {
237
377
  acceptCharset: "accept-charset",
238
378
  autoFocus: "autofocus",
239
379
  autoPlay: "autoplay",
@@ -258,6 +398,62 @@ const HTML_ATTRIBUTE_ALIASES = {
258
398
  srcSet: "srcset",
259
399
  tabIndex: "tabindex",
260
400
  useMap: "usemap",
401
+ alignmentBaseline: "alignment-baseline",
402
+ baselineShift: "baseline-shift",
403
+ clipPath: "clip-path",
404
+ clipRule: "clip-rule",
405
+ colorInterpolation: "color-interpolation",
406
+ colorInterpolationFilters: "color-interpolation-filters",
407
+ colorProfile: "color-profile",
408
+ colorRendering: "color-rendering",
409
+ dominantBaseline: "dominant-baseline",
410
+ enableBackground: "enable-background",
411
+ fillOpacity: "fill-opacity",
412
+ fillRule: "fill-rule",
413
+ floodColor: "flood-color",
414
+ floodOpacity: "flood-opacity",
415
+ fontFamily: "font-family",
416
+ fontSize: "font-size",
417
+ fontSizeAdjust: "font-size-adjust",
418
+ fontStretch: "font-stretch",
419
+ fontStyle: "font-style",
420
+ fontVariant: "font-variant",
421
+ fontWeight: "font-weight",
422
+ glyphOrientationHorizontal: "glyph-orientation-horizontal",
423
+ glyphOrientationVertical: "glyph-orientation-vertical",
424
+ imageRendering: "image-rendering",
425
+ letterSpacing: "letter-spacing",
426
+ lightingColor: "lighting-color",
427
+ markerEnd: "marker-end",
428
+ markerMid: "marker-mid",
429
+ markerStart: "marker-start",
430
+ overlinePosition: "overline-position",
431
+ overlineThickness: "overline-thickness",
432
+ paintOrder: "paint-order",
433
+ pointerEvents: "pointer-events",
434
+ shapeRendering: "shape-rendering",
435
+ stopColor: "stop-color",
436
+ stopOpacity: "stop-opacity",
437
+ strikethroughPosition: "strikethrough-position",
438
+ strikethroughThickness: "strikethrough-thickness",
439
+ strokeDasharray: "stroke-dasharray",
440
+ strokeDashoffset: "stroke-dashoffset",
441
+ strokeLinecap: "stroke-linecap",
442
+ strokeLinejoin: "stroke-linejoin",
443
+ strokeMiterlimit: "stroke-miterlimit",
444
+ strokeOpacity: "stroke-opacity",
445
+ strokeWidth: "stroke-width",
446
+ textAnchor: "text-anchor",
447
+ textDecoration: "text-decoration",
448
+ textRendering: "text-rendering",
449
+ transformOrigin: "transform-origin",
450
+ underlinePosition: "underline-position",
451
+ underlineThickness: "underline-thickness",
452
+ unicodeBidi: "unicode-bidi",
453
+ vectorEffect: "vector-effect",
454
+ wordSpacing: "word-spacing",
455
+ writingMode: "writing-mode",
456
+ xHeight: "x-height",
261
457
  };
262
458
  function isStyleObject(value) {
263
459
  return typeof value === "object" && value !== null;