@nudojs/parser 0.4.0 → 0.4.2

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { File, Node } from '@babel/types';
2
- import { TypeValue, MockHelper } from '@nudojs/core';
2
+ import { Abs, MockHelper } from '@nudojs/core';
3
3
  export { stripTypes } from '@nudojs/core';
4
4
 
5
5
  /**
@@ -18,8 +18,10 @@ declare function parse(source: string, opts?: {
18
18
  type CaseDirective = {
19
19
  kind: "case";
20
20
  name: string;
21
- args: TypeValue[];
22
- expected?: TypeValue;
21
+ /** 无损参数 Abs(case 文法唯一真理源;约束表达式与 T.* 均产出) */
22
+ argsAbs: Abs[];
23
+ /** `=> expected` 断言 Abs */
24
+ expected?: Abs;
23
25
  commentLine?: number;
24
26
  };
25
27
  type MockDirective = {
@@ -37,16 +39,16 @@ type MockDirective = {
37
39
  };
38
40
  type SinonExpression = {
39
41
  type: "stub" | "spy" | "mock";
40
- returnValue?: TypeValue;
41
- resolvedValue?: TypeValue;
42
- rejectedValue?: TypeValue;
42
+ returnValue?: Abs;
43
+ resolvedValue?: Abs;
44
+ rejectedValue?: Abs;
43
45
  };
44
46
  type PureDirective = {
45
47
  kind: "pure";
46
48
  };
47
49
  type SkipDirective = {
48
50
  kind: "skip";
49
- returns?: TypeValue;
51
+ returns?: Abs;
50
52
  };
51
53
  type SampleDirective = {
52
54
  kind: "sample";
@@ -64,12 +66,12 @@ type MockModuleDirective = {
64
66
  };
65
67
  type AsDirective = {
66
68
  kind: "as";
67
- typeExpr: TypeValue;
69
+ typeAbs: Abs;
68
70
  };
69
71
  type ReplaceDirective = {
70
72
  kind: "replace";
71
73
  targetSource: string;
72
- typeExpr: TypeValue;
74
+ typeAbs: Abs;
73
75
  };
74
76
  type InlineDirective = AsDirective | ReplaceDirective;
75
77
  type FileDirective = EnvDirective | MockModuleDirective;
@@ -79,9 +81,15 @@ type FunctionWithDirectives = {
79
81
  name: string;
80
82
  directives: Directive[];
81
83
  };
82
- declare function parseTypeValueExpr(expr: string): TypeValue;
84
+ /**
85
+ * case 实参主路径:始终产出 Abs;约束表达式优先。
86
+ * `T.*` 文法 **已弃用**,仅为旧 fixture 兼容保留——新代码用 `number()`/`lit()`。
87
+ */
88
+ declare function parseCaseArgExpr(expr: string): Abs;
89
+ /** @deprecated 旧名兼容;等价 parseCaseArgExpr */
90
+ declare function parseTypeValueExpr(expr: string): Abs;
83
91
  declare function extractDirectives(ast: Node): FunctionWithDirectives[];
84
92
  declare function extractInlineDirectives(node: Node): InlineDirective[];
85
93
  declare function extractFileDirectives(ast: Node): FileDirective[];
86
94
 
87
- export { type AsDirective, type CaseDirective, type Directive, type EnvDirective, type FileDirective, type FunctionWithDirectives, type InlineDirective, type MockDirective, type MockModuleDirective, type PureDirective, type ReplaceDirective, type SampleDirective, type SinonExpression, type SkipDirective, extractDirectives, extractFileDirectives, extractInlineDirectives, parse, parseTypeValueExpr };
95
+ export { type AsDirective, type CaseDirective, type Directive, type EnvDirective, type FileDirective, type FunctionWithDirectives, type InlineDirective, type MockDirective, type MockModuleDirective, type PureDirective, type ReplaceDirective, type SampleDirective, type SinonExpression, type SkipDirective, extractDirectives, extractFileDirectives, extractInlineDirectives, parse, parseCaseArgExpr, parseTypeValueExpr };
package/dist/index.js CHANGED
@@ -8,98 +8,194 @@ function parse(source, opts) {
8
8
  import { stripTypes } from "@nudojs/core";
9
9
 
10
10
  // src/directives.ts
11
- import { T, createEnvironment, stub, spy, mock } from "@nudojs/core";
11
+ import {
12
+ stub,
13
+ spy,
14
+ mock,
15
+ abs as makeAbs,
16
+ lit as termLit,
17
+ num,
18
+ str,
19
+ bool,
20
+ numLit,
21
+ strLit,
22
+ boolLit,
23
+ absFunction,
24
+ joinAbs,
25
+ execNudoModule,
26
+ isNudoConstraint,
27
+ constraintToEntryAbs,
28
+ SELF
29
+ } from "@nudojs/core";
30
+ function absExact(shape) {
31
+ return makeAbs(shape, void 0, void 0, "exact");
32
+ }
33
+ function absUnknown() {
34
+ return makeAbs({ k: "unknown" }, void 0, void 0, "partial");
35
+ }
36
+ function absNullLit() {
37
+ return makeAbs({ k: "unknown" }, termLit(null), void 0, "exact");
38
+ }
39
+ function absUndefLit() {
40
+ return makeAbs({ k: "unknown" }, termLit(void 0), void 0, "exact");
41
+ }
42
+ function absLit(v) {
43
+ if (typeof v === "number") return numLit(v);
44
+ if (typeof v === "string") return strLit(v);
45
+ if (typeof v === "boolean") return boolLit(v);
46
+ if (v === null) return absNullLit();
47
+ return absUndefLit();
48
+ }
49
+ function absUnion(members) {
50
+ if (members.length === 0) return absExact({ k: "never" });
51
+ return members.reduce((acc, m) => joinAbs(acc, m));
52
+ }
12
53
  var CASE_NAME_REGEX = /@nudo:case\s+"([^"]+)"\s*\(/g;
13
54
  var MOCK_INLINE_REGEX = /@nudo:mock\s+(\w+)\s*=\s*(.+)/g;
14
55
  var MOCK_FROM_REGEX = /@nudo:mock\s+(\w+)\s+from\s+"([^"]+)"/g;
15
56
  var PURE_REGEX = /@nudo:pure\b/g;
16
57
  var SKIP_REGEX = /@nudo:skip(?:\s+(.+))?/g;
17
58
  var SAMPLE_REGEX = /@nudo:sample\s+(\d+)/g;
59
+ var CONSTRAINT_EXPR_RE = /^(number|string|boolean|array|shape|lit|union|fn|partial|pick|omit|record|required|readonly|nonNullable|and)\s*\(/;
60
+ function tryParseConstraint(expr) {
61
+ const s = expr.trim();
62
+ if (!CONSTRAINT_EXPR_RE.test(s)) return void 0;
63
+ try {
64
+ const src = `export const __nudo_case_arg = (${s});`;
65
+ const exports = execNudoModule(src);
66
+ const v = exports.__nudo_case_arg;
67
+ if (!isNudoConstraint(v)) return void 0;
68
+ return v;
69
+ } catch {
70
+ return void 0;
71
+ }
72
+ }
73
+ function flattenPreds(preds) {
74
+ const out = [];
75
+ const visit = (p) => {
76
+ if (p.op === "and") {
77
+ p.args.forEach(visit);
78
+ return;
79
+ }
80
+ out.push(p);
81
+ };
82
+ preds.forEach(visit);
83
+ return out;
84
+ }
85
+ function constraintSelfEqLit(c, selfIds) {
86
+ const leaves = flattenPreds(c.preds);
87
+ const eqs = leaves.filter((p2) => p2.op === "eq");
88
+ if (eqs.length !== 1) return void 0;
89
+ const p = eqs[0];
90
+ if (p.op !== "eq") return void 0;
91
+ const isSelf = (t) => t.op === "var" && typeof t.id === "string" && selfIds.includes(t.id);
92
+ const a = p.a;
93
+ const b = p.b;
94
+ if (isSelf(a) && b.op === "lit") return b.value;
95
+ if (isSelf(b) && a.op === "lit") return a.value;
96
+ return void 0;
97
+ }
98
+ function constraintToCaseArgAbs(c) {
99
+ if (c.members && c.members.length > 0) {
100
+ const parts = c.members.map((m) => constraintToCaseArgAbs(m));
101
+ if (parts.length === 1) return parts[0];
102
+ return { shape: { k: "sum", members: parts }, conf: "path" };
103
+ }
104
+ const selfIds = [SELF, "__arg", "__nudo_self__"];
105
+ const lv = constraintSelfEqLit(c, selfIds);
106
+ if (typeof lv === "number" && !Number.isNaN(lv)) return numLit(lv);
107
+ if (typeof lv === "string") return strLit(lv);
108
+ if (typeof lv === "boolean") return boolLit(lv);
109
+ return constraintToEntryAbs(c, "__arg");
110
+ }
111
+ function parseCaseArgExpr(expr) {
112
+ const constraint = tryParseConstraint(expr);
113
+ if (constraint) {
114
+ try {
115
+ return constraintToCaseArgAbs(constraint);
116
+ } catch {
117
+ return absUnknown();
118
+ }
119
+ }
120
+ return parseLegacyTypeExpr(expr);
121
+ }
18
122
  function parseTypeValueExpr(expr) {
123
+ return parseCaseArgExpr(expr);
124
+ }
125
+ function parseLegacyTypeExpr(expr) {
19
126
  const s = expr.trim();
20
- if (s === "T.number") return T.number;
21
- if (s === "T.string") return T.string;
22
- if (s === "T.boolean") return T.boolean;
23
- if (s === "T.unknown") return T.unknown;
24
- if (s === "T.never") return T.never;
25
- if (s === "T.null") return T.null;
26
- if (s === "T.undefined") return T.undefined;
27
- if (s === "true") return T.literal(true);
28
- if (s === "false") return T.literal(false);
29
- if (s === "null") return T.literal(null);
30
- if (s === "undefined") return T.literal(void 0);
127
+ if (s === "T.number") return num();
128
+ if (s === "T.string") return str();
129
+ if (s === "T.boolean") return bool();
130
+ if (s === "T.unknown") return absUnknown();
131
+ if (s === "T.never") return absExact({ k: "never" });
132
+ if (s === "T.null") return absNullLit();
133
+ if (s === "T.undefined") return absUndefLit();
134
+ if (s === "true") return absLit(true);
135
+ if (s === "false") return absLit(false);
136
+ if (s === "null") return absLit(null);
137
+ if (s === "undefined") return absLit(void 0);
31
138
  const literalMatch = s.match(/^T\.literal\((.+)\)$/);
32
139
  if (literalMatch) {
33
- return T.literal(parsePrimitiveValue(literalMatch[1].trim()));
140
+ return absLit(parsePrimitiveValue(literalMatch[1].trim()));
34
141
  }
35
142
  if (s.startsWith("T.union(") && s.endsWith(")")) {
36
143
  const inner = s.slice("T.union(".length, -1);
37
144
  const args = splitTopLevelArgs(inner);
38
- return T.union(...args.map(parseTypeValueExpr));
145
+ return absUnion(args.map(parseLegacyTypeExpr));
39
146
  }
40
147
  if (s.startsWith("T.array(") && s.endsWith(")")) {
41
148
  const inner = s.slice("T.array(".length, -1);
42
- return T.array(parseTypeValueExpr(inner));
149
+ return absExact({ k: "arr", element: parseLegacyTypeExpr(inner) });
43
150
  }
44
151
  if (s.startsWith("T.tuple(") && s.endsWith(")")) {
45
152
  const inner = s.slice("T.tuple(".length, -1).trim();
46
153
  if (inner.startsWith("[") && inner.endsWith("]")) {
47
154
  const elements = splitTopLevelArgs(inner.slice(1, -1));
48
- return T.tuple(elements.map(parseTypeValueExpr));
155
+ return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
49
156
  }
50
- return T.tuple([]);
157
+ return absExact({ k: "tuple", elements: [] });
51
158
  }
52
159
  if (s.startsWith("T.object(") && s.endsWith(")")) {
53
160
  const inner = s.slice("T.object(".length, -1).trim();
54
161
  if (inner.startsWith("{") && inner.endsWith("}")) {
55
- const content = inner.slice(1, -1).trim();
56
- if (!content) return T.object({});
57
- const entries = splitTopLevelArgs(content);
58
- const props = {};
59
- for (const entry of entries) {
60
- const colonIdx = entry.indexOf(":");
61
- if (colonIdx === -1) continue;
62
- const key = entry.slice(0, colonIdx).trim();
63
- const val = entry.slice(colonIdx + 1).trim();
64
- props[key] = parseTypeValueExpr(val);
65
- }
66
- return T.object(props);
162
+ return parseObjectLiteral(inner.slice(1, -1).trim());
67
163
  }
68
- return T.object({});
164
+ return absExact({ k: "obj", slots: {} });
69
165
  }
70
166
  if (findTopLevelArrow(s) !== -1 || /^function\s*[\w$]*\s*\(/.test(s)) {
71
167
  const fnExpr = parseArrowFunctionExpr(s);
72
168
  if (fnExpr) {
73
- const fnType = T.fn(fnExpr.params, fnExpr.body, createEnvironment());
74
- fnType._paramPatterns = fnExpr.paramPatterns;
75
- return fnType;
169
+ return absFunction(fnExpr.params, { body: fnExpr.body });
76
170
  }
77
171
  }
78
- if (/^-?\d+(\.\d+)?$/.test(s)) return T.literal(Number(s));
172
+ if (/^-?\d+(\.\d+)?$/.test(s)) return absLit(Number(s));
79
173
  if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
80
- return T.literal(s.slice(1, -1));
174
+ return absLit(s.slice(1, -1));
81
175
  }
82
176
  if (s.startsWith("{") && s.endsWith("}")) {
83
- const content = s.slice(1, -1).trim();
84
- if (!content) return T.object({});
85
- const entries = splitTopLevelArgs(content);
86
- const props = {};
87
- for (const entry of entries) {
88
- const colonIdx = findTopLevelColon(entry);
89
- if (colonIdx === -1) continue;
90
- const key = entry.slice(0, colonIdx).trim().replace(/^["']|["']$/g, "");
91
- const val = entry.slice(colonIdx + 1).trim();
92
- props[key] = parseTypeValueExpr(val);
93
- }
94
- return T.object(props);
177
+ return parseObjectLiteral(s.slice(1, -1).trim());
95
178
  }
96
179
  if (s.startsWith("[") && s.endsWith("]")) {
97
180
  const content = s.slice(1, -1).trim();
98
- if (!content) return T.tuple([]);
181
+ if (!content) return absExact({ k: "tuple", elements: [] });
99
182
  const elements = splitTopLevelArgs(content);
100
- return T.tuple(elements.map(parseTypeValueExpr));
183
+ return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
101
184
  }
102
- return T.unknown;
185
+ return absUnknown();
186
+ }
187
+ function parseObjectLiteral(content) {
188
+ if (!content) return absExact({ k: "obj", slots: {} });
189
+ const entries = splitTopLevelArgs(content);
190
+ const slots = {};
191
+ for (const entry of entries) {
192
+ const colonIdx = findTopLevelColon(entry);
193
+ if (colonIdx === -1) continue;
194
+ const key = entry.slice(0, colonIdx).trim().replace(/^["']|["']$/g, "");
195
+ const val = entry.slice(colonIdx + 1).trim();
196
+ slots[key] = { value: parseLegacyTypeExpr(val) };
197
+ }
198
+ return absExact({ k: "obj", slots });
103
199
  }
104
200
  function parsePrimitiveValue(s) {
105
201
  if (s === "true") return true;
@@ -189,62 +285,65 @@ function parseArrowFunctionExpr(expr) {
189
285
  return null;
190
286
  }
191
287
  }
288
+ function parseAbsExpr(expr) {
289
+ return parseCaseArgExpr(expr);
290
+ }
192
291
  function parseSinonExpr(expr) {
193
292
  const s = expr.trim();
194
293
  const stubReturnsMatch = s.match(/^sinon\.stub\(\)\.returns\((.+)\)$/);
195
294
  if (stubReturnsMatch) {
196
295
  return {
197
296
  type: "stub",
198
- returnValue: parseTypeValueExpr(stubReturnsMatch[1].trim())
297
+ returnValue: parseAbsExpr(stubReturnsMatch[1].trim())
199
298
  };
200
299
  }
201
300
  const stubResolvesMatch = s.match(/^sinon\.stub\(\)\.resolves\((.+)\)$/);
202
301
  if (stubResolvesMatch) {
203
302
  return {
204
303
  type: "stub",
205
- resolvedValue: parseTypeValueExpr(stubResolvesMatch[1].trim())
304
+ resolvedValue: parseAbsExpr(stubResolvesMatch[1].trim())
206
305
  };
207
306
  }
208
307
  const stubRejectsMatch = s.match(/^sinon\.stub\(\)\.rejects\((.+)\)$/);
209
308
  if (stubRejectsMatch) {
210
309
  return {
211
310
  type: "stub",
212
- rejectedValue: parseTypeValueExpr(stubRejectsMatch[1].trim())
311
+ rejectedValue: parseAbsExpr(stubRejectsMatch[1].trim())
213
312
  };
214
313
  }
215
314
  const onFirstCallReturnsMatch = s.match(/^sinon\.stub\(\)\.onFirstCall\(\)\.returns\((.+)\)$/);
216
315
  if (onFirstCallReturnsMatch) {
217
316
  return {
218
317
  type: "stub",
219
- returnValue: parseTypeValueExpr(onFirstCallReturnsMatch[1].trim())
318
+ returnValue: parseAbsExpr(onFirstCallReturnsMatch[1].trim())
220
319
  };
221
320
  }
222
321
  const onSecondCallReturnsMatch = s.match(/^sinon\.stub\(\)\.onSecondCall\(\)\.returns\((.+)\)$/);
223
322
  if (onSecondCallReturnsMatch) {
224
323
  return {
225
324
  type: "stub",
226
- returnValue: parseTypeValueExpr(onSecondCallReturnsMatch[1].trim())
325
+ returnValue: parseAbsExpr(onSecondCallReturnsMatch[1].trim())
227
326
  };
228
327
  }
229
328
  const onThirdCallReturnsMatch = s.match(/^sinon\.stub\(\)\.onThirdCall\(\)\.returns\((.+)\)$/);
230
329
  if (onThirdCallReturnsMatch) {
231
330
  return {
232
331
  type: "stub",
233
- returnValue: parseTypeValueExpr(onThirdCallReturnsMatch[1].trim())
332
+ returnValue: parseAbsExpr(onThirdCallReturnsMatch[1].trim())
234
333
  };
235
334
  }
236
335
  const onCallReturnsMatch = s.match(/^sinon\.stub\(\)\.onCall\(\d+\)\.returns\((.+)\)$/);
237
336
  if (onCallReturnsMatch) {
238
337
  return {
239
338
  type: "stub",
240
- returnValue: parseTypeValueExpr(onCallReturnsMatch[1].trim())
339
+ returnValue: parseAbsExpr(onCallReturnsMatch[1].trim())
241
340
  };
242
341
  }
243
342
  const withArgsReturnsMatch = s.match(/^sinon\.stub\(\)\.withArgs\(.+\)\.returns\((.+)\)$/);
244
343
  if (withArgsReturnsMatch) {
245
344
  return {
246
345
  type: "stub",
247
- returnValue: parseTypeValueExpr(withArgsReturnsMatch[1].trim())
346
+ returnValue: parseAbsExpr(withArgsReturnsMatch[1].trim())
248
347
  };
249
348
  }
250
349
  const callsFakeMatch = s.match(/^sinon\.stub\(\)\.callsFake\((.+)\)$/);
@@ -253,7 +352,9 @@ function parseSinonExpr(expr) {
253
352
  if (arrowFn) {
254
353
  return {
255
354
  type: "stub",
256
- returnValue: T.fn(arrowFn.params, arrowFn.body, createEnvironment())
355
+ returnValue: absFunction(arrowFn.params, {
356
+ body: arrowFn.body
357
+ })
257
358
  };
258
359
  }
259
360
  return { type: "stub" };
@@ -262,7 +363,7 @@ function parseSinonExpr(expr) {
262
363
  if (complexChainMatch) {
263
364
  return {
264
365
  type: "stub",
265
- returnValue: parseTypeValueExpr(complexChainMatch[1].trim())
366
+ returnValue: parseAbsExpr(complexChainMatch[1].trim())
266
367
  };
267
368
  }
268
369
  if (s === "sinon.stub()") {
@@ -280,62 +381,64 @@ function parseNudoMockExpr(expr) {
280
381
  const s = expr.trim();
281
382
  const stubReturnsOnFirstMatch = s.match(/^stub\(\)\.returns\((.+)\)\.onFirstCall\(\)$/);
282
383
  if (stubReturnsOnFirstMatch) {
283
- const helper = stub.returns(parseTypeValueExpr(stubReturnsOnFirstMatch[1].trim()));
384
+ const helper = stub.returns(parseAbsExpr(stubReturnsOnFirstMatch[1].trim()));
284
385
  helper.onFirstCallValue = helper.returnValue;
285
386
  return helper;
286
387
  }
287
388
  const stubReturnsOnSecondMatch = s.match(/^stub\(\)\.returns\((.+)\)\.onSecondCall\(\)$/);
288
389
  if (stubReturnsOnSecondMatch) {
289
- return stub.returns(parseTypeValueExpr(stubReturnsOnSecondMatch[1].trim()));
390
+ return stub.returns(parseAbsExpr(stubReturnsOnSecondMatch[1].trim()));
290
391
  }
291
392
  const stubReturnsOnCallMatch = s.match(/^stub\(\)\.returns\((.+)\)\.onCall\(\d+\)$/);
292
393
  if (stubReturnsOnCallMatch) {
293
- return stub.returns(parseTypeValueExpr(stubReturnsOnCallMatch[1].trim()));
394
+ return stub.returns(parseAbsExpr(stubReturnsOnCallMatch[1].trim()));
294
395
  }
295
396
  const stubCallsFakeMatch = s.match(/^stub\(\)\.callsFake\((.+)\)$/);
296
397
  if (stubCallsFakeMatch) {
297
- const helper = { kind: "mock-helper" };
298
- helper.callsFakeImpl = parseTypeValueExpr(stubCallsFakeMatch[1].trim());
299
- return helper;
398
+ const arrowFn = parseArrowFunctionExpr(stubCallsFakeMatch[1].trim());
399
+ if (arrowFn) {
400
+ return stub.callsFake({ params: arrowFn.params, body: arrowFn.body, async: false });
401
+ }
402
+ return { kind: "mock-helper" };
300
403
  }
301
404
  const stubWithArgsMatch = s.match(/^stub\(\)\.withArgs\((.+)\)\.returns\((.+)\)$/);
302
405
  if (stubWithArgsMatch) {
303
- const retVal = parseTypeValueExpr(stubWithArgsMatch[2].trim());
406
+ const retVal = parseAbsExpr(stubWithArgsMatch[2].trim());
304
407
  const argsStr = stubWithArgsMatch[1].trim();
305
- const args = splitTopLevelArgs(argsStr).map(parseTypeValueExpr);
408
+ const args = splitTopLevelArgs(argsStr).map((a) => parseAbsExpr(a));
306
409
  const helper = { kind: "mock-helper" };
307
410
  helper.withArgsCases = [{ args, returnValue: retVal }];
308
411
  return helper;
309
412
  }
310
413
  const stubOnFirstReturnsMatch = s.match(/^stub\(\)\.onFirstCall\(\)\.returns\((.+)\)$/);
311
414
  if (stubOnFirstReturnsMatch) {
312
- return stub.returns(parseTypeValueExpr(stubOnFirstReturnsMatch[1].trim()));
415
+ return stub.returns(parseAbsExpr(stubOnFirstReturnsMatch[1].trim()));
313
416
  }
314
417
  const stubOnFirstValueMatch = s.match(/^stub\(\)\.onFirstCall\(([^()]*)\)$/);
315
418
  if (stubOnFirstValueMatch && stubOnFirstValueMatch[1].trim() !== "") {
316
419
  const helper = { kind: "mock-helper" };
317
- helper.onFirstCallValue = parseTypeValueExpr(stubOnFirstValueMatch[1].trim());
420
+ helper.onFirstCallValue = parseAbsExpr(stubOnFirstValueMatch[1].trim());
318
421
  return helper;
319
422
  }
320
423
  const spyReturnsMatch = s.match(/^spy\(\)\.returns\((.+)\)$/);
321
424
  if (spyReturnsMatch) {
322
- return spy.returns(parseTypeValueExpr(spyReturnsMatch[1].trim()));
425
+ return spy.returns(parseAbsExpr(spyReturnsMatch[1].trim()));
323
426
  }
324
427
  const stubResolvesOnFirstMatch = s.match(/^stub\(\)\.resolves\((.+)\)\.onFirstCall\(\)$/);
325
428
  if (stubResolvesOnFirstMatch) {
326
- return stub.resolves(parseTypeValueExpr(stubResolvesOnFirstMatch[1].trim()));
429
+ return stub.resolves(parseAbsExpr(stubResolvesOnFirstMatch[1].trim()));
327
430
  }
328
431
  const stubReturnsMatch = s.match(/^stub\(\)\.returns\((.+)\)$/);
329
432
  if (stubReturnsMatch) {
330
- return stub.returns(parseTypeValueExpr(stubReturnsMatch[1].trim()));
433
+ return stub.returns(parseAbsExpr(stubReturnsMatch[1].trim()));
331
434
  }
332
435
  const stubResolvesMatch = s.match(/^stub\(\)\.resolves\((.+)\)$/);
333
436
  if (stubResolvesMatch) {
334
- return stub.resolves(parseTypeValueExpr(stubResolvesMatch[1].trim()));
437
+ return stub.resolves(parseAbsExpr(stubResolvesMatch[1].trim()));
335
438
  }
336
439
  const stubRejectsMatch = s.match(/^stub\(\)\.rejects\((.+)\)$/);
337
440
  if (stubRejectsMatch) {
338
- return stub.rejects(parseTypeValueExpr(stubRejectsMatch[1].trim()));
441
+ return stub.rejects(parseAbsExpr(stubRejectsMatch[1].trim()));
339
442
  }
340
443
  if (s === "stub()") {
341
444
  return stub();
@@ -401,14 +504,20 @@ function parseDirectivesFromComments(comments) {
401
504
  const argsStr = extractBalancedParens(text, parenStart);
402
505
  if (argsStr === null) continue;
403
506
  const cleaned = argsStr.split("\n").map((line) => line.replace(/^\s*\*\s?/, "")).join("\n");
404
- const args = splitTopLevelArgs(cleaned).map(parseTypeValueExpr);
507
+ const argsAbs = splitTopLevelArgs(cleaned).map(parseCaseArgExpr);
405
508
  const afterParen = parenStart + argsStr.length + 2;
406
509
  const restLine = text.slice(afterParen).split("\n")[0].trim();
407
510
  const arrowMatch = restLine.match(/^=>\s*(.+)/);
408
- const expected = arrowMatch ? parseTypeValueExpr(arrowMatch[1].trim()) : void 0;
511
+ const expected = arrowMatch ? parseCaseArgExpr(arrowMatch[1].trim()) : void 0;
409
512
  const linesBeforeMatch = text.slice(0, match.index).split("\n").length - 1;
410
513
  const commentLine = commentStartLine + linesBeforeMatch;
411
- directives.push({ kind: "case", name, args, expected, commentLine });
514
+ directives.push({
515
+ kind: "case",
516
+ name,
517
+ argsAbs,
518
+ expected,
519
+ commentLine
520
+ });
412
521
  }
413
522
  PURE_REGEX.lastIndex = 0;
414
523
  if (PURE_REGEX.test(text)) {
@@ -420,7 +529,7 @@ function parseDirectivesFromComments(comments) {
420
529
  const returnsExpr = skipMatch[1]?.trim();
421
530
  directives.push({
422
531
  kind: "skip",
423
- returns: returnsExpr ? parseTypeValueExpr(returnsExpr) : void 0
532
+ returns: returnsExpr ? parseAbsExpr(returnsExpr) : void 0
424
533
  });
425
534
  }
426
535
  SAMPLE_REGEX.lastIndex = 0;
@@ -470,7 +579,7 @@ function extractInlineDirectives(node) {
470
579
  const text = comment.value;
471
580
  const asMatch = text.match(AS_REGEX);
472
581
  if (asMatch) {
473
- results.push({ kind: "as", typeExpr: parseTypeValueExpr(asMatch[1].trim()) });
582
+ results.push({ kind: "as", typeAbs: parseAbsExpr(asMatch[1].trim()) });
474
583
  continue;
475
584
  }
476
585
  const replaceMatch = text.match(REPLACE_REGEX);
@@ -483,7 +592,7 @@ function extractInlineDirectives(node) {
483
592
  results.push({
484
593
  kind: "replace",
485
594
  targetSource,
486
- typeExpr: parseTypeValueExpr(typeExprStr)
595
+ typeAbs: parseAbsExpr(typeExprStr)
487
596
  });
488
597
  }
489
598
  }
@@ -563,6 +672,7 @@ export {
563
672
  extractFileDirectives,
564
673
  extractInlineDirectives,
565
674
  parse,
675
+ parseCaseArgExpr,
566
676
  parseTypeValueExpr,
567
677
  stripTypes
568
678
  };
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "@nudojs/parser",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
+ "engines": {
5
+ "node": ">=20"
6
+ },
4
7
  "description": "Babel-based parser and directive extraction for Nudo",
5
8
  "type": "module",
6
9
  "license": "MIT",
@@ -36,7 +39,7 @@
36
39
  "registry": "https://registry.npmjs.org"
37
40
  },
38
41
  "dependencies": {
39
- "@nudojs/core": "0.4.0"
42
+ "@nudojs/core": "1.0.1"
40
43
  },
41
44
  "scripts": {
42
45
  "build": "tsup src/index.ts --format esm --dts"