@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,430 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { parse, extractDirectives, type CaseDirective } from "@nudojs/parser";
3
- import { typeValueToString, createEnvironment } from "@nudojs/core";
4
- import { evaluateFunctionFull } from "../evaluator.js";
5
-
6
- function runTest(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
- const caseDirectives = fn.directives.filter((d): d is CaseDirective => d.kind === "case");
14
- for (const dir of caseDirectives) {
15
- const result = evaluateFunctionFull(fn.node, dir.args, env);
16
- results.push({
17
- name: fn.name,
18
- caseName: dir.name,
19
- result: typeValueToString(result.value),
20
- });
21
- }
22
- }
23
- return results;
24
- }
25
-
26
- describe("Real-World Pattern Tests", () => {
27
- describe("Data Processing Pipelines", () => {
28
- it("user data transformation", () => {
29
- const results = runTest(`
30
- // @nudo:case "transform-users" ([{ id: 1, firstName: "Alice", lastName: "Smith", active: true, isAdmin: false }, { id: 2, firstName: "Bob", lastName: "Jones", active: true, isAdmin: true }])
31
- function transformUsers(users) {
32
- return users
33
- .filter(u => u.active)
34
- .map(u => ({
35
- id: u.id,
36
- displayName: u.firstName + " " + u.lastName,
37
- role: u.isAdmin ? "admin" : "user"
38
- }));
39
- }
40
- `);
41
- expect(results[0].result).toContain("[");
42
- expect(results[0].result).toContain("displayName");
43
- expect(results[0].result).toContain("role");
44
- });
45
-
46
- it("group by key", () => {
47
- const results = runTest(`
48
- // @nudo:case "group-by" ([{ type: "a", value: 1 }, { type: "b", value: 2 }, { type: "a", value: 3 }])
49
- function groupByType(items) {
50
- const groups = new Map();
51
- for (const item of items) {
52
- if (!groups.has(item.type)) {
53
- groups.set(item.type, []);
54
- }
55
- groups.get(item.type).push(item.value);
56
- }
57
- return groups;
58
- }
59
- `);
60
- expect(results[0].result).toContain("Map");
61
- });
62
-
63
- it("flatten and deduplicate", () => {
64
- const results = runTest(`
65
- // @nudo:case "flatten-dedup" ([[1, 2], [2, 3], [3, 4]])
66
- function flattenDedup(arrays) {
67
- const flat = arrays.reduce((acc, arr) => acc.concat(arr), []);
68
- return Array.from(new Set(flat));
69
- }
70
- `);
71
- expect(results[0].result).toContain("[");
72
- });
73
- });
74
-
75
- describe("Error Handling Patterns", () => {
76
- it("Result type pattern", () => {
77
- const results = runTest(`
78
- // @nudo:case "result-type" (10, 2)
79
- function safeDivide(a, b) {
80
- try {
81
- if (b === 0) throw new Error("Division by zero");
82
- return { ok: true, value: a / b };
83
- } catch (e) {
84
- return { ok: false, error: e.message };
85
- }
86
- }
87
- `);
88
- expect(results[0].result).toContain("ok");
89
- expect(results[0].result).toContain("value");
90
- });
91
-
92
- it("retry pattern", () => {
93
- const results = runTest(`
94
- // @nudo:case "retry" ()
95
- async function withRetry(fn, maxRetries) {
96
- let lastError;
97
- for (let i = 0; i < maxRetries; i++) {
98
- try {
99
- return { success: true, data: await fn() };
100
- } catch (e) {
101
- lastError = e;
102
- }
103
- }
104
- return { success: false, error: "max retries exceeded" };
105
- }
106
- `);
107
- // Async function returns Promise
108
- expect(results[0].result).toContain("Promise");
109
- });
110
-
111
- it("validation pattern", () => {
112
- const results = runTest(`
113
- // @nudo:case "validate" ({ email: "test@example.com", age: 25 })
114
- function validateUser(user) {
115
- const errors = [];
116
- if (!user.email || !user.email.includes("@")) {
117
- errors.push("Invalid email");
118
- }
119
- if (user.age < 0 || user.age > 150) {
120
- errors.push("Invalid age");
121
- }
122
- return {
123
- valid: errors.length === 0,
124
- errors,
125
- user: errors.length === 0 ? user : undefined
126
- };
127
- }
128
- `);
129
- expect(results[0].result).toContain("valid");
130
- expect(results[0].result).toContain("errors");
131
- });
132
- });
133
-
134
- describe("Event Emitter Pattern", () => {
135
- it("basic event emitter", () => {
136
- const results = runTest(`
137
- // @nudo:case "emitter" ()
138
- function createEmitter() {
139
- const listeners = new Map();
140
- return {
141
- on(event, fn) {
142
- if (!listeners.has(event)) {
143
- listeners.set(event, []);
144
- }
145
- listeners.get(event).push(fn);
146
- },
147
- off(event, fn) {
148
- const fns = listeners.get(event);
149
- if (fns) {
150
- const idx = fns.indexOf(fn);
151
- if (idx >= 0) fns.splice(idx, 1);
152
- }
153
- },
154
- emit(event, ...args) {
155
- const fns = listeners.get(event);
156
- if (fns) {
157
- for (const fn of fns) fn(...args);
158
- }
159
- }
160
- };
161
- }
162
- `);
163
- expect(results[0].result).toContain("on");
164
- expect(results[0].result).toContain("off");
165
- expect(results[0].result).toContain("emit");
166
- });
167
- });
168
-
169
- describe("Configuration Patterns", () => {
170
- it("config with defaults", () => {
171
- const results = runTest(`
172
- // @nudo:case "config" ({ host: "localhost" })
173
- function createConfig(userConfig) {
174
- const defaults = {
175
- port: 3000,
176
- debug: false,
177
- logLevel: "info"
178
- };
179
- return { ...defaults, ...userConfig };
180
- }
181
- `);
182
- expect(results[0].result).toContain("host");
183
- expect(results[0].result).toContain("port");
184
- expect(results[0].result).toContain("debug");
185
- });
186
-
187
- it("config validation", () => {
188
- const results = runTest(`
189
- // @nudo:case "validate-config" ({ host: "localhost", port: 3000 })
190
- function validateConfig(config) {
191
- const errors = [];
192
- if (!config.host) errors.push("host is required");
193
- if (config.port < 1 || config.port > 65535) errors.push("port must be 1-65535");
194
- return {
195
- valid: errors.length === 0,
196
- errors,
197
- config: errors.length === 0 ? config : undefined
198
- };
199
- }
200
- `);
201
- expect(results[0].result).toContain("valid");
202
- expect(results[0].result).toContain("config");
203
- });
204
- });
205
-
206
- describe("API Response Patterns", () => {
207
- it("API response handler", () => {
208
- const results = runTest(`
209
- // @nudo:case "api-response" ({ statusCode: 200, body: { data: "test" }, headers: { "x-request-id": "123" } })
210
- function handleResponse(response) {
211
- if (!response) {
212
- return { status: "error", message: "No response" };
213
- }
214
- if (response.statusCode >= 200 && response.statusCode < 300) {
215
- return {
216
- status: "success",
217
- data: response.body,
218
- meta: { requestId: response.headers["x-request-id"] }
219
- };
220
- }
221
- if (response.statusCode >= 400) {
222
- return {
223
- status: "error",
224
- code: response.statusCode,
225
- message: response.body?.error || "Unknown error"
226
- };
227
- }
228
- return { status: "unknown", code: response.statusCode };
229
- }
230
- `);
231
- expect(results[0].result).toContain("success");
232
- expect(results[0].result).toContain("data");
233
- });
234
-
235
- it("paginated response", () => {
236
- const results = runTest(`
237
- // @nudo:case "paginate" ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1, 3)
238
- function paginate(items, page, pageSize) {
239
- const start = (page - 1) * pageSize;
240
- const end = start + pageSize;
241
- const pageItems = items.slice(start, end);
242
- return {
243
- items: pageItems,
244
- page,
245
- pageSize,
246
- total: items.length,
247
- totalPages: Math.ceil(items.length / pageSize)
248
- };
249
- }
250
- `);
251
- expect(results[0].result).toContain("items");
252
- expect(results[0].result).toContain("page");
253
- expect(results[0].result).toContain("total");
254
- });
255
- });
256
-
257
- describe("State Management Patterns", () => {
258
- it("counter with actions", () => {
259
- const results = runTest(`
260
- // @nudo:case "counter" ()
261
- function createCounter(initial = 0) {
262
- let count = initial;
263
- return {
264
- increment() { return ++count; },
265
- decrement() { return --count; },
266
- reset() { count = initial; return count; },
267
- get() { return count; }
268
- };
269
- }
270
- `);
271
- expect(results[0].result).toContain("increment");
272
- expect(results[0].result).toContain("decrement");
273
- expect(results[0].result).toContain("reset");
274
- expect(results[0].result).toContain("get");
275
- });
276
-
277
- it("todo list", () => {
278
- const results = runTest(`
279
- // @nudo:case "todo-list" ()
280
- function createTodoList() {
281
- const todos = [];
282
- let nextId = 1;
283
- return {
284
- add(text) {
285
- const todo = { id: nextId++, text, completed: false };
286
- todos.push(todo);
287
- return todo;
288
- },
289
- complete(id) {
290
- const todo = todos.find(t => t.id === id);
291
- if (todo) todo.completed = true;
292
- return todo;
293
- },
294
- getAll() { return todos; },
295
- getActive() { return todos.filter(t => !t.completed); }
296
- };
297
- }
298
- `);
299
- expect(results[0].result).toContain("add");
300
- expect(results[0].result).toContain("complete");
301
- expect(results[0].result).toContain("getAll");
302
- expect(results[0].result).toContain("getActive");
303
- });
304
- });
305
-
306
- describe("Utility Functions", () => {
307
- it("deep clone", () => {
308
- const results = runTest(`
309
- // @nudo:case "deep-clone" ({ a: { b: { c: 1 } }, d: [1, 2, 3] })
310
- function deepClone(obj) {
311
- if (obj === null || typeof obj !== "object") return obj;
312
- if (Array.isArray(obj)) return obj.map(deepClone);
313
- const clone = {};
314
- for (const key in obj) {
315
- clone[key] = deepClone(obj[key]);
316
- }
317
- return clone;
318
- }
319
- `);
320
- expect(results[0].result).toContain("a");
321
- expect(results[0].result).toContain("b");
322
- expect(results[0].result).toContain("d");
323
- });
324
-
325
- it("debounce", () => {
326
- const results = runTest(`
327
- // @nudo:case "debounce" ()
328
- function createDebounce(fn, delay) {
329
- let timeoutId;
330
- return function(...args) {
331
- clearTimeout(timeoutId);
332
- timeoutId = setTimeout(() => fn(...args), delay);
333
- };
334
- }
335
- `);
336
- // Functions are displayed as arrow function syntax
337
- expect(results[0].result).toContain("=>");
338
- });
339
-
340
- it("memoize", () => {
341
- const results = runTest(`
342
- // @nudo:case "memoize" ()
343
- function memoize(fn) {
344
- const cache = new Map();
345
- return function(...args) {
346
- const key = JSON.stringify(args);
347
- if (cache.has(key)) return cache.get(key);
348
- const result = fn(...args);
349
- cache.set(key, result);
350
- return result;
351
- };
352
- }
353
- `);
354
- // Functions are displayed as arrow function syntax
355
- expect(results[0].result).toContain("=>");
356
- });
357
-
358
- it("escape dispatch through a string-keyed method table", () => {
359
- const results = runTest(`
360
- // @nudo:case "table-dispatch" (T.string)
361
- function escapeViaTable(char) {
362
- const Strings = {
363
- '<': () => 'LT',
364
- '>': () => 'GT',
365
- '&': () => 'AMP'
366
- };
367
- return Strings[char]();
368
- }
369
- `);
370
- // Strings[char] unions the table's functions; calling the union runs
371
- // each member, so the result is the union of their results.
372
- const r = results[0].result;
373
- expect(r).toContain("LT");
374
- expect(r).toContain("GT");
375
- expect(r).toContain("AMP");
376
- });
377
- });
378
- });
379
-
380
- describe("Named Function Callbacks through builtin HOFs", () => {
381
- it("arr.map(double) with a named function declaration yields precise literals", () => {
382
- const results = runTest(`
383
- // @nudo:case "xs" ([1, 2, 3])
384
- function doubleAll(xs) {
385
- function double(x) { return x * 2; }
386
- return xs.map(double);
387
- }
388
- `);
389
- // 具名函数回调从其 TypeValue 闭包求值,而非降级 unknown
390
- expect(results[0].result).toBe("[2, 4, 6]");
391
- });
392
-
393
- it("arr.reduce(sum, 0) with a named reducer yields number", () => {
394
- const results = runTest(`
395
- // @nudo:case "xs" ([1, 2, 3])
396
- function sumAll(xs) {
397
- function sum(a, b) { return a + b; }
398
- return xs.reduce(sum, 0);
399
- }
400
- `);
401
- expect(results[0].result).toBe("6");
402
- });
403
-
404
- it("arr.filter(big) with a named predicate keeps the element type", () => {
405
- const results = runTest(`
406
- // @nudo:case "xs" ([1, 2, 3, 4])
407
- function keepBig(xs) {
408
- function big(x) { return x > 2; }
409
- return xs.filter(big);
410
- }
411
- `);
412
- // 过滤结果元素类型不丢失:保留 3、4 字面量成员
413
- expect(results[0].result).toContain("3");
414
- expect(results[0].result).toContain("4");
415
- expect(results[0].result).toContain("[");
416
- });
417
-
418
- it("named callbacks over a union receiver distribute per member", () => {
419
- const results = runTest(`
420
- // @nudo:case "go" ()
421
- function pickThenMap() {
422
- const xs = Math.random() > 0.5 ? [1, 2] : [3, 4, 5];
423
- function double(x) { return x * 2; }
424
- return xs.map(double);
425
- }
426
- `);
427
- // union 接收者:每个数组成员分别经回调求值后 union
428
- expect(results[0].result).toBe("[2, 4] | [6, 8, 10]");
429
- });
430
- });