juxscript 1.1.378 → 1.1.380

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 (38) hide show
  1. package/dist/components/button.d.ts +4 -0
  2. package/dist/components/button.d.ts.map +1 -1
  3. package/dist/components/button.js +13 -1
  4. package/dist/components/button.js.map +1 -1
  5. package/dist/components/checkbox.d.ts +6 -0
  6. package/dist/components/checkbox.d.ts.map +1 -1
  7. package/dist/components/checkbox.js +14 -0
  8. package/dist/components/checkbox.js.map +1 -1
  9. package/dist/components/input.d.ts +12 -0
  10. package/dist/components/input.d.ts.map +1 -1
  11. package/dist/components/input.js +43 -0
  12. package/dist/components/input.js.map +1 -1
  13. package/dist/components/link.d.ts +4 -0
  14. package/dist/components/link.d.ts.map +1 -1
  15. package/dist/components/link.js +13 -1
  16. package/dist/components/link.js.map +1 -1
  17. package/dist/components/list.d.ts +6 -0
  18. package/dist/components/list.d.ts.map +1 -1
  19. package/dist/components/list.js +26 -1
  20. package/dist/components/list.js.map +1 -1
  21. package/dist/components/nav.d.ts +6 -0
  22. package/dist/components/nav.d.ts.map +1 -1
  23. package/dist/components/nav.js +16 -0
  24. package/dist/components/nav.js.map +1 -1
  25. package/dist/components/radio.d.ts +3 -0
  26. package/dist/components/radio.d.ts.map +1 -1
  27. package/dist/components/radio.js +7 -0
  28. package/dist/components/radio.js.map +1 -1
  29. package/dist/components/select.d.ts +9 -0
  30. package/dist/components/select.d.ts.map +1 -1
  31. package/dist/components/select.js +31 -0
  32. package/dist/components/select.js.map +1 -1
  33. package/dist/components/tag.d.ts +5 -0
  34. package/dist/components/tag.d.ts.map +1 -1
  35. package/dist/components/tag.js +16 -1
  36. package/dist/components/tag.js.map +1 -1
  37. package/machinery/autowrap.js +103 -24
  38. package/package.json +1 -1
@@ -31,6 +31,31 @@ function isPageStateAccess(node) {
31
31
  return false;
32
32
  }
33
33
 
34
+ /**
35
+ * Checks if a node is a `.state.xxx` access on a known jux component variable.
36
+ * e.g. `btn2.state.click` where `btn2` is in juxVarNames
37
+ */
38
+ function isComponentStateAccess(node, juxVarNames) {
39
+ if (node.type !== 'MemberExpression') return false;
40
+ const obj = node.object;
41
+ // Pattern: <varName>.state.<prop>
42
+ if (obj.type === 'MemberExpression' &&
43
+ obj.object.type === 'Identifier' &&
44
+ juxVarNames.has(obj.object.name) &&
45
+ obj.property.type === 'Identifier' &&
46
+ obj.property.name === 'state') {
47
+ return true;
48
+ }
49
+ // Pattern: <varName>.state (without further property — still reactive)
50
+ if (node.object.type === 'Identifier' &&
51
+ juxVarNames.has(node.object.name) &&
52
+ node.property.type === 'Identifier' &&
53
+ node.property.name === 'state') {
54
+ return true;
55
+ }
56
+ return false;
57
+ }
58
+
34
59
  function containsPageStateRef(node) {
35
60
  let found = false;
36
61
  walkSimple(node, {
@@ -39,6 +64,24 @@ function containsPageStateRef(node) {
39
64
  return found;
40
65
  }
41
66
 
67
+ /**
68
+ * Checks if a node tree contains any .state. access on known jux component variables.
69
+ */
70
+ function containsComponentStateRef(node, juxVarNames) {
71
+ let found = false;
72
+ walkSimple(node, {
73
+ MemberExpression(n) { if (isComponentStateAccess(n, juxVarNames)) found = true; }
74
+ });
75
+ return found;
76
+ }
77
+
78
+ /**
79
+ * Returns true if the node contains either a pageState ref or a component .state. ref.
80
+ */
81
+ function containsAnyReactiveRef(node, juxVarNames) {
82
+ return containsPageStateRef(node) || containsComponentStateRef(node, juxVarNames);
83
+ }
84
+
42
85
  function containsJuxCall(node) {
43
86
  let found = false;
44
87
  walkSimple(node, {
@@ -118,6 +161,51 @@ function containsAwait(node) {
118
161
  return found;
119
162
  }
120
163
 
164
+ /**
165
+ * Collects variable names that are assigned from jux.xxx() calls.
166
+ * e.g. `let btn2 = jux.btn('id')` → adds 'btn2' to the set.
167
+ */
168
+ function collectJuxVarNames(ast) {
169
+ const names = new Set();
170
+ for (const stmt of ast.body) {
171
+ if (stmt.type === 'VariableDeclaration') {
172
+ for (const decl of stmt.declarations) {
173
+ if (decl.id.type === 'Identifier' && decl.init) {
174
+ let call = decl.init;
175
+ // Unwrap chained calls: jux.btn('x').onClick(...)
176
+ while (call.type === 'CallExpression' &&
177
+ call.callee.type === 'MemberExpression' &&
178
+ call.callee.object.type === 'CallExpression') {
179
+ call = call.callee.object;
180
+ }
181
+ if (call.type === 'CallExpression' &&
182
+ call.callee?.type === 'MemberExpression' &&
183
+ call.callee.object?.type === 'Identifier' &&
184
+ call.callee.object.name === 'jux') {
185
+ names.add(decl.id.name);
186
+ }
187
+ // Also handle: await jux.xxx()
188
+ if (call.type === 'AwaitExpression') {
189
+ let inner = call.argument;
190
+ while (inner?.type === 'CallExpression' &&
191
+ inner.callee?.type === 'MemberExpression' &&
192
+ inner.callee.object?.type === 'CallExpression') {
193
+ inner = inner.callee.object;
194
+ }
195
+ if (inner?.type === 'CallExpression' &&
196
+ inner.callee?.type === 'MemberExpression' &&
197
+ inner.callee.object?.type === 'Identifier' &&
198
+ inner.callee.object.name === 'jux') {
199
+ names.add(decl.id.name);
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ return names;
207
+ }
208
+
121
209
  /**
122
210
  * @param {string} source - Raw .jux source code
123
211
  * @param {string} [filename] - For logging
@@ -136,23 +224,23 @@ export function autowrap(source, filename = '') {
136
224
  return { code: source, wrappedCount: 0, details: [`parse error: ${err.message}`] };
137
225
  }
138
226
 
227
+ // Collect jux component variable names for .state. detection
228
+ const juxVarNames = collectJuxVarNames(ast);
229
+
139
230
  const needsWatch = [];
140
231
  let i = 0;
141
232
 
142
233
  while (i < ast.body.length) {
143
234
  const stmt = ast.body[i];
144
235
 
145
- if (isSetupStatement(stmt)) { i++; continue; }
236
+ if (isSetupStatement(stmt) && !containsComponentStateRef(stmt, juxVarNames)) { i++; continue; }
146
237
 
147
238
  // Check if this is a single __watch that contains multiple independent concerns
148
- // If so, unwrap it and re-wrap each concern separately
149
239
  if (isAlreadyWrapped(stmt)) {
150
240
  const bodyStmts = getWatchBody(stmt);
151
241
  if (bodyStmts && bodyStmts.length > 1) {
152
- // Check if the body has multiple independent reactive groups
153
- const groups = groupBodyStatements(bodyStmts, source);
242
+ const groups = groupBodyStatements(bodyStmts, source, juxVarNames);
154
243
  if (groups.length > 1) {
155
- // Verify no cross-group variable dependencies before splitting
156
244
  const allDeclared = [];
157
245
  let hasCrossDep = false;
158
246
  for (const group of groups) {
@@ -160,7 +248,6 @@ export function autowrap(source, filename = '') {
160
248
  for (const s of group.stmts) {
161
249
  groupVars.push(...getDeclaredVarNames(s));
162
250
  }
163
- // Check if this group uses vars declared in a previous group
164
251
  for (const s of group.stmts) {
165
252
  if (allDeclared.some(v => usesIdentifier(s, v))) {
166
253
  hasCrossDep = true;
@@ -187,19 +274,16 @@ export function autowrap(source, filename = '') {
187
274
  continue;
188
275
  }
189
276
 
190
- // VariableDeclaration reading pageState group with subsequent stmts that use declared vars
191
- if (stmt.type === 'VariableDeclaration' && containsPageStateRef(stmt)) {
277
+ // VariableDeclaration reading pageState or component .state.
278
+ if (stmt.type === 'VariableDeclaration' && containsAnyReactiveRef(stmt, juxVarNames)) {
192
279
  const varNames = [...getDeclaredVarNames(stmt)];
193
280
  const groupStmts = [stmt];
194
281
  let j = i + 1;
195
282
 
196
- // Greedily consume following statements that use any of the declared variable names
197
283
  while (j < ast.body.length) {
198
284
  const next = ast.body[j];
199
- // Check variable dependency FIRST — if it uses our vars, consume it
200
285
  if (varNames.some(v => usesIdentifier(next, v))) {
201
286
  groupStmts.push(next);
202
- // Track any new variable declarations in the consumed statement
203
287
  getDeclaredVarNames(next).forEach(v => varNames.push(v));
204
288
  j++;
205
289
  } else {
@@ -215,8 +299,8 @@ export function autowrap(source, filename = '') {
215
299
  continue;
216
300
  }
217
301
 
218
- // Any other statement referencing pageState
219
- if (containsPageStateRef(stmt)) {
302
+ // Any other statement referencing pageState or component .state.
303
+ if (containsAnyReactiveRef(stmt, juxVarNames)) {
220
304
  needsWatch.push({
221
305
  line: getLineNumber(source, stmt.start),
222
306
  endLine: getLineNumber(source, stmt.end),
@@ -287,29 +371,24 @@ export function autowrap(source, filename = '') {
287
371
 
288
372
  /**
289
373
  * Groups statements inside a __watch body into independent reactive groups.
290
- * Uses the same logic as the top-level grouping: VariableDeclarations that
291
- * read pageState get grouped with following statements that use those vars.
292
- * Standalone if-statements with pageState become their own group.
293
374
  */
294
- function groupBodyStatements(bodyStmts, source) {
375
+ function groupBodyStatements(bodyStmts, source, juxVarNames = new Set()) {
295
376
  const groups = [];
296
377
  let i = 0;
297
378
 
298
379
  while (i < bodyStmts.length) {
299
380
  const stmt = bodyStmts[i];
300
381
 
301
- // VariableDeclaration reading pageState — group with dependents
302
- if (stmt.type === 'VariableDeclaration' && containsPageStateRef(stmt)) {
382
+ // VariableDeclaration reading pageState or .state. — group with dependents
383
+ if (stmt.type === 'VariableDeclaration' && containsAnyReactiveRef(stmt, juxVarNames)) {
303
384
  const varNames = getDeclaredVarNames(stmt);
304
385
  const groupStmts = [stmt];
305
386
  let j = i + 1;
306
387
 
307
388
  while (j < bodyStmts.length) {
308
389
  const next = bodyStmts[j];
309
- // Also collect any new variable names declared within grouped statements
310
- if (varNames.some(v => usesIdentifier(next, v)) || !containsPageStateRef(next)) {
390
+ if (varNames.some(v => usesIdentifier(next, v)) || !containsAnyReactiveRef(next, juxVarNames)) {
311
391
  groupStmts.push(next);
312
- // Track any new variable declarations in the consumed statement
313
392
  getDeclaredVarNames(next).forEach(v => varNames.push(v));
314
393
  j++;
315
394
  } else {
@@ -325,8 +404,8 @@ function groupBodyStatements(bodyStmts, source) {
325
404
  continue;
326
405
  }
327
406
 
328
- // Any statement referencing pageState — standalone group
329
- if (containsPageStateRef(stmt)) {
407
+ // Any statement referencing reactive state — standalone group
408
+ if (containsAnyReactiveRef(stmt, juxVarNames)) {
330
409
  groups.push({
331
410
  stmts: [stmt],
332
411
  lines: extractSourceLines([stmt], source),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.378",
3
+ "version": "1.1.380",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "./dist/index.js",