

















@@ -43,6 +43,19 @@ type NpmFreshnessConfigScope = {
4343};
44444545const NPM_CONFIG_PATH_PROBE_PARENT_ENV_KEYS = ["PATH", "Path", "PATHEXT", "SystemRoot", "ComSpec"];
46+const NPM_GLOBAL_CONFIG_PATH_CACHE = new Map<string, string | null>();
47+const NPM_GLOBAL_CONFIG_PATH_CACHE_ENV_KEYS = [
48+ ...NPM_CONFIG_PATH_PROBE_PARENT_ENV_KEYS,
49+"NPM_CONFIG_GLOBALCONFIG",
50+"npm_config_globalconfig",
51+"NPM_CONFIG_PREFIX",
52+"npm_config_prefix",
53+"NPM_CONFIG_USERCONFIG",
54+"npm_config_userconfig",
55+"HOME",
56+"PREFIX",
57+"USERPROFILE",
58+] as const;
46594760function resolveEnvPath(env: NodeJS.ProcessEnv, upperKey: string, lowerKey: string): string | null {
4861const raw = env[upperKey]?.trim() || env[lowerKey]?.trim();
@@ -94,6 +107,10 @@ function readNpmGlobalConfigPath(
94107env: NodeJS.ProcessEnv,
95108scope: NpmFreshnessConfigScope,
96109): string | null {
110+const cacheKey = buildNpmGlobalConfigPathCacheKey(env, scope);
111+if (NPM_GLOBAL_CONFIG_PATH_CACHE.has(cacheKey)) {
112+return NPM_GLOBAL_CONFIG_PATH_CACHE.get(cacheKey) ?? null;
113+}
97114try {
98115const raw = execFileSync("npm", ["config", "get", "globalconfig"], {
99116encoding: "utf-8",
@@ -104,12 +121,58 @@ function readNpmGlobalConfigPath(
104121stdio: ["ignore", "pipe", "ignore"],
105122timeout: 2_000,
106123}).trim();
107-return raw && raw !== "null" && raw !== "undefined" ? raw : null;
124+const resolved = raw && raw !== "null" && raw !== "undefined" ? raw : null;
125+NPM_GLOBAL_CONFIG_PATH_CACHE.set(cacheKey, resolved);
126+return resolved;
108127} catch {
128+NPM_GLOBAL_CONFIG_PATH_CACHE.set(cacheKey, null);
109129return null;
110130}
111131}
112132133+function buildNpmGlobalConfigPathCacheKey(
134+env: NodeJS.ProcessEnv,
135+scope: NpmFreshnessConfigScope,
136+): string {
137+const configFiles = uniqueStrings(
138+[
139+resolveScopedProjectNpmrc(scope),
140+resolveEnvPath(env, "NPM_CONFIG_USERCONFIG", "npm_config_userconfig") ??
141+resolveHomeNpmrc(env),
142+resolveEnvPath(env, "NPM_CONFIG_GLOBALCONFIG", "npm_config_globalconfig"),
143+resolveScopedGlobalNpmrc(scope),
144+].filter((file): file is string => Boolean(file)),
145+);
146+return JSON.stringify({
147+cwd: scope.npmConfigCwd?.trim() || safeCwd(),
148+prefix: scope.npmConfigPrefix?.trim() ?? "",
149+env: Object.fromEntries(
150+NPM_GLOBAL_CONFIG_PATH_CACHE_ENV_KEYS.map((key) => [key, env[key] ?? process.env[key] ?? ""]),
151+),
152+configFiles: configFiles.map((filePath) => ({
153+path: filePath,
154+signature: readFileSignature(filePath),
155+})),
156+});
157+}
158+159+function readFileSignature(filePath: string): string {
160+try {
161+const stat = fsSync.statSync(filePath);
162+return `${stat.mtimeMs}:${stat.size}`;
163+} catch {
164+return "missing";
165+}
166+}
167+168+function safeCwd(): string {
169+try {
170+return process.cwd();
171+} catch {
172+return "";
173+}
174+}
175+113176function resolveScopedProjectNpmrc(scope: NpmFreshnessConfigScope): string | null {
114177const scopedCwd = scope.npmConfigCwd?.trim();
115178if (scopedCwd) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。