@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,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
- });