@opentf/web 0.3.0 → 0.5.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 (179) hide show
  1. package/components/Link.jsx +29 -0
  2. package/core/errors.js +48 -0
  3. package/core/signals.js +188 -0
  4. package/index.js +12 -0
  5. package/package.json +27 -18
  6. package/runtime/context.js +89 -0
  7. package/runtime/dom.js +280 -0
  8. package/runtime/error-boundary.js +109 -0
  9. package/runtime/events.js +17 -0
  10. package/runtime/index.js +16 -0
  11. package/runtime/lifecycle.js +9 -0
  12. package/runtime/mount.js +49 -0
  13. package/runtime/portal.js +59 -0
  14. package/runtime/router.js +246 -0
  15. package/server/builtins.js +11 -0
  16. package/server/index.js +7 -0
  17. package/server/render.js +76 -0
  18. package/server/ssg-runtime.js +109 -0
  19. package/dist/CHANGELOG.md +0 -24
  20. package/dist/babel.config.cjs +0 -6
  21. package/dist/compiler/babel-plugin.cjs +0 -731
  22. package/dist/core/mount.js +0 -32
  23. package/dist/index.js +0 -7
  24. package/dist/node_modules/@babel/core/LICENSE +0 -22
  25. package/dist/node_modules/@babel/core/README.md +0 -19
  26. package/dist/node_modules/@babel/core/lib/config/cache-contexts.js +0 -5
  27. package/dist/node_modules/@babel/core/lib/config/cache-contexts.js.map +0 -1
  28. package/dist/node_modules/@babel/core/lib/config/caching.js +0 -261
  29. package/dist/node_modules/@babel/core/lib/config/caching.js.map +0 -1
  30. package/dist/node_modules/@babel/core/lib/config/config-chain.js +0 -469
  31. package/dist/node_modules/@babel/core/lib/config/config-chain.js.map +0 -1
  32. package/dist/node_modules/@babel/core/lib/config/config-descriptors.js +0 -190
  33. package/dist/node_modules/@babel/core/lib/config/config-descriptors.js.map +0 -1
  34. package/dist/node_modules/@babel/core/lib/config/files/configuration.js +0 -290
  35. package/dist/node_modules/@babel/core/lib/config/files/configuration.js.map +0 -1
  36. package/dist/node_modules/@babel/core/lib/config/files/import.cjs +0 -6
  37. package/dist/node_modules/@babel/core/lib/config/files/import.cjs.map +0 -1
  38. package/dist/node_modules/@babel/core/lib/config/files/index-browser.js +0 -58
  39. package/dist/node_modules/@babel/core/lib/config/files/index-browser.js.map +0 -1
  40. package/dist/node_modules/@babel/core/lib/config/files/index.js +0 -78
  41. package/dist/node_modules/@babel/core/lib/config/files/index.js.map +0 -1
  42. package/dist/node_modules/@babel/core/lib/config/files/module-types.js +0 -203
  43. package/dist/node_modules/@babel/core/lib/config/files/module-types.js.map +0 -1
  44. package/dist/node_modules/@babel/core/lib/config/files/package.js +0 -61
  45. package/dist/node_modules/@babel/core/lib/config/files/package.js.map +0 -1
  46. package/dist/node_modules/@babel/core/lib/config/files/plugins.js +0 -220
  47. package/dist/node_modules/@babel/core/lib/config/files/plugins.js.map +0 -1
  48. package/dist/node_modules/@babel/core/lib/config/files/types.js +0 -5
  49. package/dist/node_modules/@babel/core/lib/config/files/types.js.map +0 -1
  50. package/dist/node_modules/@babel/core/lib/config/files/utils.js +0 -36
  51. package/dist/node_modules/@babel/core/lib/config/files/utils.js.map +0 -1
  52. package/dist/node_modules/@babel/core/lib/config/full.js +0 -312
  53. package/dist/node_modules/@babel/core/lib/config/full.js.map +0 -1
  54. package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js +0 -85
  55. package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js.map +0 -1
  56. package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js +0 -23
  57. package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js.map +0 -1
  58. package/dist/node_modules/@babel/core/lib/config/helpers/environment.js +0 -12
  59. package/dist/node_modules/@babel/core/lib/config/helpers/environment.js.map +0 -1
  60. package/dist/node_modules/@babel/core/lib/config/index.js +0 -87
  61. package/dist/node_modules/@babel/core/lib/config/index.js.map +0 -1
  62. package/dist/node_modules/@babel/core/lib/config/item.js +0 -67
  63. package/dist/node_modules/@babel/core/lib/config/item.js.map +0 -1
  64. package/dist/node_modules/@babel/core/lib/config/partial.js +0 -158
  65. package/dist/node_modules/@babel/core/lib/config/partial.js.map +0 -1
  66. package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js +0 -38
  67. package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js.map +0 -1
  68. package/dist/node_modules/@babel/core/lib/config/plugin.js +0 -33
  69. package/dist/node_modules/@babel/core/lib/config/plugin.js.map +0 -1
  70. package/dist/node_modules/@babel/core/lib/config/printer.js +0 -113
  71. package/dist/node_modules/@babel/core/lib/config/printer.js.map +0 -1
  72. package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js +0 -41
  73. package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js.map +0 -1
  74. package/dist/node_modules/@babel/core/lib/config/resolve-targets.js +0 -61
  75. package/dist/node_modules/@babel/core/lib/config/resolve-targets.js.map +0 -1
  76. package/dist/node_modules/@babel/core/lib/config/util.js +0 -31
  77. package/dist/node_modules/@babel/core/lib/config/util.js.map +0 -1
  78. package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js +0 -277
  79. package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js.map +0 -1
  80. package/dist/node_modules/@babel/core/lib/config/validation/options.js +0 -187
  81. package/dist/node_modules/@babel/core/lib/config/validation/options.js.map +0 -1
  82. package/dist/node_modules/@babel/core/lib/config/validation/plugins.js +0 -67
  83. package/dist/node_modules/@babel/core/lib/config/validation/plugins.js.map +0 -1
  84. package/dist/node_modules/@babel/core/lib/config/validation/removed.js +0 -68
  85. package/dist/node_modules/@babel/core/lib/config/validation/removed.js.map +0 -1
  86. package/dist/node_modules/@babel/core/lib/errors/config-error.js +0 -18
  87. package/dist/node_modules/@babel/core/lib/errors/config-error.js.map +0 -1
  88. package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js +0 -98
  89. package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js.map +0 -1
  90. package/dist/node_modules/@babel/core/lib/gensync-utils/async.js +0 -90
  91. package/dist/node_modules/@babel/core/lib/gensync-utils/async.js.map +0 -1
  92. package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js +0 -31
  93. package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js.map +0 -1
  94. package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js +0 -58
  95. package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js.map +0 -1
  96. package/dist/node_modules/@babel/core/lib/index.js +0 -230
  97. package/dist/node_modules/@babel/core/lib/index.js.map +0 -1
  98. package/dist/node_modules/@babel/core/lib/parse.js +0 -45
  99. package/dist/node_modules/@babel/core/lib/parse.js.map +0 -1
  100. package/dist/node_modules/@babel/core/lib/parser/index.js +0 -85
  101. package/dist/node_modules/@babel/core/lib/parser/index.js.map +0 -1
  102. package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js +0 -337
  103. package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js.map +0 -1
  104. package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js +0 -144
  105. package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js.map +0 -1
  106. package/dist/node_modules/@babel/core/lib/transform-ast.js +0 -48
  107. package/dist/node_modules/@babel/core/lib/transform-ast.js.map +0 -1
  108. package/dist/node_modules/@babel/core/lib/transform-file-browser.js +0 -23
  109. package/dist/node_modules/@babel/core/lib/transform-file-browser.js.map +0 -1
  110. package/dist/node_modules/@babel/core/lib/transform-file.js +0 -40
  111. package/dist/node_modules/@babel/core/lib/transform-file.js.map +0 -1
  112. package/dist/node_modules/@babel/core/lib/transform.js +0 -47
  113. package/dist/node_modules/@babel/core/lib/transform.js.map +0 -1
  114. package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js +0 -84
  115. package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js.map +0 -1
  116. package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs +0 -4
  117. package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs.map +0 -1
  118. package/dist/node_modules/@babel/core/lib/transformation/file/file.js +0 -204
  119. package/dist/node_modules/@babel/core/lib/transformation/file/file.js.map +0 -1
  120. package/dist/node_modules/@babel/core/lib/transformation/file/generate.js +0 -84
  121. package/dist/node_modules/@babel/core/lib/transformation/file/generate.js.map +0 -1
  122. package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js +0 -37
  123. package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js.map +0 -1
  124. package/dist/node_modules/@babel/core/lib/transformation/index.js +0 -90
  125. package/dist/node_modules/@babel/core/lib/transformation/index.js.map +0 -1
  126. package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js +0 -127
  127. package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js.map +0 -1
  128. package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js +0 -59
  129. package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js.map +0 -1
  130. package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js +0 -48
  131. package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js.map +0 -1
  132. package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js +0 -54
  133. package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js.map +0 -1
  134. package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js +0 -1042
  135. package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js.map +0 -1
  136. package/dist/node_modules/@babel/core/package.json +0 -82
  137. package/dist/node_modules/@babel/core/src/config/files/index-browser.ts +0 -115
  138. package/dist/node_modules/@babel/core/src/config/files/index.ts +0 -30
  139. package/dist/node_modules/@babel/core/src/config/resolve-targets-browser.ts +0 -42
  140. package/dist/node_modules/@babel/core/src/config/resolve-targets.ts +0 -54
  141. package/dist/node_modules/@babel/core/src/transform-file-browser.ts +0 -33
  142. package/dist/node_modules/@babel/core/src/transform-file.ts +0 -56
  143. package/dist/node_modules/@babel/helper-module-imports/LICENSE +0 -22
  144. package/dist/node_modules/@babel/helper-module-imports/README.md +0 -19
  145. package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js +0 -122
  146. package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js.map +0 -1
  147. package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js +0 -304
  148. package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js.map +0 -1
  149. package/dist/node_modules/@babel/helper-module-imports/lib/index.js +0 -37
  150. package/dist/node_modules/@babel/helper-module-imports/lib/index.js.map +0 -1
  151. package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js +0 -11
  152. package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js.map +0 -1
  153. package/dist/node_modules/@babel/helper-module-imports/package.json +0 -28
  154. package/dist/node_modules/@babel/plugin-syntax-jsx/LICENSE +0 -22
  155. package/dist/node_modules/@babel/plugin-syntax-jsx/README.md +0 -19
  156. package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js +0 -21
  157. package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js.map +0 -1
  158. package/dist/node_modules/@babel/plugin-syntax-jsx/package.json +0 -33
  159. package/dist/node_modules/@preact/signals-core/CHANGELOG.md +0 -331
  160. package/dist/node_modules/@preact/signals-core/LICENSE +0 -21
  161. package/dist/node_modules/@preact/signals-core/README.md +0 -258
  162. package/dist/node_modules/@preact/signals-core/dist/signals-core.d.ts +0 -149
  163. package/dist/node_modules/@preact/signals-core/dist/signals-core.js +0 -1
  164. package/dist/node_modules/@preact/signals-core/dist/signals-core.js.map +0 -1
  165. package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js +0 -1
  166. package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js.map +0 -1
  167. package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs +0 -1
  168. package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs.map +0 -1
  169. package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js +0 -1
  170. package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js.map +0 -1
  171. package/dist/node_modules/@preact/signals-core/package.json +0 -48
  172. package/dist/node_modules/@preact/signals-core/src/index.ts +0 -1121
  173. package/dist/package.json +0 -30
  174. package/dist/router/Link.js +0 -73
  175. package/dist/router/index.js +0 -216
  176. package/dist/runtime/For.js +0 -16
  177. package/dist/runtime/dom.js +0 -73
  178. package/dist/runtime/lifecycle.js +0 -25
  179. package/dist/runtime/props.js +0 -103
@@ -1,731 +0,0 @@
1
- const { addNamed } = require("@babel/helper-module-imports");
2
- const path = require("path");
3
-
4
- module.exports = function (babel) {
5
- const { types: t } = babel;
6
-
7
- return {
8
- name: "web-compiler",
9
- visitor: {
10
- Program: {
11
- enter(p, state) {
12
- state.runtimeSource = state.opts.runtimeSource || "@opentf/web";
13
- state.importsNeeded = new Map();
14
- state.importSources = new Map();
15
- state.components = new Map(); // name -> { path, isDefault, isPage }
16
-
17
- const filename = state.filename || "";
18
- const pagePatterns = ["page.jsx", "page.tsx", "layout.jsx", "layout.tsx", "404.jsx", "404.tsx"];
19
- state.isPageFile = pagePatterns.some(p => filename.endsWith(p));
20
- },
21
- exit(p, state) {
22
- // Process all collected components
23
- state.components.forEach((info, name) => {
24
- const isRenderFn = state.isPageFile && info.isDefault;
25
- transformComponent(info.path, name, isRenderFn, t, state);
26
- });
27
- }
28
- },
29
-
30
- FunctionDeclaration(path, state) {
31
- if (!path.node.id) return;
32
- const name = path.node.id.name;
33
- if (/^[A-Z]/.test(name)) {
34
- // Check if this function actually uses JSX. If not, it's just a regular function, not a component.
35
- let hasJSX = false;
36
- path.traverse({
37
- JSXElement() { hasJSX = true; },
38
- JSXFragment() { hasJSX = true; }
39
- });
40
-
41
- if (!hasJSX) return;
42
-
43
- const propsNode = path.node.params[0];
44
- const observedAttributes = new Set();
45
-
46
- if (t.isObjectPattern(propsNode)) {
47
- propsNode.properties.forEach(prop => {
48
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
49
- observedAttributes.add(prop.key.name);
50
- }
51
- });
52
- }
53
-
54
- if (state.components.has(name)) {
55
- const info = state.components.get(name);
56
- info.observedAttributes = observedAttributes;
57
- info.path = path;
58
- } else {
59
- state.components.set(name, { path, isDefault: false, observedAttributes });
60
- }
61
- }
62
- },
63
-
64
- ExportDefaultDeclaration(path, state) {
65
- const decl = path.node.declaration;
66
- if (t.isFunctionDeclaration(decl)) {
67
- let hasJSX = false;
68
- path.traverse({
69
- JSXElement() { hasJSX = true; },
70
- JSXFragment() { hasJSX = true; }
71
- });
72
-
73
- if (!hasJSX) return;
74
-
75
- if (decl.id) {
76
- const name = decl.id.name;
77
- if (state.components.has(name)) {
78
- state.components.get(name).isDefault = true;
79
- } else {
80
- state.components.set(name, { path: path.get("declaration"), isDefault: true });
81
- }
82
- } else {
83
- // Anonymous function
84
- state.components.set("_default", { path: path.get("declaration"), isDefault: true });
85
- }
86
- }
87
- },
88
-
89
- ImportDeclaration(path, state) {
90
- const source = path.node.source.value;
91
- path.node.specifiers.forEach(spec => {
92
- if (t.isImportDefaultSpecifier(spec) || t.isImportSpecifier(spec)) {
93
- state.importSources.set(spec.local.name, source);
94
- }
95
- });
96
- },
97
-
98
- CallExpression(path, state) {
99
- if (t.isIdentifier(path.node.callee)) {
100
- const name = path.node.callee.name;
101
- const getImport = (importName, source) => {
102
- const key = `${importName}:${source}`;
103
- if (!state.importsNeeded.has(key)) {
104
- state.importsNeeded.set(key, addNamed(path, importName, source));
105
- }
106
- return state.importsNeeded.get(key);
107
- };
108
-
109
- if (name === "$state" || name === "$derived" || name === "$ref") {
110
- // Track this variable as a state variable in the current scope
111
- const parent = path.findParent(p => p.isVariableDeclarator());
112
- if (parent && t.isIdentifier(parent.node.id)) {
113
- if (!state.stateVars) state.stateVars = new Set();
114
- state.stateVars.add(parent.node.id.name);
115
- }
116
- if (name === "$state") {
117
- path.get("callee").replaceWith(getImport("signal", state.runtimeSource));
118
- } else if (name === "$derived") {
119
- path.get("callee").replaceWith(getImport("computed", state.runtimeSource));
120
- } else if (name === "$ref") {
121
- // Track this variable as a ref variable
122
- const parent = path.findParent(p => p.isVariableDeclarator());
123
- if (parent && t.isIdentifier(parent.node.id)) {
124
- if (!state.refVars) state.refVars = new Set();
125
- state.refVars.add(parent.node.id.name);
126
- if (!state.stateVars) state.stateVars = new Set();
127
- state.stateVars.add(parent.node.id.name);
128
- }
129
- path.get("callee").replaceWith(getImport("signal", state.runtimeSource));
130
- }
131
- } else if (name === "$effect") {
132
- path.get("callee").replaceWith(getImport("effect", state.runtimeSource));
133
- } else if (name === "$expose") {
134
- // $expose({ a, b }) -> Object.assign(this, { a, b })
135
- path.get("callee").replaceWith(t.memberExpression(t.identifier("Object"), t.identifier("assign")));
136
- path.node.arguments.unshift(t.thisExpression());
137
- } else if (name === "onMount") {
138
- path.get("callee").replaceWith(getImport("onMount", state.runtimeSource));
139
- } else if (name === "onCleanup") {
140
- path.get("callee").replaceWith(getImport("onCleanup", state.runtimeSource));
141
- } else if (name === "$renderDynamic") {
142
- path.get("callee").replaceWith(getImport("renderDynamic", state.runtimeSource));
143
- }
144
- }
145
- },
146
-
147
- Identifier(path, state) {
148
- if (!state.stateVars || !state.stateVars.has(path.node.name)) return;
149
- if (path.node._processed) return;
150
-
151
- // Strictly forbid manual .value access on $state variables
152
- if (path.parentPath.isMemberExpression() && !path.parentPath.node.computed) {
153
- if (t.isIdentifier(path.parentPath.node.property, { name: "value" })) {
154
- throw path.parentPath.buildCodeFrameError(
155
- `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'.`
156
- );
157
- }
158
- if (path.parentPath.node.property === path.node) return;
159
- }
160
-
161
- if (path.parentPath.isVariableDeclarator() && path.parentPath.node.id === path.node) return;
162
- if (path.parentPath.isObjectProperty() && path.parentPath.node.key === path.node && !path.parentPath.node.computed) return;
163
- if (path.parentPath.isClassProperty() && path.parentPath.node.key === path.node) return;
164
- if (path.parentPath.isClassMethod() && path.parentPath.node.key === path.node) return;
165
-
166
- const innerId = t.identifier(path.node.name);
167
- innerId._processed = true;
168
- const newNode = t.memberExpression(innerId, t.identifier("value"));
169
- path.replaceWith(newNode);
170
- },
171
-
172
- AssignmentExpression(path, state) {
173
- if (t.isIdentifier(path.node.left) && state.stateVars?.has(path.node.left.name)) {
174
- const innerId = t.identifier(path.node.left.name);
175
- innerId._processed = true;
176
- path.node.left = t.memberExpression(innerId, t.identifier("value"));
177
- }
178
- },
179
-
180
- UpdateExpression(path, state) {
181
- if (t.isIdentifier(path.node.argument) && state.stateVars?.has(path.node.argument.name)) {
182
- const innerId = t.identifier(path.node.argument.name);
183
- innerId._processed = true;
184
- path.node.argument = t.memberExpression(innerId, t.identifier("value"));
185
- }
186
- },
187
-
188
- JSXElement(path, state) {
189
- handleJSXVisitor(path, state, t);
190
- },
191
-
192
- JSXFragment(path, state) {
193
- handleJSXVisitor(path, state, t);
194
- }
195
- }
196
- };
197
-
198
- function handleJSXVisitor(path, state, t) {
199
- if (path.node._processed) return;
200
- const getImport = (importName, source) => {
201
- const key = `${importName}:${source}`;
202
- if (!state.importsNeeded.has(key)) {
203
- state.importsNeeded.set(key, addNamed(path, importName, source));
204
- }
205
- return state.importsNeeded.get(key);
206
- };
207
-
208
- const { statements, rootId, signals } = transformJSX(path.node, t, state, getImport, path);
209
-
210
- // Propagate signals to parent component for observedAttributes
211
- const parentFunc = path.findParent(p =>
212
- p.isFunctionDeclaration() && (
213
- /^[A-Z]/.test(p.node.id?.name) ||
214
- (p.parentPath.isExportDefaultDeclaration() && state.isPageFile)
215
- )
216
- );
217
-
218
- if (parentFunc) {
219
- let compName = parentFunc.node.id?.name;
220
- if (!compName && parentFunc.parentPath.isExportDefaultDeclaration()) {
221
- compName = "_default";
222
- }
223
- if (compName) {
224
- const info = state.components.get(compName);
225
- if (info) {
226
- if (!info.observedAttributes) info.observedAttributes = new Set();
227
- signals.forEach(s => info.observedAttributes.add(s));
228
- }
229
- }
230
- }
231
-
232
- const iife = t.callExpression(t.arrowFunctionExpression([], t.blockStatement([
233
- ...statements,
234
- t.returnStatement(rootId)
235
- ])), []);
236
- iife._processed = true;
237
- path.replaceWith(iife);
238
- }
239
-
240
- function transformComponent(componentPath, name, isRenderFn, t, state) {
241
- const node = componentPath.node;
242
- const body = node.body;
243
-
244
- const getImport = (importName, source) => {
245
- const key = `${importName}:${source}`;
246
- if (!state.importsNeeded.has(key)) {
247
- state.importsNeeded.set(key, addNamed(componentPath, importName, source));
248
- }
249
- return state.importsNeeded.get(key);
250
- };
251
-
252
- let jsxNode = null;
253
- const originalStatements = [];
254
-
255
- body.body.forEach(stmt => {
256
- if (t.isReturnStatement(stmt)) {
257
- jsxNode = stmt.argument;
258
- } else {
259
- originalStatements.push(stmt);
260
- }
261
- });
262
-
263
- if (!jsxNode) return;
264
-
265
- const res = transformJSX(jsxNode, t, state, getImport, componentPath);
266
- let statements = res.statements;
267
- let rootId = res.rootId;
268
- let signals = res.signals;
269
-
270
- // Capture the root element in a variable to avoid re-evaluating the IIFE if it is one
271
- const rootVar = t.identifier("rootElement");
272
- statements.push(t.variableDeclaration("const", [t.variableDeclarator(rootVar, rootId)]));
273
- rootId = rootVar;
274
-
275
-
276
-
277
- const componentInfo = state.components.get(name);
278
-
279
- // Prop Transformation: If the component uses destructuring in params,
280
- // we convert it to use `props` and replace usages to maintain reactivity.
281
- if (!isRenderFn) {
282
- const propsNode = node.params[0];
283
- if (t.isObjectPattern(propsNode)) {
284
- const propNames = new Set();
285
- propsNode.properties.forEach(prop => {
286
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
287
- propNames.add(prop.key.name);
288
- }
289
- });
290
-
291
- node.params[0] = t.identifier("props");
292
-
293
- const replaceProps = (node) => {
294
- if (!node || typeof node !== "object") return;
295
-
296
- if (Array.isArray(node)) {
297
- for (let i = 0; i < node.length; i++) {
298
- const child = node[i];
299
- if (t.isIdentifier(child) && propNames.has(child.name)) {
300
- node[i] = t.memberExpression(t.identifier("props"), t.identifier(child.name));
301
- } else {
302
- replaceProps(child);
303
- }
304
- }
305
- return;
306
- }
307
-
308
- for (const key in node) {
309
- const child = node[key];
310
- if (t.isIdentifier(child) && propNames.has(child.name)) {
311
- // Only replace if it's NOT a property key in an object or member expression
312
- if (t.isObjectProperty(node) && key === "key" && !node.computed) continue;
313
- if (t.isMemberExpression(node) && key === "property" && !node.computed) continue;
314
- if (t.isClassMethod(node) && key === "key") continue;
315
- if (t.isJSXAttribute(node) && key === "name") continue;
316
-
317
- node[key] = t.memberExpression(t.identifier("props"), t.identifier(child.name));
318
- } else {
319
- replaceProps(child);
320
- }
321
- }
322
- };
323
-
324
- replaceProps(originalStatements);
325
- replaceProps(statements);
326
- }
327
- }
328
-
329
-
330
-
331
- const allSignals = new Set([...signals, ...(componentInfo?.observedAttributes || [])]);
332
-
333
-
334
- if (isRenderFn) {
335
- // ... (rest of render logic remains same)
336
- // Transform to export function render(root) { ... }
337
- const renderFn = t.functionDeclaration(
338
- t.identifier("render"),
339
- [t.identifier("root"), t.identifier("props")],
340
- t.blockStatement([
341
- ...(t.isObjectPattern(node.params[0]) ? [t.variableDeclaration("const", [t.variableDeclarator(node.params[0], t.identifier("props"))])] : []),
342
- ...originalStatements,
343
- ...statements,
344
- t.expressionStatement(
345
- t.callExpression(
346
- t.memberExpression(t.identifier("root"), t.identifier("appendChild")),
347
- [rootId]
348
- )
349
- )
350
- ])
351
- );
352
-
353
- const parent = componentPath.parentPath;
354
- if (parent.isExportDefaultDeclaration()) {
355
- parent.replaceWith(t.exportNamedDeclaration(renderFn));
356
- } else {
357
- componentPath.replaceWith(renderFn);
358
- }
359
- } else {
360
- // Transform to Web Component Class
361
- const tagName = "web-" + name.toLowerCase();
362
- const observedAttributes = Array.from(allSignals);
363
- const signalId = getImport("signal", state.runtimeSource);
364
- const createPropsProxyId = getImport("createPropsProxy", state.runtimeSource);
365
- const classId = t.identifier(name + "Element");
366
-
367
- const classDecl = t.classDeclaration(
368
- classId,
369
- t.identifier("HTMLElement"),
370
- t.classBody([
371
- t.classProperty(t.identifier("observedAttributes"), t.arrayExpression(observedAttributes.map(s => t.stringLiteral(s))), null, null, false, true),
372
- // Add getters and setters for each observed attribute
373
- ...observedAttributes.map(s => t.classMethod(
374
- "set",
375
- t.identifier(s),
376
- [t.identifier("val")],
377
- t.blockStatement([
378
- t.expressionStatement(t.assignmentExpression(
379
- "=",
380
- t.memberExpression(
381
- t.memberExpression(
382
- t.memberExpression(t.thisExpression(), t.identifier("_propsSignals")),
383
- t.stringLiteral(s),
384
- true
385
- ),
386
- t.identifier("value")
387
- ),
388
- t.identifier("val")
389
- )),
390
- // Sync back to native attributes for class and style
391
- ...(s === "className" ? [
392
- t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier("setAttribute")), [t.stringLiteral("class"), t.identifier("val")]))
393
- ] : s === "style" ? [
394
- t.ifStatement(
395
- t.logicalExpression("&&", t.identifier("val"), t.binaryExpression("===", t.unaryExpression("typeof", t.identifier("val"), false), t.stringLiteral("object"))),
396
- t.expressionStatement(t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [t.memberExpression(t.thisExpression(), t.identifier("style")), t.identifier("val")]))
397
- )
398
- ] : [])
399
- ])
400
- )),
401
- ...observedAttributes.map(s => t.classMethod(
402
- "get",
403
- t.identifier(s),
404
- [],
405
- t.blockStatement([
406
- t.returnStatement(t.memberExpression(
407
- t.memberExpression(
408
- t.memberExpression(t.thisExpression(), t.identifier("_propsSignals")),
409
- t.stringLiteral(s),
410
- true
411
- ),
412
- t.identifier("value")
413
- ))
414
- ])
415
- )),
416
- t.classMethod("constructor", t.identifier("constructor"), [], t.blockStatement([
417
- t.expressionStatement(t.callExpression(t.super(), [])),
418
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.thisExpression(), t.identifier("_propsSignals")), t.objectExpression(
419
- observedAttributes.map(s => t.objectProperty(t.identifier(s), t.callExpression(signalId, [t.nullLiteral()])))
420
- )))
421
- ])),
422
- t.classMethod("method", t.identifier("attributeChangedCallback"), [t.identifier("name"), t.identifier("_"), t.identifier("value")], t.blockStatement([
423
- 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")))
424
- ])),
425
- t.classMethod("method", t.identifier("connectedCallback"), [], t.blockStatement([
426
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.thisExpression(), t.identifier("_onMounts")), t.arrayExpression([]))),
427
- t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.thisExpression(), t.identifier("_onCleanups")), t.arrayExpression([]))),
428
- t.variableDeclaration("const", [t.variableDeclarator(t.identifier("props"), t.callExpression(createPropsProxyId, [t.thisExpression()]))]),
429
- 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"))]))),
430
- 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"))]))),
431
-
432
- // Wrap setup in withInstance(this, () => { ... })
433
- t.expressionStatement(t.callExpression(getImport("withInstance", state.runtimeSource), [
434
- t.thisExpression(),
435
- t.arrowFunctionExpression([], t.blockStatement([
436
- ...originalStatements,
437
- ...statements,
438
- t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier("appendChild")), [rootId]))
439
- ]))
440
- ])),
441
- // Run onMounts
442
- 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"), []))]))
443
- ])),
444
-
445
-
446
- t.classMethod("method", t.identifier("disconnectedCallback"), [], t.blockStatement([
447
- 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"), []))]))
448
- ]))
449
- ])
450
- );
451
-
452
- const parent = componentPath.parentPath;
453
-
454
- if (parent.isExportDefaultDeclaration()) {
455
- componentPath.parentPath.insertBefore(classDecl);
456
- componentPath.parentPath.insertBefore(t.expressionStatement(
457
- t.callExpression(
458
- t.memberExpression(t.identifier("customElements"), t.identifier("define")),
459
- [t.stringLiteral(tagName), classId]
460
- )
461
- ));
462
- parent.replaceWith(t.exportDefaultDeclaration(classId));
463
- } else if (parent.isExportNamedDeclaration()) {
464
- // For named exports, we transform the function into an exported class directly
465
- const exportedClass = t.classDeclaration(
466
- t.identifier(name),
467
- t.identifier("HTMLElement"),
468
- classDecl.body
469
- );
470
- parent.replaceWith(t.exportNamedDeclaration(exportedClass, []));
471
- parent.insertAfter(t.expressionStatement(
472
- t.callExpression(
473
- t.memberExpression(t.identifier("customElements"), t.identifier("define")),
474
- [t.stringLiteral(tagName), t.identifier(name)]
475
- )
476
- ));
477
- } else {
478
- componentPath.insertBefore(classDecl);
479
- componentPath.insertBefore(t.expressionStatement(
480
- t.callExpression(
481
- t.memberExpression(t.identifier("customElements"), t.identifier("define")),
482
- [t.stringLiteral(tagName), classId]
483
- )
484
- ));
485
- componentPath.remove();
486
- }
487
- }
488
- }
489
-
490
- function transformJSX(node, t, state, getImport, path) {
491
- if (node._processed) return { statements: [], rootId: node, signals: new Set() };
492
- node._processed = true;
493
-
494
- const statements = [];
495
-
496
- const signals = new Set();
497
- let counter = 0;
498
- const nextId = (prefix = "el") => t.identifier(prefix + (counter++));
499
- const toKebabCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
500
-
501
- function collectSignals(exprNode) {
502
- if (!exprNode || typeof exprNode !== "object") return;
503
- if (Array.isArray(exprNode)) {
504
- exprNode.forEach(collectSignals);
505
- return;
506
- }
507
- if (t.isMemberExpression(exprNode) && t.isIdentifier(exprNode.object, { name: "props" }) && t.isIdentifier(exprNode.property)) {
508
- signals.add(exprNode.property.name);
509
- }
510
- Object.keys(exprNode).forEach(key => {
511
- if (key === "property" && t.isMemberExpression(exprNode) && !exprNode.computed) return;
512
- collectSignals(exprNode[key]);
513
- });
514
- }
515
-
516
- function processNode(n, parentElId) {
517
- if (t.isJSXElement(n)) {
518
- const tagName = n.openingElement.name.name;
519
- const isComponent = /^[A-Z]/.test(tagName);
520
- const elId = nextId();
521
-
522
- if (isComponent) {
523
- const componentTagName = "web-" + tagName.toLowerCase();
524
- statements.push(t.variableDeclaration("const", [
525
- t.variableDeclarator(elId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElement")), [t.stringLiteral(componentTagName)]))
526
- ]));
527
-
528
- n.openingElement.attributes.forEach(attr => {
529
- if (t.isJSXSpreadAttribute(attr)) {
530
- const applySpreadId = getImport("applySpread", state.runtimeSource);
531
- statements.push(t.expressionStatement(t.callExpression(applySpreadId, [elId, attr.argument])));
532
- return;
533
- }
534
- const name = attr.name.name;
535
- const value = attr.value;
536
- const targetProp = name === "class" || name === "className" ? "className" : name;
537
-
538
- if (t.isJSXExpressionContainer(value)) {
539
- if (name === "ref" && t.isIdentifier(value.expression) && state.refVars?.has(value.expression.name)) {
540
- const refName = value.expression.name;
541
- const innerId = t.identifier(refName);
542
- innerId._processed = true;
543
- statements.push(t.expressionStatement(t.assignmentExpression(
544
- "=",
545
- t.memberExpression(innerId, t.identifier("value")),
546
- elId
547
- )));
548
- return;
549
- }
550
- collectSignals(value.expression);
551
- const effectId = getImport("effect", state.runtimeSource);
552
- statements.push(t.expressionStatement(t.callExpression(effectId, [
553
- t.arrowFunctionExpression([], t.assignmentExpression("=", t.memberExpression(elId, t.identifier(targetProp)), value.expression))
554
- ])));
555
- } else {
556
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier(targetProp)), value)));
557
- }
558
- });
559
-
560
-
561
- n.children.forEach(child => {
562
- const childId = processNode(child, elId);
563
- if (childId) statements.push(t.expressionStatement(t.callExpression(t.memberExpression(elId, t.identifier("appendChild")), [childId])));
564
- });
565
- return elId;
566
- }
567
-
568
- const svgTags = ['svg', 'path', 'polyline', 'line', 'circle', 'rect', 'ellipse', 'polygon', 'g', 'text', 'tspan', 'defs', 'lineargradient', 'stop'];
569
- const isSvg = svgTags.includes(tagName.toLowerCase());
570
-
571
- statements.push(t.variableDeclaration("const", [
572
- t.variableDeclarator(
573
- elId,
574
- isSvg
575
- ? t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElementNS")), [t.stringLiteral("http://www.w3.org/2000/svg"), t.stringLiteral(tagName)])
576
- : t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createElement")), [t.stringLiteral(tagName)])
577
- )
578
- ]));
579
-
580
- n.openingElement.attributes.forEach(attr => {
581
- if (t.isJSXSpreadAttribute(attr)) {
582
- const applySpreadId = getImport("applySpread", state.runtimeSource);
583
- statements.push(t.expressionStatement(t.callExpression(applySpreadId, [elId, attr.argument])));
584
- return;
585
- }
586
- const originalName = attr.name.name;
587
- const name = originalName.toLowerCase();
588
- const value = attr.value;
589
-
590
- if (name.startsWith("on")) {
591
- if (t.isJSXExpressionContainer(value) && t.isJSXEmptyExpression(value.expression)) return;
592
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier(name)), t.isJSXExpressionContainer(value) ? value.expression : value)));
593
- } else if (t.isJSXExpressionContainer(value)) {
594
- if (t.isJSXEmptyExpression(value.expression)) return;
595
-
596
- if (originalName === "ref" && t.isIdentifier(value.expression) && state.refVars?.has(value.expression.name)) {
597
- const refName = value.expression.name;
598
- const innerId = t.identifier(refName);
599
- innerId._processed = true;
600
- statements.push(t.expressionStatement(t.assignmentExpression(
601
- "=",
602
- t.memberExpression(innerId, t.identifier("value")),
603
- elId
604
- )));
605
- return;
606
- }
607
-
608
- collectSignals(value.expression);
609
-
610
- const effectId = getImport("effect", state.runtimeSource);
611
- const attrProp = (name === "class" || name === "classname") ? "className" : name;
612
- const isStyle = attrProp === "style";
613
- const isProperty = ["className", "style", "value", "checked", "id", "title", "href", "src", "key"].includes(attrProp);
614
-
615
- if (attrProp === "key") {
616
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier("_key")), value.expression)));
617
- } else {
618
- statements.push(t.expressionStatement(t.callExpression(effectId, [t.arrowFunctionExpression([],
619
- isStyle ? t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [t.memberExpression(elId, t.identifier("style")), value.expression])
620
- : (isProperty && (!isSvg || attrProp !== "className")) ? t.assignmentExpression("=", t.memberExpression(elId, t.identifier(attrProp)), value.expression)
621
- : t.callExpression(t.memberExpression(elId, t.identifier("setAttribute")), [t.stringLiteral(attrProp === "className" ? "class" : toKebabCase(originalName)), value.expression])
622
- )])));
623
- }
624
- } else {
625
- const attrProp = (name === "class" || name === "classname") ? "className" : name;
626
- const isProperty = ["className", "style", "value", "checked", "id", "title", "href", "src", "key"].includes(attrProp);
627
-
628
- if (isProperty && (!isSvg || attrProp !== "className")) {
629
- statements.push(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(elId, t.identifier(attrProp === "key" ? "_key" : attrProp)), value)));
630
- } else {
631
- statements.push(t.expressionStatement(t.callExpression(t.memberExpression(elId, t.identifier("setAttribute")), [t.stringLiteral(attrProp === "className" ? "class" : toKebabCase(originalName)), value])));
632
- }
633
- }
634
- });
635
-
636
- n.children.forEach(child => {
637
- const childId = processNode(child, elId);
638
- if (childId) statements.push(t.expressionStatement(t.callExpression(t.memberExpression(elId, t.identifier("appendChild")), [childId])));
639
- });
640
- return elId;
641
- } else if (t.isJSXText(n)) {
642
- const text = n.value.replace(/\n\s*/g, "");
643
- if (!text) return null;
644
- const textId = nextId("text");
645
- statements.push(t.variableDeclaration("const", [t.variableDeclarator(textId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createTextNode")), [t.stringLiteral(text)]))]));
646
- return textId;
647
- } else if (t.isJSXFragment(n)) {
648
- const fragId = nextId("frag");
649
- statements.push(t.variableDeclaration("const", [t.variableDeclarator(fragId, t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createDocumentFragment")), []))]));
650
- n.children.forEach(child => {
651
- const childId = processNode(child, fragId);
652
- if (childId) statements.push(t.expressionStatement(t.callExpression(t.memberExpression(fragId, t.identifier("appendChild")), [childId])));
653
- });
654
- return fragId;
655
- } else if (t.isJSXExpressionContainer(n)) {
656
- if (t.isJSXEmptyExpression(n.expression)) return null;
657
-
658
- collectSignals(n.expression);
659
-
660
- // Recursive helper to transform nested JSX elements and .map() calls
661
- const transformExpression = (exprNode) => {
662
- if (!exprNode || typeof exprNode !== "object") return;
663
- if (Array.isArray(exprNode)) {
664
- exprNode.forEach(transformExpression);
665
- return;
666
- }
667
-
668
- Object.keys(exprNode).forEach(key => {
669
- const child = exprNode[key];
670
- if (!child || typeof child !== "object") return;
671
-
672
- // Transform nested JSX elements into imperative IIFEs
673
- if (t.isJSXElement(child) || t.isJSXFragment(child)) {
674
- if (child._processed) return;
675
- const { statements: innerStatements, rootId: innerRootId, signals: innerSignals } = transformJSX(child, t, state, getImport, path);
676
- innerSignals.forEach(s => signals.add(s));
677
- exprNode[key] = t.callExpression(t.arrowFunctionExpression([], t.blockStatement([
678
- ...innerStatements,
679
- t.returnStatement(innerRootId)
680
- ])), []);
681
- exprNode[key]._processed = true;
682
- }
683
- // Transform .map() into optimized keyed mapped() call
684
- else if (t.isCallExpression(child) &&
685
- t.isMemberExpression(child.callee) &&
686
- t.isIdentifier(child.callee.property, { name: "map" })) {
687
-
688
- const mappedId = getImport("mapped", state.runtimeSource);
689
- const sourceArray = child.callee.object;
690
- const mapFn = child.arguments[0];
691
-
692
- const mappedInstanceId = nextId("mapped");
693
- statements.push(t.variableDeclaration("const", [
694
- t.variableDeclarator(mappedInstanceId, t.callExpression(mappedId, [
695
- t.arrowFunctionExpression([], sourceArray),
696
- mapFn
697
- ]))
698
- ]));
699
-
700
- exprNode[key] = t.callExpression(mappedInstanceId, []);
701
- // Recurse into the map function body if needed
702
- transformExpression(mapFn);
703
- }
704
- else {
705
- transformExpression(child);
706
- }
707
- });
708
- };
709
-
710
- const wrapper = { expr: n.expression };
711
- transformExpression(wrapper);
712
- const finalExpression = wrapper.expr;
713
-
714
- const renderDynamicId = getImport("renderDynamic", state.runtimeSource);
715
- statements.push(t.expressionStatement(t.callExpression(renderDynamicId, [
716
- parentElId,
717
- t.arrowFunctionExpression([], finalExpression)
718
- ])));
719
-
720
- return null;
721
- } else if (t.isExpression(n)) {
722
- return n;
723
- }
724
- }
725
-
726
-
727
- const rootId = processNode(node, t.identifier("root"));
728
- return { statements, rootId, signals };
729
- }
730
-
731
- };