@@ -1221,13 +1221,14 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (parsed.type === "npm") {
|
if (parsed.type === "npm") {
|
||||||
const installedPath = this.getNpmInstallPath(parsed, scope);
|
let installedPath = this.getNpmInstallPath(parsed, scope);
|
||||||
const needsInstall =
|
const needsInstall =
|
||||||
!existsSync(installedPath) ||
|
!existsSync(installedPath) ||
|
||||||
(parsed.pinned && !(await this.installedNpmMatchesPinnedVersion(parsed, installedPath)));
|
(parsed.pinned && !(await this.installedNpmMatchesPinnedVersion(parsed, installedPath)));
|
||||||
if (needsInstall) {
|
if (needsInstall) {
|
||||||
const installed = await installMissing();
|
const installed = await installMissing();
|
||||||
if (!installed) continue;
|
if (!installed) continue;
|
||||||
|
installedPath = this.getNpmInstallPath(parsed, scope);
|
||||||
}
|
}
|
||||||
metadata.baseDir = installedPath;
|
metadata.baseDir = installedPath;
|
||||||
this.collectPackageResources(installedPath, accumulator, filter, metadata);
|
this.collectPackageResources(installedPath, accumulator, filter, metadata);
|
||||||
@@ -1855,6 +1856,27 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
return this.globalNpmRoot;
|
return this.globalNpmRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getPnpmGlobalPackagePath(packageName: string): string | undefined {
|
||||||
|
const npmCommand = this.getNpmCommand();
|
||||||
|
const commandParts = [npmCommand.command, ...npmCommand.args];
|
||||||
|
const separatorIndex = commandParts.lastIndexOf("--");
|
||||||
|
const packageManagerCommand = separatorIndex >= 0 ? commandParts[separatorIndex + 1] : npmCommand.command;
|
||||||
|
const packageManagerName = packageManagerCommand
|
||||||
|
? basename(packageManagerCommand).replace(/\.(cmd|exe)$/i, "")
|
||||||
|
: "";
|
||||||
|
if (packageManagerName !== "pnpm") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = this.runNpmCommandSync(["list", "-g", "--depth", "0", "--json"]);
|
||||||
|
const entries = JSON.parse(output) as Array<{ dependencies?: Record<string, { path?: string }> }>;
|
||||||
|
for (const entry of entries) {
|
||||||
|
const path = entry.dependencies?.[packageName]?.path;
|
||||||
|
if (path) return path;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
private getNpmInstallPath(source: NpmSource, scope: SourceScope): string {
|
private getNpmInstallPath(source: NpmSource, scope: SourceScope): string {
|
||||||
if (scope === "temporary") {
|
if (scope === "temporary") {
|
||||||
return join(this.getTemporaryDir("npm"), "node_modules", source.name);
|
return join(this.getTemporaryDir("npm"), "node_modules", source.name);
|
||||||
@@ -1862,7 +1884,7 @@ export class DefaultPackageManager implements PackageManager {
|
|||||||
if (scope === "project") {
|
if (scope === "project") {
|
||||||
return join(this.cwd, CONFIG_DIR_NAME, "npm", "node_modules", source.name);
|
return join(this.cwd, CONFIG_DIR_NAME, "npm", "node_modules", source.name);
|
||||||
}
|
}
|
||||||
return join(this.getGlobalNpmRoot(), source.name);
|
return this.getPnpmGlobalPackagePath(source.name) ?? join(this.getGlobalNpmRoot(), source.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getGitInstallPath(source: GitSource, scope: SourceScope): string {
|
private getGitInstallPath(source: GitSource, scope: SourceScope): string {
|
||||||
|
|||||||
@@ -798,6 +798,105 @@ Content`,
|
|||||||
expect(packageManager.getInstalledPath("npm:@scope/pkg", "user")).toBeUndefined();
|
expect(packageManager.getInstalledPath("npm:@scope/pkg", "user")).toBeUndefined();
|
||||||
expect(runCommandSyncSpy).toHaveBeenNthCalledWith(2, "mise", ["exec", "node@22", "--", "npm", "root", "-g"]);
|
expect(runCommandSyncSpy).toHaveBeenNthCalledWith(2, "mise", ["exec", "node@22", "--", "npm", "root", "-g"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should resolve pnpm global package paths from pnpm list output", async () => {
|
||||||
|
settingsManager = SettingsManager.inMemory({
|
||||||
|
npmCommand: ["pnpm"],
|
||||||
|
packages: ["npm:pnpm-pkg"],
|
||||||
|
});
|
||||||
|
packageManager = new DefaultPackageManager({
|
||||||
|
cwd: tempDir,
|
||||||
|
agentDir,
|
||||||
|
settingsManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pnpmRoot = join(tempDir, "pnpm", "global", "v11");
|
||||||
|
const packagePath = join(pnpmRoot, "20-hash", "node_modules", "pnpm-pkg");
|
||||||
|
let installed = false;
|
||||||
|
|
||||||
|
vi.spyOn(packageManager as any, "runCommandSync").mockImplementation((...callArgs: unknown[]) => {
|
||||||
|
const [command, args] = callArgs as [string, string[]];
|
||||||
|
if (command !== "pnpm") {
|
||||||
|
throw new Error(`unexpected command ${command}`);
|
||||||
|
}
|
||||||
|
if (args.join(" ") === "list -g --depth 0 --json") {
|
||||||
|
return JSON.stringify([
|
||||||
|
{
|
||||||
|
path: pnpmRoot,
|
||||||
|
dependencies: installed ? { "pnpm-pkg": { version: "1.0.0", path: packagePath } } : {},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (args.join(" ") === "root -g") {
|
||||||
|
return pnpmRoot;
|
||||||
|
}
|
||||||
|
throw new Error(`unexpected args ${args.join(" ")}`);
|
||||||
|
});
|
||||||
|
const runCommandSpy = vi
|
||||||
|
.spyOn(packageManager as any, "runCommand")
|
||||||
|
.mockImplementation(async (...callArgs: unknown[]) => {
|
||||||
|
const [command, args] = callArgs as [string, string[]];
|
||||||
|
expect(command).toBe("pnpm");
|
||||||
|
expect(args).toEqual(["install", "-g", "pnpm-pkg"]);
|
||||||
|
mkdirSync(join(packagePath, "extensions"), { recursive: true });
|
||||||
|
writeFileSync(join(packagePath, "package.json"), JSON.stringify({ name: "pnpm-pkg", version: "1.0.0" }));
|
||||||
|
writeFileSync(join(packagePath, "extensions", "index.ts"), "export default function() {};");
|
||||||
|
installed = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const first = await packageManager.resolve();
|
||||||
|
const second = await packageManager.resolve();
|
||||||
|
|
||||||
|
expect(first.extensions.some((r) => r.path === join(packagePath, "extensions", "index.ts") && r.enabled)).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
second.extensions.some((r) => r.path === join(packagePath, "extensions", "index.ts") && r.enabled),
|
||||||
|
).toBe(true);
|
||||||
|
expect(runCommandSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(packageManager.getInstalledPath("npm:pnpm-pkg", "user")).toBe(packagePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should resolve wrapped pnpm global package paths from pnpm list output", () => {
|
||||||
|
settingsManager = SettingsManager.inMemory({
|
||||||
|
npmCommand: ["mise", "exec", "node@20", "--", "pnpm"],
|
||||||
|
});
|
||||||
|
packageManager = new DefaultPackageManager({
|
||||||
|
cwd: tempDir,
|
||||||
|
agentDir,
|
||||||
|
settingsManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pnpmRoot = join(tempDir, "pnpm", "global", "v11");
|
||||||
|
const packagePath = join(pnpmRoot, "20-hash", "node_modules", "pnpm-pkg");
|
||||||
|
mkdirSync(packagePath, { recursive: true });
|
||||||
|
|
||||||
|
vi.spyOn(packageManager as any, "runCommandSync").mockImplementation((...callArgs: unknown[]) => {
|
||||||
|
const [command, args] = callArgs as [string, string[]];
|
||||||
|
expect(command).toBe("mise");
|
||||||
|
if (args.join(" ") === "exec node@20 -- pnpm list -g --depth 0 --json") {
|
||||||
|
return JSON.stringify([{ path: pnpmRoot, dependencies: { "pnpm-pkg": { path: packagePath } } }]);
|
||||||
|
}
|
||||||
|
throw new Error(`unexpected args ${args.join(" ")}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(packageManager.getInstalledPath("npm:pnpm-pkg", "user")).toBe(packagePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail when pnpm global package list is malformed", () => {
|
||||||
|
settingsManager = SettingsManager.inMemory({
|
||||||
|
npmCommand: ["pnpm"],
|
||||||
|
});
|
||||||
|
packageManager = new DefaultPackageManager({
|
||||||
|
cwd: tempDir,
|
||||||
|
agentDir,
|
||||||
|
settingsManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.spyOn(packageManager as any, "runCommandSync").mockReturnValue("not json");
|
||||||
|
|
||||||
|
expect(() => packageManager.getInstalledPath("npm:pnpm-pkg", "user")).toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("source parsing", () => {
|
describe("source parsing", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user