juxscript 1.1.280 → 1.1.282

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.
@@ -39,29 +39,6 @@ function containsPageStateRef(node) {
39
39
  return found;
40
40
  }
41
41
 
42
- function containsJuxCall(node) {
43
- let found = false;
44
- walkSimple(node, {
45
- CallExpression(n) { if (n.callee?.object?.name === 'jux') found = true; }
46
- });
47
- return found;
48
- }
49
-
50
- function getDeclaredVarNames(stmt) {
51
- if (stmt.type !== 'VariableDeclaration') return [];
52
- return stmt.declarations
53
- .filter(d => d.id.type === 'Identifier')
54
- .map(d => d.id.name);
55
- }
56
-
57
- function usesIdentifier(node, name) {
58
- let found = false;
59
- walkSimple(node, {
60
- Identifier(n) { if (n.name === name) found = true; }
61
- });
62
- return found;
63
- }
64
-
65
42
  function isAlreadyWrapped(stmt) {
66
43
  return stmt.type === 'ExpressionStatement' &&
67
44
  stmt.expression?.type === 'CallExpression' &&
@@ -69,20 +46,10 @@ function isAlreadyWrapped(stmt) {
69
46
  stmt.expression?.callee?.property?.name === '__watch';
70
47
  }
71
48
 
72
- function isSetupStatement(stmt) {
73
- // Never treat statements referencing pageState as setup
74
- if (containsPageStateRef(stmt)) return false;
75
-
49
+ function isSetupOnly(stmt) {
50
+ // Statements that should stay outside __watch
76
51
  if (stmt.type === 'ImportDeclaration') return true;
77
52
  if (stmt.type === 'FunctionDeclaration') return true;
78
- if (stmt.type === 'ExpressionStatement') {
79
- const expr = stmt.expression;
80
- if (expr.type === 'CallExpression' &&
81
- expr.callee?.object?.name === 'jux') return true;
82
- if (expr.type === 'AwaitExpression' &&
83
- expr.argument?.callee?.object?.name === 'jux') return true;
84
- }
85
- if (stmt.type === 'VariableDeclaration') return true;
86
53
  return false;
87
54
  }
88
55
 
@@ -108,86 +75,68 @@ export function autowrap(source, filename = '') {
108
75
  allowAwaitOutsideFunction: true,
109
76
  });
110
77
  } catch (err) {
111
- // Can't parse — return as-is
112
78
  return { code: source, wrappedCount: 0, details: [`parse error: ${err.message}`] };
113
79
  }
114
80
 
115
- const needsWatch = [];
116
- let i = 0;
81
+ // Check if there are any unwrapped pageState references at all
82
+ let hasUnwrappedPageState = false;
83
+ let firstUnwrappedIdx = -1;
84
+ let lastUnwrappedIdx = -1;
117
85
 
118
- while (i < ast.body.length) {
86
+ for (let i = 0; i < ast.body.length; i++) {
119
87
  const stmt = ast.body[i];
120
-
121
- if (isSetupStatement(stmt)) { i++; continue; }
122
- if (isAlreadyWrapped(stmt)) { i++; continue; }
123
-
124
- // VariableDeclaration reading pageState — group with subsequent stmts that use declared vars
125
- if (stmt.type === 'VariableDeclaration' && containsPageStateRef(stmt)) {
126
- const varNames = getDeclaredVarNames(stmt);
127
- const groupStmts = [stmt];
128
- let j = i + 1;
129
-
130
- // Greedily consume following statements that use any of the declared variable names
131
- while (j < ast.body.length) {
132
- const next = ast.body[j];
133
- if (isAlreadyWrapped(next) || isSetupStatement(next)) break;
134
- if (varNames.some(v => usesIdentifier(next, v))) {
135
- groupStmts.push(next);
136
- j++;
137
- } else {
138
- break;
139
- }
140
- }
141
-
142
- needsWatch.push({
143
- line: getLineNumber(source, groupStmts[0].start),
144
- endLine: getLineNumber(source, groupStmts[groupStmts.length - 1].end),
145
- });
146
- i = j;
147
- continue;
148
- }
149
-
150
- // Any other statement referencing pageState
88
+ if (isAlreadyWrapped(stmt)) continue;
89
+ if (isSetupOnly(stmt)) continue;
151
90
  if (containsPageStateRef(stmt)) {
152
- needsWatch.push({
153
- line: getLineNumber(source, stmt.start),
154
- endLine: getLineNumber(source, stmt.end),
155
- });
156
- i++;
157
- continue;
91
+ hasUnwrappedPageState = true;
92
+ if (firstUnwrappedIdx === -1) firstUnwrappedIdx = i;
93
+ lastUnwrappedIdx = i;
158
94
  }
159
-
160
- i++;
161
95
  }
162
96
 
163
- if (needsWatch.length === 0) {
97
+ if (!hasUnwrappedPageState) {
164
98
  return { code: source, wrappedCount: 0, details: [] };
165
99
  }
166
100
 
167
- // Apply wraps bottom-up to preserve line numbers
101
+ // Find the contiguous range: from first unwrapped pageState ref
102
+ // to end of file (or last unwrapped ref), including any non-pageState
103
+ // statements in between (they may depend on reactive variables).
104
+ // But exclude leading setup-only statements (imports, function decls).
105
+
106
+ // Walk backwards from firstUnwrappedIdx to include any immediately
107
+ // preceding non-setup statements that declare variables used later
108
+ let rangeStart = firstUnwrappedIdx;
109
+ let rangeEnd = lastUnwrappedIdx;
110
+
111
+ // Collect all statements in the range
168
112
  const lines = source.split('\n');
169
- const sorted = [...needsWatch].sort((a, b) => b.line - a.line);
170
- const details = [];
171
-
172
- for (const item of sorted) {
173
- const startIdx = item.line - 1;
174
- const endIdx = item.endLine - 1;
175
- const blockLines = lines.slice(startIdx, endIdx + 1);
176
- const indent = blockLines[0].match(/^(\s*)/)[1];
177
-
178
- const wrapped = [
179
- `${indent}pageState.__watch(() => {`,
180
- ...blockLines.map(l => `${indent} ${l.trim()}`),
181
- `${indent}});`,
182
- ];
183
-
184
- lines.splice(startIdx, endIdx - startIdx + 1, ...wrapped);
185
- details.push(`L${item.line}-${item.endLine}`);
113
+ const stmtsInRange = ast.body.slice(rangeStart, rangeEnd + 1);
114
+
115
+ // Check if ALL pageState refs are already wrapped (nothing to do)
116
+ const unwrappedInRange = stmtsInRange.filter(s => !isAlreadyWrapped(s) && containsPageStateRef(s));
117
+ if (unwrappedInRange.length === 0) {
118
+ return { code: source, wrappedCount: 0, details: [] };
186
119
  }
187
120
 
121
+ const startLine = getLineNumber(source, stmtsInRange[0].start);
122
+ const endLine = getLineNumber(source, stmtsInRange[stmtsInRange.length - 1].end);
123
+
124
+ const startIdx = startLine - 1;
125
+ const endIdx = endLine - 1;
126
+ const blockLines = lines.slice(startIdx, endIdx + 1);
127
+ const indent = blockLines[0].match(/^(\s*)/)[1];
128
+
129
+ const wrapped = [
130
+ `${indent}pageState.__watch(() => {`,
131
+ ...blockLines.map(l => `${indent} ${l.trimEnd()}`),
132
+ `${indent}});`,
133
+ ];
134
+
135
+ lines.splice(startIdx, endIdx - startIdx + 1, ...wrapped);
136
+
188
137
  return {
189
138
  code: lines.join('\n'),
190
- wrappedCount: needsWatch.length,
191
- details
139
+ wrappedCount: 1,
140
+ details: [`L${startLine}-${endLine} (single watch block)`],
192
141
  };
193
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.280",
3
+ "version": "1.1.282",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "./dist/lib/index.js",