@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.
Files changed (72) hide show
  1. package/dist/index.d.ts +18 -3
  2. package/dist/index.js +80 -55
  3. package/package.json +16 -6
  4. package/CHANGELOG.md +0 -68
  5. package/src/__tests__/abs-bindings.test.ts +0 -53
  6. package/src/__tests__/abs-call-records.test.ts +0 -33
  7. package/src/__tests__/abs-calls.test.ts +0 -59
  8. package/src/__tests__/abs-module-cache.test.ts +0 -121
  9. package/src/__tests__/abs-modules-graph.test.ts +0 -100
  10. package/src/__tests__/abs-typevalue-diff.test.ts +0 -111
  11. package/src/__tests__/analyzer.test.ts +0 -880
  12. package/src/__tests__/as-replace.test.ts +0 -165
  13. package/src/__tests__/bpath-arg-provenance.test.ts +0 -104
  14. package/src/__tests__/bpath-as.test.ts +0 -54
  15. package/src/__tests__/bpath-cache-key.test.ts +0 -33
  16. package/src/__tests__/bpath-calls.test.ts +0 -55
  17. package/src/__tests__/bpath-diagnostics.test.ts +0 -78
  18. package/src/__tests__/bpath-env-modules.test.ts +0 -30
  19. package/src/__tests__/bpath-env.test.ts +0 -80
  20. package/src/__tests__/bpath-hosted-skip.test.ts +0 -71
  21. package/src/__tests__/bpath-import-default-ns.test.ts +0 -106
  22. package/src/__tests__/bpath-method-dedupe.test.ts +0 -65
  23. package/src/__tests__/bpath-method-diag.test.ts +0 -45
  24. package/src/__tests__/bpath-module-diags.test.ts +0 -101
  25. package/src/__tests__/bpath-path-env.test.ts +0 -46
  26. package/src/__tests__/bpath-reexport.test.ts +0 -131
  27. package/src/__tests__/bpath-replace.test.ts +0 -52
  28. package/src/__tests__/bpath-require.test.ts +0 -66
  29. package/src/__tests__/bpath-run.test.ts +0 -109
  30. package/src/__tests__/case-emitter.test.ts +0 -636
  31. package/src/__tests__/cjs-require.test.ts +0 -54
  32. package/src/__tests__/diagnostics.test.ts +0 -60
  33. package/src/__tests__/dts-generator.test.ts +0 -382
  34. package/src/__tests__/fn-cache-evict.test.ts +0 -56
  35. package/src/__tests__/fn-sig-impl.test.ts +0 -247
  36. package/src/__tests__/guard-generator.test.ts +0 -29
  37. package/src/__tests__/harvest-auto.test.ts +0 -43
  38. package/src/__tests__/harvest-node.test.ts +0 -24
  39. package/src/__tests__/harvest-package.test.ts +0 -38
  40. package/src/__tests__/harvest-to-abs.test.ts +0 -46
  41. package/src/__tests__/hover-intension.test.ts +0 -39
  42. package/src/__tests__/infer-json.test.ts +0 -35
  43. package/src/__tests__/integration.test.ts +0 -421
  44. package/src/__tests__/m3-intension.test.ts +0 -28
  45. package/src/__tests__/mock-seed-fingerprint.test.ts +0 -64
  46. package/src/__tests__/schema-generator.test.ts +0 -38
  47. package/src/__tests__/session-cache-evict.test.ts +0 -90
  48. package/src/__tests__/static-imports.test.ts +0 -44
  49. package/src/abs-modules-graph.ts +0 -387
  50. package/src/analysis-file-cache.ts +0 -51
  51. package/src/analyzer.ts +0 -3133
  52. package/src/bpath-diagnostics.ts +0 -283
  53. package/src/bpath-run.ts +0 -409
  54. package/src/case-emitter.ts +0 -477
  55. package/src/dts-generator.ts +0 -291
  56. package/src/env-to-abs.ts +0 -57
  57. package/src/fn-analysis-cache.ts +0 -119
  58. package/src/guard-generator.ts +0 -57
  59. package/src/harvest-auto.ts +0 -86
  60. package/src/harvest-node.ts +0 -48
  61. package/src/harvest-package.ts +0 -204
  62. package/src/harvest-to-abs.ts +0 -129
  63. package/src/index.ts +0 -150
  64. package/src/infer-json.ts +0 -137
  65. package/src/load-module.ts +0 -34
  66. package/src/mock-abs.ts +0 -275
  67. package/src/schema-generator.ts +0 -43
  68. package/src/semantic-tokens.ts +0 -216
  69. package/src/session-cache.ts +0 -50
  70. package/src/static-imports.ts +0 -241
  71. package/src/target-path.ts +0 -17
  72. 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
- /** B 路径求值具名导出(结果 + throws);opts.collectCalls 时附带调用点记录 */
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:保留 fnSig impl,使 Promise.resolve / URL 等
535
- * 在 B 路径也能产出精确字面量。
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?.impl) {
1005
- const params = tv.kind === "function" && tv.params?.length ? tv.params : sig.paramTypes.map((_, i) => `_arg${i}`);
1006
- const dummyBody = {
1007
- type: "BlockStatement",
1008
- body: [],
1009
- directives: []
1010
- };
1011
- const impl = sig.impl;
1012
- return absFunction4(params, {
1013
- body: dummyBody,
1014
- apply: (args) => {
1015
- const tvArgs = args.map((a) => absToTypeValue(a));
1016
- try {
1017
- const r = impl(tvArgs);
1018
- if (!r) return typeValueToAbs3(sig.returnType);
1019
- return envValueToAbs(r);
1020
- } catch {
1021
- return typeValueToAbs3(sig.returnType);
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 = [...run.calls ?? []];
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 || run.calls?.length ? collected : void 0,
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, parseTypeValueExpr(d.expression));
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 && !findFunctionNameAtPosition(source, line, column, file)) {
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.0",
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": "src/index.ts",
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.0",
32
- "@nudojs/parser": "0.3.0",
33
- "@nudojs/cli": "0.3.0",
34
- "@nudojs/harvester": "0.2.0"
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
- });