| import { readPackageJSON } from 'pkg-types'; | ||
| import { createRequire } from 'node:module'; | ||
| import { resolve, normalize, join } from 'pathe'; | ||
| import { x } from 'tinyexec'; | ||
| import * as fs from 'node:fs'; | ||
| import { existsSync } from 'node:fs'; | ||
| import { readFile } from 'node:fs/promises'; | ||
| async function findup(cwd, match, options = {}) { | ||
| const segments = normalize(cwd).split("/"); | ||
| while (segments.length > 0) { | ||
| const path = segments.join("/") || "/"; | ||
| const result = await match(path); | ||
| if (result || !options.includeParentDirs) { | ||
| return result; | ||
| } | ||
| segments.pop(); | ||
| } | ||
| } | ||
| function cached(fn) { | ||
| let v; | ||
| return () => { | ||
| if (v === void 0) { | ||
| v = fn().then((r) => { | ||
| v = r; | ||
| return v; | ||
| }); | ||
| } | ||
| return v; | ||
| }; | ||
| } | ||
| const hasCorepack = cached(async () => { | ||
| if (globalThis.process?.versions?.webcontainer) { | ||
| return false; | ||
| } | ||
| try { | ||
| const { exitCode } = await x("corepack", ["--version"]); | ||
| return exitCode === 0; | ||
| } catch { | ||
| return false; | ||
| } | ||
| }); | ||
| async function executeCommand(command, args, options = {}) { | ||
| const xArgs = command === "npm" || command === "bun" || command === "deno" || !await hasCorepack() ? [command, args] : ["corepack", [command, ...args]]; | ||
| const { exitCode, stdout, stderr } = await x(xArgs[0], xArgs[1], { | ||
| nodeOptions: { | ||
| cwd: resolve(options.cwd || process.cwd()), | ||
| stdio: options.silent ? "pipe" : "inherit" | ||
| } | ||
| }); | ||
| if (exitCode !== 0) { | ||
| throw new Error( | ||
| `\`${xArgs.flat().join(" ")}\` failed.${options.silent ? ["", stdout, stderr].join("\n") : ""}` | ||
| ); | ||
| } | ||
| } | ||
| const NO_PACKAGE_MANAGER_DETECTED_ERROR_MSG = "No package manager auto-detected."; | ||
| async function resolveOperationOptions(options = {}) { | ||
| const cwd = options.cwd || process.cwd(); | ||
| const packageManager = (typeof options.packageManager === "string" ? packageManagers.find((pm) => pm.name === options.packageManager) : options.packageManager) || await detectPackageManager(options.cwd || process.cwd()); | ||
| if (!packageManager) { | ||
| throw new Error(NO_PACKAGE_MANAGER_DETECTED_ERROR_MSG); | ||
| } | ||
| return { | ||
| cwd, | ||
| silent: options.silent ?? false, | ||
| packageManager, | ||
| dev: options.dev ?? false, | ||
| workspace: options.workspace, | ||
| global: options.global ?? false, | ||
| dry: options.dry ?? false | ||
| }; | ||
| } | ||
| function getWorkspaceArgs(options) { | ||
| if (!options.workspace) { | ||
| return []; | ||
| } | ||
| const workspacePkg = typeof options.workspace === "string" && options.workspace !== "" ? options.workspace : void 0; | ||
| if (options.packageManager.name === "pnpm") { | ||
| return workspacePkg ? ["--filter", workspacePkg] : ["--workspace-root"]; | ||
| } | ||
| if (options.packageManager.name === "npm") { | ||
| return workspacePkg ? ["-w", workspacePkg] : ["--workspaces"]; | ||
| } | ||
| if (options.packageManager.name === "yarn") { | ||
| if (!options.packageManager.majorVersion || options.packageManager.majorVersion === "1") { | ||
| return workspacePkg ? ["--cwd", workspacePkg] : ["-W"]; | ||
| } else { | ||
| return workspacePkg ? ["workspace", workspacePkg] : []; | ||
| } | ||
| } | ||
| return []; | ||
| } | ||
| function getWorkspaceArgs2(options) { | ||
| if (!options.workspace) { | ||
| return []; | ||
| } | ||
| const workspacePkg = typeof options.workspace === "string" && options.workspace !== "" ? options.workspace : void 0; | ||
| if (options.packageManager === "pnpm") { | ||
| return workspacePkg ? ["--filter", workspacePkg] : ["--workspace-root"]; | ||
| } | ||
| if (options.packageManager === "npm") { | ||
| return workspacePkg ? ["-w", workspacePkg] : ["--workspaces"]; | ||
| } | ||
| if (options.packageManager === "yarn") { | ||
| if (options.yarnBerry) { | ||
| return workspacePkg ? ["workspace", workspacePkg] : []; | ||
| } else { | ||
| return workspacePkg ? ["--cwd", workspacePkg] : ["-W"]; | ||
| } | ||
| } | ||
| return []; | ||
| } | ||
| function fmtCommand(args) { | ||
| return args.filter(Boolean).map((arg, i) => i > 0 && arg.includes(" ") ? `"${arg}"` : arg).join(" "); | ||
| } | ||
| function doesDependencyExist(name, options) { | ||
| const require = createRequire( | ||
| options.cwd.endsWith("/") ? options.cwd : options.cwd + "/" | ||
| ); | ||
| try { | ||
| const resolvedPath = require.resolve(name); | ||
| return resolvedPath.startsWith(options.cwd); | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
| function parsePackageManagerField(packageManager) { | ||
| const [name, _version] = (packageManager || "").split("@"); | ||
| const [version, buildMeta] = _version?.split("+") || []; | ||
| if (name && name !== "-" && /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name)) { | ||
| return { name, version, buildMeta }; | ||
| } | ||
| const sanitized = name.replace(/\W+/g, ""); | ||
| const warnings = [ | ||
| `Abnormal characters found in \`packageManager\` field, sanitizing from \`${name}\` to \`${sanitized}\`` | ||
| ]; | ||
| return { | ||
| name: sanitized, | ||
| version, | ||
| buildMeta, | ||
| warnings | ||
| }; | ||
| } | ||
| const packageManagers = [ | ||
| { | ||
| name: "npm", | ||
| command: "npm", | ||
| lockFile: "package-lock.json" | ||
| }, | ||
| { | ||
| name: "pnpm", | ||
| command: "pnpm", | ||
| lockFile: "pnpm-lock.yaml", | ||
| files: ["pnpm-workspace.yaml"] | ||
| }, | ||
| { | ||
| name: "bun", | ||
| command: "bun", | ||
| lockFile: ["bun.lockb", "bun.lock"] | ||
| }, | ||
| { | ||
| name: "yarn", | ||
| command: "yarn", | ||
| lockFile: "yarn.lock", | ||
| files: [".yarnrc.yml"] | ||
| }, | ||
| { | ||
| name: "deno", | ||
| command: "deno", | ||
| lockFile: "deno.lock", | ||
| files: ["deno.json"] | ||
| } | ||
| ]; | ||
| async function detectPackageManager(cwd, options = {}) { | ||
| const detected = await findup( | ||
| resolve(cwd || "."), | ||
| async (path) => { | ||
| if (!options.ignorePackageJSON) { | ||
| const packageJSONPath = join(path, "package.json"); | ||
| if (existsSync(packageJSONPath)) { | ||
| const packageJSON = JSON.parse( | ||
| await readFile(packageJSONPath, "utf8") | ||
| ); | ||
| if (packageJSON?.packageManager) { | ||
| const { | ||
| name, | ||
| version = "0.0.0", | ||
| buildMeta, | ||
| warnings | ||
| } = parsePackageManagerField(packageJSON.packageManager); | ||
| if (name) { | ||
| const majorVersion = version.split(".")[0]; | ||
| const packageManager = packageManagers.find( | ||
| (pm) => pm.name === name && pm.majorVersion === majorVersion | ||
| ) || packageManagers.find((pm) => pm.name === name); | ||
| return { | ||
| name, | ||
| command: name, | ||
| version, | ||
| majorVersion, | ||
| buildMeta, | ||
| warnings, | ||
| files: packageManager?.files, | ||
| lockFile: packageManager?.lockFile | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| const denoJSONPath = join(path, "deno.json"); | ||
| if (existsSync(denoJSONPath)) { | ||
| return packageManagers.find((pm) => pm.name === "deno"); | ||
| } | ||
| } | ||
| if (!options.ignoreLockFile) { | ||
| for (const packageManager of packageManagers) { | ||
| const detectionsFiles = [ | ||
| packageManager.lockFile, | ||
| packageManager.files | ||
| ].flat().filter(Boolean); | ||
| if (detectionsFiles.some((file) => existsSync(resolve(path, file)))) { | ||
| return { | ||
| ...packageManager | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| includeParentDirs: options.includeParentDirs ?? true | ||
| } | ||
| ); | ||
| if (!detected && !options.ignoreArgv) { | ||
| const scriptArg = process.argv[1]; | ||
| if (scriptArg) { | ||
| for (const packageManager of packageManagers) { | ||
| const re = new RegExp(`[/\\\\]\\.?${packageManager.command}`); | ||
| if (re.test(scriptArg)) { | ||
| return packageManager; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return detected; | ||
| } | ||
| async function installDependencies(options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const pmToFrozenLockfileInstallCommand = { | ||
| npm: ["ci"], | ||
| yarn: ["install", "--immutable"], | ||
| bun: ["install", "--frozen-lockfile"], | ||
| pnpm: ["install", "--frozen-lockfile"], | ||
| deno: ["install", "--frozen"] | ||
| }; | ||
| const commandArgs = options.frozenLockFile ? pmToFrozenLockfileInstallCommand[resolvedOptions.packageManager.name] : ["install"]; | ||
| if (!resolvedOptions.dry) { | ||
| await executeCommand(resolvedOptions.packageManager.command, commandArgs, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| } | ||
| return { | ||
| exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args: commandArgs | ||
| } | ||
| }; | ||
| } | ||
| async function addDependency(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const names = Array.isArray(name) ? name : [name]; | ||
| if (resolvedOptions.packageManager.name === "deno") { | ||
| for (let i = 0; i < names.length; i++) { | ||
| if (!/^(npm|jsr|file):.+$/.test(names[i])) { | ||
| names[i] = `npm:${names[i]}`; | ||
| } | ||
| } | ||
| } | ||
| if (names.length === 0) { | ||
| return {}; | ||
| } | ||
| const args = (resolvedOptions.packageManager.name === "yarn" ? [ | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| // Global is not supported in berry: yarnpkg/berry#821 | ||
| resolvedOptions.global && resolvedOptions.packageManager.majorVersion === "1" ? "global" : "", | ||
| "add", | ||
| resolvedOptions.dev ? "-D" : "", | ||
| ...names | ||
| ] : [ | ||
| resolvedOptions.packageManager.name === "npm" ? "install" : "add", | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| resolvedOptions.dev ? "-D" : "", | ||
| resolvedOptions.global ? "-g" : "", | ||
| ...names | ||
| ]).filter(Boolean); | ||
| if (!resolvedOptions.dry) { | ||
| await executeCommand(resolvedOptions.packageManager.command, args, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| } | ||
| if (!resolvedOptions.dry && options.installPeerDependencies) { | ||
| const existingPkg = await readPackageJSON(resolvedOptions.cwd); | ||
| const peerDeps = []; | ||
| const peerDevDeps = []; | ||
| for (const _name of names) { | ||
| const pkgName = _name.match(/^(.[^@]+)/)?.[0]; | ||
| const pkg = await readPackageJSON(pkgName, { | ||
| url: resolvedOptions.cwd | ||
| }).catch(() => ({})); | ||
| if (!pkg.peerDependencies || pkg.name !== pkgName) { | ||
| continue; | ||
| } | ||
| for (const [peerDependency, version] of Object.entries( | ||
| pkg.peerDependencies | ||
| )) { | ||
| if (pkg.peerDependenciesMeta?.[peerDependency]?.optional) { | ||
| continue; | ||
| } | ||
| if (existingPkg.dependencies?.[peerDependency] || existingPkg.devDependencies?.[peerDependency]) { | ||
| continue; | ||
| } | ||
| const isDev = pkg.peerDependenciesMeta?.[peerDependency]?.dev; | ||
| (isDev ? peerDevDeps : peerDeps).push(`${peerDependency}@${version}`); | ||
| } | ||
| } | ||
| if (peerDeps.length > 0) { | ||
| await addDependency(peerDeps, { ...resolvedOptions }); | ||
| } | ||
| if (peerDevDeps.length > 0) { | ||
| await addDevDependency(peerDevDeps, { ...resolvedOptions }); | ||
| } | ||
| } | ||
| return { | ||
| exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args | ||
| } | ||
| }; | ||
| } | ||
| async function addDevDependency(name, options = {}) { | ||
| return await addDependency(name, { ...options, dev: true }); | ||
| } | ||
| async function removeDependency(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const names = Array.isArray(name) ? name : [name]; | ||
| if (names.length === 0) { | ||
| return {}; | ||
| } | ||
| const args = (resolvedOptions.packageManager.name === "yarn" ? [ | ||
| // Global is not supported in berry: yarnpkg/berry#821 | ||
| resolvedOptions.global && resolvedOptions.packageManager.majorVersion === "1" ? "global" : "", | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| "remove", | ||
| resolvedOptions.dev ? "-D" : "", | ||
| resolvedOptions.global ? "-g" : "", | ||
| ...names | ||
| ] : [ | ||
| resolvedOptions.packageManager.name === "npm" ? "uninstall" : "remove", | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| resolvedOptions.dev ? "-D" : "", | ||
| resolvedOptions.global ? "-g" : "", | ||
| ...names | ||
| ]).filter(Boolean); | ||
| if (!resolvedOptions.dry) { | ||
| await executeCommand(resolvedOptions.packageManager.command, args, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| } | ||
| return { | ||
| exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args | ||
| } | ||
| }; | ||
| } | ||
| async function ensureDependencyInstalled(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const dependencyExists = doesDependencyExist(name, resolvedOptions); | ||
| if (dependencyExists) { | ||
| return true; | ||
| } | ||
| await addDependency(name, resolvedOptions); | ||
| } | ||
| async function dedupeDependencies(options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const isSupported = !["bun", "deno"].includes( | ||
| resolvedOptions.packageManager.name | ||
| ); | ||
| const recreateLockfile = options.recreateLockfile ?? !isSupported; | ||
| if (recreateLockfile) { | ||
| const lockfiles = Array.isArray(resolvedOptions.packageManager.lockFile) ? resolvedOptions.packageManager.lockFile : [resolvedOptions.packageManager.lockFile]; | ||
| for (const lockfile of lockfiles) { | ||
| if (lockfile) | ||
| fs.rmSync(resolve(resolvedOptions.cwd, lockfile), { force: true }); | ||
| } | ||
| return await installDependencies(resolvedOptions); | ||
| } | ||
| if (isSupported) { | ||
| const isyarnv1 = resolvedOptions.packageManager.name === "yarn" && resolvedOptions.packageManager.majorVersion === "1"; | ||
| if (!resolvedOptions.dry) { | ||
| await executeCommand( | ||
| resolvedOptions.packageManager.command, | ||
| [isyarnv1 ? "install" : "dedupe"], | ||
| { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| } | ||
| ); | ||
| } | ||
| return { | ||
| exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args: [isyarnv1 ? "install" : "dedupe"] | ||
| } | ||
| }; | ||
| } | ||
| throw new Error( | ||
| `Deduplication is not supported for ${resolvedOptions.packageManager.name}` | ||
| ); | ||
| } | ||
| async function runScript(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const args = [ | ||
| resolvedOptions.packageManager.name === "deno" ? "task" : "run", | ||
| name, | ||
| ...options.args || [] | ||
| ]; | ||
| if (!resolvedOptions.dry) { | ||
| await executeCommand(resolvedOptions.packageManager.command, args, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| } | ||
| return { | ||
| exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args | ||
| } | ||
| }; | ||
| } | ||
| export { addDependency as a, addDevDependency as b, dedupeDependencies as c, detectPackageManager as d, ensureDependencyInstalled as e, fmtCommand as f, getWorkspaceArgs2 as g, runScript as h, installDependencies as i, packageManagers as p, removeDependency as r }; |
+22
-6
@@ -5,3 +5,3 @@ #!/usr/bin/env node | ||
| import { consola } from 'consola'; | ||
| import { a as addDependency, i as installDependencies, r as removeDependency, d as detectPackageManager, c as dedupeDependencies, f as runScript } from './shared/nypm.Bcw9TJOu.mjs'; | ||
| import { a as addDependency, i as installDependencies, r as removeDependency, d as detectPackageManager, c as dedupeDependencies, h as runScript } from './shared/nypm.COyAVAIB.mjs'; | ||
| import 'pkg-types'; | ||
@@ -14,3 +14,3 @@ import 'node:module'; | ||
| const name = "nypm"; | ||
| const version = "0.6.0"; | ||
| const version = "0.6.1"; | ||
| const description = "Unified Package Manager for Node.js"; | ||
@@ -30,2 +30,6 @@ | ||
| description: "Run in silent mode" | ||
| }, | ||
| dry: { | ||
| type: "boolean", | ||
| description: "Run in dry run mode (does not execute commands)" | ||
| } | ||
@@ -60,3 +64,4 @@ }; | ||
| run: async ({ args }) => { | ||
| await (args._.length > 0 ? addDependency(args._, args) : installDependencies(args)); | ||
| const result = await (args._.length > 0 ? addDependency(args._, args) : installDependencies(args)); | ||
| handleRes(result, args); | ||
| } | ||
@@ -77,3 +82,4 @@ }); | ||
| run: async ({ args }) => { | ||
| await removeDependency(args.name, args); | ||
| const result = await removeDependency(args._, args); | ||
| handleRes(result, args); | ||
| } | ||
@@ -127,3 +133,4 @@ }); | ||
| run: async ({ args }) => { | ||
| await dedupeDependencies(args); | ||
| const result = await dedupeDependencies(args); | ||
| handleRes(result, args); | ||
| } | ||
@@ -144,3 +151,7 @@ }); | ||
| run: async ({ args }) => { | ||
| await runScript(args.name, args); | ||
| const result = await runScript(args.name, { | ||
| ...args, | ||
| args: args._.slice(1) | ||
| }); | ||
| handleRes(result, args); | ||
| } | ||
@@ -168,1 +179,6 @@ }); | ||
| runMain(main); | ||
| function handleRes(result, args) { | ||
| if (args.dry && !args.silent) { | ||
| consola.log(`${result.exec?.command} ${result.exec?.args.join(" ")}`); | ||
| } | ||
| } |
+69
-9
@@ -19,3 +19,11 @@ type PackageManagerName = "npm" | "yarn" | "pnpm" | "bun" | "deno"; | ||
| global?: boolean; | ||
| /** Do not execute actual command */ | ||
| dry?: boolean; | ||
| }; | ||
| type OperationResult = { | ||
| exec?: { | ||
| command: string; | ||
| args: string[]; | ||
| }; | ||
| }; | ||
@@ -67,5 +75,5 @@ type DetectPackageManagerOptions = { | ||
| */ | ||
| declare function installDependencies(options?: Pick<OperationOptions, "cwd" | "silent" | "packageManager"> & { | ||
| declare function installDependencies(options?: Pick<OperationOptions, "cwd" | "silent" | "packageManager" | "dry"> & { | ||
| frozenLockFile?: boolean; | ||
| }): Promise<void>; | ||
| }): Promise<OperationResult>; | ||
| /** | ||
@@ -83,3 +91,3 @@ * Adds dependency to the project. | ||
| */ | ||
| declare function addDependency(name: string | string[], options?: OperationOptions): Promise<void>; | ||
| declare function addDependency(name: string | string[], options?: OperationOptions): Promise<OperationResult>; | ||
| /** | ||
@@ -97,3 +105,3 @@ * Adds dev dependency to the project. | ||
| */ | ||
| declare function addDevDependency(name: string | string[], options?: Omit<OperationOptions, "dev">): Promise<void>; | ||
| declare function addDevDependency(name: string | string[], options?: Omit<OperationOptions, "dev">): Promise<OperationResult>; | ||
| /** | ||
@@ -111,3 +119,3 @@ * Removes dependency from the project. | ||
| */ | ||
| declare function removeDependency(name: string, options?: OperationOptions): Promise<void>; | ||
| declare function removeDependency(name: string | string[], options?: OperationOptions): Promise<OperationResult>; | ||
| /** | ||
@@ -123,7 +131,59 @@ * Ensures dependency is installed. | ||
| declare function ensureDependencyInstalled(name: string, options?: Pick<OperationOptions, "cwd" | "dev" | "workspace">): Promise<true | undefined>; | ||
| declare function dedupeDependencies(options?: Pick<OperationOptions, "cwd" | "silent"> & { | ||
| /** | ||
| * Dedupe dependencies in the project. | ||
| * | ||
| * @param options - Options to pass to the API call. | ||
| * @param options.cwd - The directory to run the command in. | ||
| * @param options.packageManager - The package manager info to use (auto-detected). | ||
| * @param options.silent - Whether to run the command in silent mode. | ||
| * @param options.recreateLockfile - Whether to recreate the lockfile instead of deduping. | ||
| */ | ||
| declare function dedupeDependencies(options?: Pick<OperationOptions, "cwd" | "silent" | "packageManager" | "dry"> & { | ||
| recreateLockfile?: boolean; | ||
| }): Promise<void>; | ||
| declare function runScript(name: string, options?: Pick<OperationOptions, "cwd" | "silent" | "packageManager">): Promise<void>; | ||
| }): Promise<OperationResult>; | ||
| /** | ||
| * Runs a script defined in the package.json file. | ||
| * | ||
| * @param name - Name of the script to run. | ||
| * @param options - Options to pass to the API call. | ||
| * @param options.cwd - The directory to run the command in. | ||
| * @param options.silent - Whether to run the command in silent mode. | ||
| * @param options.packageManager - The package manager info to use (auto-detected). | ||
| */ | ||
| declare function runScript(name: string, options?: Pick<OperationOptions, "cwd" | "silent" | "packageManager" | "dry"> & { | ||
| args?: string[]; | ||
| }): Promise<OperationResult>; | ||
| export { type DetectPackageManagerOptions, type OperationOptions, type PackageManager, type PackageManagerName, addDependency, addDevDependency, dedupeDependencies, detectPackageManager, ensureDependencyInstalled, installDependencies, packageManagers, removeDependency, runScript }; | ||
| /** | ||
| * Get the command to install dependencies with the package manager. | ||
| */ | ||
| declare function installDependenciesCommand(packageManager: PackageManagerName, options?: { | ||
| short?: boolean; | ||
| frozenLockFile?: boolean; | ||
| }): string; | ||
| /** | ||
| * Get the command to add a dependency with the package manager. | ||
| */ | ||
| declare function addDependencyCommand(packageManager: PackageManagerName, name: string | string[], options?: { | ||
| dev?: boolean; | ||
| global?: boolean; | ||
| yarnBerry?: boolean; | ||
| workspace?: boolean | string; | ||
| short?: boolean; | ||
| }): string; | ||
| /** | ||
| * Get the command to run a script with the package manager. | ||
| */ | ||
| declare function runScriptCommand(packageManager: PackageManagerName, name: string, options?: { | ||
| args?: string[]; | ||
| }): string; | ||
| /** | ||
| * Get the command to download and execute a package with the package manager. | ||
| */ | ||
| declare function dlxCommand(packageManager: PackageManagerName, name: string, options: { | ||
| args?: string[]; | ||
| short?: boolean; | ||
| }): string; | ||
| export { addDependency, addDependencyCommand, addDevDependency, dedupeDependencies, detectPackageManager, dlxCommand, ensureDependencyInstalled, installDependencies, installDependenciesCommand, packageManagers, removeDependency, runScript, runScriptCommand }; | ||
| export type { DetectPackageManagerOptions, OperationOptions, OperationResult, PackageManager, PackageManagerName }; |
+64
-1
@@ -1,2 +0,3 @@ | ||
| export { a as addDependency, b as addDevDependency, c as dedupeDependencies, d as detectPackageManager, e as ensureDependencyInstalled, i as installDependencies, p as packageManagers, r as removeDependency, f as runScript } from './shared/nypm.Bcw9TJOu.mjs'; | ||
| import { f as fmtCommand, g as getWorkspaceArgs2 } from './shared/nypm.COyAVAIB.mjs'; | ||
| export { a as addDependency, b as addDevDependency, c as dedupeDependencies, d as detectPackageManager, e as ensureDependencyInstalled, i as installDependencies, p as packageManagers, r as removeDependency, h as runScript } from './shared/nypm.COyAVAIB.mjs'; | ||
| import 'pkg-types'; | ||
@@ -8,1 +9,63 @@ import 'node:module'; | ||
| import 'node:fs/promises'; | ||
| function installDependenciesCommand(packageManager, options = {}) { | ||
| const installCmd = options.short ? "i" : "install"; | ||
| const pmToFrozenLockfileInstallCommand = { | ||
| npm: ["ci"], | ||
| yarn: [installCmd, "--immutable"], | ||
| bun: [installCmd, "--frozen-lockfile"], | ||
| pnpm: [installCmd, "--frozen-lockfile"], | ||
| deno: [installCmd, "--frozen"] | ||
| }; | ||
| const commandArgs = options.frozenLockFile ? pmToFrozenLockfileInstallCommand[packageManager] : [installCmd]; | ||
| return fmtCommand([packageManager, ...commandArgs]); | ||
| } | ||
| function addDependencyCommand(packageManager, name, options = {}) { | ||
| const names = Array.isArray(name) ? name : [name]; | ||
| if (packageManager === "deno") { | ||
| for (let i = 0; i < names.length; i++) { | ||
| if (!/^(npm|jsr|file):.+$/.test(names[i])) { | ||
| names[i] = `npm:${names[i]}`; | ||
| } | ||
| } | ||
| } | ||
| const args = (packageManager === "yarn" ? [ | ||
| ...getWorkspaceArgs2({ packageManager, ...options }), | ||
| // Global is not supported in berry: yarnpkg/berry#821 | ||
| options.global && !options.yarnBerry ? "global" : "", | ||
| "add", | ||
| options.dev ? options.short ? "-D" : "--dev" : "", | ||
| ...names | ||
| ] : [ | ||
| packageManager === "npm" ? options.short ? "i" : "install" : "add", | ||
| ...getWorkspaceArgs2({ packageManager, ...options }), | ||
| options.dev ? options.short ? "-D" : "--dev" : "", | ||
| options.global ? "-g" : "", | ||
| ...names | ||
| ]).filter(Boolean); | ||
| return fmtCommand([packageManager, ...args]); | ||
| } | ||
| function runScriptCommand(packageManager, name, options = {}) { | ||
| const args = [ | ||
| packageManager === "deno" ? "task" : "run", | ||
| name, | ||
| ...options.args || [] | ||
| ]; | ||
| return fmtCommand([packageManager, ...args]); | ||
| } | ||
| function dlxCommand(packageManager, name, options) { | ||
| const pmToDlxCommand = { | ||
| npm: options.short ? "npx" : "npm dlx", | ||
| yarn: "yarn dlx", | ||
| pnpm: options.short ? "pnpx" : "pnpm dlx", | ||
| bun: options.short ? "bunx" : "bun x", | ||
| deno: "deno run -A" | ||
| }; | ||
| const command = pmToDlxCommand[packageManager]; | ||
| if (packageManager === "deno" && !name.startsWith("npm:")) { | ||
| name = `npm:${name}`; | ||
| } | ||
| return fmtCommand([command, name, ...options.args || []]); | ||
| } | ||
| export { addDependencyCommand, dlxCommand, installDependenciesCommand, runScriptCommand }; |
+17
-17
| { | ||
| "name": "nypm", | ||
| "version": "0.6.0", | ||
| "version": "0.6.1", | ||
| "description": "Unified Package Manager for Node.js", | ||
@@ -23,21 +23,21 @@ "repository": "unjs/nypm", | ||
| "citty": "^0.1.6", | ||
| "consola": "^3.4.0", | ||
| "consola": "^3.4.2", | ||
| "pathe": "^2.0.3", | ||
| "pkg-types": "^2.0.0", | ||
| "tinyexec": "^0.3.2" | ||
| "pkg-types": "^2.2.0", | ||
| "tinyexec": "^1.0.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^22.13.5", | ||
| "@vitest/coverage-v8": "^3.0.7", | ||
| "automd": "^0.3.12", | ||
| "changelogen": "^0.5.7", | ||
| "eslint": "^9.21.0", | ||
| "eslint-config-unjs": "^0.4.2", | ||
| "jiti": "^2.4.2", | ||
| "prettier": "^3.5.2", | ||
| "std-env": "^3.8.0", | ||
| "typescript": "^5.7.3", | ||
| "ufo": "^1.5.4", | ||
| "unbuild": "^3.3.1", | ||
| "vitest": "^3.0.7" | ||
| "@types/node": "^24.1.0", | ||
| "@vitest/coverage-v8": "^3.2.4", | ||
| "automd": "^0.4.0", | ||
| "changelogen": "^0.6.2", | ||
| "eslint": "^9.32.0", | ||
| "eslint-config-unjs": "^0.5.0", | ||
| "jiti": "^2.5.1", | ||
| "prettier": "^3.6.2", | ||
| "std-env": "^3.9.0", | ||
| "typescript": "^5.8.3", | ||
| "ufo": "^1.6.1", | ||
| "unbuild": "^3.6.0", | ||
| "vitest": "^3.2.4" | ||
| }, | ||
@@ -44,0 +44,0 @@ "engines": { |
+19
-3
@@ -13,3 +13,3 @@ # 🌈 nypm | ||
| ✅ Supports [npm](https://docs.npmjs.com/cli/v10/commands/npm), [yarn](https://yarnpkg.com/), [pnpm](https://pnpm.io/) and [bun](https://bun.sh/package-manager) and [deno](https://deno.com/) out of the box with a unified API. | ||
| ✅ Supports [npm](https://docs.npmjs.com/cli/v10/commands/npm), [yarn](https://yarnpkg.com/), [pnpm](https://pnpm.io/), [bun](https://bun.sh/package-manager) and [deno](https://deno.com/) out of the box with a unified API. | ||
@@ -111,2 +111,5 @@ ✅ Provides an **API interface** to interact with package managers. | ||
| > [!NOTE] | ||
| > For `bun` and `deno` it will remove the lockfile and reinstall all dependencies. | ||
| ### `runScript(name, options)` | ||
@@ -116,5 +119,18 @@ | ||
| > [!NOTE] | ||
| > For `bun` and `deno` it will remove the lockfile and reinstall all dependencies. | ||
| ### `installDependenciesCommand(<pm>, { short?, frozenLockFile? })` | ||
| Get the command to install dependencies with the package manager. | ||
| ### `addDependencyCommand(<pm>, <name>, { dev?, global?, workspace?, yarnBerry?, short? })` | ||
| Get the command to add a dependency with the package manager. | ||
| ### `runScriptCommand(<pm>, <name>, { args? })` | ||
| Get the command to run a script with the package manager. | ||
| ### `dlxCommand(<pm>, <name>, { args?, short? })` | ||
| Get the command to download and execute a package with the package manager. | ||
| ## 💻 Development | ||
@@ -121,0 +137,0 @@ |
| import { readPackageJSON } from 'pkg-types'; | ||
| import { createRequire } from 'node:module'; | ||
| import { resolve, normalize, join } from 'pathe'; | ||
| import { x } from 'tinyexec'; | ||
| import * as fs from 'node:fs'; | ||
| import { existsSync } from 'node:fs'; | ||
| import { readFile } from 'node:fs/promises'; | ||
| async function findup(cwd, match, options = {}) { | ||
| const segments = normalize(cwd).split("/"); | ||
| while (segments.length > 0) { | ||
| const path = segments.join("/") || "/"; | ||
| const result = await match(path); | ||
| if (result || !options.includeParentDirs) { | ||
| return result; | ||
| } | ||
| segments.pop(); | ||
| } | ||
| } | ||
| function cached(fn) { | ||
| let v; | ||
| return () => { | ||
| if (v === void 0) { | ||
| v = fn().then((r) => { | ||
| v = r; | ||
| return v; | ||
| }); | ||
| } | ||
| return v; | ||
| }; | ||
| } | ||
| const hasCorepack = cached(async () => { | ||
| if (globalThis.process?.versions?.webcontainer) { | ||
| return false; | ||
| } | ||
| try { | ||
| const { exitCode } = await x("corepack", ["--version"]); | ||
| return exitCode === 0; | ||
| } catch { | ||
| return false; | ||
| } | ||
| }); | ||
| async function executeCommand(command, args, options = {}) { | ||
| const xArgs = command === "npm" || command === "bun" || command === "deno" || !await hasCorepack() ? [command, args] : ["corepack", [command, ...args]]; | ||
| const { exitCode, stdout, stderr } = await x(xArgs[0], xArgs[1], { | ||
| nodeOptions: { | ||
| cwd: resolve(options.cwd || process.cwd()), | ||
| stdio: options.silent ? "pipe" : "inherit" | ||
| } | ||
| }); | ||
| if (exitCode !== 0) { | ||
| throw new Error( | ||
| `\`${xArgs.flat().join(" ")}\` failed.${options.silent ? ["", stdout, stderr].join("\n") : ""}` | ||
| ); | ||
| } | ||
| } | ||
| const NO_PACKAGE_MANAGER_DETECTED_ERROR_MSG = "No package manager auto-detected."; | ||
| async function resolveOperationOptions(options = {}) { | ||
| const cwd = options.cwd || process.cwd(); | ||
| const packageManager = (typeof options.packageManager === "string" ? packageManagers.find((pm) => pm.name === options.packageManager) : options.packageManager) || await detectPackageManager(options.cwd || process.cwd()); | ||
| if (!packageManager) { | ||
| throw new Error(NO_PACKAGE_MANAGER_DETECTED_ERROR_MSG); | ||
| } | ||
| return { | ||
| cwd, | ||
| silent: options.silent ?? false, | ||
| packageManager, | ||
| dev: options.dev ?? false, | ||
| workspace: options.workspace, | ||
| global: options.global ?? false | ||
| }; | ||
| } | ||
| function getWorkspaceArgs(options) { | ||
| if (!options.workspace) { | ||
| return []; | ||
| } | ||
| const workspacePkg = typeof options.workspace === "string" && options.workspace !== "" ? options.workspace : void 0; | ||
| if (options.packageManager.name === "pnpm") { | ||
| return workspacePkg ? ["--filter", workspacePkg] : ["--workspace-root"]; | ||
| } | ||
| if (options.packageManager.name === "npm") { | ||
| return workspacePkg ? ["-w", workspacePkg] : ["--workspaces"]; | ||
| } | ||
| if (options.packageManager.name === "yarn") { | ||
| if (!options.packageManager.majorVersion || options.packageManager.majorVersion === "1") { | ||
| return workspacePkg ? ["--cwd", workspacePkg] : ["-W"]; | ||
| } else { | ||
| return workspacePkg ? ["workspace", workspacePkg] : []; | ||
| } | ||
| } | ||
| return []; | ||
| } | ||
| function doesDependencyExist(name, options) { | ||
| const require = createRequire( | ||
| options.cwd.endsWith("/") ? options.cwd : options.cwd + "/" | ||
| ); | ||
| try { | ||
| const resolvedPath = require.resolve(name); | ||
| return resolvedPath.startsWith(options.cwd); | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
| function parsePackageManagerField(packageManager) { | ||
| const [name, _version] = (packageManager || "").split("@"); | ||
| const [version, buildMeta] = _version?.split("+") || []; | ||
| if (name && name !== "-" && /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name)) { | ||
| return { name, version, buildMeta }; | ||
| } | ||
| const sanitized = name.replace(/\W+/g, ""); | ||
| const warnings = [ | ||
| `Abnormal characters found in \`packageManager\` field, sanitizing from \`${name}\` to \`${sanitized}\`` | ||
| ]; | ||
| return { | ||
| name: sanitized, | ||
| version, | ||
| buildMeta, | ||
| warnings | ||
| }; | ||
| } | ||
| const packageManagers = [ | ||
| { | ||
| name: "npm", | ||
| command: "npm", | ||
| lockFile: "package-lock.json" | ||
| }, | ||
| { | ||
| name: "pnpm", | ||
| command: "pnpm", | ||
| lockFile: "pnpm-lock.yaml", | ||
| files: ["pnpm-workspace.yaml"] | ||
| }, | ||
| { | ||
| name: "bun", | ||
| command: "bun", | ||
| lockFile: ["bun.lockb", "bun.lock"] | ||
| }, | ||
| { | ||
| name: "yarn", | ||
| command: "yarn", | ||
| lockFile: "yarn.lock", | ||
| files: [".yarnrc.yml"] | ||
| }, | ||
| { | ||
| name: "deno", | ||
| command: "deno", | ||
| lockFile: "deno.lock", | ||
| files: ["deno.json"] | ||
| } | ||
| ]; | ||
| async function detectPackageManager(cwd, options = {}) { | ||
| const detected = await findup( | ||
| resolve(cwd || "."), | ||
| async (path) => { | ||
| if (!options.ignorePackageJSON) { | ||
| const packageJSONPath = join(path, "package.json"); | ||
| if (existsSync(packageJSONPath)) { | ||
| const packageJSON = JSON.parse( | ||
| await readFile(packageJSONPath, "utf8") | ||
| ); | ||
| if (packageJSON?.packageManager) { | ||
| const { | ||
| name, | ||
| version = "0.0.0", | ||
| buildMeta, | ||
| warnings | ||
| } = parsePackageManagerField(packageJSON.packageManager); | ||
| if (name) { | ||
| const majorVersion = version.split(".")[0]; | ||
| const packageManager = packageManagers.find( | ||
| (pm) => pm.name === name && pm.majorVersion === majorVersion | ||
| ) || packageManagers.find((pm) => pm.name === name); | ||
| return { | ||
| name, | ||
| command: name, | ||
| version, | ||
| majorVersion, | ||
| buildMeta, | ||
| warnings, | ||
| files: packageManager?.files, | ||
| lockFile: packageManager?.lockFile | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| const denoJSONPath = join(path, "deno.json"); | ||
| if (existsSync(denoJSONPath)) { | ||
| return packageManagers.find((pm) => pm.name === "deno"); | ||
| } | ||
| } | ||
| if (!options.ignoreLockFile) { | ||
| for (const packageManager of packageManagers) { | ||
| const detectionsFiles = [ | ||
| packageManager.lockFile, | ||
| packageManager.files | ||
| ].flat().filter(Boolean); | ||
| if (detectionsFiles.some((file) => existsSync(resolve(path, file)))) { | ||
| return { | ||
| ...packageManager | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| includeParentDirs: options.includeParentDirs ?? true | ||
| } | ||
| ); | ||
| if (!detected && !options.ignoreArgv) { | ||
| const scriptArg = process.argv[1]; | ||
| if (scriptArg) { | ||
| for (const packageManager of packageManagers) { | ||
| const re = new RegExp(`[/\\\\]\\.?${packageManager.command}`); | ||
| if (re.test(scriptArg)) { | ||
| return packageManager; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return detected; | ||
| } | ||
| async function installDependencies(options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const pmToFrozenLockfileInstallCommand = { | ||
| npm: ["ci"], | ||
| yarn: ["install", "--immutable"], | ||
| bun: ["install", "--frozen-lockfile"], | ||
| pnpm: ["install", "--frozen-lockfile"], | ||
| deno: ["install", "--frozen"] | ||
| }; | ||
| const commandArgs = options.frozenLockFile ? pmToFrozenLockfileInstallCommand[resolvedOptions.packageManager.name] : ["install"]; | ||
| await executeCommand(resolvedOptions.packageManager.command, commandArgs, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| } | ||
| async function addDependency(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const names = Array.isArray(name) ? name : [name]; | ||
| if (resolvedOptions.packageManager.name === "deno") { | ||
| for (let i = 0; i < names.length; i++) { | ||
| if (!/^(npm|jsr|file):.+$/.test(names[i])) { | ||
| names[i] = `npm:${names[i]}`; | ||
| } | ||
| } | ||
| } | ||
| if (names.length === 0) { | ||
| return; | ||
| } | ||
| const args = (resolvedOptions.packageManager.name === "yarn" ? [ | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| // Global is not supported in berry: yarnpkg/berry#821 | ||
| resolvedOptions.global && resolvedOptions.packageManager.majorVersion === "1" ? "global" : "", | ||
| "add", | ||
| resolvedOptions.dev ? "-D" : "", | ||
| ...names | ||
| ] : [ | ||
| resolvedOptions.packageManager.name === "npm" ? "install" : "add", | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| resolvedOptions.dev ? "-D" : "", | ||
| resolvedOptions.global ? "-g" : "", | ||
| ...names | ||
| ]).filter(Boolean); | ||
| await executeCommand(resolvedOptions.packageManager.command, args, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| if (options.installPeerDependencies) { | ||
| const existingPkg = await readPackageJSON(resolvedOptions.cwd); | ||
| const peerDeps = []; | ||
| const peerDevDeps = []; | ||
| for (const _name of names) { | ||
| const pkgName = _name.match(/^(.[^@]+)/)?.[0]; | ||
| const pkg = await readPackageJSON(pkgName, { | ||
| url: resolvedOptions.cwd | ||
| }).catch(() => ({})); | ||
| if (!pkg.peerDependencies || pkg.name !== pkgName) { | ||
| continue; | ||
| } | ||
| for (const [peerDependency, version] of Object.entries( | ||
| pkg.peerDependencies | ||
| )) { | ||
| if (pkg.peerDependenciesMeta?.[peerDependency]?.optional) { | ||
| continue; | ||
| } | ||
| if (existingPkg.dependencies?.[peerDependency] || existingPkg.devDependencies?.[peerDependency]) { | ||
| continue; | ||
| } | ||
| const isDev = pkg.peerDependenciesMeta?.[peerDependency]?.dev; | ||
| (isDev ? peerDevDeps : peerDeps).push(`${peerDependency}@${version}`); | ||
| } | ||
| } | ||
| if (peerDeps.length > 0) { | ||
| await addDependency(peerDeps, { ...resolvedOptions }); | ||
| } | ||
| if (peerDevDeps.length > 0) { | ||
| await addDevDependency(peerDevDeps, { ...resolvedOptions }); | ||
| } | ||
| } | ||
| } | ||
| async function addDevDependency(name, options = {}) { | ||
| await addDependency(name, { ...options, dev: true }); | ||
| } | ||
| async function removeDependency(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const args = (resolvedOptions.packageManager.name === "yarn" ? [ | ||
| // Global is not supported in berry: yarnpkg/berry#821 | ||
| resolvedOptions.global && resolvedOptions.packageManager.majorVersion === "1" ? "global" : "", | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| "remove", | ||
| resolvedOptions.dev ? "-D" : "", | ||
| resolvedOptions.global ? "-g" : "", | ||
| name | ||
| ] : [ | ||
| resolvedOptions.packageManager.name === "npm" ? "uninstall" : "remove", | ||
| ...getWorkspaceArgs(resolvedOptions), | ||
| resolvedOptions.dev ? "-D" : "", | ||
| resolvedOptions.global ? "-g" : "", | ||
| name | ||
| ]).filter(Boolean); | ||
| await executeCommand(resolvedOptions.packageManager.command, args, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| } | ||
| async function ensureDependencyInstalled(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const dependencyExists = doesDependencyExist(name, resolvedOptions); | ||
| if (dependencyExists) { | ||
| return true; | ||
| } | ||
| await addDependency(name, resolvedOptions); | ||
| } | ||
| async function dedupeDependencies(options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const isSupported = !["bun", "deno"].includes( | ||
| resolvedOptions.packageManager.name | ||
| ); | ||
| const recreateLockfile = options.recreateLockfile ?? !isSupported; | ||
| if (recreateLockfile) { | ||
| const lockfiles = Array.isArray(resolvedOptions.packageManager.lockFile) ? resolvedOptions.packageManager.lockFile : [resolvedOptions.packageManager.lockFile]; | ||
| for (const lockfile of lockfiles) { | ||
| if (lockfile) | ||
| fs.rmSync(resolve(resolvedOptions.cwd, lockfile), { force: true }); | ||
| } | ||
| await installDependencies(resolvedOptions); | ||
| return; | ||
| } | ||
| if (isSupported) { | ||
| const isyarnv1 = resolvedOptions.packageManager.name === "yarn" && resolvedOptions.packageManager.majorVersion === "1"; | ||
| await executeCommand( | ||
| resolvedOptions.packageManager.command, | ||
| [isyarnv1 ? "install" : "dedupe"], | ||
| { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| } | ||
| ); | ||
| return; | ||
| } | ||
| throw new Error( | ||
| `Deduplication is not supported for ${resolvedOptions.packageManager.name}` | ||
| ); | ||
| } | ||
| async function runScript(name, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const args = [ | ||
| resolvedOptions.packageManager.name === "deno" ? "task" : "run", | ||
| name | ||
| ]; | ||
| await executeCommand(resolvedOptions.packageManager.command, args, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent | ||
| }); | ||
| } | ||
| export { addDependency as a, addDevDependency as b, dedupeDependencies as c, detectPackageManager as d, ensureDependencyInstalled as e, runScript as f, installDependencies as i, packageManagers as p, removeDependency as r }; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
34946
26.3%684
26.67%168
10.53%+ Added
- Removed
Updated
Updated
Updated