Skip to content

Handle lock file 3 version when caching the typings ensuring we can reuse already installed packages #61730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions src/testRunner/unittests/tsserver/typingsInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,113 @@ describe("unittests:: tsserver:: typingsInstaller:: General functionality", () =
host.runPendingInstalls();
baselineTsserverLogs("typingsInstaller", "non expired cache entry", session);
});

it("expired cache entry (inferred project, should install typings) lockFile3", () => {
const file1 = {
path: "/home/src/projects/project/app.js",
content: "",
};
const packageJson = {
path: "/home/src/projects/project/package.json",
content: jsonToReadableText({
name: "test",
dependencies: {
jquery: "^3.1.0",
},
}),
};
const jquery = {
path: getPathForTypeScriptTypingInstallerCacheTest("node_modules/@types/jquery/index.d.ts"),
content: "declare const $: { x: number }",
};
const cacheConfig = {
path: getPathForTypeScriptTypingInstallerCacheTest("package.json"),
content: jsonToReadableText({
dependencies: {
"types-registry": "^0.1.317",
},
devDependencies: {
"@types/jquery": "^1.0.0",
},
}),
};
const cacheLockConfig = {
path: getPathForTypeScriptTypingInstallerCacheTest("package-lock.json"),
content: jsonToReadableText({
packages: {
"node_modules/@types/jquery": {
version: "1.0.0",
},
},
}),
};
const host = TestServerHost.createServerHost(
[file1, packageJson, jquery, cacheConfig, cacheLockConfig],
{ typingsInstallerTypesRegistry: "jquery" },
);
const session = new TestSession({
host,
useSingleInferredProject: true,
installAction: [jquery],
});
openFilesForSession([file1], session);
host.runPendingInstalls();
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("typingsInstaller", "expired cache entry lockFile3", session);
});

it("non-expired cache entry (inferred project, should not install typings) lockFile3", () => {
const file1 = {
path: "/home/src/projects/project/app.js",
content: "",
};
const packageJson = {
path: "/home/src/projects/project/package.json",
content: jsonToReadableText({
name: "test",
dependencies: {
jquery: "^3.1.0",
},
}),
};
const cacheConfig = {
path: getPathForTypeScriptTypingInstallerCacheTest("package.json"),
content: jsonToReadableText({
dependencies: {
"types-registry": "^0.1.317",
},
devDependencies: {
"@types/jquery": "^1.3.0",
},
}),
};
const cacheLockConfig = {
path: getPathForTypeScriptTypingInstallerCacheTest("package-lock.json"),
content: jsonToReadableText({
packages: {
"node_modules/@types/jquery": {
version: "1.3.0",
},
},
}),
};
const jquery = {
path: getPathForTypeScriptTypingInstallerCacheTest("node_modules/@types/jquery/index.d.ts"),
content: "declare const $: { x: number }",
};
const host = TestServerHost.createServerHost(
[file1, packageJson, cacheConfig, cacheLockConfig, jquery],
{ typingsInstallerTypesRegistry: "jquery" },
);
const session = new TestSession({
host,
useSingleInferredProject: true,
installAction: true,
});
openFilesForSession([file1], session);
host.runPendingInstalls();
baselineTsserverLogs("typingsInstaller", "non expired cache entry lockFile3", session);
});
});

describe("unittests:: tsserver:: typingsInstaller:: Validate package name:", () => {
Expand Down
15 changes: 11 additions & 4 deletions src/typingsInstallerCore/typingsInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ interface NpmConfig {
}

interface NpmLock {
dependencies: { [packageName: string]: { version: string; }; };
dependencies?: { [packageName: string]: { version: string; }; };
packages?: {
[nodeModulesAtTypesPackage: string]: { version: string; };
};
}

export interface Log {
Expand Down Expand Up @@ -304,9 +307,12 @@ export abstract class TypingsInstaller {
this.log.writeLine(`Loaded content of '${packageJson}':${stringifyIndented(npmConfig)}`);
this.log.writeLine(`Loaded content of '${packageLockJson}':${stringifyIndented(npmLock)}`);
}
if (npmConfig.devDependencies && npmLock.dependencies) {
if (npmConfig.devDependencies && (npmLock.packages || npmLock.dependencies)) {
for (const key in npmConfig.devDependencies) {
if (!hasProperty(npmLock.dependencies, key)) {
if (
(npmLock.packages && !hasProperty(npmLock.packages, `node_modules/${key}`)) ||
(npmLock.dependencies && !hasProperty(npmLock.dependencies, key))
) {
// if package in package.json but not package-lock.json, skip adding to cache so it is reinstalled on next use
continue;
}
Expand All @@ -333,7 +339,8 @@ export abstract class TypingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Adding entry into typings cache: '${packageName}' => '${typingFile}'`);
}
const info = getProperty(npmLock.dependencies, key);
const info = npmLock.packages && getProperty(npmLock.packages, `node_modules/${key}`) ||
getProperty(npmLock.dependencies!, key);
const version = info && info.version;
if (!version) {
continue;
Expand Down
Loading