@nudojs/cli 0.2.1 → 0.3.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.
Files changed (58) hide show
  1. package/package.json +12 -7
  2. package/src/abs-route.ts +273 -0
  3. package/src/builtins/builtin-prototype.ts +279 -0
  4. package/src/builtins/builtin-static.ts +260 -0
  5. package/src/eval-binary.ts +113 -0
  6. package/src/evaluator-api.ts +3 -0
  7. package/src/evaluator.ts +174 -633
  8. package/src/index.ts +373 -270
  9. package/src/narrowing.ts +7 -0
  10. package/src/phi-from-test.ts +42 -0
  11. package/src/run-test.ts +92 -0
  12. package/src/term-registry.ts +96 -0
  13. package/CHANGELOG.md +0 -41
  14. package/dist/chunk-3HCPXPEW.js +0 -5286
  15. package/dist/evaluator-api.d.ts +0 -151
  16. package/dist/evaluator-api.js +0 -214
  17. package/dist/index.d.ts +0 -2
  18. package/dist/index.js +0 -4035
  19. package/examples/real-world.js +0 -66
  20. package/examples/validate-real-world.ts +0 -160
  21. package/src/__tests__/builtin-advanced.test.ts +0 -292
  22. package/src/__tests__/builtin-api.test.ts +0 -308
  23. package/src/__tests__/builtin-functions.test.ts +0 -313
  24. package/src/__tests__/builtin-map.test.ts +0 -129
  25. package/src/__tests__/builtin-promise.test.ts +0 -150
  26. package/src/__tests__/builtin-regexp.test.ts +0 -133
  27. package/src/__tests__/builtin-set.test.ts +0 -163
  28. package/src/__tests__/builtin-url.test.ts +0 -256
  29. package/src/__tests__/builtin-web.test.ts +0 -340
  30. package/src/__tests__/combination-scenarios.test.ts +0 -384
  31. package/src/__tests__/edge-cases.test.ts +0 -749
  32. package/src/__tests__/evaluator-loops.test.ts +0 -283
  33. package/src/__tests__/evaluator-nullish-coalescing.test.ts +0 -86
  34. package/src/__tests__/evaluator-optional-chaining.test.ts +0 -37
  35. package/src/__tests__/evaluator-phase2.test.ts +0 -564
  36. package/src/__tests__/evaluator-phase3.test.ts +0 -864
  37. package/src/__tests__/evaluator-refined.test.ts +0 -121
  38. package/src/__tests__/evaluator-string-methods.test.ts +0 -151
  39. package/src/__tests__/evaluator.test.ts +0 -943
  40. package/src/__tests__/mock-arrow-function.test.ts +0 -169
  41. package/src/__tests__/mock-comparison.test.ts +0 -221
  42. package/src/__tests__/mock-comprehensive.test.ts +0 -250
  43. package/src/__tests__/mock-final-integration.test.ts +0 -263
  44. package/src/__tests__/mock-helpers.test.ts +0 -174
  45. package/src/__tests__/mock-realworld.test.ts +0 -191
  46. package/src/__tests__/mock-rest-debug.test.ts +0 -110
  47. package/src/__tests__/mock-rest-edge.test.ts +0 -176
  48. package/src/__tests__/mock-sinon-runtime.test.ts +0 -168
  49. package/src/__tests__/narrowing-discriminated.test.ts +0 -35
  50. package/src/__tests__/narrowing-in-operator.test.ts +0 -56
  51. package/src/__tests__/narrowing-switch.test.ts +0 -56
  52. package/src/__tests__/narrowing-truthiness.test.ts +0 -52
  53. package/src/__tests__/narrowing.test.ts +0 -82
  54. package/src/__tests__/nudo-mock-syntax.test.ts +0 -245
  55. package/src/__tests__/realworld-patterns.test.ts +0 -430
  56. package/src/__tests__/syntax-sugar.test.ts +0 -345
  57. package/src/builtins/index.ts +0 -61
  58. package/tsconfig.json +0 -9
@@ -1,82 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { T, typeValueEquals, typeValueToString, createEnvironment } from "@nudojs/core";
3
- import { parse } from "@nudojs/parser";
4
- import { narrow } from "../narrowing.ts";
5
- import type { ExpressionStatement } from "@babel/types";
6
-
7
- function getTestExpr(source: string) {
8
- const ast = parse(source);
9
- const stmt = ast.program.body[0] as ExpressionStatement;
10
- return stmt.expression;
11
- }
12
-
13
- describe("narrow: typeof", () => {
14
- it("narrows typeof x === 'number' on union", () => {
15
- const env = createEnvironment();
16
- env.bind("x", T.union(T.number, T.string));
17
- const expr = getTestExpr('typeof x === "number"');
18
- const [trueEnv, falseEnv] = narrow(expr, env);
19
- expect(typeValueEquals(trueEnv.lookup("x"), T.number)).toBe(true);
20
- expect(typeValueEquals(falseEnv.lookup("x"), T.string)).toBe(true);
21
- });
22
-
23
- it("narrows typeof x === 'string' on union", () => {
24
- const env = createEnvironment();
25
- env.bind("x", T.union(T.number, T.string, T.boolean));
26
- const expr = getTestExpr('typeof x === "string"');
27
- const [trueEnv, falseEnv] = narrow(expr, env);
28
- expect(typeValueEquals(trueEnv.lookup("x"), T.string)).toBe(true);
29
- const falseType = falseEnv.lookup("x");
30
- expect(typeValueToString(falseType)).toBe("number | boolean");
31
- });
32
-
33
- it("narrows typeof x !== 'number' (inverted)", () => {
34
- const env = createEnvironment();
35
- env.bind("x", T.union(T.number, T.string));
36
- const expr = getTestExpr('typeof x !== "number"');
37
- const [trueEnv, falseEnv] = narrow(expr, env);
38
- expect(typeValueEquals(trueEnv.lookup("x"), T.string)).toBe(true);
39
- expect(typeValueEquals(falseEnv.lookup("x"), T.number)).toBe(true);
40
- });
41
- });
42
-
43
- describe("narrow: strict equality", () => {
44
- it("narrows x === null", () => {
45
- const env = createEnvironment();
46
- env.bind("x", T.union(T.null, T.number));
47
- const expr = getTestExpr("x === null");
48
- const [trueEnv, falseEnv] = narrow(expr, env);
49
- expect(typeValueEquals(trueEnv.lookup("x"), T.null)).toBe(true);
50
- expect(typeValueEquals(falseEnv.lookup("x"), T.number)).toBe(true);
51
- });
52
-
53
- it("narrows x === 1", () => {
54
- const env = createEnvironment();
55
- env.bind("x", T.union(T.literal(1), T.literal(2), T.literal(3)));
56
- const expr = getTestExpr("x === 1");
57
- const [trueEnv, falseEnv] = narrow(expr, env);
58
- expect(typeValueEquals(trueEnv.lookup("x"), T.literal(1))).toBe(true);
59
- const falseType = falseEnv.lookup("x");
60
- expect(typeValueToString(falseType)).toBe("2 | 3");
61
- });
62
-
63
- it("narrows x !== 1 (inverted)", () => {
64
- const env = createEnvironment();
65
- env.bind("x", T.union(T.literal(1), T.literal(2)));
66
- const expr = getTestExpr("x !== 1");
67
- const [trueEnv, falseEnv] = narrow(expr, env);
68
- expect(typeValueEquals(trueEnv.lookup("x"), T.literal(2))).toBe(true);
69
- expect(typeValueEquals(falseEnv.lookup("x"), T.literal(1))).toBe(true);
70
- });
71
- });
72
-
73
- describe("narrow: negation", () => {
74
- it("narrows !(typeof x === 'number')", () => {
75
- const env = createEnvironment();
76
- env.bind("x", T.union(T.number, T.string));
77
- const expr = getTestExpr('!(typeof x === "number")');
78
- const [trueEnv, falseEnv] = narrow(expr, env);
79
- expect(typeValueEquals(trueEnv.lookup("x"), T.string)).toBe(true);
80
- expect(typeValueEquals(falseEnv.lookup("x"), T.number)).toBe(true);
81
- });
82
- });
@@ -1,245 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { parse, extractDirectives, type CaseDirective, type MockDirective, parseTypeValueExpr } from "@nudojs/parser";
3
- import { typeValueToString, createEnvironment, T, mockHelperToTypeValue } from "@nudojs/core";
4
- import { evaluateFunctionFull } from "../evaluator.js";
5
-
6
- function runNudoTest(source: string): { name: string; caseName: string; result: string }[] {
7
- const ast = parse(source);
8
- const directives = extractDirectives(ast);
9
- const env = createEnvironment();
10
- const results: { name: string; caseName: string; result: string }[] = [];
11
-
12
- for (const fn of directives) {
13
- for (const d of fn.directives) {
14
- if (d.kind === "mock") {
15
- if (d.arrowFn) {
16
- const fnType = T.fn(d.arrowFn.params, d.arrowFn.body, env);
17
- (fnType as any)._paramPatterns = d.arrowFn.paramPatterns;
18
- env.bind(d.name, fnType);
19
- } else if (d.nudoMock) {
20
- const typeVal = mockHelperToTypeValue(d.nudoMock, env);
21
- env.bind(d.name, typeVal);
22
- } else if (d.sinonExpr) {
23
- // Handle sinon expressions
24
- const body = { type: "BlockStatement", body: [] } as any;
25
- const fn = T.fn(["...args"], body, env);
26
- if (d.sinonExpr.returnValue) {
27
- (fn as any)._directReturn = d.sinonExpr.returnValue;
28
- } else if (d.sinonExpr.resolvedValue) {
29
- (fn as any)._directReturn = T.promise(d.sinonExpr.resolvedValue);
30
- } else if (d.sinonExpr.rejectedValue) {
31
- (fn as any)._directReturn = T.never;
32
- } else {
33
- (fn as any)._directReturn = T.unknown;
34
- }
35
- env.bind(d.name, fn);
36
- } else if (d.expression) {
37
- env.bind(d.name, parseTypeValueExpr(d.expression));
38
- }
39
- }
40
- }
41
-
42
- const caseDirectives = fn.directives.filter((d): d is CaseDirective => d.kind === "case");
43
- for (const dir of caseDirectives) {
44
- const result = evaluateFunctionFull(fn.node, dir.args, env);
45
- results.push({
46
- name: fn.name,
47
- caseName: dir.name,
48
- result: typeValueToString(result.value),
49
- });
50
- }
51
- }
52
- return results;
53
- }
54
-
55
- describe("Nudo Mock Helper Syntax", () => {
56
- it("stub() should bind as a function returning unknown", () => {
57
- const results = runNudoTest(`
58
- // @nudo:mock handler = stub()
59
- // @nudo:case "test" (42)
60
- function testFn(x) {
61
- return handler(x);
62
- }
63
- `);
64
- expect(results[0].result).toBe("unknown");
65
- });
66
-
67
- it("stub().returns({ status: 200 }) should return the specified value", () => {
68
- const results = runNudoTest(`
69
- // @nudo:mock fetch = stub().returns({ status: 200, data: { ok: true } })
70
- // @nudo:case "api" ("/users")
71
- function fetchUsers(url) {
72
- return fetch(url);
73
- }
74
- `);
75
- expect(results[0].result).toContain("status: 200");
76
- expect(results[0].result).toContain("ok: true");
77
- });
78
-
79
- it("stub().resolves('hello') should return Promise<\"hello\">", () => {
80
- const results = runNudoTest(`
81
- // @nudo:mock fetchData = stub().resolves("hello")
82
- // @nudo:case "async" ()
83
- function getData() {
84
- return fetchData();
85
- }
86
- `);
87
- expect(results[0].result).toBe('Promise<"hello">');
88
- });
89
-
90
- it("spy() should bind as a function returning unknown", () => {
91
- const results = runNudoTest(`
92
- // @nudo:mock listener = spy()
93
- // @nudo:case "event" ({ type: "click" })
94
- function handleEvent(event) {
95
- return listener(event);
96
- }
97
- `);
98
- expect(results[0].result).toBe("unknown");
99
- });
100
-
101
- it("mock() should bind as a function returning unknown", () => {
102
- const results = runNudoTest(`
103
- // @nudo:mock service = mock()
104
- // @nudo:case "call" ()
105
- function useService() {
106
- return service();
107
- }
108
- `);
109
- expect(results[0].result).toBe("unknown");
110
- });
111
-
112
- it("stub().returns with complex object", () => {
113
- const results = runNudoTest(`
114
- // @nudo:mock httpClient = stub().returns({ status: 200, data: { users: [{ id: 1, name: "Alice" }] } })
115
- // @nudo:case "users" ()
116
- function getUsers() {
117
- const response = httpClient();
118
- return response.data.users;
119
- }
120
- `);
121
- expect(results[0].result).toContain("id: 1");
122
- expect(results[0].result).toContain("name: \"Alice\"");
123
- });
124
-
125
- it("mixed nudo mocks and arrow function mocks", () => {
126
- const results = runNudoTest(`
127
- // @nudo:mock validate = (x) => x !== null
128
- // @nudo:mock transform = stub().returns({ processed: true })
129
- // @nudo:case "mix" (42)
130
- function process(x) {
131
- if (!validate(x)) return null;
132
- return transform(x);
133
- }
134
- `);
135
- expect(results[0].result).toContain("processed: true");
136
- });
137
- });
138
-
139
- describe("Chain call syntax", () => {
140
- it("stub().returns(v).onFirstCall() should parse and use returns value", () => {
141
- const results = runNudoTest(`
142
- // @nudo:mock handler = stub().returns(42).onFirstCall()
143
- // @nudo:case "test" ()
144
- function fn() {
145
- return handler();
146
- }
147
- `);
148
- expect(results[0].result).toBe("42");
149
- });
150
-
151
- it("stub().callsFake(expr) should parse", () => {
152
- const results = runNudoTest(`
153
- // @nudo:mock transform = stub().callsFake((x) => x)
154
- // @nudo:case "test" (21)
155
- function fn(x) {
156
- return transform(x);
157
- }
158
- `);
159
- expect(results[0].result).toBeDefined();
160
- });
161
-
162
- it("spy().returns(v) should parse", () => {
163
- const results = runNudoTest(`
164
- // @nudo:mock listener = spy().returns({ handled: true })
165
- // @nudo:case "test" ()
166
- function fn() {
167
- return listener();
168
- }
169
- `);
170
- expect(results[0].result).toContain("handled: true");
171
- });
172
-
173
- it("stub().withArgs(a, b).returns(v) should parse", () => {
174
- const results = runNudoTest(`
175
- // @nudo:mock fetch = stub().withArgs("GET", "/api").returns({ status: 200 })
176
- // @nudo:case "test" ()
177
- function fn() {
178
- return fetch("GET", "/api");
179
- }
180
- `);
181
- expect(results[0].result).toContain("status: 200");
182
- });
183
- });
184
-
185
- describe("callsFake execution semantics (unified stub/sinon paths)", () => {
186
- it("stub().callsFake lifts the fake onto arrowFn (same binding as plain arrow mocks)", () => {
187
- const ast = parse(`
188
- // @nudo:mock transform = stub().callsFake((x) => ({ v: x }))
189
- function applyTransform(x) {
190
- return transform(x);
191
- }
192
- `);
193
- const directives = extractDirectives(ast);
194
- const mockDir = directives[0].directives.find((d): d is MockDirective => d.kind === "mock")!;
195
- // 与 @nudo:mock f = (x) => ... 相同的 arrowFn 形态:T.fn + _paramPatterns + 全局 env
196
- expect(mockDir.arrowFn).toBeDefined();
197
- expect(mockDir.arrowFn!.params).toEqual(["x"]);
198
- });
199
-
200
- it("stub().callsFake((x) => ({ v: x })) direct call with 21 yields { v: 21 }", () => {
201
- const results = runNudoTest(`
202
- // @nudo:mock transform = stub().callsFake((x) => ({ v: x }))
203
- // @nudo:case "direct" (21)
204
- function applyTransform(x) {
205
- return transform(x);
206
- }
207
- `);
208
- expect(results[0].result).toBe("{ v: 21 }");
209
- });
210
-
211
- it("sinon.stub().callsFake((x) => ({ v: x })) direct call with 21 yields { v: 21 }", () => {
212
- const results = runNudoTest(`
213
- // @nudo:mock transform = sinon.stub().callsFake((x) => ({ v: x }))
214
- // @nudo:case "direct" (21)
215
- function applyTransform(x) {
216
- return transform(x);
217
- }
218
- `);
219
- expect(results[0].result).toBe("{ v: 21 }");
220
- });
221
-
222
- it("callsFake propagates precisely through arr.map(transform) HOF", () => {
223
- const results = runNudoTest(`
224
- // @nudo:mock transform = stub().callsFake((x) => ({ v: x }))
225
- // @nudo:case "hof" ([1, 2, 3])
226
- function mapAll(arr) {
227
- return arr.map(transform);
228
- }
229
- `);
230
- expect(results[0].result).toBe("[{ v: 1 }, { v: 2 }, { v: 3 }]");
231
- });
232
-
233
- it("withArgs(...).returns(...) mixed chain: matched args win, unmatched use default stub behavior", () => {
234
- const results = runNudoTest(`
235
- // @nudo:mock handler = stub().withArgs(21).returns("hit")
236
- // @nudo:case "match" (21)
237
- // @nudo:case "miss" (22)
238
- function call(x) {
239
- return handler(x);
240
- }
241
- `);
242
- expect(results[0].result).toBe('"hit"');
243
- expect(results[1].result).toBe("unknown");
244
- });
245
- });