@opentf/web 0.4.0 → 0.6.0

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 (187) hide show
  1. package/README.md +30 -28
  2. package/components/Link.jsx +43 -0
  3. package/core/errors.js +48 -0
  4. package/core/reactive.js +190 -0
  5. package/core/signals.js +207 -0
  6. package/index.js +13 -0
  7. package/package.json +27 -18
  8. package/runtime/clipboard.js +47 -0
  9. package/runtime/code-block.js +47 -0
  10. package/runtime/context.js +99 -0
  11. package/runtime/dom.js +368 -0
  12. package/runtime/error-boundary.js +109 -0
  13. package/runtime/events.js +17 -0
  14. package/runtime/hydrate.js +121 -0
  15. package/runtime/index.js +22 -0
  16. package/runtime/lifecycle.js +9 -0
  17. package/runtime/mount.js +49 -0
  18. package/runtime/portal.js +59 -0
  19. package/runtime/raw-html.js +33 -0
  20. package/runtime/router.js +392 -0
  21. package/server/builtins.js +16 -0
  22. package/server/head.js +191 -0
  23. package/server/index.js +8 -0
  24. package/server/render.js +98 -0
  25. package/server/ssg-runtime.js +112 -0
  26. package/dist/CHANGELOG.md +0 -49
  27. package/dist/README.md +0 -41
  28. package/dist/babel.config.cjs +0 -6
  29. package/dist/compiler/babel-plugin.cjs +0 -798
  30. package/dist/core/mount.js +0 -32
  31. package/dist/index.js +0 -7
  32. package/dist/node_modules/@babel/core/LICENSE +0 -22
  33. package/dist/node_modules/@babel/core/README.md +0 -19
  34. package/dist/node_modules/@babel/core/lib/config/cache-contexts.js +0 -5
  35. package/dist/node_modules/@babel/core/lib/config/cache-contexts.js.map +0 -1
  36. package/dist/node_modules/@babel/core/lib/config/caching.js +0 -261
  37. package/dist/node_modules/@babel/core/lib/config/caching.js.map +0 -1
  38. package/dist/node_modules/@babel/core/lib/config/config-chain.js +0 -469
  39. package/dist/node_modules/@babel/core/lib/config/config-chain.js.map +0 -1
  40. package/dist/node_modules/@babel/core/lib/config/config-descriptors.js +0 -190
  41. package/dist/node_modules/@babel/core/lib/config/config-descriptors.js.map +0 -1
  42. package/dist/node_modules/@babel/core/lib/config/files/configuration.js +0 -290
  43. package/dist/node_modules/@babel/core/lib/config/files/configuration.js.map +0 -1
  44. package/dist/node_modules/@babel/core/lib/config/files/import.cjs +0 -6
  45. package/dist/node_modules/@babel/core/lib/config/files/import.cjs.map +0 -1
  46. package/dist/node_modules/@babel/core/lib/config/files/index-browser.js +0 -58
  47. package/dist/node_modules/@babel/core/lib/config/files/index-browser.js.map +0 -1
  48. package/dist/node_modules/@babel/core/lib/config/files/index.js +0 -78
  49. package/dist/node_modules/@babel/core/lib/config/files/index.js.map +0 -1
  50. package/dist/node_modules/@babel/core/lib/config/files/module-types.js +0 -203
  51. package/dist/node_modules/@babel/core/lib/config/files/module-types.js.map +0 -1
  52. package/dist/node_modules/@babel/core/lib/config/files/package.js +0 -61
  53. package/dist/node_modules/@babel/core/lib/config/files/package.js.map +0 -1
  54. package/dist/node_modules/@babel/core/lib/config/files/plugins.js +0 -220
  55. package/dist/node_modules/@babel/core/lib/config/files/plugins.js.map +0 -1
  56. package/dist/node_modules/@babel/core/lib/config/files/types.js +0 -5
  57. package/dist/node_modules/@babel/core/lib/config/files/types.js.map +0 -1
  58. package/dist/node_modules/@babel/core/lib/config/files/utils.js +0 -36
  59. package/dist/node_modules/@babel/core/lib/config/files/utils.js.map +0 -1
  60. package/dist/node_modules/@babel/core/lib/config/full.js +0 -312
  61. package/dist/node_modules/@babel/core/lib/config/full.js.map +0 -1
  62. package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js +0 -85
  63. package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js.map +0 -1
  64. package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js +0 -23
  65. package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js.map +0 -1
  66. package/dist/node_modules/@babel/core/lib/config/helpers/environment.js +0 -12
  67. package/dist/node_modules/@babel/core/lib/config/helpers/environment.js.map +0 -1
  68. package/dist/node_modules/@babel/core/lib/config/index.js +0 -87
  69. package/dist/node_modules/@babel/core/lib/config/index.js.map +0 -1
  70. package/dist/node_modules/@babel/core/lib/config/item.js +0 -67
  71. package/dist/node_modules/@babel/core/lib/config/item.js.map +0 -1
  72. package/dist/node_modules/@babel/core/lib/config/partial.js +0 -158
  73. package/dist/node_modules/@babel/core/lib/config/partial.js.map +0 -1
  74. package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js +0 -38
  75. package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js.map +0 -1
  76. package/dist/node_modules/@babel/core/lib/config/plugin.js +0 -33
  77. package/dist/node_modules/@babel/core/lib/config/plugin.js.map +0 -1
  78. package/dist/node_modules/@babel/core/lib/config/printer.js +0 -113
  79. package/dist/node_modules/@babel/core/lib/config/printer.js.map +0 -1
  80. package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js +0 -41
  81. package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js.map +0 -1
  82. package/dist/node_modules/@babel/core/lib/config/resolve-targets.js +0 -61
  83. package/dist/node_modules/@babel/core/lib/config/resolve-targets.js.map +0 -1
  84. package/dist/node_modules/@babel/core/lib/config/util.js +0 -31
  85. package/dist/node_modules/@babel/core/lib/config/util.js.map +0 -1
  86. package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js +0 -277
  87. package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js.map +0 -1
  88. package/dist/node_modules/@babel/core/lib/config/validation/options.js +0 -187
  89. package/dist/node_modules/@babel/core/lib/config/validation/options.js.map +0 -1
  90. package/dist/node_modules/@babel/core/lib/config/validation/plugins.js +0 -67
  91. package/dist/node_modules/@babel/core/lib/config/validation/plugins.js.map +0 -1
  92. package/dist/node_modules/@babel/core/lib/config/validation/removed.js +0 -68
  93. package/dist/node_modules/@babel/core/lib/config/validation/removed.js.map +0 -1
  94. package/dist/node_modules/@babel/core/lib/errors/config-error.js +0 -18
  95. package/dist/node_modules/@babel/core/lib/errors/config-error.js.map +0 -1
  96. package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js +0 -98
  97. package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js.map +0 -1
  98. package/dist/node_modules/@babel/core/lib/gensync-utils/async.js +0 -90
  99. package/dist/node_modules/@babel/core/lib/gensync-utils/async.js.map +0 -1
  100. package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js +0 -31
  101. package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js.map +0 -1
  102. package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js +0 -58
  103. package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js.map +0 -1
  104. package/dist/node_modules/@babel/core/lib/index.js +0 -230
  105. package/dist/node_modules/@babel/core/lib/index.js.map +0 -1
  106. package/dist/node_modules/@babel/core/lib/parse.js +0 -45
  107. package/dist/node_modules/@babel/core/lib/parse.js.map +0 -1
  108. package/dist/node_modules/@babel/core/lib/parser/index.js +0 -85
  109. package/dist/node_modules/@babel/core/lib/parser/index.js.map +0 -1
  110. package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js +0 -337
  111. package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js.map +0 -1
  112. package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js +0 -144
  113. package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js.map +0 -1
  114. package/dist/node_modules/@babel/core/lib/transform-ast.js +0 -48
  115. package/dist/node_modules/@babel/core/lib/transform-ast.js.map +0 -1
  116. package/dist/node_modules/@babel/core/lib/transform-file-browser.js +0 -23
  117. package/dist/node_modules/@babel/core/lib/transform-file-browser.js.map +0 -1
  118. package/dist/node_modules/@babel/core/lib/transform-file.js +0 -40
  119. package/dist/node_modules/@babel/core/lib/transform-file.js.map +0 -1
  120. package/dist/node_modules/@babel/core/lib/transform.js +0 -47
  121. package/dist/node_modules/@babel/core/lib/transform.js.map +0 -1
  122. package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js +0 -84
  123. package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js.map +0 -1
  124. package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs +0 -4
  125. package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs.map +0 -1
  126. package/dist/node_modules/@babel/core/lib/transformation/file/file.js +0 -204
  127. package/dist/node_modules/@babel/core/lib/transformation/file/file.js.map +0 -1
  128. package/dist/node_modules/@babel/core/lib/transformation/file/generate.js +0 -84
  129. package/dist/node_modules/@babel/core/lib/transformation/file/generate.js.map +0 -1
  130. package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js +0 -37
  131. package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js.map +0 -1
  132. package/dist/node_modules/@babel/core/lib/transformation/index.js +0 -90
  133. package/dist/node_modules/@babel/core/lib/transformation/index.js.map +0 -1
  134. package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js +0 -127
  135. package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js.map +0 -1
  136. package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js +0 -59
  137. package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js.map +0 -1
  138. package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js +0 -48
  139. package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js.map +0 -1
  140. package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js +0 -54
  141. package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js.map +0 -1
  142. package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js +0 -1042
  143. package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js.map +0 -1
  144. package/dist/node_modules/@babel/core/package.json +0 -82
  145. package/dist/node_modules/@babel/core/src/config/files/index-browser.ts +0 -115
  146. package/dist/node_modules/@babel/core/src/config/files/index.ts +0 -30
  147. package/dist/node_modules/@babel/core/src/config/resolve-targets-browser.ts +0 -42
  148. package/dist/node_modules/@babel/core/src/config/resolve-targets.ts +0 -54
  149. package/dist/node_modules/@babel/core/src/transform-file-browser.ts +0 -33
  150. package/dist/node_modules/@babel/core/src/transform-file.ts +0 -56
  151. package/dist/node_modules/@babel/helper-module-imports/LICENSE +0 -22
  152. package/dist/node_modules/@babel/helper-module-imports/README.md +0 -19
  153. package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js +0 -122
  154. package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js.map +0 -1
  155. package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js +0 -304
  156. package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js.map +0 -1
  157. package/dist/node_modules/@babel/helper-module-imports/lib/index.js +0 -37
  158. package/dist/node_modules/@babel/helper-module-imports/lib/index.js.map +0 -1
  159. package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js +0 -11
  160. package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js.map +0 -1
  161. package/dist/node_modules/@babel/helper-module-imports/package.json +0 -28
  162. package/dist/node_modules/@babel/plugin-syntax-jsx/LICENSE +0 -22
  163. package/dist/node_modules/@babel/plugin-syntax-jsx/README.md +0 -19
  164. package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js +0 -21
  165. package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js.map +0 -1
  166. package/dist/node_modules/@babel/plugin-syntax-jsx/package.json +0 -33
  167. package/dist/node_modules/@preact/signals-core/CHANGELOG.md +0 -331
  168. package/dist/node_modules/@preact/signals-core/LICENSE +0 -21
  169. package/dist/node_modules/@preact/signals-core/README.md +0 -258
  170. package/dist/node_modules/@preact/signals-core/dist/signals-core.d.ts +0 -149
  171. package/dist/node_modules/@preact/signals-core/dist/signals-core.js +0 -1
  172. package/dist/node_modules/@preact/signals-core/dist/signals-core.js.map +0 -1
  173. package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js +0 -1
  174. package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js.map +0 -1
  175. package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs +0 -1
  176. package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs.map +0 -1
  177. package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js +0 -1
  178. package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js.map +0 -1
  179. package/dist/node_modules/@preact/signals-core/package.json +0 -48
  180. package/dist/node_modules/@preact/signals-core/src/index.ts +0 -1121
  181. package/dist/package.json +0 -30
  182. package/dist/router/Link.js +0 -73
  183. package/dist/router/index.js +0 -216
  184. package/dist/runtime/For.js +0 -16
  185. package/dist/runtime/dom.js +0 -73
  186. package/dist/runtime/lifecycle.js +0 -25
  187. package/dist/runtime/props.js +0 -103
@@ -1,798 +0,0 @@
1
- const { addNamed } = require("@babel/helper-module-imports");
2
- const path = require("path");
3
-
4
- const SVG_TAGS = ['svg', 'path', 'polyline', 'line', 'circle', 'rect', 'ellipse', 'polygon', 'g', 'text', 'tspan', 'defs', 'lineargradient', 'stop'];
5
-
6
- const SVG_CAMEL_CASE = [
7
- "allowReorder", "attributeName", "attributeType", "autoReverse", "baseFrequency", "baseProfile", "calcMode", "clipPathUnits", "contentScriptType", "contentStyleType", "cursor", "cx", "cy", "d", "diffuseConstant", "direction", "display", "divisor", "dominantBaseline", "dur", "dx", "dy", "edgeMode", "elevation", "enableBackground", "externalResourcesRequired", "fill", "fillOpacity", "fillRule", "filter", "filterRes", "filterUnits", "floodColor", "floodOpacity", "focusable", "fontFamily", "fontSize", "fontSizeAdjust", "fontStretch", "fontStyle", "fontVariant", "fontWeight", "format", "from", "fx", "fy", "g1", "g2", "glyphName", "glyphOrientationHorizontal", "glyphOrientationVertical", "glyphRef", "gradientTransform", "gradientUnits", "hanging", "horizAdvX", "horizOriginX", "ideographic", "imageRendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kernelMatrix", "kernelUnitLength", "kerning", "keyPoints", "keySplines", "keyTimes", "lengthAdjust", "letterSpacing", "lightingColor", "limitingConeAngle", "local", "markerEnd", "markerMid", "markerStart", "markerHeight", "markerUnits", "markerWidth", "mask", "maskContentUnits", "maskUnits", "mathematical", "mode", "numOctaves", "offset", "opacity", "operator", "order", "orient", "orientation", "origin", "overflow", "overlinePosition", "overlineThickness", "panose1", "paintOrder", "pathLength", "patternContentUnits", "patternTransform", "patternUnits", "pointerEvents", "points", "pointsAtX", "pointsAtY", "pointsAtZ", "preserveAlpha", "preserveAspectRatio", "primitiveUnits", "r", "radius", "refX", "refY", "renderingIntent", "repeatCount", "repeatDur", "requiredExtensions", "requiredFeatures", "restart", "result", "rotate", "rx", "ry", "scale", "seed", "shapeRendering", "slope", "spacing", "specularConstant", "specularExponent", "speed", "spreadMethod", "startOffset", "stdDeviation", "stemh", "stemv", "stitchTiles", "stopColor", "stopOpacity", "strikethroughPosition", "strikethroughThickness", "string", "stroke", "strokeDasharray", "strokeDashoffset", "strokeLinecap", "strokeLinejoin", "strokeMiterlimit", "strokeOpacity", "strokeWidth", "surfaceScale", "systemLanguage", "tableValues", "targetX", "targetY", "textAnchor", "textDecoration", "textLength", "textRendering", "to", "transform", "u1", "u2", "underlinePosition", "underlineThickness", "unicode", "unicodeBidi", "unicodeRange", "unitsPerEm", "vAlphabetic", "vHanging", "vIdeographic", "vMathematical", "values", "version", "vertAdvY", "vertOriginX", "vertOriginY", "viewBox", "viewTarget", "visibility", "widths", "wordSpacing", "writingMode", "x", "x1", "x2", "xChannelSelector", "xHeight", "xlinkActuate", "xlinkArcrole", "xlinkHref", "xlinkRole", "xlinkShow", "xlinkTitle", "xlinkType", "xmlBase", "xmlLang", "xmlSpace", "y", "y1", "y2", "yChannelSelector", "z", "zoomAndPan"
8
- ];
9
-
10
- const IS_PROPERTY = ["className", "style", "value", "checked", "id", "title", "href", "src", "key"];
11
-
12
- const STANDARD_TAGS = ["div", "span", "p", "a", "ul", "li", "button", "input", "img", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "nav", "header", "footer", "main", "aside", "form", "label", "select", "option", "textarea", "table", "tr", "td", "th", "thead", "tbody", "tfoot", "canvas", "video", "audio", "svg"];
13
-
14
-
15
- module.exports = function (babel) {
16
- const { types: t } = babel;
17
-
18
- return {
19
- name: "web-compiler",
20
- visitor: {
21
- Program: {
22
- enter(p, state) {
23
- state.runtimeSource = state.opts.runtimeSource || "@opentf/web";
24
- state.importsNeeded = new Map();
25
- state.importSources = new Map();
26
- state.components = new Map(); // name -> { path, isDefault, isPage }
27
-
28
- const filename = state.filename || "";
29
- const pagePatterns = ["page.jsx", "page.tsx", "layout.jsx", "layout.tsx", "404.jsx", "404.tsx"];
30
- state.isPageFile = pagePatterns.some(p => filename.endsWith(p));
31
- },
32
- exit(p, state) {
33
- // Process all collected components
34
- state.components.forEach((info, name) => {
35
- const isRenderFn = state.isPageFile && info.isDefault;
36
- transformComponent(info.path, name, isRenderFn, t, state);
37
- });
38
- }
39
- },
40
-
41
- FunctionDeclaration(path, state) {
42
- if (!path.node.id) return;
43
- const name = path.node.id.name;
44
- if (/^[A-Z]/.test(name)) {
45
- // Check if this function actually uses JSX. If not, it's just a regular function, not a component.
46
- let hasJSX = false;
47
- path.traverse({
48
- JSXElement() { hasJSX = true; },
49
- JSXFragment() { hasJSX = true; }
50
- });
51
-
52
- if (!hasJSX) return;
53
-
54
- const propsNode = path.node.params[0];
55
- const observedAttributes = new Set();
56
-
57
- if (t.isObjectPattern(propsNode)) {
58
- propsNode.properties.forEach(prop => {
59
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
60
- observedAttributes.add(prop.key.name);
61
- }
62
- });
63
- }
64
-
65
- if (state.components.has(name)) {
66
- const info = state.components.get(name);
67
- info.observedAttributes = observedAttributes;
68
- info.path = path;
69
- } else {
70
- state.components.set(name, { path, isDefault: false, observedAttributes });
71
- }
72
- }
73
- },
74
-
75
- ExportDefaultDeclaration(path, state) {
76
- const decl = path.node.declaration;
77
- if (t.isFunctionDeclaration(decl)) {
78
- let hasJSX = false;
79
- path.traverse({
80
- JSXElement() { hasJSX = true; },
81
- JSXFragment() { hasJSX = true; }
82
- });
83
-
84
- if (!hasJSX) return;
85
-
86
- if (decl.id) {
87
- const name = decl.id.name;
88
- if (state.components.has(name)) {
89
- state.components.get(name).isDefault = true;
90
- } else {
91
- state.components.set(name, { path: path.get("declaration"), isDefault: true });
92
- }
93
- } else {
94
- // Anonymous function
95
- state.components.set("_default", { path: path.get("declaration"), isDefault: true });
96
- }
97
- }
98
- },
99
-
100
- ImportDeclaration(path, state) {
101
- const source = path.node.source.value;
102
- path.node.specifiers.forEach(spec => {
103
- if (t.isImportDefaultSpecifier(spec) || t.isImportSpecifier(spec)) {
104
- state.importSources.set(spec.local.name, source);
105
- }
106
- });
107
- },
108
-
109
- CallExpression(path, state) {
110
- if (t.isIdentifier(path.node.callee)) {
111
- const name = path.node.callee.name;
112
- const getImport = (importName, source) => {
113
- const key = `${importName}:${source}`;
114
- if (!state.importsNeeded.has(key)) {
115
- state.importsNeeded.set(key, addNamed(path, importName, source));
116
- }
117
- return state.importsNeeded.get(key);
118
- };
119
-
120
- if (name === "$state" || name === "$derived" || name === "$ref") {
121
- // Track this variable as a state variable in the current scope
122
- const parent = path.findParent(p => p.isVariableDeclarator());
123
- if (parent && t.isIdentifier(parent.node.id)) {
124
- if (!state.stateVars) state.stateVars = new Set();
125
- state.stateVars.add(parent.node.id.name);
126
- }
127
- if (name === "$state") {
128
- path.get("callee").replaceWith(getImport("signal", state.runtimeSource));
129
- } else if (name === "$derived") {
130
- path.get("callee").replaceWith(getImport("computed", state.runtimeSource));
131
- } else if (name === "$ref") {
132
- // Track this variable as a ref variable
133
- const parent = path.findParent(p => p.isVariableDeclarator());
134
- if (parent && t.isIdentifier(parent.node.id)) {
135
- if (!state.refVars) state.refVars = new Set();
136
- state.refVars.add(parent.node.id.name);
137
- if (!state.stateVars) state.stateVars = new Set();
138
- state.stateVars.add(parent.node.id.name);
139
- }
140
- path.get("callee").replaceWith(getImport("signal", state.runtimeSource));
141
- }
142
- } else if (name === "$effect") {
143
- path.get("callee").replaceWith(getImport("effect", state.runtimeSource));
144
- } else if (name === "$expose") {
145
- // $expose({ a, b }) -> Object.assign(this, { a, b })
146
- path.get("callee").replaceWith(t.memberExpression(t.identifier("Object"), t.identifier("assign")));
147
- path.node.arguments.unshift(t.thisExpression());
148
- } else if (name === "onMount") {
149
- path.get("callee").replaceWith(getImport("onMount", state.runtimeSource));
150
- } else if (name === "onCleanup") {
151
- path.get("callee").replaceWith(getImport("onCleanup", state.runtimeSource));
152
- } else if (name === "$renderDynamic") {
153
- path.get("callee").replaceWith(getImport("renderDynamic", state.runtimeSource));
154
- }
155
- }
156
- },
157
-
158
- Identifier(path, state) {
159
- if (!state.stateVars || !state.stateVars.has(path.node.name)) return;
160
- if (path.node._processed) return;
161
-
162
- // Strictly forbid manual .value access on $state variables
163
- if (path.parentPath.isMemberExpression() && !path.parentPath.node.computed) {
164
- if (t.isIdentifier(path.parentPath.node.property, { name: "value" })) {
165
- throw path.parentPath.buildCodeFrameError(
166
- `Manual .value access is forbidden for variables declared with $state. The Web App Framework compiler handles this automatically. Remove the .value from '${path.node.name}.value'.`
167
- );
168
- }
169
- if (path.parentPath.node.property === path.node) return;
170
- }
171
-
172
- if (path.parentPath.isVariableDeclarator() && path.parentPath.node.id === path.node) return;
173
- if (path.parentPath.isObjectProperty() && path.parentPath.node.key === path.node && !path.parentPath.node.computed) return;
174
- if (path.parentPath.isClassProperty() && path.parentPath.node.key === path.node) return;
175
- if (path.parentPath.isClassMethod() && path.parentPath.node.key === path.node) return;
176
-
177
- const innerId = t.identifier(path.node.name);
178
- innerId._processed = true;
179
- const newNode = t.memberExpression(innerId, t.identifier("value"));
180
- path.replaceWith(newNode);
181
- },
182
-
183
- AssignmentExpression(path, state) {
184
- if (t.isIdentifier(path.node.left) && state.stateVars?.has(path.node.left.name)) {
185
- const innerId = t.identifier(path.node.left.name);
186
- innerId._processed = true;
187
- path.node.left = t.memberExpression(innerId, t.identifier("value"));
188
- }
189
- },
190
-
191
- UpdateExpression(path, state) {
192
- if (t.isIdentifier(path.node.argument) && state.stateVars?.has(path.node.argument.name)) {
193
- const innerId = t.identifier(path.node.argument.name);
194
- innerId._processed = true;
195
- path.node.argument = t.memberExpression(innerId, t.identifier("value"));
196
- }
197
- },
198
-
199
- JSXElement(path, state) {
200
- handleJSXVisitor(path, state, t);
201
- },
202
-
203
- JSXFragment(path, state) {
204
- handleJSXVisitor(path, state, t);
205
- }
206
- }
207
- };
208
-
209
- function handleJSXVisitor(path, state, t) {
210
- if (path.node._processed) return;
211
- const getImport = (importName, source) => {
212
- const key = `${importName}:${source}`;
213
- if (!state.importsNeeded.has(key)) {
214
- state.importsNeeded.set(key, addNamed(path, importName, source));
215
- }
216
- return state.importsNeeded.get(key);
217
- };
218
-
219
- const { statements, rootId, signals } = transformJSX(path.node, t, state, getImport, path);
220
-
221
- // Propagate signals to parent component for observedAttributes
222
- const parentFunc = path.findParent(p =>
223
- p.isFunctionDeclaration() && (
224
- /^[A-Z]/.test(p.node.id?.name) ||
225
- (p.parentPath.isExportDefaultDeclaration() && state.isPageFile)
226
- )
227
- );
228
-
229
- if (parentFunc) {
230
- let compName = parentFunc.node.id?.name;
231
- if (!compName && parentFunc.parentPath.isExportDefaultDeclaration()) {
232
- compName = "_default";
233
- }
234
- if (compName) {
235
- const info = state.components.get(compName);
236
- if (info) {
237
- if (!info.observedAttributes) info.observedAttributes = new Set();
238
- signals.forEach(s => info.observedAttributes.add(s));
239
- }
240
- }
241
- }
242
-
243
- const iife = t.callExpression(t.arrowFunctionExpression([], t.blockStatement([
244
- ...statements,
245
- t.returnStatement(rootId)
246
- ])), []);
247
- iife._processed = true;
248
- path.replaceWith(iife);
249
- }
250
-
251
- function transformComponent(componentPath, name, isRenderFn, t, state) {
252
- const node = componentPath.node;
253
- const body = node.body;
254
-
255
- const getImport = (importName, source) => {
256
- const key = `${importName}:${source}`;
257
- if (!state.importsNeeded.has(key)) {
258
- state.importsNeeded.set(key, addNamed(componentPath, importName, source));
259
- }
260
- return state.importsNeeded.get(key);
261
- };
262
-
263
- let jsxNode = null;
264
- const originalStatements = [];
265
-
266
- body.body.forEach(stmt => {
267
- if (t.isReturnStatement(stmt)) {
268
- jsxNode = stmt.argument;
269
- } else {
270
- originalStatements.push(stmt);
271
- }
272
- });
273
-
274
- if (!jsxNode) return;
275
-
276
- const res = transformJSX(jsxNode, t, state, getImport, componentPath);
277
- let statements = res.statements;
278
- let rootId = res.rootId;
279
- let signals = res.signals;
280
-
281
- // Capture the root element in a variable to avoid re-evaluating the IIFE if it is one
282
- const rootVar = t.identifier("rootElement");
283
- statements.push(t.variableDeclaration("const", [t.variableDeclarator(rootVar, rootId)]));
284
- rootId = rootVar;
285
-
286
-
287
-
288
- const componentInfo = state.components.get(name);
289
-
290
- // Prop Transformation: If the component uses destructuring in params,
291
- // we convert it to use `props` and replace usages to maintain reactivity.
292
- if (!isRenderFn) {
293
- const propsNode = node.params[0];
294
- if (t.isObjectPattern(propsNode)) {
295
- const propNames = new Set();
296
- propsNode.properties.forEach(prop => {
297
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
298
- propNames.add(prop.key.name);
299
- }
300
- });
301
-
302
- node.params[0] = t.identifier("props");
303
-
304
- const replaceProps = (node) => {
305
- if (!node || typeof node !== "object") return;
306
-
307
- if (Array.isArray(node)) {
308
- for (let i = 0; i < node.length; i++) {
309
- const child = node[i];
310
- if (t.isIdentifier(child) && propNames.has(child.name)) {
311
- node[i] = t.memberExpression(t.identifier("props"), t.identifier(child.name));
312
- } else {
313
- replaceProps(child);
314
- }
315
- }
316
- return;
317
- }
318
-
319
- for (const key in node) {
320
- const child = node[key];
321
- if (t.isIdentifier(child) && propNames.has(child.name)) {
322
- // Only replace if it's NOT a property key in an object or member expression
323
- if (t.isObjectProperty(node) && key === "key" && !node.computed) continue;
324
- if (t.isMemberExpression(node) && key === "property" && !node.computed) continue;
325
- if (t.isClassMethod(node) && key === "key") continue;
326
- if (t.isJSXAttribute(node) && key === "name") continue;
327
-
328
- node[key] = t.memberExpression(t.identifier("props"), t.identifier(child.name));
329
- } else {
330
- replaceProps(child);
331
- }
332
- }
333
- };
334
-
335
- replaceProps(originalStatements);
336
- replaceProps(statements);
337
- }
338
- }
339
-
340
-
341
-
342
- const allSignals = new Set([...signals, ...(componentInfo?.observedAttributes || [])]);
343
-
344
-
345
- if (isRenderFn) {
346
- // ... (rest of render logic remains same)
347
- // Transform to export function render(root) { ... }
348
- const renderFn = t.functionDeclaration(
349
- t.identifier("render"),
350
- [t.identifier("root"), t.identifier("props")],
351
- t.blockStatement([
352
- ...(t.isObjectPattern(node.params[0]) ? [t.variableDeclaration("const", [t.variableDeclarator(node.params[0], t.identifier("props"))])] : []),
353
- ...originalStatements,
354
- ...statements,
355
- t.expressionStatement(
356
- t.callExpression(
357
- t.memberExpression(t.identifier("root"), t.identifier("appendChild")),
358
- [rootId]
359
- )
360
- )
361
- ])
362
- );
363
-
364
- const parent = componentPath.parentPath;
365
- if (parent.isExportDefaultDeclaration()) {
366
- parent.replaceWith(t.exportNamedDeclaration(renderFn));
367
- } else {
368
- componentPath.replaceWith(renderFn);
369
- }
370
- } else {
371
- // Transform to Web Component Class
372
- const tagName = "web-" + name.toLowerCase();
373
- const observedAttributes = Array.from(allSignals);
374
- const signalId = getImport("signal", state.runtimeSource);
375
- const createPropsProxyId = getImport("createPropsProxy", state.runtimeSource);
376
- const classId = t.identifier(name + "Element");
377
-
378
- const classDecl = t.classDeclaration(
379
- classId,
380
- t.identifier("HTMLElement"),
381
- t.classBody([
382
- t.classProperty(t.identifier("observedAttributes"), t.arrayExpression(observedAttributes.map(s => t.stringLiteral(s))), null, null, false, true),
383
- // Add getters and setters for each observed attribute
384
- ...observedAttributes.map(s => t.classMethod(
385
- "set",
386
- t.identifier(s),
387
- [t.identifier("val")],
388
- t.blockStatement([
389
- t.expressionStatement(t.assignmentExpression(
390
- "=",
391
- t.memberExpression(
392
- t.memberExpression(
393
- t.memberExpression(t.thisExpression(), t.identifier("_propsSignals")),
394
- t.stringLiteral(s),
395
- true
396
- ),
397
- t.identifier("value")
398
- ),
399
- t.identifier("val")
400
- )),
401
- // Sync back to native attributes for class and style
402
- ...(s === "className" ? [
403
- t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier("setAttribute")), [t.stringLiteral("class"), t.identifier("val")]))
404
- ] : s === "style" ? [
405
- t.ifStatement(
406
- t.logicalExpression("&&", t.identifier("val"), t.binaryExpression("===", t.unaryExpression("typeof", t.identifier("val"), false), t.stringLiteral("object"))),
407
- t.expressionStatement(t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [t.memberExpression(t.thisExpression(), t.identifier("style")), t.identifier("val")]))
408
- )
409
- ] : [])
410
- ])
411
- )),
412
- ...observedAttributes.map(s => t.classMethod(
413
- "get",
414
- t.identifier(s),
415
- [],
416
- t.blockStatement([
417
- t.returnStatement(t.memberExpression(
418
- t.memberExpression(
419
- t.memberExpression(t.thisExpression(), t.identifier("_propsSignals")),
420
- t.stringLiteral(s),
421
- true
422
- ),
423
- t.identifier("value")
424
- ))
425
- ])
426
- )),
427
- t.classMethod("constructor", t.identifier("constructor"), [], t.blockStatement([
428
- t.expressionStatement(t.callExpression(t.super(), [])),
429
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.thisExpression(), t.identifier("_propsSignals")), t.objectExpression(
430
- observedAttributes.map(s => t.objectProperty(t.identifier(s), t.callExpression(signalId, [t.nullLiteral()])))
431
- )))
432
- ])),
433
- t.classMethod("method", t.identifier("attributeChangedCallback"), [t.identifier("name"), t.identifier("_"), t.identifier("value")], t.blockStatement([
434
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier("_propsSignals")), t.identifier("name"), true), t.identifier("value")), t.identifier("value")))
435
- ])),
436
- t.classMethod("method", t.identifier("connectedCallback"), [], t.blockStatement([
437
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.thisExpression(), t.identifier("_onMounts")), t.arrayExpression([]))),
438
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.thisExpression(), t.identifier("_onCleanups")), t.arrayExpression([]))),
439
- t.variableDeclaration("const", [t.variableDeclarator(t.identifier("props"), t.callExpression(createPropsProxyId, [t.thisExpression()]))]),
440
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.thisExpression(), t.identifier("_children")), t.callExpression(t.memberExpression(t.identifier("Array"), t.identifier("from")), [t.memberExpression(t.thisExpression(), t.identifier("childNodes"))]))),
441
- t.whileStatement(t.memberExpression(t.thisExpression(), t.identifier("firstChild")), t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier("removeChild")), [t.memberExpression(t.thisExpression(), t.identifier("firstChild"))]))),
442
-
443
- // Wrap setup in withInstance(this, () => { ... })
444
- t.expressionStatement(t.callExpression(getImport("withInstance", state.runtimeSource), [
445
- t.thisExpression(),
446
- t.arrowFunctionExpression([], t.blockStatement([
447
- ...originalStatements,
448
- ...statements,
449
- t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier("appendChild")), [rootId]))
450
- ]))
451
- ])),
452
- // Run onMounts
453
- t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier("_onMounts")), t.identifier("forEach")), [t.arrowFunctionExpression([t.identifier("fn")], t.callExpression(t.identifier("fn"), []))]))
454
- ])),
455
-
456
-
457
- t.classMethod("method", t.identifier("disconnectedCallback"), [], t.blockStatement([
458
- t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier("_onCleanups")), t.identifier("forEach")), [t.arrowFunctionExpression([t.identifier("fn")], t.callExpression(t.identifier("fn"), []))]))
459
- ]))
460
- ])
461
- );
462
-
463
- const parent = componentPath.parentPath;
464
-
465
- if (parent.isExportDefaultDeclaration()) {
466
- componentPath.parentPath.insertBefore(classDecl);
467
- componentPath.parentPath.insertBefore(t.expressionStatement(
468
- t.callExpression(
469
- t.memberExpression(t.identifier("customElements"), t.identifier("define")),
470
- [t.stringLiteral(tagName), classId]
471
- )
472
- ));
473
- parent.replaceWith(t.exportDefaultDeclaration(classId));
474
- } else if (parent.isExportNamedDeclaration()) {
475
- // For named exports, we transform the function into an exported class directly
476
- const exportedClass = t.classDeclaration(
477
- t.identifier(name),
478
- t.identifier("HTMLElement"),
479
- classDecl.body
480
- );
481
- parent.replaceWith(t.exportNamedDeclaration(exportedClass, []));
482
- parent.insertAfter(t.expressionStatement(
483
- t.callExpression(
484
- t.memberExpression(t.identifier("customElements"), t.identifier("define")),
485
- [t.stringLiteral(tagName), t.identifier(name)]
486
- )
487
- ));
488
- } else {
489
- componentPath.insertBefore(classDecl);
490
- componentPath.insertBefore(t.expressionStatement(
491
- t.callExpression(
492
- t.memberExpression(t.identifier("customElements"), t.identifier("define")),
493
- [t.stringLiteral(tagName), classId]
494
- )
495
- ));
496
- componentPath.remove();
497
- }
498
- }
499
- }
500
-
501
- function transformJSX(node, t, state, getImport, path) {
502
- if (node._processed) return { statements: [], rootId: node, signals: new Set() };
503
- node._processed = true;
504
-
505
- const statements = [];
506
-
507
- const signals = new Set();
508
- let counter = 0;
509
- const nextId = (prefix = "el") => t.identifier(prefix + (counter++));
510
- const toKebabCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
511
-
512
- function collectSignals(exprNode) {
513
- if (!exprNode || typeof exprNode !== "object") return;
514
- if (Array.isArray(exprNode)) {
515
- exprNode.forEach(collectSignals);
516
- return;
517
- }
518
- if (t.isMemberExpression(exprNode) && t.isIdentifier(exprNode.object, { name: "props" }) && t.isIdentifier(exprNode.property)) {
519
- signals.add(exprNode.property.name);
520
- }
521
- Object.keys(exprNode).forEach(key => {
522
- if (key === "property" && t.isMemberExpression(exprNode) && !exprNode.computed) return;
523
- collectSignals(exprNode[key]);
524
- });
525
- }
526
-
527
- function processNode(n, parentElId) {
528
- if (t.isJSXElement(n)) {
529
- const tagNameNode = n.openingElement.name;
530
- let tagName = "";
531
- let isDynamic = false;
532
-
533
- if (t.isJSXIdentifier(tagNameNode)) {
534
- tagName = tagNameNode.name;
535
- // If it's an identifier that starts with Uppercase, it might be a component.
536
- // But if it's NOT a globally defined component or a local function, it might be a dynamic tag variable.
537
- // For now, let's assume if it's not a standard HTML tag and it's Uppercase, it's a component.
538
- // BUT, if it's in scope as a variable, it might be dynamic.
539
- } else if (t.isJSXMemberExpression(tagNameNode)) {
540
- const getMemberName = (node) => {
541
- if (t.isJSXIdentifier(node)) return node.name;
542
- if (t.isJSXMemberExpression(node)) {
543
- return `${getMemberName(node.object)}.${getMemberName(node.property)}`;
544
- }
545
- return "";
546
- };
547
- tagName = getMemberName(tagNameNode);
548
- }
549
-
550
- const isComponent = /^[A-Z]/.test(tagName) || tagName.includes(".");
551
- const elId = nextId();
552
-
553
- if (isComponent) {
554
- // Check if the identifier is actually in scope (not a global component)
555
- // This is a heuristic: if it's Uppercase and NOT a standard tag, we treat it as a component.
556
- // For dynamic tags like <Tag />, we need to check if 'Tag' is a variable.
557
- const binding = path.scope.getBinding(tagName);
558
- const isStandardTag = /^[a-z]/.test(tagName) && STANDARD_TAGS.includes(tagName.toLowerCase());
559
-
560
- let componentTagName = tagName;
561
- if (tagName.includes(".")) {
562
- componentTagName = "web-" + tagName.replace(/\./g, "-").toLowerCase();
563
- } else if (!isStandardTag) {
564
- componentTagName = "web-" + tagName.toLowerCase();
565
- }
566
-
567
- const isFunction = binding && (t.isFunctionDeclaration(binding.path.node) || t.isFunctionExpression(binding.path.node) || t.isArrowFunctionExpression(binding.path.node));
568
- const isImport = binding && binding.kind === "module";
569
- const isVariable = binding && t.isVariableDeclarator(binding.path.node);
570
-
571
- if (isFunction || isImport || (!binding && !isStandardTag)) {
572
- statements.push(t.variableDeclaration("const", [
573
- t.variableDeclarator(elId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElement")), [t.stringLiteral(componentTagName)]))
574
- ]));
575
- } else if (isVariable) {
576
- // Truly dynamic tag from local variable
577
- statements.push(t.variableDeclaration("const", [
578
- t.variableDeclarator(elId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElement")), [t.identifier(tagName)]))
579
- ]));
580
- } else {
581
- statements.push(t.variableDeclaration("const", [
582
- t.variableDeclarator(elId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElement")), [t.stringLiteral(componentTagName)]))
583
- ]));
584
- }
585
-
586
-
587
- n.openingElement.attributes.forEach(attr => {
588
- if (t.isJSXSpreadAttribute(attr)) {
589
- const applySpreadId = getImport("applySpread", state.runtimeSource);
590
- statements.push(t.expressionStatement(t.callExpression(applySpreadId, [elId, attr.argument])));
591
- return;
592
- }
593
- const name = attr.name.name;
594
- const value = attr.value || t.booleanLiteral(true);
595
- const targetProp = name === "class" || name === "className" ? "className" : name;
596
-
597
- if (t.isJSXExpressionContainer(value)) {
598
- if (name === "ref" && t.isIdentifier(value.expression) && state.refVars?.has(value.expression.name)) {
599
- const refName = value.expression.name;
600
- const innerId = t.identifier(refName);
601
- innerId._processed = true;
602
- statements.push(t.expressionStatement(t.assignmentExpression(
603
- "=",
604
- t.memberExpression(innerId, t.identifier("value")),
605
- elId
606
- )));
607
- return;
608
- }
609
- collectSignals(value.expression);
610
- const effectId = getImport("effect", state.runtimeSource);
611
- statements.push(t.expressionStatement(t.callExpression(effectId, [
612
- t.arrowFunctionExpression([], t.assignmentExpression("=", t.memberExpression(elId, t.identifier(targetProp)), value.expression))
613
- ])));
614
- } else {
615
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier(targetProp)), value)));
616
- }
617
- });
618
-
619
-
620
- n.children.forEach(child => {
621
- const childId = processNode(child, elId);
622
- if (childId) statements.push(t.expressionStatement(t.callExpression(t.memberExpression(elId, t.identifier("appendChild")), [childId])));
623
- });
624
- return elId;
625
- }
626
-
627
- const isSvg = SVG_TAGS.includes(tagName.toLowerCase());
628
-
629
- statements.push(t.variableDeclaration("const", [
630
- t.variableDeclarator(
631
- elId,
632
- isSvg
633
- ? t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElementNS")), [t.stringLiteral("http://www.w3.org/2000/svg"), t.stringLiteral(tagName)])
634
- : t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElement")), [t.stringLiteral(tagName)])
635
- )
636
- ]));
637
-
638
-
639
- n.openingElement.attributes.forEach(attr => {
640
- if (t.isJSXSpreadAttribute(attr)) {
641
- const applySpreadId = getImport("applySpread", state.runtimeSource);
642
- statements.push(t.expressionStatement(t.callExpression(applySpreadId, [elId, attr.argument])));
643
- return;
644
- }
645
- const originalName = attr.name.name;
646
- const name = originalName.toLowerCase();
647
- const value = attr.value || t.booleanLiteral(true);
648
-
649
- if (name.startsWith("on")) {
650
- if (t.isJSXExpressionContainer(value) && t.isJSXEmptyExpression(value.expression)) return;
651
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier(name)), t.isJSXExpressionContainer(value) ? value.expression : value)));
652
- } else if (t.isJSXExpressionContainer(value)) {
653
- if (t.isJSXEmptyExpression(value.expression)) return;
654
-
655
- if (originalName === "ref" && t.isIdentifier(value.expression) && state.refVars?.has(value.expression.name)) {
656
- const refName = value.expression.name;
657
- const innerId = t.identifier(refName);
658
- innerId._processed = true;
659
- statements.push(t.expressionStatement(t.assignmentExpression(
660
- "=",
661
- t.memberExpression(innerId, t.identifier("value")),
662
- elId
663
- )));
664
- return;
665
- }
666
-
667
- collectSignals(value.expression);
668
-
669
- const effectId = getImport("effect", state.runtimeSource);
670
- const attrProp = (name === "class" || name === "classname") ? "className" : name;
671
- const isStyle = attrProp === "style";
672
- const isProperty = IS_PROPERTY.includes(attrProp);
673
-
674
- const isSvgCamel = isSvg && SVG_CAMEL_CASE.includes(originalName);
675
- const finalAttrName = isSvgCamel ? originalName : (attrProp === "className" ? "class" : toKebabCase(originalName));
676
-
677
- if (attrProp === "key") {
678
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier("_key")), value.expression)));
679
- } else {
680
- statements.push(t.expressionStatement(t.callExpression(effectId, [t.arrowFunctionExpression([],
681
- isStyle ? t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [t.memberExpression(elId, t.identifier("style")), value.expression])
682
- : (isProperty && (!isSvg || attrProp !== "className")) ? t.assignmentExpression("=", t.memberExpression(elId, t.identifier(attrProp)), value.expression)
683
- : t.callExpression(t.memberExpression(elId, t.identifier("setAttribute")), [t.stringLiteral(finalAttrName), value.expression])
684
- )])));
685
- }
686
-
687
- } else {
688
- const attrProp = (name === "class" || name === "classname") ? "className" : name;
689
- const isProperty = IS_PROPERTY.includes(attrProp);
690
- const isSvgCamel = isSvg && SVG_CAMEL_CASE.includes(originalName);
691
- const finalAttrName = isSvgCamel ? originalName : (attrProp === "className" ? "class" : toKebabCase(originalName));
692
-
693
- if (isProperty && (!isSvg || attrProp !== "className")) {
694
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier(attrProp === "key" ? "_key" : attrProp)), value)));
695
- } else {
696
- statements.push(t.expressionStatement(t.callExpression(t.memberExpression(elId, t.identifier("setAttribute")), [t.stringLiteral(finalAttrName), value])));
697
- }
698
-
699
- }
700
- });
701
-
702
- n.children.forEach(child => {
703
- const childId = processNode(child, elId);
704
- if (childId) statements.push(t.expressionStatement(t.callExpression(t.memberExpression(elId, t.identifier("appendChild")), [childId])));
705
- });
706
- return elId;
707
- } else if (t.isJSXText(n)) {
708
- if (n.value.includes("\n") && n.value.trim() === "") return null;
709
- const text = n.value.replace(/\n\s*/g, " ");
710
- if (!text) return null;
711
- const textId = nextId("text");
712
- statements.push(t.variableDeclaration("const", [t.variableDeclarator(textId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createTextNode")), [t.stringLiteral(text)]))]));
713
- return textId;
714
- } else if (t.isJSXFragment(n)) {
715
- const fragId = nextId("frag");
716
- statements.push(t.variableDeclaration("const", [t.variableDeclarator(fragId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createDocumentFragment")), []))]));
717
- n.children.forEach(child => {
718
- const childId = processNode(child, fragId);
719
- if (childId) statements.push(t.expressionStatement(t.callExpression(t.memberExpression(fragId, t.identifier("appendChild")), [childId])));
720
- });
721
- return fragId;
722
- } else if (t.isJSXExpressionContainer(n)) {
723
- if (t.isJSXEmptyExpression(n.expression)) return null;
724
-
725
- collectSignals(n.expression);
726
-
727
- // Recursive helper to transform nested JSX elements and .map() calls
728
- const transformExpression = (exprNode) => {
729
- if (!exprNode || typeof exprNode !== "object") return;
730
- if (Array.isArray(exprNode)) {
731
- exprNode.forEach(transformExpression);
732
- return;
733
- }
734
-
735
- Object.keys(exprNode).forEach(key => {
736
- const child = exprNode[key];
737
- if (!child || typeof child !== "object") return;
738
-
739
- // Transform nested JSX elements into imperative IIFEs
740
- if (t.isJSXElement(child) || t.isJSXFragment(child)) {
741
- if (child._processed) return;
742
- const { statements: innerStatements, rootId: innerRootId, signals: innerSignals } = transformJSX(child, t, state, getImport, path);
743
- innerSignals.forEach(s => signals.add(s));
744
- exprNode[key] = t.callExpression(t.arrowFunctionExpression([], t.blockStatement([
745
- ...innerStatements,
746
- t.returnStatement(innerRootId)
747
- ])), []);
748
- exprNode[key]._processed = true;
749
- }
750
- // Transform .map() into optimized keyed mapped() call
751
- else if (t.isCallExpression(child) &&
752
- t.isMemberExpression(child.callee) &&
753
- t.isIdentifier(child.callee.property, { name: "map" })) {
754
-
755
- const mappedId = getImport("mapped", state.runtimeSource);
756
- const sourceArray = child.callee.object;
757
- const mapFn = child.arguments[0];
758
-
759
- const mappedInstanceId = nextId("mapped");
760
- statements.push(t.variableDeclaration("const", [
761
- t.variableDeclarator(mappedInstanceId, t.callExpression(mappedId, [
762
- t.arrowFunctionExpression([], sourceArray),
763
- mapFn
764
- ]))
765
- ]));
766
-
767
- exprNode[key] = t.callExpression(mappedInstanceId, []);
768
- // Recurse into the map function body if needed
769
- transformExpression(mapFn);
770
- }
771
- else {
772
- transformExpression(child);
773
- }
774
- });
775
- };
776
-
777
- const wrapper = { expr: n.expression };
778
- transformExpression(wrapper);
779
- const finalExpression = wrapper.expr;
780
-
781
- const renderDynamicId = getImport("renderDynamic", state.runtimeSource);
782
- statements.push(t.expressionStatement(t.callExpression(renderDynamicId, [
783
- parentElId,
784
- t.arrowFunctionExpression([], finalExpression)
785
- ])));
786
-
787
- return null;
788
- } else if (t.isExpression(n)) {
789
- return n;
790
- }
791
- }
792
-
793
-
794
- const rootId = processNode(node, t.identifier("root"));
795
- return { statements, rootId, signals };
796
- }
797
-
798
- };