@nudojs/service 0.3.0 → 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.
- package/dist/index.d.ts +18 -3
- package/dist/index.js +80 -55
- package/package.json +16 -6
- package/CHANGELOG.md +0 -68
- package/src/__tests__/abs-bindings.test.ts +0 -53
- package/src/__tests__/abs-call-records.test.ts +0 -33
- package/src/__tests__/abs-calls.test.ts +0 -59
- package/src/__tests__/abs-module-cache.test.ts +0 -121
- package/src/__tests__/abs-modules-graph.test.ts +0 -100
- package/src/__tests__/abs-typevalue-diff.test.ts +0 -111
- package/src/__tests__/analyzer.test.ts +0 -880
- package/src/__tests__/as-replace.test.ts +0 -165
- package/src/__tests__/bpath-arg-provenance.test.ts +0 -104
- package/src/__tests__/bpath-as.test.ts +0 -54
- package/src/__tests__/bpath-cache-key.test.ts +0 -33
- package/src/__tests__/bpath-calls.test.ts +0 -55
- package/src/__tests__/bpath-diagnostics.test.ts +0 -78
- package/src/__tests__/bpath-env-modules.test.ts +0 -30
- package/src/__tests__/bpath-env.test.ts +0 -80
- package/src/__tests__/bpath-hosted-skip.test.ts +0 -71
- package/src/__tests__/bpath-import-default-ns.test.ts +0 -106
- package/src/__tests__/bpath-method-dedupe.test.ts +0 -65
- package/src/__tests__/bpath-method-diag.test.ts +0 -45
- package/src/__tests__/bpath-module-diags.test.ts +0 -101
- package/src/__tests__/bpath-path-env.test.ts +0 -46
- package/src/__tests__/bpath-reexport.test.ts +0 -131
- package/src/__tests__/bpath-replace.test.ts +0 -52
- package/src/__tests__/bpath-require.test.ts +0 -66
- package/src/__tests__/bpath-run.test.ts +0 -109
- package/src/__tests__/case-emitter.test.ts +0 -636
- package/src/__tests__/cjs-require.test.ts +0 -54
- package/src/__tests__/diagnostics.test.ts +0 -60
- package/src/__tests__/dts-generator.test.ts +0 -382
- package/src/__tests__/fn-cache-evict.test.ts +0 -56
- package/src/__tests__/fn-sig-impl.test.ts +0 -247
- package/src/__tests__/guard-generator.test.ts +0 -29
- package/src/__tests__/harvest-auto.test.ts +0 -43
- package/src/__tests__/harvest-node.test.ts +0 -24
- package/src/__tests__/harvest-package.test.ts +0 -38
- package/src/__tests__/harvest-to-abs.test.ts +0 -46
- package/src/__tests__/hover-intension.test.ts +0 -39
- package/src/__tests__/infer-json.test.ts +0 -35
- package/src/__tests__/integration.test.ts +0 -421
- package/src/__tests__/m3-intension.test.ts +0 -28
- package/src/__tests__/mock-seed-fingerprint.test.ts +0 -64
- package/src/__tests__/schema-generator.test.ts +0 -38
- package/src/__tests__/session-cache-evict.test.ts +0 -90
- package/src/__tests__/static-imports.test.ts +0 -44
- package/src/abs-modules-graph.ts +0 -387
- package/src/analysis-file-cache.ts +0 -51
- package/src/analyzer.ts +0 -3133
- package/src/bpath-diagnostics.ts +0 -283
- package/src/bpath-run.ts +0 -409
- package/src/case-emitter.ts +0 -477
- package/src/dts-generator.ts +0 -291
- package/src/env-to-abs.ts +0 -57
- package/src/fn-analysis-cache.ts +0 -119
- package/src/guard-generator.ts +0 -57
- package/src/harvest-auto.ts +0 -86
- package/src/harvest-node.ts +0 -48
- package/src/harvest-package.ts +0 -204
- package/src/harvest-to-abs.ts +0 -129
- package/src/index.ts +0 -150
- package/src/infer-json.ts +0 -137
- package/src/load-module.ts +0 -34
- package/src/mock-abs.ts +0 -275
- package/src/schema-generator.ts +0 -43
- package/src/semantic-tokens.ts +0 -216
- package/src/session-cache.ts +0 -50
- package/src/static-imports.ts +0 -241
- package/src/target-path.ts +0 -17
- package/tsconfig.json +0 -13
package/dist/index.d.ts
CHANGED
|
@@ -206,6 +206,11 @@ type HoverInfo = {
|
|
|
206
206
|
/**
|
|
207
207
|
* LSP hover:优先无损 Abs(类型即计算本体),TypeValue 仅作外延对照。
|
|
208
208
|
* 节点表也是 Abs(collectAbsNodeTypes),不经 bridge。
|
|
209
|
+
*
|
|
210
|
+
* 函数名/调用 callee 位置(design-hof-relations §7):
|
|
211
|
+
* intension 一律走 generalize/formatPoly(HOF fnRels 在这里);
|
|
212
|
+
* typeText 仍落 B-path / TypeValue(调用点显示结果类型,不是函数签名)。
|
|
213
|
+
* 禁止用 B-path 的 arity-only fn Abs 冒充权威关系源。
|
|
209
214
|
*/
|
|
210
215
|
declare function getHoverAtPosition(filePath: string, source: string, line: number, column: number, activeCases?: Map<string, number>): HoverInfo | null;
|
|
211
216
|
declare function getCompletionsAtPosition(filePath: string, source: string, line: number, column: number): CompletionItem[];
|
|
@@ -491,7 +496,16 @@ declare function tryRunBPath(source: string, filePath: string, opts?: {
|
|
|
491
496
|
/** @nudo:mock → Abs,注入为全局绑定(防止顶层调用真 fetch 等) */
|
|
492
497
|
mocks?: Record<string, Abs>;
|
|
493
498
|
}): BPathRunResult | undefined;
|
|
494
|
-
/**
|
|
499
|
+
/**
|
|
500
|
+
* B 路径求值具名导出(结果 + throws);opts.collectCalls 时附带调用点记录。
|
|
501
|
+
*
|
|
502
|
+
* `calls` 只含**本次具名调用期间**(callTranspiledExportFull 内)发生的
|
|
503
|
+
* 调用点——不含模块级顶层调用(run.calls)。模块级记录由主分析流程
|
|
504
|
+
* 另行收集(analyzer 的 bTopCallRecords / absCallRecords);在此重复返回
|
|
505
|
+
* 会把 B 路径结果(常为 unknown)当第二组记录推给 analyzer,dedupe 因
|
|
506
|
+
* 结果形态不同而保留,污染被调函数的 case 列表(多余 call@L 重复 +
|
|
507
|
+
* call@symbolic 聚合 case,见 analyzer directive-case 分支)。
|
|
508
|
+
*/
|
|
495
509
|
declare function tryBPathCallFull(source: string, filePath: string, fnName: string, args: Abs[], opts?: {
|
|
496
510
|
collectCalls?: boolean;
|
|
497
511
|
collectMemberDiags?: boolean;
|
|
@@ -531,8 +545,9 @@ declare function clearAnalysisSessionCaches(): void;
|
|
|
531
545
|
declare function resetAllAnalysisCaches(): void;
|
|
532
546
|
|
|
533
547
|
/**
|
|
534
|
-
* env TypeValue → Abs
|
|
535
|
-
*
|
|
548
|
+
* env TypeValue → Abs:
|
|
549
|
+
* - fnSig 带 impl(Promise.resolve / URL / path.join…)→ apply 路径,保留精确字面量
|
|
550
|
+
* - fnSig 仅有声明(readFileSync…)→ relationFn,调用产出声明返回类型而非 unknown
|
|
536
551
|
*/
|
|
537
552
|
|
|
538
553
|
declare function envValueToAbs(tv: TypeValue): Abs;
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
stableAnalyzeKeySource as stableAnalyzeKeySource2,
|
|
30
30
|
fnFingerprints
|
|
31
31
|
} from "@nudojs/core";
|
|
32
|
-
import { parse as parse5, extractDirectives, extractFileDirectives, parseTypeValueExpr } from "@nudojs/parser";
|
|
32
|
+
import { parse as parse5, extractDirectives, extractFileDirectives, parseTypeValueExpr as parseTypeValueExpr2 } from "@nudojs/parser";
|
|
33
33
|
import {
|
|
34
34
|
evaluate,
|
|
35
35
|
evaluateFunction,
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
} from "@nudojs/cli/evaluator";
|
|
63
63
|
|
|
64
64
|
// src/mock-abs.ts
|
|
65
|
+
import { parseTypeValueExpr } from "@nudojs/parser";
|
|
65
66
|
import {
|
|
66
67
|
absFunction,
|
|
67
68
|
emptyEnv,
|
|
@@ -255,6 +256,12 @@ function mockDirectivesToAbsSeeds(functions) {
|
|
|
255
256
|
seedVars[d.name] = markMockConf(absFromMockHelper(d.nudoMock));
|
|
256
257
|
} else if (d.sinonExpr) {
|
|
257
258
|
seedVars[d.name] = markMockConf(absFromSinon(d.sinonExpr));
|
|
259
|
+
} else if (d.expression) {
|
|
260
|
+
try {
|
|
261
|
+
seedVars[d.name] = typeValueToAbs(parseTypeValueExpr(d.expression));
|
|
262
|
+
} catch {
|
|
263
|
+
seedVars[d.name] = absUnknown;
|
|
264
|
+
}
|
|
258
265
|
}
|
|
259
266
|
}
|
|
260
267
|
}
|
|
@@ -995,32 +1002,40 @@ import {
|
|
|
995
1002
|
absToTypeValue,
|
|
996
1003
|
getFnSig as getFnSig2,
|
|
997
1004
|
absFunction as absFunction4,
|
|
1005
|
+
relationFn,
|
|
998
1006
|
objOf
|
|
999
1007
|
} from "@nudojs/core";
|
|
1000
1008
|
function envValueToAbs(tv) {
|
|
1001
1009
|
if (!tv) return typeValueToAbs3({ kind: "unknown" });
|
|
1002
1010
|
if (tv.kind === "function") {
|
|
1003
1011
|
const sig = getFnSig2(tv);
|
|
1004
|
-
if (sig
|
|
1005
|
-
const
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1012
|
+
if (sig) {
|
|
1013
|
+
const paramTypes = sig.paramTypes.map(typeValueToAbs3);
|
|
1014
|
+
const returnType = typeValueToAbs3(sig.returnType);
|
|
1015
|
+
if (sig.impl) {
|
|
1016
|
+
const params = tv.params?.length ? tv.params : sig.paramTypes.map((_, i) => `_arg${i}`);
|
|
1017
|
+
const dummyBody = {
|
|
1018
|
+
type: "BlockStatement",
|
|
1019
|
+
body: [],
|
|
1020
|
+
directives: []
|
|
1021
|
+
};
|
|
1022
|
+
const impl = sig.impl;
|
|
1023
|
+
return absFunction4(params, {
|
|
1024
|
+
body: dummyBody,
|
|
1025
|
+
apply: (args) => {
|
|
1026
|
+
const tvArgs = args.map((a) => absToTypeValue(a));
|
|
1027
|
+
try {
|
|
1028
|
+
const r = impl(tvArgs);
|
|
1029
|
+
if (!r) return returnType;
|
|
1030
|
+
return envValueToAbs(r);
|
|
1031
|
+
} catch {
|
|
1032
|
+
return returnType;
|
|
1033
|
+
}
|
|
1022
1034
|
}
|
|
1023
|
-
}
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
return relationFn(paramTypes, returnType, {
|
|
1038
|
+
params: tv.params?.length ? tv.params : void 0
|
|
1024
1039
|
});
|
|
1025
1040
|
}
|
|
1026
1041
|
}
|
|
@@ -1322,7 +1337,7 @@ function tryBPathCallFull(source, filePath, fnName, args, opts = {}) {
|
|
|
1322
1337
|
const run = tryRunBPath(source, filePath, { envNames: opts.envNames, mocks: opts.mocks });
|
|
1323
1338
|
if (!run) return void 0;
|
|
1324
1339
|
if (!(fnName in run.exports)) return void 0;
|
|
1325
|
-
const collected = [
|
|
1340
|
+
const collected = [];
|
|
1326
1341
|
const memberDiags = [];
|
|
1327
1342
|
if (opts.collectCalls) {
|
|
1328
1343
|
setBCallCollector((r) => collected.push(r));
|
|
@@ -1335,7 +1350,7 @@ function tryBPathCallFull(source, filePath, fnName, args, opts = {}) {
|
|
|
1335
1350
|
const all = [...run.memberDiags ?? [], ...memberDiags];
|
|
1336
1351
|
return {
|
|
1337
1352
|
...full,
|
|
1338
|
-
calls: opts.collectCalls
|
|
1353
|
+
calls: opts.collectCalls ? collected : void 0,
|
|
1339
1354
|
memberDiags: all.length ? all : void 0,
|
|
1340
1355
|
moduleIssues: run.moduleIssues,
|
|
1341
1356
|
truncatedFns: run.truncatedFns
|
|
@@ -1740,7 +1755,7 @@ function applyMocks(directives, env, filePath, diagnostics) {
|
|
|
1740
1755
|
]
|
|
1741
1756
|
});
|
|
1742
1757
|
}
|
|
1743
|
-
env.bind(d.name,
|
|
1758
|
+
env.bind(d.name, parseTypeValueExpr2(d.expression));
|
|
1744
1759
|
} else if (d.fromPath) {
|
|
1745
1760
|
const mockPath = resolve4(dirname5(filePath), d.fromPath);
|
|
1746
1761
|
const mockSource = readFileSync4(mockPath, "utf-8");
|
|
@@ -2426,7 +2441,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2426
2441
|
seedVars: seeds.seedVars,
|
|
2427
2442
|
seedFns: seeds.seedFns
|
|
2428
2443
|
});
|
|
2429
|
-
absGraphModules = g.modules;
|
|
2444
|
+
absGraphModules = { ...collectEnvModules(envNames), ...g.modules };
|
|
2430
2445
|
pushBModuleIssues(g.issues);
|
|
2431
2446
|
} catch {
|
|
2432
2447
|
}
|
|
@@ -2477,7 +2492,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2477
2492
|
if (selfContained || canAbsModules) {
|
|
2478
2493
|
setAbsTruncationCollector2((label) => bTruncatedFns.add(label));
|
|
2479
2494
|
try {
|
|
2480
|
-
absCallRecords = collectAbsCallRecords(source, seeds, filePath, absGraphModules);
|
|
2495
|
+
absCallRecords = collectAbsCallRecords(source, seeds, filePath, absGraphModules, envNames);
|
|
2481
2496
|
} finally {
|
|
2482
2497
|
setAbsTruncationCollector2(null);
|
|
2483
2498
|
}
|
|
@@ -3172,6 +3187,34 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3172
3187
|
file = void 0;
|
|
3173
3188
|
}
|
|
3174
3189
|
const envNames = collectEnvNames(filePath, source, false);
|
|
3190
|
+
const fnName = findFunctionNameAtPosition(source, line, column, file);
|
|
3191
|
+
let gDisplay;
|
|
3192
|
+
let gAbs;
|
|
3193
|
+
let gMulti;
|
|
3194
|
+
if (fnName) {
|
|
3195
|
+
try {
|
|
3196
|
+
const g = generalizeFromAst2(fnName, source, file ? { file } : {});
|
|
3197
|
+
if (g) {
|
|
3198
|
+
gDisplay = g.display;
|
|
3199
|
+
gAbs = formatAbs3(g.symbolic);
|
|
3200
|
+
gMulti = formatAbsMultiline(g.symbolic, fnName);
|
|
3201
|
+
}
|
|
3202
|
+
} catch {
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
const attachIntension = (info2) => {
|
|
3206
|
+
if (!gDisplay) return info2;
|
|
3207
|
+
if (!info2) {
|
|
3208
|
+
return { typeText: gDisplay, intension: gDisplay, abs: gAbs, absMultiline: gMulti };
|
|
3209
|
+
}
|
|
3210
|
+
return {
|
|
3211
|
+
...info2,
|
|
3212
|
+
intension: gDisplay,
|
|
3213
|
+
// 外延侧已有更准 Abs 时保留;否则用 symbolic 兜底
|
|
3214
|
+
abs: info2.abs ?? gAbs,
|
|
3215
|
+
absMultiline: info2.absMultiline ?? gMulti
|
|
3216
|
+
};
|
|
3217
|
+
};
|
|
3175
3218
|
if (isBPathCapable(source, envNames)) {
|
|
3176
3219
|
try {
|
|
3177
3220
|
const seeds = mockDirectivesToAbsSeeds(extractDirectives(file ?? parse5(source)));
|
|
@@ -3183,7 +3226,7 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3183
3226
|
});
|
|
3184
3227
|
const absAt = findAbsAtPosition(absNodes, line, column);
|
|
3185
3228
|
const ident2 = findIdentNameAtPosition(source, line, column, file);
|
|
3186
|
-
if (ident2 && !
|
|
3229
|
+
if (ident2 && !fnName) {
|
|
3187
3230
|
const binds = collectAbsBindingsFromGraph(source, filePath, {
|
|
3188
3231
|
seedVars: seeds.seedVars,
|
|
3189
3232
|
seedFns: seeds.seedFns
|
|
@@ -3192,40 +3235,19 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3192
3235
|
if (bound) {
|
|
3193
3236
|
const absLine = formatAbs3(bound);
|
|
3194
3237
|
const absMulti = formatAbsMultiline(bound, ident2);
|
|
3195
|
-
return { typeText: absLine, abs: absLine, absMultiline: absMulti };
|
|
3238
|
+
return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
|
|
3196
3239
|
}
|
|
3197
3240
|
}
|
|
3198
3241
|
if (absAt) {
|
|
3199
3242
|
const absLine = formatAbs3(absAt);
|
|
3200
3243
|
const absMulti = formatAbsMultiline(absAt, void 0);
|
|
3201
|
-
return { typeText: absLine, abs: absLine, absMultiline: absMulti };
|
|
3244
|
+
return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
|
|
3202
3245
|
}
|
|
3203
3246
|
} catch {
|
|
3204
3247
|
}
|
|
3205
3248
|
}
|
|
3206
3249
|
const tv = getTypeAtPosition(filePath, source, line, column, activeCases);
|
|
3207
3250
|
const info = tv ? { typeText: typeValueToString3(tv) } : null;
|
|
3208
|
-
const fnName = findFunctionNameAtPosition(source, line, column, file);
|
|
3209
|
-
if (fnName) {
|
|
3210
|
-
try {
|
|
3211
|
-
const g = generalizeFromAst2(fnName, source, file ? { file } : {});
|
|
3212
|
-
if (g) {
|
|
3213
|
-
if (info) {
|
|
3214
|
-
info.intension = g.display;
|
|
3215
|
-
info.abs = formatAbs3(g.symbolic);
|
|
3216
|
-
info.absMultiline = formatAbsMultiline(g.symbolic, fnName);
|
|
3217
|
-
} else {
|
|
3218
|
-
return {
|
|
3219
|
-
typeText: g.display,
|
|
3220
|
-
intension: g.display,
|
|
3221
|
-
abs: formatAbs3(g.symbolic),
|
|
3222
|
-
absMultiline: formatAbsMultiline(g.symbolic, fnName)
|
|
3223
|
-
};
|
|
3224
|
-
}
|
|
3225
|
-
}
|
|
3226
|
-
} catch {
|
|
3227
|
-
}
|
|
3228
|
-
}
|
|
3229
3251
|
const ident = findIdentNameAtPosition(source, line, column, file);
|
|
3230
3252
|
if (ident && !fnName) {
|
|
3231
3253
|
try {
|
|
@@ -3242,9 +3264,9 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3242
3264
|
if (info) {
|
|
3243
3265
|
info.abs = absLine;
|
|
3244
3266
|
info.absMultiline = absMulti;
|
|
3245
|
-
return info;
|
|
3267
|
+
return attachIntension(info);
|
|
3246
3268
|
}
|
|
3247
|
-
return { typeText: absLine, abs: absLine, absMultiline: absMulti };
|
|
3269
|
+
return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
|
|
3248
3270
|
}
|
|
3249
3271
|
}
|
|
3250
3272
|
} catch {
|
|
@@ -3269,13 +3291,13 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3269
3291
|
info.abs = absLine;
|
|
3270
3292
|
info.absMultiline = absMulti;
|
|
3271
3293
|
} else {
|
|
3272
|
-
return { typeText: absLine, abs: absLine, absMultiline: absMulti };
|
|
3294
|
+
return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
|
|
3273
3295
|
}
|
|
3274
3296
|
}
|
|
3275
3297
|
}
|
|
3276
3298
|
} catch {
|
|
3277
3299
|
}
|
|
3278
|
-
return info;
|
|
3300
|
+
return attachIntension(info);
|
|
3279
3301
|
}
|
|
3280
3302
|
function findIdentNameAtPosition(source, line, column, fileAst) {
|
|
3281
3303
|
try {
|
|
@@ -3565,11 +3587,11 @@ function absModulesOk(source, envNames) {
|
|
|
3565
3587
|
void envNames;
|
|
3566
3588
|
return true;
|
|
3567
3589
|
}
|
|
3568
|
-
function collectAbsCallRecords(source, seeds, filePath, precomputedModules) {
|
|
3590
|
+
function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = []) {
|
|
3569
3591
|
const absCalls = [];
|
|
3570
3592
|
let modules = precomputedModules;
|
|
3571
3593
|
let importLocals = /* @__PURE__ */ new Map();
|
|
3572
|
-
if (filePath && absModulesOk(source,
|
|
3594
|
+
if (filePath && absModulesOk(source, envNames)) {
|
|
3573
3595
|
if (!modules) {
|
|
3574
3596
|
try {
|
|
3575
3597
|
const graph = evalAbsModuleGraph(source, filePath, {
|
|
@@ -3581,6 +3603,9 @@ function collectAbsCallRecords(source, seeds, filePath, precomputedModules) {
|
|
|
3581
3603
|
modules = void 0;
|
|
3582
3604
|
}
|
|
3583
3605
|
}
|
|
3606
|
+
if (envNames.length > 0) {
|
|
3607
|
+
modules = { ...collectEnvModules(envNames), ...modules ?? {} };
|
|
3608
|
+
}
|
|
3584
3609
|
try {
|
|
3585
3610
|
importLocals = buildAbsImportLocalMap(source, filePath);
|
|
3586
3611
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/service",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Shared inference service for Nudo IDE integrations — file analysis, completions, and .d.ts generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,7 +12,17 @@
|
|
|
12
12
|
"dts-generation",
|
|
13
13
|
"analysis"
|
|
14
14
|
],
|
|
15
|
-
"main": "
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
16
26
|
"repository": {
|
|
17
27
|
"type": "git",
|
|
18
28
|
"url": "git+https://github.com/nudojs/nudo.git",
|
|
@@ -28,10 +38,10 @@
|
|
|
28
38
|
"dependencies": {
|
|
29
39
|
"@babel/traverse": "^7.29.0",
|
|
30
40
|
"@babel/types": "^7.29.0",
|
|
31
|
-
"@nudojs/core": "0.3.
|
|
32
|
-
"@nudojs/parser": "0.3.
|
|
33
|
-
"@nudojs/cli": "0.3.
|
|
34
|
-
"@nudojs/harvester": "0.2.
|
|
41
|
+
"@nudojs/core": "0.3.1",
|
|
42
|
+
"@nudojs/parser": "0.3.1",
|
|
43
|
+
"@nudojs/cli": "0.3.1",
|
|
44
|
+
"@nudojs/harvester": "0.2.1"
|
|
35
45
|
},
|
|
36
46
|
"scripts": {
|
|
37
47
|
"build": "tsup src/index.ts --format esm --dts"
|
package/CHANGELOG.md
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# @nudojs/service
|
|
2
|
-
|
|
3
|
-
## 0.3.0
|
|
4
|
-
|
|
5
|
-
### Minor Changes
|
|
6
|
-
|
|
7
|
-
- 5786fa5: Promote the B-path (transpile + in-process Abs runtime) to the primary evaluation path for capable sources.
|
|
8
|
-
|
|
9
|
-
- Module graph now supports relative imports, bare-package harvest, default/namespace imports, re-exports, `export *`, require, cycle/depth/missing guards, and env modules (`path` / `node:path` etc.).
|
|
10
|
-
- Diagnostics, `@nudo:env` / mock injection, `call@` / `entry@` provenance, method-missing, unknown-recv, generators, classes, async/await, optional chaining, and destructuring run through B.
|
|
11
|
-
- When B hosts a file, `evaluateProgram` is skipped so TypeValue no longer double-reports.
|
|
12
|
-
- CLI `check` / `types` / `test` accept directories; check uses an Abs-only gate.
|
|
13
|
-
- LSP hover / `getTypeAtPosition` on capable files read the Abs node table; `*.nudo.js` edits evict L0 and recheck open parents.
|
|
14
|
-
- Public core surface now re-exports the algebra API (`Abs`, check, generalize, exec, bridge, `parseSource`, `stripTypes`).
|
|
15
|
-
|
|
16
|
-
- 5786fa5: Speed up repeated analysis with layered memos and tighter cache contracts.
|
|
17
|
-
|
|
18
|
-
- Shared parse/AST LRU, `generalizeFromAst` L0–L3 (instantiate, α-equivalence, dep fingerprint + LRU), Abs module cache, and session-wide memos with an incremental after-edit path.
|
|
19
|
-
- Host cache-eviction contract, AST/function-fingerprint memory caps, conservative call-scan depth cap, and fail-open truncated dependency fingerprints.
|
|
20
|
-
- Fix session-memo staleness and identity holes so after-edit results stay correct.
|
|
21
|
-
|
|
22
|
-
### Patch Changes
|
|
23
|
-
|
|
24
|
-
- Updated dependencies [5786fa5]
|
|
25
|
-
- Updated dependencies [5786fa5]
|
|
26
|
-
- Updated dependencies [5786fa5]
|
|
27
|
-
- Updated dependencies [5786fa5]
|
|
28
|
-
- @nudojs/core@0.3.0
|
|
29
|
-
- @nudojs/cli@0.3.0
|
|
30
|
-
- @nudojs/harvester@0.2.0
|
|
31
|
-
- @nudojs/parser@0.3.0
|
|
32
|
-
|
|
33
|
-
## 0.2.1
|
|
34
|
-
|
|
35
|
-
### Patch Changes
|
|
36
|
-
|
|
37
|
-
- Updated dependencies [0fd0718]
|
|
38
|
-
- @nudojs/cli@0.2.1
|
|
39
|
-
|
|
40
|
-
## 0.2.0
|
|
41
|
-
|
|
42
|
-
### Minor Changes
|
|
43
|
-
|
|
44
|
-
- 6c38283: docs and ci
|
|
45
|
-
- 9f7f819: fix pkg info
|
|
46
|
-
- c175f71: version
|
|
47
|
-
|
|
48
|
-
### Patch Changes
|
|
49
|
-
|
|
50
|
-
- Updated dependencies [6c38283]
|
|
51
|
-
- Updated dependencies [9f7f819]
|
|
52
|
-
- Updated dependencies [c175f71]
|
|
53
|
-
- @nudojs/cli@0.2.0
|
|
54
|
-
- @nudojs/core@0.2.0
|
|
55
|
-
- @nudojs/parser@0.2.0
|
|
56
|
-
|
|
57
|
-
## 0.1.0
|
|
58
|
-
|
|
59
|
-
### Minor Changes
|
|
60
|
-
|
|
61
|
-
- Conceptual design and basic implementation.
|
|
62
|
-
|
|
63
|
-
### Patch Changes
|
|
64
|
-
|
|
65
|
-
- Updated dependencies
|
|
66
|
-
- nudo@0.1.0
|
|
67
|
-
- @nudojs/core@0.1.0
|
|
68
|
-
- @nudojs/parser@0.1.0
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterAll } from "vitest";
|
|
2
|
-
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { collectAbsBindingsFromGraph, analyzeFile, getHoverAtPosition } from "@nudojs/service";
|
|
6
|
-
import { formatAbs, absToString } from "@nudojs/core";
|
|
7
|
-
|
|
8
|
-
const dirs: string[] = [];
|
|
9
|
-
afterAll(() => {
|
|
10
|
-
for (const d of dirs) rmSync(d, { recursive: true, force: true });
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe("Abs bindings from module graph", () => {
|
|
14
|
-
it("includes local functions and imported names", () => {
|
|
15
|
-
const dir = mkdtempSync(join(tmpdir(), "nudo-abs-bind-"));
|
|
16
|
-
dirs.push(dir);
|
|
17
|
-
writeFileSync(join(dir, "math.js"), `export function double(x) { return x * 2; }\n`);
|
|
18
|
-
const main = `import { double } from "./math.js";
|
|
19
|
-
export function run(n) { return double(n); }
|
|
20
|
-
const k = 1;
|
|
21
|
-
`;
|
|
22
|
-
const mainPath = join(dir, "main.js");
|
|
23
|
-
writeFileSync(mainPath, main, "utf-8");
|
|
24
|
-
const binds = collectAbsBindingsFromGraph(main, mainPath);
|
|
25
|
-
expect(binds.has("run")).toBe(true);
|
|
26
|
-
expect(binds.has("double")).toBe(true);
|
|
27
|
-
expect(binds.has("k")).toBe(true);
|
|
28
|
-
expect(formatAbs(binds.get("k")!)).toContain("1");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("analyzeFile bindings prefer Abs projection for capable files", () => {
|
|
32
|
-
const dir = mkdtempSync(join(tmpdir(), "nudo-abs-bind2-"));
|
|
33
|
-
dirs.push(dir);
|
|
34
|
-
writeFileSync(join(dir, "math.js"), `export function double(x) { return x * 2; }\n`);
|
|
35
|
-
const main = `import { double } from "./math.js";
|
|
36
|
-
export function run(n) { return double(n); }
|
|
37
|
-
`;
|
|
38
|
-
const mainPath = join(dir, "main.js");
|
|
39
|
-
writeFileSync(mainPath, main, "utf-8");
|
|
40
|
-
const result = analyzeFile(mainPath, main);
|
|
41
|
-
expect(result.bindings.has("run")).toBe(true);
|
|
42
|
-
// imported binding should exist
|
|
43
|
-
expect(result.bindings.has("double")).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("hover on local function returns abs", () => {
|
|
47
|
-
const src = `export function add(a, b) { return a + b; }
|
|
48
|
-
`;
|
|
49
|
-
const h = getHoverAtPosition("/tmp/hover-abs.js", src, 1, 16);
|
|
50
|
-
// 不要求精确命中,但不应抛错
|
|
51
|
-
expect(h === null || typeof h.typeText === "string").toBe(true);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterAll } from "vitest";
|
|
2
|
-
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
5
|
-
import { analyzeFile } from "@nudojs/service";
|
|
6
|
-
import { typeValueToString } from "@nudojs/core";
|
|
7
|
-
|
|
8
|
-
const dirs: string[] = [];
|
|
9
|
-
afterAll(() => {
|
|
10
|
-
for (const d of dirs) rmSync(d, { recursive: true, force: true });
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe("Abs call records with relative import", () => {
|
|
14
|
-
it("tags imported calls with targetModule and feeds externalFunctions", () => {
|
|
15
|
-
const dir = mkdtempSync(join(tmpdir(), "nudo-abs-call-"));
|
|
16
|
-
dirs.push(dir);
|
|
17
|
-
const bSrc = `export function triple(x) { return x * 3; }\n`;
|
|
18
|
-
const aSrc = `import { triple } from "./b.js";\nfunction caller(n) { return triple(n); }\nconst r = caller(4);\n`;
|
|
19
|
-
writeFileSync(join(dir, "b.js"), bSrc);
|
|
20
|
-
const aPath = resolve(dir, "a.js");
|
|
21
|
-
writeFileSync(aPath, aSrc);
|
|
22
|
-
|
|
23
|
-
const result = analyzeFile(aPath, aSrc);
|
|
24
|
-
|
|
25
|
-
expect(result.externalFunctions).toBeDefined();
|
|
26
|
-
expect(result.externalFunctions).toHaveLength(1);
|
|
27
|
-
const triple = result.externalFunctions![0];
|
|
28
|
-
expect(triple.name).toBe("triple");
|
|
29
|
-
expect(triple.fromModule).toContain("b.js");
|
|
30
|
-
expect(triple.cases).toHaveLength(1);
|
|
31
|
-
expect(typeValueToString(triple.cases[0].result)).toBe("12");
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { analyzeFile } from "../analyzer.ts";
|
|
3
|
-
import { typeValueToString } from "@nudojs/core";
|
|
4
|
-
|
|
5
|
-
describe("self-contained Abs call records", () => {
|
|
6
|
-
it("collects call sites from Abs program eval", () => {
|
|
7
|
-
const source = `
|
|
8
|
-
function add(a, b) { return a + b; }
|
|
9
|
-
const r = add(2, 3);
|
|
10
|
-
`;
|
|
11
|
-
const result = analyzeFile("/t/abs-call.js", source);
|
|
12
|
-
const add = result.functions.find((f) => f.name === "add");
|
|
13
|
-
expect(add).toBeDefined();
|
|
14
|
-
expect(add!.cases.length).toBeGreaterThan(0);
|
|
15
|
-
expect(add!.cases[0]!.source).toBe("callsite");
|
|
16
|
-
expect(typeValueToString(add!.cases[0]!.result)).toBe("5");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("self-contained entry still works", () => {
|
|
20
|
-
const source = `function scale(x) { return x + 1; }`;
|
|
21
|
-
const result = analyzeFile("/t/abs-entry.js", source);
|
|
22
|
-
const scale = result.functions.find((f) => f.name === "scale");
|
|
23
|
-
expect(scale!.entryOnly).toBe(true);
|
|
24
|
-
expect(scale!.cases[0]!.intension).toBeDefined();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("mock seeds feed Abs path (call@ stays exact)", () => {
|
|
28
|
-
const source = `
|
|
29
|
-
// @nudo:mock parse = stub().returns(42)
|
|
30
|
-
function run() {
|
|
31
|
-
return parse();
|
|
32
|
-
}
|
|
33
|
-
const r = run();
|
|
34
|
-
`;
|
|
35
|
-
const result = analyzeFile("/t/abs-mock.js", source);
|
|
36
|
-
const run = result.functions.find((f) => f.name === "run");
|
|
37
|
-
expect(run).toBeDefined();
|
|
38
|
-
const call = run!.cases.find((c) => c.source === "callsite");
|
|
39
|
-
expect(call).toBeDefined();
|
|
40
|
-
expect(typeValueToString(call!.result)).toBe("42");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("directive expected is carried on CaseResult", () => {
|
|
44
|
-
const source = `
|
|
45
|
-
/**
|
|
46
|
-
* @nudo:case "ok" (1) => T.number
|
|
47
|
-
* @nudo:case "bad" (1) => T.string
|
|
48
|
-
*/
|
|
49
|
-
function id(x) { return x; }
|
|
50
|
-
`;
|
|
51
|
-
const result = analyzeFile("/t/abs-expected.js", source);
|
|
52
|
-
const id = result.functions.find((f) => f.name === "id");
|
|
53
|
-
const ok = id!.cases.find((c) => c.name === "ok");
|
|
54
|
-
const bad = id!.cases.find((c) => c.name === "bad");
|
|
55
|
-
expect(ok!.expected).toBeDefined();
|
|
56
|
-
expect(bad!.expected).toBeDefined();
|
|
57
|
-
expect(result.diagnostics.some((d) => d.code === "nudo:case-expected")).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
});
|