@nudojs/service 2.0.0 → 3.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 +70 -3
- package/dist/index.js +235 -12
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -479,7 +479,7 @@ declare function lookupHarvested(h: PackageHarvest, moduleName: string, exportNa
|
|
|
479
479
|
* 手动 `nudo harvest` 仍保留;这里是分析路径上的按需注入。
|
|
480
480
|
*/
|
|
481
481
|
|
|
482
|
-
/** 裸说明符 → 包名(含 scope);相对/绝对/node: 内建返回 undefined */
|
|
482
|
+
/** 裸说明符 → 包名(含 scope);相对/绝对/node: 与裸 Node 内建返回 undefined */
|
|
483
483
|
declare function barePackageName(spec: string): string | undefined;
|
|
484
484
|
declare function collectBarePackages(source: string): string[];
|
|
485
485
|
declare function harvestPackageCached(pkg: string, fromDir: string): PackageHarvest | null;
|
|
@@ -573,6 +573,34 @@ declare function bareSpecToAbsModules(spec: string, fromFile: string): AbsModule
|
|
|
573
573
|
declare function collectEnvGlobals(envNames: string[]): Record<string, Abs>;
|
|
574
574
|
/** @nudo:env modules(path / node:path / fs…)→ AbsModuleExports */
|
|
575
575
|
declare function collectEnvModules(envNames: string[]): Record<string, AbsModuleExports>;
|
|
576
|
+
/** Conflict when handwritten env overwrote a harvest module/export (B8). */
|
|
577
|
+
type EnvHarvestConflict = {
|
|
578
|
+
module: string;
|
|
579
|
+
/** export names where env replaced a harvest binding (empty + defaulted = default only) */
|
|
580
|
+
exports: string[];
|
|
581
|
+
defaultOverwritten: boolean;
|
|
582
|
+
};
|
|
583
|
+
/**
|
|
584
|
+
* Install conflict collector; returns the previous one so nested/concurrent
|
|
585
|
+
* analyzeFile callers can save/restore (module-global is not re-entrant).
|
|
586
|
+
*/
|
|
587
|
+
declare function setEnvHarvestConflictCollector(collector: ((c: EnvHarvestConflict) => void) | null): ((c: EnvHarvestConflict) => void) | null;
|
|
588
|
+
/** Read-only peek for tests / nested restore. */
|
|
589
|
+
declare function getEnvHarvestConflictCollector(): ((c: EnvHarvestConflict) => void) | null;
|
|
590
|
+
type MergeHarvestOptions = {
|
|
591
|
+
/**
|
|
592
|
+
* Per-call conflict sink. Takes precedence over the module-global collector
|
|
593
|
+
* installed via `setEnvHarvestConflictCollector`.
|
|
594
|
+
*/
|
|
595
|
+
onConflict?: (c: EnvHarvestConflict) => void;
|
|
596
|
+
};
|
|
597
|
+
/**
|
|
598
|
+
* Handwritten `@nudojs/env` wins over harvest / graph modules on overlapping
|
|
599
|
+
* module keys and overlapping export names (docs/versioning.md B8 + website
|
|
600
|
+
* harvester API). Harvest-only modules/exports are kept as fill-in.
|
|
601
|
+
* Overwrites notify `opts.onConflict` or the global collector.
|
|
602
|
+
*/
|
|
603
|
+
declare function mergeHarvestUnderEnv(harvestModules: Record<string, AbsModuleExports>, envModules: Record<string, AbsModuleExports>, opts?: MergeHarvestOptions): Record<string, AbsModuleExports>;
|
|
576
604
|
/** 收集 @nudo:replace + @nudo:as → transpile 注入表 */
|
|
577
605
|
declare function collectBPathReplacements(source: string): {
|
|
578
606
|
targets: Array<{
|
|
@@ -821,18 +849,57 @@ declare function collectBPathDiagnostics(source: string, extraKnown?: Iterable<s
|
|
|
821
849
|
/**
|
|
822
850
|
* 预置 Node API env:从 @types/node 的 .d.ts harvest。
|
|
823
851
|
* 与 packages/env 的手写 env 并行;此路径自动、可刷新。
|
|
852
|
+
*
|
|
853
|
+
* Productization (P0-B B2/B7):
|
|
854
|
+
* - In-process cache keyed by package root + package.json mtime/size.
|
|
855
|
+
* - Terminal failures (`not-found`/`no-dts`/`failed`) also cache — no retry storm.
|
|
856
|
+
* `disabled` is never cached (env var can flip mid-process).
|
|
857
|
+
* - `clearNodeHarvestCache()` for tests / watch invalidation.
|
|
858
|
+
* - `NUDO_HARVEST_NODE=off` disables harvest (explicit skip, not silent).
|
|
859
|
+
* - Defaults stay IDE-budgeted: maxFiles=12, maxMs=2500.
|
|
824
860
|
*/
|
|
825
861
|
|
|
862
|
+
/** IDE-startup budgets for @types/node harvest. Do not raise casually. */
|
|
863
|
+
declare const HARVEST_NODE_DEFAULT_MAX_FILES = 12;
|
|
864
|
+
declare const HARVEST_NODE_DEFAULT_MAX_MS = 2500;
|
|
865
|
+
type HarvestNodeStats = {
|
|
866
|
+
files: number;
|
|
867
|
+
symbols: number;
|
|
868
|
+
skipped: number;
|
|
869
|
+
};
|
|
826
870
|
type NodeEnvResult = {
|
|
827
871
|
ok: true;
|
|
828
872
|
env: HarvestedEnv;
|
|
829
873
|
root: string;
|
|
830
874
|
files: number;
|
|
875
|
+
stats: HarvestNodeStats;
|
|
876
|
+
/** true when this result came from the in-process cache */
|
|
877
|
+
cached: boolean;
|
|
831
878
|
} | {
|
|
832
879
|
ok: false;
|
|
833
880
|
error: string;
|
|
881
|
+
reason: "disabled" | "not-found" | "no-dts" | "failed";
|
|
882
|
+
/** true when this failure came from the in-process cache */
|
|
883
|
+
cached?: boolean;
|
|
834
884
|
};
|
|
835
|
-
/**
|
|
885
|
+
/**
|
|
886
|
+
* Clear the in-process @types/node harvest cache.
|
|
887
|
+
* Drops **success and terminal-failure** entries (including `not-found`), so
|
|
888
|
+
* a mid-session `@types/node` install becomes visible without process restart.
|
|
889
|
+
* Tests, watch-mode dep-hash invalidation, and package-manager hooks should
|
|
890
|
+
* call this when `node_modules/@types/node` changes underneath the process.
|
|
891
|
+
*/
|
|
892
|
+
declare function clearNodeHarvestCache(): void;
|
|
893
|
+
/** Number of cached harvest results (diagnostics / tests). */
|
|
894
|
+
declare function getNodeHarvestCacheSize(): number;
|
|
895
|
+
declare function isHarvestNodeDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
896
|
+
/**
|
|
897
|
+
* harvest @types/node(限制文件数 + 时间预算,避免拖垮启动)。
|
|
898
|
+
* Success **and** terminal failures (`not-found` / `no-dts` / `failed`) are
|
|
899
|
+
* cached in-process so IDE analysis does not re-walk / re-timeout every file.
|
|
900
|
+
* `disabled` is never cached — the env var can flip mid-process.
|
|
901
|
+
* Call `clearNodeHarvestCache()` after `@types/node` changes.
|
|
902
|
+
*/
|
|
836
903
|
declare function harvestNodeTypes(fromDir?: string, maxFiles?: number, maxMs?: number): NodeEnvResult;
|
|
837
904
|
/** 把 harvest 结果压成「模块名 → 导出名列表」摘要,便于日志/测试 */
|
|
838
905
|
declare function summarizeNodeEnv(env: HarvestedEnv): {
|
|
@@ -1321,4 +1388,4 @@ declare function emitDerivedFromRoot(rootFile: string, opts: {
|
|
|
1321
1388
|
refreshExistingOnly?: boolean;
|
|
1322
1389
|
}): EmitDerivedResult;
|
|
1323
1390
|
|
|
1324
|
-
export { ANALYSIS_ABI, type AbsGraphOptions, type AbsMockSeeds, type AbsModuleCacheEntry, type AbsModuleGraphResult, type AbsModuleLoadIssue, AnalysisConfig, type AnalysisResult, type AnalysisSession, type BPathBuiltinUnknown, type BPathDiagnostics, type BPathRunResult, type BPathUnreachable, type BindingInfo, type BuildSemanticTokensOpts, CallRecord, type CaseHint, type CaseInfo, type CaseResult, type CompletionItem, type ConstraintSourceExpr, type DepContent, type DerivedExport, type DerivedParam, type Diagnostic, type DiagnosticSeverity, type DiagnosticTag, DiagnosticsLevel, DiskCache, type DiskCacheOptions, type DraftEvidence, type EmitDerivedResult, type EmitInterfaceOpts, type EmitInterfaceResult, type EmitInterfaceSkipReason, type EmitResult, type EmitSkipReason, type FunctionAnalysis, type HoverInfo, type InferJson, type InferJsonCase, type InferJsonFunction, type InterfaceDraftEntry, type InterfaceDraftOpts, type InterfaceDraftResult, type InterfaceSurfaceEntry, type InterfaceSurfaceOpts, type LoadModule, type ModuleExports, type ModuleGraphCache, type NodeEnvResult, type PackageHarvest, type ReferenceInfo, type RootDeriveOpts, type RootDeriveResult, SEMANTIC_TOKEN_MODIFIERS, SEMANTIC_TOKEN_TYPES, type SemanticToken, type SourceLocation, type SymbolInfo, type SymbolTable, type WriteDraftResult, absToTSType, absToZodSchema, ambientSourcesOfSidecar, analysisFileCacheKey, analyzeExportsFromSource, analyzeFile, analyzeFileAsync, autoHarvestModules, barePackageName, bareSpecToAbsModules, buildCaseDirective, buildModuleGraph, buildSemanticTokens, checkCacheKey, clearAbsModuleCache, clearAnalysisFileCache, clearAnalysisSessionCaches, clearBPathCache, clearEnvPathDeps, clearFnAnalysisCache, clearHarvestCache, collectAbsBindingsFromGraph, collectBPathDiagnostics, collectBPathReplacements, collectBarePackages, collectCallRecords, collectDependencySpecs, collectDtsFromEntry, collectEnvGlobals, collectEnvModules, collectLoadDepContents, collectParamBodyAccesses, collectStaticImports, computeDirtySet, defaultAbsLoadModule, defaultLoadModule, deriveFromRoot, diagnosticsLevelForFile, draftInterface, emitDerivedFromRoot, emitInterface, encodeSemanticTokens, envPathDependents, evalAbsModuleGraph, evalProgramAbsWithModules, evictAbsModuleCacheFiles, evictAnalysisCachesForFiles, evictAnalysisFileCacheForFiles, evictBPathCacheForFiles, evictFnAnalysisCacheForFiles, extractFnConstraintSources, extractNudoImportSpecs, filterDiagnosticsByLevel, formatDerivedSection, formatDraftModule, formatDraftSummary, formatEmitSummary, formatHarvestSummary, formatInterfaceSurfaceLine, generateDts, generateFunctionDtsLines, generateGuardFunction, generateGuardFunctionFromAbs, getAbsAtPosition, getAbsAtPositionAsync, getAnalysisFileCacheSize, getAnalysisSession, getCasesForFile, getCompletionsAtPosition, getHoverAtPosition, getTypeAtPosition, getTypeAtPositionAsync, harvestNodeTypes, harvestPackage, harvestPackageCached, harvestToAbsModules, harvestedValueToAbs, ifaceCacheKey, insertGeneratedCaseDirectives, interfaceSurface, interfaceTierModifierBit, isBPathCapable, isDraftableEntry, isEnvTemplatePath, isNudoTargetPath, isProjectConfigPath, isSidecarPath, isWatchRelevantPath, lookupHarvested, mockDirectivesToAbsSeeds, noteEnvPathDeps, packageHarvestToAbsModules, relativizePath, resetAllAnalysisCaches, resolvePackageRoot, serializeCaseArg, serializeInferJson, setAnalysisSession, sha256Hex, shouldAnalyzeFile, sidecarDraftPath, hasNudoDirectives as sourceHasNudoDirectives, stripGeneratedCaseDirectives, summarizeNodeEnv, topoSortDirty, tryBPathCall, tryBPathCallFull, tryRunBPath, unifiedDiff, writeInterfaceDraft };
|
|
1391
|
+
export { ANALYSIS_ABI, type AbsGraphOptions, type AbsMockSeeds, type AbsModuleCacheEntry, type AbsModuleGraphResult, type AbsModuleLoadIssue, AnalysisConfig, type AnalysisResult, type AnalysisSession, type BPathBuiltinUnknown, type BPathDiagnostics, type BPathRunResult, type BPathUnreachable, type BindingInfo, type BuildSemanticTokensOpts, CallRecord, type CaseHint, type CaseInfo, type CaseResult, type CompletionItem, type ConstraintSourceExpr, type DepContent, type DerivedExport, type DerivedParam, type Diagnostic, type DiagnosticSeverity, type DiagnosticTag, DiagnosticsLevel, DiskCache, type DiskCacheOptions, type DraftEvidence, type EmitDerivedResult, type EmitInterfaceOpts, type EmitInterfaceResult, type EmitInterfaceSkipReason, type EmitResult, type EmitSkipReason, type EnvHarvestConflict, type FunctionAnalysis, HARVEST_NODE_DEFAULT_MAX_FILES, HARVEST_NODE_DEFAULT_MAX_MS, type HarvestNodeStats, type HoverInfo, type InferJson, type InferJsonCase, type InferJsonFunction, type InterfaceDraftEntry, type InterfaceDraftOpts, type InterfaceDraftResult, type InterfaceSurfaceEntry, type InterfaceSurfaceOpts, type LoadModule, type MergeHarvestOptions, type ModuleExports, type ModuleGraphCache, type NodeEnvResult, type PackageHarvest, type ReferenceInfo, type RootDeriveOpts, type RootDeriveResult, SEMANTIC_TOKEN_MODIFIERS, SEMANTIC_TOKEN_TYPES, type SemanticToken, type SourceLocation, type SymbolInfo, type SymbolTable, type WriteDraftResult, absToTSType, absToZodSchema, ambientSourcesOfSidecar, analysisFileCacheKey, analyzeExportsFromSource, analyzeFile, analyzeFileAsync, autoHarvestModules, barePackageName, bareSpecToAbsModules, buildCaseDirective, buildModuleGraph, buildSemanticTokens, checkCacheKey, clearAbsModuleCache, clearAnalysisFileCache, clearAnalysisSessionCaches, clearBPathCache, clearEnvPathDeps, clearFnAnalysisCache, clearHarvestCache, clearNodeHarvestCache, collectAbsBindingsFromGraph, collectBPathDiagnostics, collectBPathReplacements, collectBarePackages, collectCallRecords, collectDependencySpecs, collectDtsFromEntry, collectEnvGlobals, collectEnvModules, collectLoadDepContents, collectParamBodyAccesses, collectStaticImports, computeDirtySet, defaultAbsLoadModule, defaultLoadModule, deriveFromRoot, diagnosticsLevelForFile, draftInterface, emitDerivedFromRoot, emitInterface, encodeSemanticTokens, envPathDependents, evalAbsModuleGraph, evalProgramAbsWithModules, evictAbsModuleCacheFiles, evictAnalysisCachesForFiles, evictAnalysisFileCacheForFiles, evictBPathCacheForFiles, evictFnAnalysisCacheForFiles, extractFnConstraintSources, extractNudoImportSpecs, filterDiagnosticsByLevel, formatDerivedSection, formatDraftModule, formatDraftSummary, formatEmitSummary, formatHarvestSummary, formatInterfaceSurfaceLine, generateDts, generateFunctionDtsLines, generateGuardFunction, generateGuardFunctionFromAbs, getAbsAtPosition, getAbsAtPositionAsync, getAnalysisFileCacheSize, getAnalysisSession, getCasesForFile, getCompletionsAtPosition, getEnvHarvestConflictCollector, getHoverAtPosition, getNodeHarvestCacheSize, getTypeAtPosition, getTypeAtPositionAsync, harvestNodeTypes, harvestPackage, harvestPackageCached, harvestToAbsModules, harvestedValueToAbs, ifaceCacheKey, insertGeneratedCaseDirectives, interfaceSurface, interfaceTierModifierBit, isBPathCapable, isDraftableEntry, isEnvTemplatePath, isHarvestNodeDisabled, isNudoTargetPath, isProjectConfigPath, isSidecarPath, isWatchRelevantPath, lookupHarvested, mergeHarvestUnderEnv, mockDirectivesToAbsSeeds, noteEnvPathDeps, packageHarvestToAbsModules, relativizePath, resetAllAnalysisCaches, resolvePackageRoot, serializeCaseArg, serializeInferJson, setAnalysisSession, setEnvHarvestConflictCollector, sha256Hex, shouldAnalyzeFile, sidecarDraftPath, hasNudoDirectives as sourceHasNudoDirectives, stripGeneratedCaseDirectives, summarizeNodeEnv, topoSortDirty, tryBPathCall, tryBPathCallFull, tryRunBPath, unifiedDiff, writeInterfaceDraft };
|
package/dist/index.js
CHANGED
|
@@ -778,6 +778,49 @@ function lookupHarvested(h, moduleName, exportName) {
|
|
|
778
778
|
}
|
|
779
779
|
|
|
780
780
|
// src/harvest-auto.ts
|
|
781
|
+
var NODE_BUILTINS = /* @__PURE__ */ new Set([
|
|
782
|
+
"assert",
|
|
783
|
+
"async_hooks",
|
|
784
|
+
"buffer",
|
|
785
|
+
"child_process",
|
|
786
|
+
"cluster",
|
|
787
|
+
"console",
|
|
788
|
+
"constants",
|
|
789
|
+
"crypto",
|
|
790
|
+
"dgram",
|
|
791
|
+
"diagnostics_channel",
|
|
792
|
+
"dns",
|
|
793
|
+
"domain",
|
|
794
|
+
"events",
|
|
795
|
+
"fs",
|
|
796
|
+
"http",
|
|
797
|
+
"http2",
|
|
798
|
+
"https",
|
|
799
|
+
"inspector",
|
|
800
|
+
"module",
|
|
801
|
+
"net",
|
|
802
|
+
"os",
|
|
803
|
+
"path",
|
|
804
|
+
"perf_hooks",
|
|
805
|
+
"process",
|
|
806
|
+
"punycode",
|
|
807
|
+
"querystring",
|
|
808
|
+
"readline",
|
|
809
|
+
"repl",
|
|
810
|
+
"stream",
|
|
811
|
+
"string_decoder",
|
|
812
|
+
"timers",
|
|
813
|
+
"tls",
|
|
814
|
+
"trace_events",
|
|
815
|
+
"tty",
|
|
816
|
+
"url",
|
|
817
|
+
"util",
|
|
818
|
+
"v8",
|
|
819
|
+
"vm",
|
|
820
|
+
"wasi",
|
|
821
|
+
"worker_threads",
|
|
822
|
+
"zlib"
|
|
823
|
+
]);
|
|
781
824
|
function barePackageName(spec) {
|
|
782
825
|
if (!spec) return void 0;
|
|
783
826
|
if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) return void 0;
|
|
@@ -785,7 +828,9 @@ function barePackageName(spec) {
|
|
|
785
828
|
if (spec.startsWith("@")) {
|
|
786
829
|
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0;
|
|
787
830
|
}
|
|
788
|
-
|
|
831
|
+
const name = parts[0];
|
|
832
|
+
if (NODE_BUILTINS.has(name)) return void 0;
|
|
833
|
+
return name;
|
|
789
834
|
}
|
|
790
835
|
function collectBarePackages(source) {
|
|
791
836
|
try {
|
|
@@ -1314,6 +1359,50 @@ function collectEnvModules(envNames) {
|
|
|
1314
1359
|
}
|
|
1315
1360
|
return out;
|
|
1316
1361
|
}
|
|
1362
|
+
var envHarvestConflictCollector = null;
|
|
1363
|
+
function setEnvHarvestConflictCollector(collector) {
|
|
1364
|
+
const prev = envHarvestConflictCollector;
|
|
1365
|
+
envHarvestConflictCollector = collector;
|
|
1366
|
+
return prev;
|
|
1367
|
+
}
|
|
1368
|
+
function getEnvHarvestConflictCollector() {
|
|
1369
|
+
return envHarvestConflictCollector;
|
|
1370
|
+
}
|
|
1371
|
+
function mergeHarvestUnderEnv(harvestModules, envModules, opts) {
|
|
1372
|
+
const out = {};
|
|
1373
|
+
for (const [mod, exports] of Object.entries(harvestModules)) {
|
|
1374
|
+
const named = { ...exports.named };
|
|
1375
|
+
out[mod] = exports.default !== void 0 ? { named, default: exports.default } : { named };
|
|
1376
|
+
}
|
|
1377
|
+
const notify = opts?.onConflict ?? envHarvestConflictCollector;
|
|
1378
|
+
for (const [mod, envExports] of Object.entries(envModules)) {
|
|
1379
|
+
const existing = out[mod];
|
|
1380
|
+
if (!existing) {
|
|
1381
|
+
const named2 = { ...envExports.named };
|
|
1382
|
+
out[mod] = envExports.default !== void 0 ? { named: named2, default: envExports.default } : { named: named2 };
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
const overwritten = [];
|
|
1386
|
+
for (const key of Object.keys(envExports.named)) {
|
|
1387
|
+
if (existing.named[key] !== void 0) overwritten.push(key);
|
|
1388
|
+
}
|
|
1389
|
+
const defaultOverwritten = envExports.default !== void 0 && existing.default !== void 0;
|
|
1390
|
+
if (notify && (overwritten.length > 0 || defaultOverwritten)) {
|
|
1391
|
+
try {
|
|
1392
|
+
notify({
|
|
1393
|
+
module: mod,
|
|
1394
|
+
exports: overwritten,
|
|
1395
|
+
defaultOverwritten
|
|
1396
|
+
});
|
|
1397
|
+
} catch {
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
const named = { ...existing.named, ...envExports.named };
|
|
1401
|
+
const defaultAbs = envExports.default !== void 0 ? envExports.default : existing.default;
|
|
1402
|
+
out[mod] = defaultAbs !== void 0 ? { named, default: defaultAbs } : { named };
|
|
1403
|
+
}
|
|
1404
|
+
return out;
|
|
1405
|
+
}
|
|
1317
1406
|
function collectBPathReplacements(source) {
|
|
1318
1407
|
const targets = [];
|
|
1319
1408
|
const values = {};
|
|
@@ -1428,7 +1517,7 @@ function tryRunBPath(source, filePath, opts = {}) {
|
|
|
1428
1517
|
try {
|
|
1429
1518
|
const { modules: graphMods, issues } = evalAbsModuleGraph(source, filePath);
|
|
1430
1519
|
const envMods = collectEnvModules(opts.envNames ?? []);
|
|
1431
|
-
const modules =
|
|
1520
|
+
const modules = mergeHarvestUnderEnv(graphMods, envMods);
|
|
1432
1521
|
const { targets, values, asTargets, asValues } = collectBPathReplacements(source);
|
|
1433
1522
|
const envGlobals = {
|
|
1434
1523
|
...collectEnvGlobals(opts.envNames ?? []),
|
|
@@ -2352,6 +2441,24 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords,
|
|
|
2352
2441
|
() => analyzeFileUncachedInner(filePath, source, activeCases, externalCallRecords, loadModule)
|
|
2353
2442
|
);
|
|
2354
2443
|
}
|
|
2444
|
+
function findModuleImportLoc(source, module) {
|
|
2445
|
+
const bare = module.startsWith("node:") ? module.slice("node:".length) : module;
|
|
2446
|
+
const alts = [.../* @__PURE__ */ new Set([module, bare, `node:${bare}`])];
|
|
2447
|
+
const lines = source.split(/\r?\n/);
|
|
2448
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2449
|
+
const line = lines[i];
|
|
2450
|
+
for (const alt of alts) {
|
|
2451
|
+
const re = new RegExp(
|
|
2452
|
+
`["'\`]${alt.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["'\`]`
|
|
2453
|
+
);
|
|
2454
|
+
const m = re.exec(line);
|
|
2455
|
+
if (m && m.index !== void 0) {
|
|
2456
|
+
return { line: i, column: m.index, length: m[0].length };
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
return null;
|
|
2461
|
+
}
|
|
2355
2462
|
function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRecords, loadModule) {
|
|
2356
2463
|
const ast = parse5(source);
|
|
2357
2464
|
const functions = extractDirectives(ast);
|
|
@@ -2360,6 +2467,18 @@ function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRec
|
|
|
2360
2467
|
const nodeAbsMap = /* @__PURE__ */ new Map();
|
|
2361
2468
|
const functionResults = [];
|
|
2362
2469
|
const caseHints = [];
|
|
2470
|
+
const envHarvestConflicts = [];
|
|
2471
|
+
const prevHarvestConflictCollector = setEnvHarvestConflictCollector((c) => {
|
|
2472
|
+
if (!envHarvestConflicts.some((x) => x.module === c.module)) {
|
|
2473
|
+
envHarvestConflicts.push(c);
|
|
2474
|
+
} else {
|
|
2475
|
+
const prev = envHarvestConflicts.find((x) => x.module === c.module);
|
|
2476
|
+
for (const e of c.exports) {
|
|
2477
|
+
if (!prev.exports.includes(e)) prev.exports.push(e);
|
|
2478
|
+
}
|
|
2479
|
+
prev.defaultOverwritten ||= c.defaultOverwritten;
|
|
2480
|
+
}
|
|
2481
|
+
});
|
|
2363
2482
|
const fileDirectives = extractFileDirectives(ast);
|
|
2364
2483
|
const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
|
|
2365
2484
|
const projectConfig = findProjectConfig(dirname7(filePath));
|
|
@@ -2449,7 +2568,7 @@ function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRec
|
|
|
2449
2568
|
seedFns: seeds.seedFns,
|
|
2450
2569
|
...loadModule ? { loadModule } : {}
|
|
2451
2570
|
});
|
|
2452
|
-
absGraphModules =
|
|
2571
|
+
absGraphModules = mergeHarvestUnderEnv(g.modules, collectEnvModules(envNames));
|
|
2453
2572
|
pushBModuleIssues(g.issues);
|
|
2454
2573
|
} catch {
|
|
2455
2574
|
}
|
|
@@ -3055,6 +3174,24 @@ function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRec
|
|
|
3055
3174
|
filePath,
|
|
3056
3175
|
analysisConfig(projectConfig?.config).callSiteBudget
|
|
3057
3176
|
);
|
|
3177
|
+
setEnvHarvestConflictCollector(prevHarvestConflictCollector);
|
|
3178
|
+
for (const c of envHarvestConflicts) {
|
|
3179
|
+
const parts = [];
|
|
3180
|
+
if (c.exports.length > 0) parts.push(`export(s) ${c.exports.join(", ")}`);
|
|
3181
|
+
if (c.defaultOverwritten) parts.push("default");
|
|
3182
|
+
const detail = parts.length > 0 ? ` \u2014 ${parts.join("; ")}` : "";
|
|
3183
|
+
const loc = findModuleImportLoc(source, c.module);
|
|
3184
|
+
const range = loc ? {
|
|
3185
|
+
start: { line: loc.line, column: loc.column },
|
|
3186
|
+
end: { line: loc.line, column: loc.column + loc.length }
|
|
3187
|
+
} : { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } };
|
|
3188
|
+
diagnostics.push({
|
|
3189
|
+
range,
|
|
3190
|
+
severity: "warning",
|
|
3191
|
+
message: `handwritten @nudo:env wins over harvest on module "${c.module}"${detail}; harvest only fills missing slots (B8). code=nudo:env-harvest-conflict`,
|
|
3192
|
+
code: "nudo:env-harvest-conflict"
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3058
3195
|
return {
|
|
3059
3196
|
functions: functionResults,
|
|
3060
3197
|
diagnostics,
|
|
@@ -3167,7 +3304,7 @@ function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envN
|
|
|
3167
3304
|
}
|
|
3168
3305
|
}
|
|
3169
3306
|
if (envNames.length > 0) {
|
|
3170
|
-
modules =
|
|
3307
|
+
modules = mergeHarvestUnderEnv(modules ?? {}, collectEnvModules(envNames));
|
|
3171
3308
|
}
|
|
3172
3309
|
try {
|
|
3173
3310
|
importLocals = buildAbsImportLocalMap(source, filePath);
|
|
@@ -4293,19 +4430,97 @@ function collectLoadDepContents(filePath, source, loadModule) {
|
|
|
4293
4430
|
}
|
|
4294
4431
|
|
|
4295
4432
|
// src/harvest-node.ts
|
|
4296
|
-
import { existsSync as existsSync8 } from "fs";
|
|
4433
|
+
import { existsSync as existsSync8, statSync as statSync6 } from "fs";
|
|
4434
|
+
import { join as join6 } from "path";
|
|
4297
4435
|
import { harvestDts as harvestDts2 } from "@nudojs/harvester";
|
|
4298
|
-
|
|
4436
|
+
var HARVEST_NODE_DEFAULT_MAX_FILES = 12;
|
|
4437
|
+
var HARVEST_NODE_DEFAULT_MAX_MS = 2500;
|
|
4438
|
+
var nodeHarvestCache = /* @__PURE__ */ new Map();
|
|
4439
|
+
function notFoundCacheKey(fromDir) {
|
|
4440
|
+
return `not-found|@types/node|${fromDir ?? ""}`;
|
|
4441
|
+
}
|
|
4442
|
+
function nodeHarvestCacheKey(root, maxFiles, maxMs) {
|
|
4443
|
+
let sig = "nostat";
|
|
4444
|
+
try {
|
|
4445
|
+
const st = statSync6(join6(root, "package.json"));
|
|
4446
|
+
sig = `${st.size}:${Math.floor(st.mtimeMs)}`;
|
|
4447
|
+
} catch {
|
|
4448
|
+
}
|
|
4449
|
+
return `${root}|${maxFiles}|${maxMs}|${sig}`;
|
|
4450
|
+
}
|
|
4451
|
+
function clearNodeHarvestCache() {
|
|
4452
|
+
nodeHarvestCache.clear();
|
|
4453
|
+
}
|
|
4454
|
+
function getNodeHarvestCacheSize() {
|
|
4455
|
+
return nodeHarvestCache.size;
|
|
4456
|
+
}
|
|
4457
|
+
function isHarvestNodeDisabled(env = process.env) {
|
|
4458
|
+
return env.NUDO_HARVEST_NODE === "off";
|
|
4459
|
+
}
|
|
4460
|
+
function harvestNodeTypes(fromDir, maxFiles = HARVEST_NODE_DEFAULT_MAX_FILES, maxMs = HARVEST_NODE_DEFAULT_MAX_MS) {
|
|
4461
|
+
if (isHarvestNodeDisabled()) {
|
|
4462
|
+
return {
|
|
4463
|
+
ok: false,
|
|
4464
|
+
error: "harvest disabled via NUDO_HARVEST_NODE=off",
|
|
4465
|
+
reason: "disabled"
|
|
4466
|
+
};
|
|
4467
|
+
}
|
|
4299
4468
|
const root = resolvePackageRoot("@types/node", fromDir) ?? resolvePackageRoot("node", fromDir);
|
|
4300
4469
|
if (!root || !existsSync8(root)) {
|
|
4301
|
-
|
|
4470
|
+
const missKey = notFoundCacheKey(fromDir);
|
|
4471
|
+
const miss = nodeHarvestCache.get(missKey);
|
|
4472
|
+
if (miss && !miss.ok && miss.reason === "not-found") {
|
|
4473
|
+
return { ...miss, cached: true };
|
|
4474
|
+
}
|
|
4475
|
+
const result = {
|
|
4476
|
+
ok: false,
|
|
4477
|
+
error: "@types/node not found",
|
|
4478
|
+
reason: "not-found"
|
|
4479
|
+
};
|
|
4480
|
+
nodeHarvestCache.set(missKey, result);
|
|
4481
|
+
return result;
|
|
4482
|
+
}
|
|
4483
|
+
const key = nodeHarvestCacheKey(root, maxFiles, maxMs);
|
|
4484
|
+
const hit = nodeHarvestCache.get(key);
|
|
4485
|
+
if (hit) {
|
|
4486
|
+
return hit.ok ? { ...hit, cached: true } : { ...hit, cached: true };
|
|
4302
4487
|
}
|
|
4303
4488
|
const dts = collectDtsFiles(root, maxFiles);
|
|
4304
4489
|
if (dts.length === 0) {
|
|
4305
|
-
|
|
4490
|
+
const result = {
|
|
4491
|
+
ok: false,
|
|
4492
|
+
error: `no .d.ts under ${root}`,
|
|
4493
|
+
reason: "no-dts"
|
|
4494
|
+
};
|
|
4495
|
+
nodeHarvestCache.set(key, result);
|
|
4496
|
+
return result;
|
|
4497
|
+
}
|
|
4498
|
+
try {
|
|
4499
|
+
const env = harvestDts2(dts, { maxMs });
|
|
4500
|
+
const stats = {
|
|
4501
|
+
files: env.stats.files,
|
|
4502
|
+
symbols: env.stats.symbols,
|
|
4503
|
+
skipped: env.stats.skipped
|
|
4504
|
+
};
|
|
4505
|
+
const result = {
|
|
4506
|
+
ok: true,
|
|
4507
|
+
env,
|
|
4508
|
+
root,
|
|
4509
|
+
files: stats.files,
|
|
4510
|
+
stats,
|
|
4511
|
+
cached: false
|
|
4512
|
+
};
|
|
4513
|
+
nodeHarvestCache.set(key, result);
|
|
4514
|
+
return result;
|
|
4515
|
+
} catch (e) {
|
|
4516
|
+
const result = {
|
|
4517
|
+
ok: false,
|
|
4518
|
+
error: `harvest failed: ${e.message}`,
|
|
4519
|
+
reason: "failed"
|
|
4520
|
+
};
|
|
4521
|
+
nodeHarvestCache.set(key, result);
|
|
4522
|
+
return result;
|
|
4306
4523
|
}
|
|
4307
|
-
const env = harvestDts2(dts, { maxMs });
|
|
4308
|
-
return { ok: true, env, root, files: env.stats.files };
|
|
4309
4524
|
}
|
|
4310
4525
|
function summarizeNodeEnv(env) {
|
|
4311
4526
|
return {
|
|
@@ -5916,7 +6131,7 @@ function joinSections(base, sectionTexts) {
|
|
|
5916
6131
|
|
|
5917
6132
|
// src/interface-draft.ts
|
|
5918
6133
|
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync3, realpathSync as realpathSync2 } from "fs";
|
|
5919
|
-
import { basename as basename3, dirname as dirname13, isAbsolute as isAbsolute2, join as
|
|
6134
|
+
import { basename as basename3, dirname as dirname13, isAbsolute as isAbsolute2, join as join7, relative as relative3 } from "path";
|
|
5920
6135
|
import {
|
|
5921
6136
|
effectiveInterface as effectiveInterface2,
|
|
5922
6137
|
formatConstraint as formatConstraint3,
|
|
@@ -6444,7 +6659,7 @@ function safeRealpath(p) {
|
|
|
6444
6659
|
return realpathSync2(p);
|
|
6445
6660
|
} catch {
|
|
6446
6661
|
try {
|
|
6447
|
-
return
|
|
6662
|
+
return join7(realpathSync2(dirname13(p)), basename3(p));
|
|
6448
6663
|
} catch {
|
|
6449
6664
|
return p;
|
|
6450
6665
|
}
|
|
@@ -7454,6 +7669,8 @@ export {
|
|
|
7454
7669
|
ANALYSIS_ABI,
|
|
7455
7670
|
DEFAULT_ANALYSIS_MODE,
|
|
7456
7671
|
DiskCache,
|
|
7672
|
+
HARVEST_NODE_DEFAULT_MAX_FILES,
|
|
7673
|
+
HARVEST_NODE_DEFAULT_MAX_MS,
|
|
7457
7674
|
SEMANTIC_TOKEN_MODIFIERS,
|
|
7458
7675
|
SEMANTIC_TOKEN_TYPES,
|
|
7459
7676
|
absToTSType,
|
|
@@ -7478,6 +7695,7 @@ export {
|
|
|
7478
7695
|
clearEnvPathDeps,
|
|
7479
7696
|
clearFnAnalysisCache,
|
|
7480
7697
|
clearHarvestCache,
|
|
7698
|
+
clearNodeHarvestCache,
|
|
7481
7699
|
clearPathEnvCaches,
|
|
7482
7700
|
collectAbsBindingsFromGraph,
|
|
7483
7701
|
collectAbsInlays,
|
|
@@ -7530,7 +7748,9 @@ export {
|
|
|
7530
7748
|
getAnalysisSession,
|
|
7531
7749
|
getCasesForFile,
|
|
7532
7750
|
getCompletionsAtPosition,
|
|
7751
|
+
getEnvHarvestConflictCollector,
|
|
7533
7752
|
getHoverAtPosition,
|
|
7753
|
+
getNodeHarvestCacheSize,
|
|
7534
7754
|
getTypeAtPosition,
|
|
7535
7755
|
getTypeAtPositionAsync,
|
|
7536
7756
|
harvestNodeTypes,
|
|
@@ -7546,12 +7766,14 @@ export {
|
|
|
7546
7766
|
isBPathCapable,
|
|
7547
7767
|
isDraftableEntry,
|
|
7548
7768
|
isEnvTemplatePath,
|
|
7769
|
+
isHarvestNodeDisabled,
|
|
7549
7770
|
isNudoTargetPath,
|
|
7550
7771
|
isProjectConfigPath,
|
|
7551
7772
|
isSidecarPath,
|
|
7552
7773
|
isWatchRelevantPath,
|
|
7553
7774
|
lookupHarvested,
|
|
7554
7775
|
matchesEmitAllowlist,
|
|
7776
|
+
mergeHarvestUnderEnv,
|
|
7555
7777
|
mockDirectivesToAbsSeeds,
|
|
7556
7778
|
noteEnvPathDeps,
|
|
7557
7779
|
packageHarvestToAbsModules,
|
|
@@ -7561,6 +7783,7 @@ export {
|
|
|
7561
7783
|
serializeCaseArg,
|
|
7562
7784
|
serializeInferJson,
|
|
7563
7785
|
setAnalysisSession,
|
|
7786
|
+
setEnvHarvestConflictCollector,
|
|
7564
7787
|
sha256Hex,
|
|
7565
7788
|
shouldAnalyzeFile,
|
|
7566
7789
|
sidecarDraftPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/service",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20"
|
|
6
6
|
},
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@babel/traverse": "^7.29.0",
|
|
47
47
|
"@babel/types": "^7.29.0",
|
|
48
|
-
"@nudojs/core": "2.0.
|
|
49
|
-
"@nudojs/parser": "0.5.
|
|
50
|
-
"@nudojs/env": "0.
|
|
51
|
-
"@nudojs/harvester": "0.2.
|
|
48
|
+
"@nudojs/core": "2.0.1",
|
|
49
|
+
"@nudojs/parser": "0.5.1",
|
|
50
|
+
"@nudojs/env": "0.4.0",
|
|
51
|
+
"@nudojs/harvester": "0.2.6"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "tsup src/index.ts src/evaluator/evaluator-api.ts --format esm --dts"
|