| export { }; |
+547
| #!/usr/bin/env node | ||
| import { createRequire } from "node:module"; | ||
| import { defineCommand, runMain } from "citty"; | ||
| import { join, normalize, resolve } from "pathe"; | ||
| import { x } from "tinyexec"; | ||
| import * as fs from "node:fs"; | ||
| import { existsSync } from "node:fs"; | ||
| import { readFile } from "node:fs/promises"; | ||
| import { join as join$1 } from "node:path"; | ||
| //#region package.json | ||
| var name = "nypm"; | ||
| var version = "0.6.3"; | ||
| var description = "Unified Package Manager for Node.js"; | ||
| //#endregion | ||
| //#region src/package-manager.ts | ||
| 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"] | ||
| } | ||
| ]; | ||
| /** | ||
| * Detect the package manager used in a directory (and up) by checking various sources: | ||
| * | ||
| * 1. Use `packageManager` field from package.json | ||
| * | ||
| * 2. Known lock files and other files | ||
| */ | ||
| 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: name$1, version: version$1 = "0.0.0", buildMeta, warnings } = parsePackageManagerField(packageJSON.packageManager); | ||
| if (name$1) { | ||
| const majorVersion = version$1.split(".")[0]; | ||
| const packageManager = packageManagers.find((pm) => pm.name === name$1 && pm.majorVersion === majorVersion) || packageManagers.find((pm) => pm.name === name$1); | ||
| return { | ||
| name: name$1, | ||
| command: name$1, | ||
| version: version$1, | ||
| majorVersion, | ||
| buildMeta, | ||
| warnings, | ||
| files: packageManager?.files, | ||
| lockFile: packageManager?.lockFile | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| if (existsSync(join(path, "deno.json"))) return packageManagers.find((pm) => pm.name === "deno"); | ||
| } | ||
| if (!options.ignoreLockFile) { | ||
| for (const packageManager of packageManagers) if ([packageManager.lockFile, packageManager.files].flat().filter(Boolean).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) if ((/* @__PURE__ */ new RegExp(`[/\\\\]\\.?${packageManager.command}`)).test(scriptArg)) return packageManager; | ||
| } | ||
| } | ||
| return detected; | ||
| } | ||
| //#endregion | ||
| //#region src/_utils.ts | ||
| async function findup(cwd, match, options = {}) { | ||
| const segments = normalize(cwd).split("/"); | ||
| while (segments.length > 0) { | ||
| const result = await match(segments.join("/") || "/"); | ||
| if (result || !options.includeParentDirs) return result; | ||
| segments.pop(); | ||
| } | ||
| } | ||
| async function readPackageJSON(cwd) { | ||
| return findup(cwd, (p) => { | ||
| const pkgPath = join$1(p, "package.json"); | ||
| if (existsSync(pkgPath)) return readFile(pkgPath, "utf8").then((data) => JSON.parse(data)); | ||
| }); | ||
| } | ||
| 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" && options.corepack !== false && await hasCorepack() ? ["corepack", [command, ...args]] : [command, args]; | ||
| const { exitCode, stdout, stderr } = await x(xArgs[0], xArgs[1], { nodeOptions: { | ||
| cwd: resolve(options.cwd || process.cwd()), | ||
| env: options.env, | ||
| 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 env = { | ||
| ...process.env, | ||
| ...options.env | ||
| }; | ||
| 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, | ||
| env, | ||
| silent: options.silent ?? false, | ||
| packageManager, | ||
| dev: options.dev ?? false, | ||
| workspace: options.workspace, | ||
| global: options.global ?? false, | ||
| dry: options.dry ?? false, | ||
| corepack: options.corepack ?? true | ||
| }; | ||
| } | ||
| 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 parsePackageManagerField(packageManager) { | ||
| const [name$1, _version] = (packageManager || "").split("@"); | ||
| const [version$1, buildMeta] = _version?.split("+") || []; | ||
| if (name$1 && name$1 !== "-" && /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name$1)) return { | ||
| name: name$1, | ||
| version: version$1, | ||
| buildMeta | ||
| }; | ||
| const sanitized = (name$1 || "").replace(/\W+/g, ""); | ||
| return { | ||
| name: sanitized, | ||
| version: version$1, | ||
| buildMeta, | ||
| warnings: [`Abnormal characters found in \`packageManager\` field, sanitizing from \`${name$1}\` to \`${sanitized}\``] | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/api.ts | ||
| /** | ||
| * Installs project dependencies. | ||
| * | ||
| * @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). | ||
| * @param options.frozenLockFile - Whether to install dependencies with frozen lock file. | ||
| */ | ||
| async function installDependencies(options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const commandArgs = options.frozenLockFile ? { | ||
| npm: ["ci"], | ||
| yarn: ["install", "--immutable"], | ||
| bun: ["install", "--frozen-lockfile"], | ||
| pnpm: ["install", "--frozen-lockfile"], | ||
| deno: ["install", "--frozen"] | ||
| }[resolvedOptions.packageManager.name] : ["install"]; | ||
| if (options.ignoreWorkspace && resolvedOptions.packageManager.name === "pnpm") commandArgs.push("--ignore-workspace"); | ||
| if (!resolvedOptions.dry) await executeCommand(resolvedOptions.packageManager.command, commandArgs, { | ||
| cwd: resolvedOptions.cwd, | ||
| silent: resolvedOptions.silent, | ||
| corepack: resolvedOptions.corepack | ||
| }); | ||
| return { exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args: commandArgs | ||
| } }; | ||
| } | ||
| /** | ||
| * Adds dependency to the project. | ||
| * | ||
| * @param name - Name of the dependency to add. | ||
| * @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). | ||
| * @param options.dev - Whether to add the dependency as dev dependency. | ||
| * @param options.workspace - The name of the workspace to use. | ||
| * @param options.global - Whether to run the command in global mode. | ||
| */ | ||
| async function addDependency(name$1, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const names = Array.isArray(name$1) ? name$1 : [name$1]; | ||
| 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), | ||
| 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, | ||
| corepack: resolvedOptions.corepack | ||
| }); | ||
| 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 = createRequire(join$1(resolvedOptions.cwd, "/_.js"))(`${pkgName}/package.json`); | ||
| if (!pkg.peerDependencies || pkg.name !== pkgName) continue; | ||
| for (const [peerDependency, version$1] of Object.entries(pkg.peerDependencies)) { | ||
| if (pkg.peerDependenciesMeta?.[peerDependency]?.optional) continue; | ||
| if (existingPkg?.dependencies?.[peerDependency] || existingPkg?.devDependencies?.[peerDependency]) continue; | ||
| (pkg.peerDependenciesMeta?.[peerDependency]?.dev ? peerDevDeps : peerDeps).push(`${peerDependency}@${version$1}`); | ||
| } | ||
| } | ||
| if (peerDeps.length > 0) await addDependency(peerDeps, { ...resolvedOptions }); | ||
| if (peerDevDeps.length > 0) await addDevDependency(peerDevDeps, { ...resolvedOptions }); | ||
| } | ||
| return { exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args | ||
| } }; | ||
| } | ||
| /** | ||
| * Adds dev dependency to the project. | ||
| * | ||
| * @param name - Name of the dev dependency to add. | ||
| * @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). | ||
| * @param options.workspace - The name of the workspace to use. | ||
| * @param options.global - Whether to run the command in global mode. | ||
| * | ||
| */ | ||
| async function addDevDependency(name$1, options = {}) { | ||
| return await addDependency(name$1, { | ||
| ...options, | ||
| dev: true | ||
| }); | ||
| } | ||
| /** | ||
| * Removes dependency from the project. | ||
| * | ||
| * @param name - Name of the dependency to remove. | ||
| * @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). | ||
| * @param options.dev - Whether to remove dev dependency. | ||
| * @param options.workspace - The name of the workspace to use. | ||
| * @param options.global - Whether to run the command in global mode. | ||
| */ | ||
| async function removeDependency(name$1, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const names = Array.isArray(name$1) ? name$1 : [name$1]; | ||
| if (names.length === 0) return {}; | ||
| const args = (resolvedOptions.packageManager.name === "yarn" ? [ | ||
| 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, | ||
| corepack: resolvedOptions.corepack | ||
| }); | ||
| return { exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args | ||
| } }; | ||
| } | ||
| /** | ||
| * 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. | ||
| */ | ||
| async function dedupeDependencies(options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const isSupported = !["bun", "deno"].includes(resolvedOptions.packageManager.name); | ||
| if (options.recreateLockfile ?? !isSupported) { | ||
| 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, | ||
| corepack: resolvedOptions.corepack | ||
| }); | ||
| return { exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args: [isyarnv1 ? "install" : "dedupe"] | ||
| } }; | ||
| } | ||
| throw new Error(`Deduplication is not supported for ${resolvedOptions.packageManager.name}`); | ||
| } | ||
| /** | ||
| * 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.env - Additional environment variables to set for the script execution. | ||
| * @param options.silent - Whether to run the command in silent mode. | ||
| * @param options.packageManager - The package manager info to use (auto-detected). | ||
| * @param options.args - Additional arguments to pass to the script. | ||
| */ | ||
| async function runScript(name$1, options = {}) { | ||
| const resolvedOptions = await resolveOperationOptions(options); | ||
| const args = [ | ||
| resolvedOptions.packageManager.name === "deno" ? "task" : "run", | ||
| name$1, | ||
| ...options.args || [] | ||
| ]; | ||
| if (!resolvedOptions.dry) await executeCommand(resolvedOptions.packageManager.command, args, { | ||
| cwd: resolvedOptions.cwd, | ||
| env: resolvedOptions.env, | ||
| silent: resolvedOptions.silent, | ||
| corepack: resolvedOptions.corepack | ||
| }); | ||
| return { exec: { | ||
| command: resolvedOptions.packageManager.command, | ||
| args | ||
| } }; | ||
| } | ||
| //#endregion | ||
| //#region src/cli.ts | ||
| const operationArgs = { | ||
| cwd: { | ||
| type: "string", | ||
| description: "Current working directory" | ||
| }, | ||
| workspace: { | ||
| type: "boolean", | ||
| description: "Add to workspace" | ||
| }, | ||
| silent: { | ||
| type: "boolean", | ||
| description: "Run in silent mode" | ||
| }, | ||
| corepack: { | ||
| type: "boolean", | ||
| default: true, | ||
| description: "Use corepack" | ||
| }, | ||
| dry: { | ||
| type: "boolean", | ||
| description: "Run in dry run mode (does not execute commands)" | ||
| } | ||
| }; | ||
| const install = defineCommand({ | ||
| meta: { description: "Install dependencies" }, | ||
| args: { | ||
| ...operationArgs, | ||
| name: { | ||
| type: "positional", | ||
| description: "Dependency name", | ||
| required: false | ||
| }, | ||
| dev: { | ||
| type: "boolean", | ||
| alias: "D", | ||
| description: "Add as dev dependency" | ||
| }, | ||
| global: { | ||
| type: "boolean", | ||
| alias: "g", | ||
| description: "Add globally" | ||
| }, | ||
| "frozen-lockfile": { | ||
| type: "boolean", | ||
| description: "Install dependencies with frozen lock file" | ||
| }, | ||
| "install-peer-dependencies": { | ||
| type: "boolean", | ||
| description: "Also install peer dependencies" | ||
| } | ||
| }, | ||
| run: async ({ args }) => { | ||
| handleRes(await (args._.length > 0 ? addDependency(args._, args) : installDependencies(args)), args); | ||
| } | ||
| }); | ||
| const remove = defineCommand({ | ||
| meta: { description: "Remove dependencies" }, | ||
| args: { | ||
| name: { | ||
| type: "positional", | ||
| description: "Dependency name", | ||
| required: true | ||
| }, | ||
| ...operationArgs | ||
| }, | ||
| run: async ({ args }) => { | ||
| handleRes(await removeDependency(args._, args), args); | ||
| } | ||
| }); | ||
| const detect = defineCommand({ | ||
| meta: { description: "Detect the current package manager" }, | ||
| args: { cwd: { | ||
| type: "string", | ||
| description: "Current working directory" | ||
| } }, | ||
| run: async ({ args }) => { | ||
| const cwd = resolve(args.cwd || "."); | ||
| const packageManager = await detectPackageManager(cwd); | ||
| if (packageManager?.warnings) for (const warning of packageManager.warnings) console.warn(warning); | ||
| if (!packageManager) { | ||
| console.error(`Cannot detect package manager in "${cwd}"`); | ||
| return process.exit(1); | ||
| } | ||
| console.log(`Detected package manager in "${cwd}": "${packageManager.name}@${packageManager.version}"`); | ||
| } | ||
| }); | ||
| const dedupe = defineCommand({ | ||
| meta: { description: "Dedupe dependencies" }, | ||
| args: { | ||
| cwd: { | ||
| type: "string", | ||
| description: "Current working directory" | ||
| }, | ||
| silent: { | ||
| type: "boolean", | ||
| description: "Run in silent mode" | ||
| }, | ||
| recreateLockFile: { | ||
| type: "boolean", | ||
| description: "Recreate lock file" | ||
| } | ||
| }, | ||
| run: async ({ args }) => { | ||
| handleRes(await dedupeDependencies(args), args); | ||
| } | ||
| }); | ||
| const run = defineCommand({ | ||
| meta: { description: "Run script" }, | ||
| args: { | ||
| name: { | ||
| type: "positional", | ||
| description: "Script name", | ||
| required: true | ||
| }, | ||
| ...operationArgs | ||
| }, | ||
| run: async ({ args }) => { | ||
| handleRes(await runScript(args.name, { | ||
| ...args, | ||
| args: args._.slice(1) | ||
| }), args); | ||
| } | ||
| }); | ||
| runMain(defineCommand({ | ||
| meta: { | ||
| name, | ||
| version, | ||
| description | ||
| }, | ||
| subCommands: { | ||
| install, | ||
| i: install, | ||
| add: install, | ||
| remove, | ||
| rm: remove, | ||
| uninstall: remove, | ||
| un: remove, | ||
| detect, | ||
| dedupe, | ||
| run | ||
| } | ||
| })); | ||
| function handleRes(result, args) { | ||
| if (args.dry && !args.silent) console.log(`${result.exec?.command} ${result.exec?.args.join(" ")}`); | ||
| } | ||
| //#endregion | ||
| export { }; |
+1
-1
| { | ||
| "name": "nypm", | ||
| "version": "0.6.3", | ||
| "version": "0.6.4", | ||
| "description": "Unified Package Manager for Node.js", | ||
@@ -5,0 +5,0 @@ "repository": "unjs/nypm", |
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
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.
54337
51.24%7
40%1075
100.93%0
-100%11
83.33%