tova 0.3.9 → 0.4.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.
- package/bin/tova.js +2 -2
- package/package.json +1 -1
- package/src/analyzer/analyzer.js +14 -5
- package/src/analyzer/client-analyzer.js +20 -23
- package/src/version.js +1 -1
package/bin/tova.js
CHANGED
|
@@ -1443,7 +1443,7 @@ shared {
|
|
|
1443
1443
|
}
|
|
1444
1444
|
|
|
1445
1445
|
server {
|
|
1446
|
-
fn get_message()
|
|
1446
|
+
fn get_message() {
|
|
1447
1447
|
Message("Hello from Tova!", Date.new().toLocaleTimeString())
|
|
1448
1448
|
}
|
|
1449
1449
|
|
|
@@ -1560,7 +1560,7 @@ client {
|
|
|
1560
1560
|
content: name => `// ${name} — Built with Tova
|
|
1561
1561
|
|
|
1562
1562
|
server {
|
|
1563
|
-
fn health()
|
|
1563
|
+
fn health() {
|
|
1564
1564
|
{ status: "ok" }
|
|
1565
1565
|
}
|
|
1566
1566
|
|
package/package.json
CHANGED
package/src/analyzer/analyzer.js
CHANGED
|
@@ -389,6 +389,8 @@ export class Analyzer {
|
|
|
389
389
|
if (sym.extern) continue;
|
|
390
390
|
if (sym._variantOf) continue; // ADT variant constructors
|
|
391
391
|
if (name === 'main') continue;
|
|
392
|
+
// Server functions are exposed as RPC endpoints callable from client blocks
|
|
393
|
+
if (scope.context === 'server') continue;
|
|
392
394
|
|
|
393
395
|
if (!sym.used && sym.loc && sym.loc.line > 0) {
|
|
394
396
|
this.warn(`Function '${name}' is declared but never used`, sym.loc, "prefix with _ to suppress", {
|
|
@@ -2418,14 +2420,21 @@ export class Analyzer {
|
|
|
2418
2420
|
// preventing silent shadowing of immutable bindings within the same function.
|
|
2419
2421
|
_lookupAssignTarget(name) {
|
|
2420
2422
|
let scope = this.currentScope;
|
|
2423
|
+
let crossedFunction = false;
|
|
2421
2424
|
while (scope) {
|
|
2422
2425
|
const sym = scope.symbols.get(name);
|
|
2423
|
-
if (sym)
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2426
|
+
if (sym) {
|
|
2427
|
+
// After crossing a function boundary, only resolve to mutable symbols (var/state)
|
|
2428
|
+
// so that inner functions can reassign outer var/state but not shadow immutables
|
|
2429
|
+
if (crossedFunction && !sym.mutable) return null;
|
|
2430
|
+
return sym;
|
|
2431
|
+
}
|
|
2432
|
+
// Track when we cross a function boundary
|
|
2433
|
+
if (scope.context === 'function') {
|
|
2434
|
+
crossedFunction = true;
|
|
2428
2435
|
}
|
|
2436
|
+
// Stop at module level
|
|
2437
|
+
if (scope.context === 'module') break;
|
|
2429
2438
|
scope = scope.parent;
|
|
2430
2439
|
}
|
|
2431
2440
|
return null;
|
|
@@ -111,15 +111,8 @@ export function installClientAnalyzer(AnalyzerClass) {
|
|
|
111
111
|
}
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
AnalyzerClass.prototype.
|
|
115
|
-
for (const
|
|
116
|
-
if (attr.type === 'JSXSpreadAttribute') {
|
|
117
|
-
this.visitExpression(attr.expression);
|
|
118
|
-
} else {
|
|
119
|
-
this.visitExpression(attr.value);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
for (const child of node.children) {
|
|
114
|
+
AnalyzerClass.prototype._visitJSXChildren = function(children) {
|
|
115
|
+
for (const child of children) {
|
|
123
116
|
if (child.type === 'JSXElement') {
|
|
124
117
|
this.visitJSXElement(child);
|
|
125
118
|
} else if (child.type === 'JSXFragment') {
|
|
@@ -132,26 +125,30 @@ export function installClientAnalyzer(AnalyzerClass) {
|
|
|
132
125
|
this.visitJSXIf(child);
|
|
133
126
|
} else if (child.type === 'JSXMatch') {
|
|
134
127
|
this.visitJSXMatch(child);
|
|
128
|
+
} else if (child.type === 'JSXText') {
|
|
129
|
+
// JSXText wraps a TemplateLiteral/StringLiteral in its .value
|
|
130
|
+
// Visit it so identifiers in interpolated strings are marked as used
|
|
131
|
+
if (child.value) this.visitExpression(child.value);
|
|
132
|
+
} else if (child.type) {
|
|
133
|
+
// Other expression-type children
|
|
134
|
+
this.visitExpression(child);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
-
AnalyzerClass.prototype.
|
|
140
|
-
for (const
|
|
141
|
-
if (
|
|
142
|
-
this.
|
|
143
|
-
} else
|
|
144
|
-
this.
|
|
145
|
-
} else if (child.type === 'JSXExpression') {
|
|
146
|
-
this.visitExpression(child.expression);
|
|
147
|
-
} else if (child.type === 'JSXFor') {
|
|
148
|
-
this.visitJSXFor(child);
|
|
149
|
-
} else if (child.type === 'JSXIf') {
|
|
150
|
-
this.visitJSXIf(child);
|
|
151
|
-
} else if (child.type === 'JSXMatch') {
|
|
152
|
-
this.visitJSXMatch(child);
|
|
139
|
+
AnalyzerClass.prototype.visitJSXElement = function(node) {
|
|
140
|
+
for (const attr of node.attributes) {
|
|
141
|
+
if (attr.type === 'JSXSpreadAttribute') {
|
|
142
|
+
this.visitExpression(attr.expression);
|
|
143
|
+
} else {
|
|
144
|
+
this.visitExpression(attr.value);
|
|
153
145
|
}
|
|
154
146
|
}
|
|
147
|
+
this._visitJSXChildren(node.children);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
AnalyzerClass.prototype.visitJSXFragment = function(node) {
|
|
151
|
+
this._visitJSXChildren(node.children);
|
|
155
152
|
};
|
|
156
153
|
|
|
157
154
|
AnalyzerClass.prototype.visitJSXFor = function(node) {
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/embed-runtime.js — do not edit
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.4.0";
|