@nudojs/cli 0.2.1 → 0.3.1

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 (64) hide show
  1. package/dist/{chunk-3HCPXPEW.js → chunk-YDM4HHQ4.js} +1783 -1421
  2. package/dist/evaluator-api.d.ts +12 -1
  3. package/dist/evaluator-api.js +52 -2
  4. package/dist/index.d.ts +1 -2
  5. package/dist/index.js +366 -3572
  6. package/package.json +23 -11
  7. package/CHANGELOG.md +0 -41
  8. package/examples/real-world.js +0 -66
  9. package/examples/validate-real-world.ts +0 -160
  10. package/src/__tests__/builtin-advanced.test.ts +0 -292
  11. package/src/__tests__/builtin-api.test.ts +0 -308
  12. package/src/__tests__/builtin-functions.test.ts +0 -313
  13. package/src/__tests__/builtin-map.test.ts +0 -129
  14. package/src/__tests__/builtin-promise.test.ts +0 -150
  15. package/src/__tests__/builtin-regexp.test.ts +0 -133
  16. package/src/__tests__/builtin-set.test.ts +0 -163
  17. package/src/__tests__/builtin-url.test.ts +0 -256
  18. package/src/__tests__/builtin-web.test.ts +0 -340
  19. package/src/__tests__/combination-scenarios.test.ts +0 -384
  20. package/src/__tests__/edge-cases.test.ts +0 -749
  21. package/src/__tests__/evaluator-loops.test.ts +0 -283
  22. package/src/__tests__/evaluator-nullish-coalescing.test.ts +0 -86
  23. package/src/__tests__/evaluator-optional-chaining.test.ts +0 -37
  24. package/src/__tests__/evaluator-phase2.test.ts +0 -564
  25. package/src/__tests__/evaluator-phase3.test.ts +0 -864
  26. package/src/__tests__/evaluator-refined.test.ts +0 -121
  27. package/src/__tests__/evaluator-string-methods.test.ts +0 -151
  28. package/src/__tests__/evaluator.test.ts +0 -943
  29. package/src/__tests__/mock-arrow-function.test.ts +0 -169
  30. package/src/__tests__/mock-comparison.test.ts +0 -221
  31. package/src/__tests__/mock-comprehensive.test.ts +0 -250
  32. package/src/__tests__/mock-final-integration.test.ts +0 -263
  33. package/src/__tests__/mock-helpers.test.ts +0 -174
  34. package/src/__tests__/mock-realworld.test.ts +0 -191
  35. package/src/__tests__/mock-rest-debug.test.ts +0 -110
  36. package/src/__tests__/mock-rest-edge.test.ts +0 -176
  37. package/src/__tests__/mock-sinon-runtime.test.ts +0 -168
  38. package/src/__tests__/narrowing-discriminated.test.ts +0 -35
  39. package/src/__tests__/narrowing-in-operator.test.ts +0 -56
  40. package/src/__tests__/narrowing-switch.test.ts +0 -56
  41. package/src/__tests__/narrowing-truthiness.test.ts +0 -52
  42. package/src/__tests__/narrowing.test.ts +0 -82
  43. package/src/__tests__/nudo-mock-syntax.test.ts +0 -245
  44. package/src/__tests__/realworld-patterns.test.ts +0 -430
  45. package/src/__tests__/syntax-sugar.test.ts +0 -345
  46. package/src/builtins/builtin-intl.ts +0 -17
  47. package/src/builtins/builtin-map.ts +0 -174
  48. package/src/builtins/builtin-promise.ts +0 -116
  49. package/src/builtins/builtin-reflect.ts +0 -30
  50. package/src/builtins/builtin-regexp.ts +0 -34
  51. package/src/builtins/builtin-set.ts +0 -131
  52. package/src/builtins/builtin-symbol.ts +0 -16
  53. package/src/builtins/builtin-url.ts +0 -58
  54. package/src/builtins/builtin-weak.ts +0 -22
  55. package/src/builtins/builtin-web.ts +0 -111
  56. package/src/builtins/index.ts +0 -61
  57. package/src/config.ts +0 -32
  58. package/src/env-loader.ts +0 -174
  59. package/src/evaluator-api.ts +0 -36
  60. package/src/evaluator.ts +0 -5623
  61. package/src/index.ts +0 -931
  62. package/src/narrowing.ts +0 -537
  63. package/src/resolve-npm.ts +0 -63
  64. package/tsconfig.json +0 -9
@@ -1,283 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import { T, typeValueEquals, typeValueToString, createEnvironment } from "@nudojs/core";
3
- import { evaluateProgram, setSampleCount } from "../evaluator.ts";
4
- import { parse } from "@nudojs/parser";
5
- import type { TypeValue } from "@nudojs/core";
6
-
7
- function evalCode(code: string): TypeValue {
8
- const ast = parse(code);
9
- const env = createEnvironment();
10
- return evaluateProgram(ast, env);
11
- }
12
-
13
- function evalWith(code: string, bindings: Record<string, TypeValue>): TypeValue {
14
- const ast = parse(code);
15
- const env = createEnvironment();
16
- for (const [k, v] of Object.entries(bindings)) env.bind(k, v);
17
- return evaluateProgram(ast, env);
18
- }
19
-
20
- describe("ForStatement", () => {
21
- beforeEach(() => setSampleCount(3));
22
-
23
- it("evaluates simple for loop with literal bounds", () => {
24
- const result = evalCode(`
25
- let sum = 0;
26
- for (let i = 0; i < 3; i++) {
27
- sum = sum + i;
28
- }
29
- sum
30
- `);
31
- expect(typeValueEquals(result, T.literal(3))).toBe(true);
32
- });
33
-
34
- it("evaluates for loop that returns early", () => {
35
- const result = evalCode(`
36
- function f() {
37
- for (let i = 0; i < 10; i++) {
38
- if (i === 2) return i;
39
- }
40
- return -1;
41
- }
42
- `);
43
- // f is a function; we just check it's defined
44
- expect(result).toBeDefined();
45
- });
46
-
47
- it("for loop with abstract bounds widens variables", () => {
48
- const result = evalWith(`
49
- let sum = 0;
50
- for (let i = 0; i < n; i++) {
51
- sum = sum + i;
52
- }
53
- sum
54
- `, { n: T.number });
55
- // After widening, sum should be T.number
56
- expect(typeValueEquals(result, T.number)).toBe(true);
57
- });
58
-
59
- it("for loop variable accessible after loop", () => {
60
- const result = evalCode(`
61
- let x = 0;
62
- for (let i = 0; i < 2; i++) {
63
- x = x + 1;
64
- }
65
- x
66
- `);
67
- expect(typeValueEquals(result, T.literal(2))).toBe(true);
68
- });
69
-
70
- it("let loop variable gets a fresh binding per iteration for closures", () => {
71
- // Real JS semantics: let creates a per-iteration scope, so each closure
72
- // captures the value at its own iteration.
73
- const result = evalCode(`
74
- const fns = [];
75
- for (let i = 0; i < 3; i++) {
76
- fns.push(() => i);
77
- }
78
- [fns[0](), fns[1](), fns[2]()]
79
- `);
80
- expect(typeValueToString(result)).toBe("[0, 1, 2]");
81
- });
82
-
83
- it("var loop variable shares one binding across iterations for closures", () => {
84
- // var keeps a single shared cell: every closure observes the final value.
85
- const result = evalCode(`
86
- const fns = [];
87
- for (var i = 0; i < 3; i++) {
88
- fns.push(() => i);
89
- }
90
- [fns[0](), fns[1](), fns[2]()]
91
- `);
92
- expect(typeValueToString(result)).toBe("[3, 3, 3]");
93
- });
94
-
95
- it("let loop closure sees body-side increments; loop still progresses", () => {
96
- // Real JS: an assignment in the body targets the per-iteration binding
97
- // the closure captured (node: fns[0]() === 1), unlike the update clause
98
- // which runs outside it. No update clause here — the copy-back from the
99
- // per-iteration binding must still drive the loop condition.
100
- const result = evalCode(`
101
- const fns = [];
102
- let total = 0;
103
- for (let i = 0; i < 3;) {
104
- fns.push(() => i);
105
- total = total + i;
106
- i = i + 1;
107
- }
108
- [fns[0](), total]
109
- `);
110
- expect(typeValueToString(result)).toBe("[1, 3]");
111
- });
112
-
113
- it("for-of over a union of tuples iterates every member", () => {
114
- const result = evalWith(`
115
- function pick(c) {
116
- if (c) { return [9]; }
117
- return [10, [11, 12]];
118
- }
119
- function f(a, t) {
120
- const r = t || [];
121
- for (const e of a) {
122
- if (Array.isArray(e)) { f(e, r) } else { r.push(e) }
123
- }
124
- return r;
125
- }
126
- f(pick(c))
127
- `, { c: T.boolean });
128
- // Both union members' elements flow through the body (concatenated),
129
- // including the recursive flattening of the nested [11, 12] member.
130
- // (typeValueEquals has no tuple branch — compare rendered form.)
131
- expect(typeValueToString(result)).toBe("[9, 10, 11, 12]");
132
- });
133
-
134
- it("for-of over a union with non-iterable members skips them", () => {
135
- const result = evalWith(`
136
- function pick(c) {
137
- if (c) { return [1, 2]; }
138
- if (c === 2) { return 42; }
139
- return "str";
140
- }
141
- function f(a, t) {
142
- const r = t || [];
143
- for (const e of a) {
144
- if (Array.isArray(e)) { f(e, r) } else { r.push(e) }
145
- }
146
- return r;
147
- }
148
- f(pick(c))
149
- `, { c: T.boolean });
150
- // number/string members of the union contribute nothing; the tuple
151
- // member's pushes still land on the shared accumulator.
152
- expect(typeValueToString(result)).toBe("[1, 2]");
153
- });
154
-
155
- it("break terminates loop iteration and skips the rest of the body", () => {
156
- // Guard-style break (reach.js pattern): the member access after break
157
- // must not execute on the falsy receiver. Before break was implemented
158
- // the loop kept evaluating the body and dereferenced the undefined ref.
159
- const result = evalWith(`
160
- function step(ref, key) {
161
- let out = null;
162
- for (const k of [key]) {
163
- if (!ref) {
164
- out = 'default';
165
- break;
166
- }
167
- out = ref[k];
168
- }
169
- return out;
170
- }
171
- step(ref, 'a')
172
- `, { ref: T.undefined });
173
- expect(typeValueToString(result)).toBe('"default"');
174
- });
175
-
176
- it("break stops the loop and continue skips to the update clause", () => {
177
- const result = evalCode(`
178
- const collected = [];
179
- for (let i = 0; i < 5; i++) {
180
- if (i === 1) { continue; }
181
- if (i === 3) { break; }
182
- collected.push(i);
183
- }
184
- collected
185
- `);
186
- // continue skips push(1); break stops before 3 and 4
187
- expect(typeValueToString(result)).toBe("[0, 2]");
188
- });
189
- });
190
-
191
- describe("WhileStatement", () => {
192
- beforeEach(() => setSampleCount(3));
193
-
194
- it("evaluates while loop with literal false condition", () => {
195
- const result = evalCode(`
196
- let x = 0;
197
- while (false) {
198
- x = 1;
199
- }
200
- x
201
- `);
202
- expect(typeValueEquals(result, T.literal(0))).toBe(true);
203
- });
204
-
205
- it("evaluates while loop with concrete iterations", () => {
206
- const result = evalCode(`
207
- let x = 0;
208
- let i = 0;
209
- while (i < 3) {
210
- x = x + 1;
211
- i = i + 1;
212
- }
213
- x
214
- `);
215
- expect(typeValueEquals(result, T.literal(3))).toBe(true);
216
- });
217
-
218
- it("while loop with abstract condition", () => {
219
- const result = evalWith(`
220
- let count = 0;
221
- while (cond) {
222
- count = count + 1;
223
- }
224
- count
225
- `, { cond: T.boolean });
226
- // After sample iterations, count should be widened to number
227
- expect(typeValueEquals(result, T.number) || result.kind === "literal").toBe(true);
228
- });
229
- });
230
-
231
- describe("DoWhileStatement", () => {
232
- beforeEach(() => setSampleCount(3));
233
-
234
- it("executes body at least once", () => {
235
- const result = evalCode(`
236
- let x = 0;
237
- do {
238
- x = x + 1;
239
- } while (false);
240
- x
241
- `);
242
- expect(typeValueEquals(result, T.literal(1))).toBe(true);
243
- });
244
-
245
- it("evaluates do-while with multiple iterations", () => {
246
- const result = evalCode(`
247
- let x = 0;
248
- let i = 0;
249
- do {
250
- x = x + 1;
251
- i = i + 1;
252
- } while (i < 3);
253
- x
254
- `);
255
- expect(typeValueEquals(result, T.literal(3))).toBe(true);
256
- });
257
- });
258
-
259
- describe("@nudo:sample integration", () => {
260
- it("respects custom sample count", () => {
261
- setSampleCount(5);
262
- const result = evalCode(`
263
- let sum = 0;
264
- for (let i = 0; i < 5; i++) {
265
- sum = sum + i;
266
- }
267
- sum
268
- `);
269
- expect(typeValueEquals(result, T.literal(10))).toBe(true);
270
- });
271
-
272
- it("default sample count is 3", () => {
273
- setSampleCount(3);
274
- const result = evalCode(`
275
- let sum = 0;
276
- for (let i = 0; i < 3; i++) {
277
- sum = sum + i;
278
- }
279
- sum
280
- `);
281
- expect(typeValueEquals(result, T.literal(3))).toBe(true);
282
- });
283
- });
@@ -1,86 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { T, typeValueToString, createEnvironment } from "@nudojs/core";
3
- import type { TypeValue } from "@nudojs/core";
4
- import { evaluateProgram, evaluateFunction } from "../evaluator.ts";
5
- import { parse } from "@nudojs/parser";
6
-
7
- function evalSource(source: string) {
8
- const ast = parse(source);
9
- const env = createEnvironment();
10
- evaluateProgram(ast, env);
11
- return env;
12
- }
13
-
14
- function evalFn(code: string, args: TypeValue[]): TypeValue {
15
- const ast = parse(code);
16
- const env = createEnvironment();
17
- evaluateProgram(ast, env);
18
- const fns = ast.type === "File" ? ast.program.body : [];
19
- const fnNode = fns.find(
20
- (n: any) => n.type === "FunctionDeclaration",
21
- );
22
- if (!fnNode) throw new Error("No function found");
23
- return evaluateFunction(fnNode, args, env);
24
- }
25
-
26
- describe("nullish coalescing narrowing", () => {
27
- it("narrows ?? to exclude null/undefined from left side", () => {
28
- const env = evalSource(`
29
- const x = "hello" ?? "default";
30
- `);
31
- expect(typeValueToString(env.lookup("x"))).toBe('"hello"');
32
- });
33
-
34
- it("returns right side when left is always null", () => {
35
- const env = evalSource(`
36
- const x = null;
37
- const result = x ?? 42;
38
- `);
39
- expect(typeValueToString(env.lookup("result"))).toBe("42");
40
- });
41
-
42
- it("returns left side when left is never null", () => {
43
- const env = evalSource(`
44
- const x = "hello";
45
- const result = x ?? "default";
46
- `);
47
- expect(typeValueToString(env.lookup("result"))).toBe('"hello"');
48
- });
49
-
50
- it("narrows union type by removing null from left side", () => {
51
- const result = evalFn(
52
- `function test(x) { return x ?? "default"; }`,
53
- [T.union(T.string, T.null)],
54
- );
55
- // null is excluded from string | null, leaving string.
56
- // "default" is a subtype of string, so result is string.
57
- expect(typeValueToString(result)).toBe("string");
58
- });
59
-
60
- it("narrows union type by removing undefined from left side", () => {
61
- const result = evalFn(
62
- `function test(x) { return x ?? "fallback"; }`,
63
- [T.union(T.string, T.undefined)],
64
- );
65
- // undefined is excluded from string | undefined, leaving string.
66
- expect(typeValueToString(result)).toBe("string");
67
- });
68
-
69
- it("narrows union type by removing null and undefined from left side", () => {
70
- const result = evalFn(
71
- `function test(x) { return x ?? "fallback"; }`,
72
- [T.union(T.string, T.null, T.undefined)],
73
- );
74
- // null and undefined are excluded, leaving string.
75
- expect(typeValueToString(result)).toBe("string");
76
- });
77
-
78
- it("returns right side when left is always null/undefined union", () => {
79
- const result = evalFn(
80
- `function test(x) { return x ?? 42; }`,
81
- [T.union(T.null, T.undefined)],
82
- );
83
- // All left members are null/undefined, so use right side.
84
- expect(typeValueToString(result)).toBe("42");
85
- });
86
- });
@@ -1,37 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { T, typeValueToString, createEnvironment } from "@nudojs/core";
3
- import { evaluateProgram } from "../evaluator.ts";
4
- import { parse } from "@nudojs/parser";
5
-
6
- function evalSource(source: string) {
7
- const ast = parse(source);
8
- const env = createEnvironment();
9
- evaluateProgram(ast, env);
10
- return env;
11
- }
12
-
13
- describe("optional chaining", () => {
14
- it("evaluates obj?.foo when obj is object", () => {
15
- const env = evalSource(`
16
- const obj = { foo: "hello" };
17
- const result = obj?.foo;
18
- `);
19
- expect(typeValueToString(env.lookup("result"))).toBe('"hello"');
20
- });
21
-
22
- it("evaluates arr?.[0] for optional computed access", () => {
23
- const env = evalSource(`
24
- const arr = [1, 2, 3];
25
- const result = arr?.[0];
26
- `);
27
- expect(typeValueToString(env.lookup("result"))).toBe("1");
28
- });
29
-
30
- it("evaluates fn?.() for optional call", () => {
31
- const env = evalSource(`
32
- const fn = () => 42;
33
- const result = fn?.();
34
- `);
35
- expect(typeValueToString(env.lookup("result"))).toBe("42");
36
- });
37
- });