juxscript 1.1.281 → 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.
- package/machinery/autowrap.js +45 -212
- package/package.json +1 -1
package/machinery/autowrap.js
CHANGED
|
@@ -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,36 +46,10 @@ function isAlreadyWrapped(stmt) {
|
|
|
69
46
|
stmt.expression?.callee?.property?.name === '__watch';
|
|
70
47
|
}
|
|
71
48
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
* Returns null if the statement is not a __watch or has no block body.
|
|
75
|
-
*/
|
|
76
|
-
function getWatchBody(stmt) {
|
|
77
|
-
if (!isAlreadyWrapped(stmt)) return null;
|
|
78
|
-
const arg = stmt.expression.arguments?.[0];
|
|
79
|
-
if (!arg) return null;
|
|
80
|
-
if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
|
|
81
|
-
if (arg.body?.type === 'BlockStatement') {
|
|
82
|
-
return arg.body.body; // array of statements
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function isSetupStatement(stmt) {
|
|
89
|
-
// Never treat statements referencing pageState as setup
|
|
90
|
-
if (containsPageStateRef(stmt)) return false;
|
|
91
|
-
|
|
49
|
+
function isSetupOnly(stmt) {
|
|
50
|
+
// Statements that should stay outside __watch
|
|
92
51
|
if (stmt.type === 'ImportDeclaration') return true;
|
|
93
52
|
if (stmt.type === 'FunctionDeclaration') return true;
|
|
94
|
-
if (stmt.type === 'ExpressionStatement') {
|
|
95
|
-
const expr = stmt.expression;
|
|
96
|
-
if (expr.type === 'CallExpression' &&
|
|
97
|
-
expr.callee?.object?.name === 'jux') return true;
|
|
98
|
-
if (expr.type === 'AwaitExpression' &&
|
|
99
|
-
expr.argument?.callee?.object?.name === 'jux') return true;
|
|
100
|
-
}
|
|
101
|
-
if (stmt.type === 'VariableDeclaration') return true;
|
|
102
53
|
return false;
|
|
103
54
|
}
|
|
104
55
|
|
|
@@ -124,186 +75,68 @@ export function autowrap(source, filename = '') {
|
|
|
124
75
|
allowAwaitOutsideFunction: true,
|
|
125
76
|
});
|
|
126
77
|
} catch (err) {
|
|
127
|
-
// Can't parse — return as-is
|
|
128
78
|
return { code: source, wrappedCount: 0, details: [`parse error: ${err.message}`] };
|
|
129
79
|
}
|
|
130
80
|
|
|
131
|
-
|
|
132
|
-
let
|
|
81
|
+
// Check if there are any unwrapped pageState references at all
|
|
82
|
+
let hasUnwrappedPageState = false;
|
|
83
|
+
let firstUnwrappedIdx = -1;
|
|
84
|
+
let lastUnwrappedIdx = -1;
|
|
133
85
|
|
|
134
|
-
|
|
86
|
+
for (let i = 0; i < ast.body.length; i++) {
|
|
135
87
|
const stmt = ast.body[i];
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
// Check if this is a single __watch that contains multiple independent concerns
|
|
140
|
-
// If so, unwrap it and re-wrap each concern separately
|
|
141
|
-
if (isAlreadyWrapped(stmt)) {
|
|
142
|
-
const bodyStmts = getWatchBody(stmt);
|
|
143
|
-
if (bodyStmts && bodyStmts.length > 1) {
|
|
144
|
-
// Check if the body has multiple independent reactive groups
|
|
145
|
-
const groups = groupBodyStatements(bodyStmts, source);
|
|
146
|
-
if (groups.length > 1) {
|
|
147
|
-
// Replace single watch with multiple watches
|
|
148
|
-
// Record the original watch range for removal
|
|
149
|
-
const watchStart = getLineNumber(source, stmt.start);
|
|
150
|
-
const watchEnd = getLineNumber(source, stmt.end);
|
|
151
|
-
needsWatch.push({
|
|
152
|
-
line: watchStart,
|
|
153
|
-
endLine: watchEnd,
|
|
154
|
-
replace: true,
|
|
155
|
-
groups: groups,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
i++;
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// VariableDeclaration reading pageState — group with subsequent stmts that use declared vars
|
|
164
|
-
if (stmt.type === 'VariableDeclaration' && containsPageStateRef(stmt)) {
|
|
165
|
-
const varNames = getDeclaredVarNames(stmt);
|
|
166
|
-
const groupStmts = [stmt];
|
|
167
|
-
let j = i + 1;
|
|
168
|
-
|
|
169
|
-
// Greedily consume following statements that use any of the declared variable names
|
|
170
|
-
while (j < ast.body.length) {
|
|
171
|
-
const next = ast.body[j];
|
|
172
|
-
if (isAlreadyWrapped(next) || isSetupStatement(next)) break;
|
|
173
|
-
if (varNames.some(v => usesIdentifier(next, v))) {
|
|
174
|
-
groupStmts.push(next);
|
|
175
|
-
j++;
|
|
176
|
-
} else {
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
needsWatch.push({
|
|
182
|
-
line: getLineNumber(source, groupStmts[0].start),
|
|
183
|
-
endLine: getLineNumber(source, groupStmts[groupStmts.length - 1].end),
|
|
184
|
-
});
|
|
185
|
-
i = j;
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Any other statement referencing pageState
|
|
88
|
+
if (isAlreadyWrapped(stmt)) continue;
|
|
89
|
+
if (isSetupOnly(stmt)) continue;
|
|
190
90
|
if (containsPageStateRef(stmt)) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
195
|
-
i++;
|
|
196
|
-
continue;
|
|
91
|
+
hasUnwrappedPageState = true;
|
|
92
|
+
if (firstUnwrappedIdx === -1) firstUnwrappedIdx = i;
|
|
93
|
+
lastUnwrappedIdx = i;
|
|
197
94
|
}
|
|
198
|
-
|
|
199
|
-
i++;
|
|
200
95
|
}
|
|
201
96
|
|
|
202
|
-
if (
|
|
97
|
+
if (!hasUnwrappedPageState) {
|
|
203
98
|
return { code: source, wrappedCount: 0, details: [] };
|
|
204
99
|
}
|
|
205
100
|
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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).
|
|
210
105
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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;
|
|
214
110
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const newLines = [];
|
|
219
|
-
for (const group of item.groups) {
|
|
220
|
-
newLines.push(`${indent}pageState.__watch(() => {`);
|
|
221
|
-
for (const gLine of group.lines) {
|
|
222
|
-
newLines.push(`${indent} ${gLine.trim()}`);
|
|
223
|
-
}
|
|
224
|
-
newLines.push(`${indent}});`);
|
|
225
|
-
}
|
|
226
|
-
lines.splice(startIdx, endIdx - startIdx + 1, ...newLines);
|
|
227
|
-
details.push(`L${item.line}-${item.endLine} (split ${item.groups.length})`);
|
|
228
|
-
} else {
|
|
229
|
-
const blockLines = lines.slice(startIdx, endIdx + 1);
|
|
230
|
-
const indent = blockLines[0].match(/^(\s*)/)[1];
|
|
231
|
-
|
|
232
|
-
const wrapped = [
|
|
233
|
-
`${indent}pageState.__watch(() => {`,
|
|
234
|
-
...blockLines.map(l => `${indent} ${l.trim()}`),
|
|
235
|
-
`${indent}});`,
|
|
236
|
-
];
|
|
111
|
+
// Collect all statements in the range
|
|
112
|
+
const lines = source.split('\n');
|
|
113
|
+
const stmtsInRange = ast.body.slice(rangeStart, rangeEnd + 1);
|
|
237
114
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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: [] };
|
|
241
119
|
}
|
|
242
120
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
wrappedCount: needsWatch.length,
|
|
246
|
-
details
|
|
247
|
-
};
|
|
248
|
-
}
|
|
121
|
+
const startLine = getLineNumber(source, stmtsInRange[0].start);
|
|
122
|
+
const endLine = getLineNumber(source, stmtsInRange[stmtsInRange.length - 1].end);
|
|
249
123
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
* Standalone if-statements with pageState become their own group.
|
|
255
|
-
*/
|
|
256
|
-
function groupBodyStatements(bodyStmts, source) {
|
|
257
|
-
const groups = [];
|
|
258
|
-
let i = 0;
|
|
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];
|
|
259
128
|
|
|
260
|
-
|
|
261
|
-
|
|
129
|
+
const wrapped = [
|
|
130
|
+
`${indent}pageState.__watch(() => {`,
|
|
131
|
+
...blockLines.map(l => `${indent} ${l.trimEnd()}`),
|
|
132
|
+
`${indent}});`,
|
|
133
|
+
];
|
|
262
134
|
|
|
263
|
-
|
|
264
|
-
if (stmt.type === 'VariableDeclaration' && containsPageStateRef(stmt)) {
|
|
265
|
-
const varNames = getDeclaredVarNames(stmt);
|
|
266
|
-
const groupStmts = [stmt];
|
|
267
|
-
let j = i + 1;
|
|
135
|
+
lines.splice(startIdx, endIdx - startIdx + 1, ...wrapped);
|
|
268
136
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
} else {
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
groups.push({
|
|
280
|
-
stmts: groupStmts,
|
|
281
|
-
lines: extractSourceLines(groupStmts, source),
|
|
282
|
-
});
|
|
283
|
-
i = j;
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Any statement referencing pageState — standalone group
|
|
288
|
-
if (containsPageStateRef(stmt)) {
|
|
289
|
-
groups.push({
|
|
290
|
-
stmts: [stmt],
|
|
291
|
-
lines: extractSourceLines([stmt], source),
|
|
292
|
-
});
|
|
293
|
-
i++;
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Non-reactive statement — attach to next reactive group or skip
|
|
298
|
-
i++;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return groups;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function extractSourceLines(stmts, source) {
|
|
305
|
-
if (stmts.length === 0) return [];
|
|
306
|
-
const start = stmts[0].start;
|
|
307
|
-
const end = stmts[stmts.length - 1].end;
|
|
308
|
-
return source.slice(start, end).split('\n');
|
|
137
|
+
return {
|
|
138
|
+
code: lines.join('\n'),
|
|
139
|
+
wrappedCount: 1,
|
|
140
|
+
details: [`L${startLine}-${endLine} (single watch block)`],
|
|
141
|
+
};
|
|
309
142
|
}
|