@nudojs/parser 1.3.2 → 2.0.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.
- package/dist/index.d.ts +28 -15
- package/dist/index.js +44 -69
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -174,36 +174,49 @@ type DirectiveDiag = {
|
|
|
174
174
|
code: string;
|
|
175
175
|
message: string;
|
|
176
176
|
};
|
|
177
|
-
declare function setDirectiveDiagCollector(fn: ((d: DirectiveDiag) => void) | null): void;
|
|
178
|
-
/** 当前诊断累计序号(since 锚:消费方只排干自身 extract 产生的增量) */
|
|
179
|
-
declare function directiveDiagCount(): number;
|
|
180
|
-
/** 取走已收集的诊断(全量排干 + 清空 seen)——CLI/测试整批消费 */
|
|
181
|
-
declare function takeDirectiveDiags(): DirectiveDiag[];
|
|
182
177
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
178
|
+
* 在指定诊断数组上执行 fn:fn 内所有指令文法诊断(含不传 diags 的 extract
|
|
179
|
+
* 与再解析路径,如 @nudo:mock 表达式的 parseCaseArgExpr 复解析)落袋并共用
|
|
180
|
+
* 同一去重域——用于「extract + 复解析」必须共享 seen 的窗口(nudojs check
|
|
181
|
+
* 的 D1 段)。fn 内显式传 diags 的 extract 自成新去重域。
|
|
186
182
|
*/
|
|
187
|
-
declare function
|
|
183
|
+
declare function runWithDirectiveDiags<T>(diags: DirectiveDiag[], fn: () => T): T;
|
|
188
184
|
/**
|
|
189
185
|
* case 实参 / 指令类型表达式唯一文法:约束构建器优先,其余为具体字面量、
|
|
190
186
|
* 结构字面量与箭头函数。`T.*` 文法已物理删除。
|
|
191
187
|
* depth 按包含关系逐层 +1 线程化传递(兄弟共享父深度,不用共享可变计数器)。
|
|
192
188
|
*/
|
|
193
189
|
declare function parseCaseArgExpr(expr: string, depth?: number): Abs;
|
|
190
|
+
/**
|
|
191
|
+
* extractDirectives 显式诊断通道 opts。
|
|
192
|
+
*
|
|
193
|
+
* `diags`:调用方自备的诊断累积数组——本次 extract 产生的指令文法诊断
|
|
194
|
+
* 全部同步落袋,单次调用内同文案去重。不传时为纯查询形态:诊断丢弃
|
|
195
|
+
* (等价 extractDirectivesQuiet);若外层有 runWithDirectiveDiags 活动域,
|
|
196
|
+
* 则落入该域并与其共享去重。
|
|
197
|
+
*/
|
|
198
|
+
type ExtractDirectivesOpts = {
|
|
199
|
+
diags?: DirectiveDiag[];
|
|
200
|
+
};
|
|
194
201
|
/**
|
|
195
202
|
* D6=G2:指令绑定 AST 最近 Function(含 nested function、class method)。
|
|
196
203
|
* 作用域清单与 refine 的 @nudo:contract 同源(core directive-scan),
|
|
197
204
|
* case 与 contract 对同一函数集合同时可见。
|
|
198
205
|
*/
|
|
199
|
-
declare function extractDirectives(ast: Node): FunctionWithDirectives[];
|
|
206
|
+
declare function extractDirectives(ast: Node, opts?: ExtractDirectivesOpts): FunctionWithDirectives[];
|
|
200
207
|
/**
|
|
201
|
-
* 纯查询 extract
|
|
202
|
-
*
|
|
203
|
-
*
|
|
208
|
+
* 纯查询 extract:诊断丢弃——hover/completion/collectSkipReturns 等探测
|
|
209
|
+
* 路径不产诊断(不污染消费方待收的诊断,也不背走别人的在途诊断)。
|
|
210
|
+
* 需要诊断的调用方用 extractDirectives(ast, { diags })。
|
|
204
211
|
*/
|
|
205
212
|
declare function extractDirectivesQuiet(ast: Node): FunctionWithDirectives[];
|
|
206
|
-
|
|
213
|
+
/**
|
|
214
|
+
* 行内指令(@nudo:as / @nudo:replace)抽取,源为节点的 leadingComments。
|
|
215
|
+
* 诊断通道与 extractDirectives 同形:传 opts.diags 落调用方数组(单次调用
|
|
216
|
+
* 内同文案去重,语句级调用各自独立去重);不传则落外层
|
|
217
|
+
* runWithDirectiveDiags 域(无则丢弃,纯查询形态)。
|
|
218
|
+
*/
|
|
219
|
+
declare function extractInlineDirectives(node: Node, opts?: ExtractDirectivesOpts): InlineDirective[];
|
|
207
220
|
declare function extractFileDirectives(ast: Node): FileDirective[];
|
|
208
221
|
|
|
209
|
-
export { type AsDirective, type CaseDirective, type Directive, type DirectiveDiag, type EnvDirective, type FileDirective, type FunctionWithDirectives, type InlineDirective, type InstanceMethod, type MockDirective, type MockModuleDirective, type PureDirective, type ReplaceDirective, type SampleDirective, type SinonExpression, type SkipDirective, asAssignmentExpression, asExpression, asIdentifier, asMemberExpression, asProgram, classIdName, classInstanceMethods, classMemberKey, classMemberKeyName,
|
|
222
|
+
export { type AsDirective, type CaseDirective, type Directive, type DirectiveDiag, type EnvDirective, type ExtractDirectivesOpts, type FileDirective, type FunctionWithDirectives, type InlineDirective, type InstanceMethod, type MockDirective, type MockModuleDirective, type PureDirective, type ReplaceDirective, type SampleDirective, type SinonExpression, type SkipDirective, asAssignmentExpression, asExpression, asIdentifier, asMemberExpression, asProgram, classIdName, classInstanceMethods, classMemberKey, classMemberKeyName, exportSpecifierExportedName, exportSpecifierLocalName, extractDirectives, extractDirectivesQuiet, extractFileDirectives, extractInlineDirectives, fnOrClassIdLoc, fnOrClassIdName, getClassMembers, getDeclarations, getDeclaratorId, getDeclaratorInit, getExportDeclaration, getExportSpecifiers, getExpressionStatementExpression, getFirstDeclaratorId, identifierName, isModuleExportsMember, memberObjectName, memberPropertyKey, memberPropertyName, methodFunctionNode, nameOrStringValue, paramDisplayName, paramName, parse, parseCaseArgExpr, programBody, runWithDirectiveDiags, unwrapDefaultExport, unwrapExport };
|
package/dist/index.js
CHANGED
|
@@ -181,7 +181,6 @@ import {
|
|
|
181
181
|
spy,
|
|
182
182
|
mock,
|
|
183
183
|
abs as makeAbs,
|
|
184
|
-
lit as termLit,
|
|
185
184
|
numLit,
|
|
186
185
|
strLit,
|
|
187
186
|
boolLit,
|
|
@@ -200,70 +199,34 @@ import {
|
|
|
200
199
|
scanCaseTags,
|
|
201
200
|
extractBalancedParens
|
|
202
201
|
} from "@nudojs/core";
|
|
202
|
+
import { absLit } from "@nudojs/core/internal";
|
|
203
203
|
function absExact(shape) {
|
|
204
204
|
return makeAbs(shape, void 0, void 0, "exact");
|
|
205
205
|
}
|
|
206
206
|
function absUnknown() {
|
|
207
207
|
return makeAbs({ k: "unknown" }, void 0, void 0, "partial");
|
|
208
208
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (typeof v === "string") return strLit(v);
|
|
218
|
-
if (typeof v === "boolean") return boolLit(v);
|
|
219
|
-
if (v === null) return absNullLit();
|
|
220
|
-
return absUndefLit();
|
|
221
|
-
}
|
|
222
|
-
var directiveDiagCollector = null;
|
|
223
|
-
var directiveDiagSeq = 0;
|
|
224
|
-
var directiveDiags = [];
|
|
225
|
-
var MAX_DIRECTIVE_DIAGS = 1024;
|
|
226
|
-
var directiveDiagSeen = /* @__PURE__ */ new Set();
|
|
227
|
-
function setDirectiveDiagCollector(fn) {
|
|
228
|
-
directiveDiagCollector = fn;
|
|
229
|
-
}
|
|
230
|
-
function directiveDiagCount() {
|
|
231
|
-
return directiveDiagSeq;
|
|
232
|
-
}
|
|
233
|
-
function takeDirectiveDiags() {
|
|
234
|
-
const out = directiveDiags.map((e) => e.d);
|
|
235
|
-
directiveDiags.length = 0;
|
|
236
|
-
directiveDiagSeen.clear();
|
|
237
|
-
return out;
|
|
238
|
-
}
|
|
239
|
-
function takeDirectiveDiagsSince(since) {
|
|
240
|
-
const out = [];
|
|
241
|
-
let kept = 0;
|
|
242
|
-
for (const e of directiveDiags) {
|
|
243
|
-
if (e.seq > since) out.push(e.d);
|
|
244
|
-
else directiveDiags[kept++] = e;
|
|
209
|
+
var activeDiagSink = null;
|
|
210
|
+
function runWithDirectiveDiags(diags, fn) {
|
|
211
|
+
const prev = activeDiagSink;
|
|
212
|
+
activeDiagSink = { list: diags, seen: /* @__PURE__ */ new Set() };
|
|
213
|
+
try {
|
|
214
|
+
return fn();
|
|
215
|
+
} finally {
|
|
216
|
+
activeDiagSink = prev;
|
|
245
217
|
}
|
|
246
|
-
directiveDiags.length = kept;
|
|
247
|
-
return out;
|
|
248
218
|
}
|
|
249
219
|
function emitDirectiveDiag(d) {
|
|
220
|
+
if (!activeDiagSink) return;
|
|
250
221
|
const key = `${d.code}\0${d.message}`;
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
directiveDiags.shift();
|
|
255
|
-
directiveDiagSeen.clear();
|
|
256
|
-
}
|
|
257
|
-
directiveDiags.push({ seq: ++directiveDiagSeq, d });
|
|
258
|
-
directiveDiagCollector?.(d);
|
|
259
|
-
}
|
|
260
|
-
function resetDirectiveDiagSeen() {
|
|
261
|
-
directiveDiagSeen.clear();
|
|
222
|
+
if (activeDiagSink.seen.has(key)) return;
|
|
223
|
+
activeDiagSink.seen.add(key);
|
|
224
|
+
activeDiagSink.list.push(d);
|
|
262
225
|
}
|
|
263
226
|
var CASE_TAG_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:case\b[ \t]*([^\n]*)/g;
|
|
264
227
|
var JS_IDENT_RE = /^[\p{L}$_][\p{L}\p{N}$_]*$/u;
|
|
265
228
|
var MOCK_INLINE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:mock\b[ \t]+([^\s=]+)[ \t]*=[ \t]*(.+)/g;
|
|
266
|
-
var MOCK_FROM_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:mock\b[ \t]+([^\s]+)[ \t]+from[ \t]+"([^"\n]+)"/g;
|
|
229
|
+
var MOCK_FROM_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:mock\b[ \t]+([^\s]+)[ \t]+from[ \t]+(?:"([^"\n]+)"|'([^'\n]+)')/g;
|
|
267
230
|
var MOCK_TAG_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:mock\b[ \t]*([^\n]*)/g;
|
|
268
231
|
var PURE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:pure\b/g;
|
|
269
232
|
var SKIP_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:skip\b(?:[ \t]+(\S[^\n]*))?/g;
|
|
@@ -397,8 +360,8 @@ function constraintToCaseArgAbs(c, depth) {
|
|
|
397
360
|
if (typeof lv === "number" && !Number.isNaN(lv)) return numLit(lv);
|
|
398
361
|
if (typeof lv === "string") return strLit(lv);
|
|
399
362
|
if (typeof lv === "boolean") return boolLit(lv);
|
|
400
|
-
if (lv === null) return
|
|
401
|
-
return
|
|
363
|
+
if (lv === null) return absLit(null);
|
|
364
|
+
return absLit(void 0);
|
|
402
365
|
}
|
|
403
366
|
if (c.element) {
|
|
404
367
|
return absExact({ k: "arr", element: constraintToCaseArgAbs(c.element, depth + 1) });
|
|
@@ -845,7 +808,7 @@ function parseDirectivesFromCommentTexts(comments) {
|
|
|
845
808
|
directives.push({
|
|
846
809
|
kind: "mock",
|
|
847
810
|
name,
|
|
848
|
-
fromPath: mockFromMatch[2]
|
|
811
|
+
fromPath: mockFromMatch[2] ?? mockFromMatch[3]
|
|
849
812
|
});
|
|
850
813
|
mockFromRanges.push([mockFromMatch.index, mockFromMatch.index + mockFromMatch[0].length]);
|
|
851
814
|
}
|
|
@@ -974,7 +937,7 @@ function scanMalformedMockNames(text, caseArgSpans) {
|
|
|
974
937
|
if (!nameMatch) {
|
|
975
938
|
emitDirectiveDiag({
|
|
976
939
|
code: "nudo:directive-syntax",
|
|
977
|
-
message: `Malformed @nudo:mock: cannot parse name (expected @nudo:mock <ident> = <expr> or @nudo:mock <ident> from "path") \u2014 got: ${rest.slice(0, 60)}`
|
|
940
|
+
message: `Malformed @nudo:mock: cannot parse name (expected @nudo:mock <ident> = <expr> or @nudo:mock <ident> from "path" / 'path') \u2014 got: ${rest.slice(0, 60)}`
|
|
978
941
|
});
|
|
979
942
|
continue;
|
|
980
943
|
}
|
|
@@ -987,16 +950,25 @@ function scanMalformedMockNames(text, caseArgSpans) {
|
|
|
987
950
|
continue;
|
|
988
951
|
}
|
|
989
952
|
const afterName = rest.slice(name.length);
|
|
990
|
-
if (/^[ \t]+from[ \t]+"[^"\n]
|
|
953
|
+
if (/^[ \t]+from[ \t]+(?:"[^"\n]*|'[^'\n]*)$/.test(afterName)) {
|
|
991
954
|
emitDirectiveDiag({
|
|
992
955
|
code: "nudo:directive-syntax",
|
|
993
|
-
message: `Malformed @nudo:mock from: unclosed quote in path (expected @nudo:mock ${name} from "path") \u2014 got: @nudo:mock ${rest.slice(0, 60)}`
|
|
956
|
+
message: `Malformed @nudo:mock from: unclosed quote in path (expected @nudo:mock ${name} from "path" / 'path') \u2014 got: @nudo:mock ${rest.slice(0, 60)}`
|
|
994
957
|
});
|
|
995
958
|
}
|
|
996
959
|
}
|
|
997
960
|
}
|
|
998
|
-
function extractDirectives(ast) {
|
|
999
|
-
|
|
961
|
+
function extractDirectives(ast, opts) {
|
|
962
|
+
if (!opts?.diags) return collectFnDirectives(ast);
|
|
963
|
+
const prev = activeDiagSink;
|
|
964
|
+
activeDiagSink = { list: opts.diags, seen: /* @__PURE__ */ new Set() };
|
|
965
|
+
try {
|
|
966
|
+
return collectFnDirectives(ast);
|
|
967
|
+
} finally {
|
|
968
|
+
activeDiagSink = prev;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function collectFnDirectives(ast) {
|
|
1000
972
|
const results = [];
|
|
1001
973
|
if (ast.type !== "File") return results;
|
|
1002
974
|
const scopes = listFnDirectiveScopes(ast);
|
|
@@ -1013,10 +985,7 @@ function extractDirectives(ast) {
|
|
|
1013
985
|
return results;
|
|
1014
986
|
}
|
|
1015
987
|
function extractDirectivesQuiet(ast) {
|
|
1016
|
-
|
|
1017
|
-
const out = extractDirectives(ast);
|
|
1018
|
-
takeDirectiveDiagsSince(since);
|
|
1019
|
-
return out;
|
|
988
|
+
return extractDirectives(ast, { diags: [] });
|
|
1020
989
|
}
|
|
1021
990
|
function stripInlineCommentPrefix(line, kind) {
|
|
1022
991
|
const s = kind === "block" ? line.replace(/^\s*\*\s?/, "") : line.replace(/^\//, "");
|
|
@@ -1044,10 +1013,19 @@ function findTrailingCommentStart(expr) {
|
|
|
1044
1013
|
}
|
|
1045
1014
|
return -1;
|
|
1046
1015
|
}
|
|
1047
|
-
function extractInlineDirectives(node) {
|
|
1048
|
-
resetDirectiveDiagSeen();
|
|
1016
|
+
function extractInlineDirectives(node, opts) {
|
|
1049
1017
|
const comments = node.leadingComments;
|
|
1050
1018
|
if (!comments) return [];
|
|
1019
|
+
if (!opts?.diags) return collectInlineDirectivesFromComments(comments);
|
|
1020
|
+
const prev = activeDiagSink;
|
|
1021
|
+
activeDiagSink = { list: opts.diags, seen: /* @__PURE__ */ new Set() };
|
|
1022
|
+
try {
|
|
1023
|
+
return collectInlineDirectivesFromComments(comments);
|
|
1024
|
+
} finally {
|
|
1025
|
+
activeDiagSink = prev;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
function collectInlineDirectivesFromComments(comments) {
|
|
1051
1029
|
const results = [];
|
|
1052
1030
|
for (const comment of comments) {
|
|
1053
1031
|
if (comment.type !== "CommentLine" && comment.type !== "CommentBlock") continue;
|
|
@@ -1194,7 +1172,6 @@ export {
|
|
|
1194
1172
|
classInstanceMethods,
|
|
1195
1173
|
classMemberKey,
|
|
1196
1174
|
classMemberKeyName,
|
|
1197
|
-
directiveDiagCount,
|
|
1198
1175
|
exportSpecifierExportedName,
|
|
1199
1176
|
exportSpecifierLocalName,
|
|
1200
1177
|
extractDirectives,
|
|
@@ -1232,14 +1209,12 @@ export {
|
|
|
1232
1209
|
parseMockModulePayload2 as parseMockModulePayload,
|
|
1233
1210
|
parseNudoImportPayload,
|
|
1234
1211
|
programBody,
|
|
1212
|
+
runWithDirectiveDiags,
|
|
1235
1213
|
scanBudgetDecl,
|
|
1236
1214
|
scanCaseTags2 as scanCaseTags,
|
|
1237
1215
|
scanContractSegments,
|
|
1238
1216
|
scanThrowsDecl,
|
|
1239
|
-
setDirectiveDiagCollector,
|
|
1240
1217
|
stripTypes,
|
|
1241
|
-
takeDirectiveDiags,
|
|
1242
|
-
takeDirectiveDiagsSince,
|
|
1243
1218
|
unwrapDefaultExport,
|
|
1244
1219
|
unwrapExport
|
|
1245
1220
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/parser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20"
|
|
6
6
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"registry": "https://registry.npmjs.org"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@nudojs/core": "1.7.
|
|
42
|
+
"@nudojs/core": "1.7.2",
|
|
43
43
|
"@babel/types": "^8.0.6"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|