@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 +19 -11
- package/dist/index.js +191 -81
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { File, Node } from '@babel/types';
|
|
2
|
-
import {
|
|
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
|
-
|
|
22
|
-
|
|
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?:
|
|
41
|
-
resolvedValue?:
|
|
42
|
-
rejectedValue?:
|
|
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?:
|
|
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
|
-
|
|
69
|
+
typeAbs: Abs;
|
|
68
70
|
};
|
|
69
71
|
type ReplaceDirective = {
|
|
70
72
|
kind: "replace";
|
|
71
73
|
targetSource: string;
|
|
72
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
21
|
-
if (s === "T.string") return
|
|
22
|
-
if (s === "T.boolean") return
|
|
23
|
-
if (s === "T.unknown") return
|
|
24
|
-
if (s === "T.never") return
|
|
25
|
-
if (s === "T.null") return
|
|
26
|
-
if (s === "T.undefined") return
|
|
27
|
-
if (s === "true") return
|
|
28
|
-
if (s === "false") return
|
|
29
|
-
if (s === "null") return
|
|
30
|
-
if (s === "undefined") return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
155
|
+
return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
|
|
49
156
|
}
|
|
50
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
172
|
+
if (/^-?\d+(\.\d+)?$/.test(s)) return absLit(Number(s));
|
|
79
173
|
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
80
|
-
return
|
|
174
|
+
return absLit(s.slice(1, -1));
|
|
81
175
|
}
|
|
82
176
|
if (s.startsWith("{") && s.endsWith("}")) {
|
|
83
|
-
|
|
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
|
|
181
|
+
if (!content) return absExact({ k: "tuple", elements: [] });
|
|
99
182
|
const elements = splitTopLevelArgs(content);
|
|
100
|
-
return
|
|
183
|
+
return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
|
|
101
184
|
}
|
|
102
|
-
return
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
394
|
+
return stub.returns(parseAbsExpr(stubReturnsOnCallMatch[1].trim()));
|
|
294
395
|
}
|
|
295
396
|
const stubCallsFakeMatch = s.match(/^stub\(\)\.callsFake\((.+)\)$/);
|
|
296
397
|
if (stubCallsFakeMatch) {
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
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 =
|
|
406
|
+
const retVal = parseAbsExpr(stubWithArgsMatch[2].trim());
|
|
304
407
|
const argsStr = stubWithArgsMatch[1].trim();
|
|
305
|
-
const args = splitTopLevelArgs(argsStr).map(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 ?
|
|
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({
|
|
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 ?
|
|
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",
|
|
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
|
-
|
|
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.
|
|
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.
|
|
42
|
+
"@nudojs/core": "1.0.1"
|
|
40
43
|
},
|
|
41
44
|
"scripts": {
|
|
42
45
|
"build": "tsup src/index.ts --format esm --dts"
|