From 5ec945a9d999065ec3ee3080bb63285d6714b1b4 Mon Sep 17 00:00:00 2001 From: KAMRONBEK Date: Fri, 17 Jul 2026 00:22:26 +0500 Subject: [PATCH] Replace minimist with dashdash for CLI arg parsing (#428) minimist is deprecated and has a history of parsing bugs. Port the argument parsing in src/index.ts to dashdash (already a transitive dependency), keeping runtime behavior identical for every flag and for positional package names. dashdash converts dashes in option names to underscores in the parsed result (--patch-dir -> opts.patch_dir) and puts positionals in opts._args; 'help'/'h' and 'version'/'v' are declared as aliases. Absent options are omitted from the result, so the existing "rebase"/"append" in argv presence checks are unchanged. Unknown options are now rejected with a clear message and exit 1 instead of being silently swallowed; the parse call is wrapped so errors print cleanly. Removes minimist and @types/minimist as direct deps (obviating the CVE bump in #521 for the direct dependency edge) and adds dashdash + @types/dashdash. Verified: yarn build (zero errors), tslint clean, and the full test suite (./run-tests.sh --runInBand) passes 47 suites / 660 tests / 108 snapshots. --- package.json | 4 ++-- src/index.ts | 68 +++++++++++++++++++++++++++++++++------------------- yarn.lock | 17 +++++-------- 3 files changed, 51 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index 176b15d6..138cdf3b 100644 --- a/package.json +++ b/package.json @@ -50,10 +50,10 @@ "devDependencies": { "@types/app-root-path": "^1.2.4", "@types/cross-spawn": "^6.0.0", + "@types/dashdash": "^1.14.3", "@types/fs-extra": "^9.0.0", "@types/jest": "^24.0.11", "@types/json-stable-stringify": "^1.0.34", - "@types/minimist": "^1.2.2", "@types/node": "^12.0.0", "@types/rimraf": "^2.0.2", "@types/semver": "^7.5.0", @@ -74,11 +74,11 @@ "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", + "dashdash": "^1.14.1", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", diff --git a/src/index.ts b/src/index.ts index 8ee449a9..28a9a1d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import chalk from "chalk" import process from "process" -import minimist from "minimist" +import dashdash from "dashdash" import { applyPatchesForApp } from "./applyPatches" import { getAppRootPath } from "./getAppRootPath" @@ -14,22 +14,40 @@ import { isCI } from "ci-info" import { rebase } from "./rebase" const appPath = getAppRootPath() -const argv = minimist(process.argv.slice(2), { - boolean: [ - "use-yarn", - "case-sensitive-path-filtering", - "reverse", - "help", - "version", - "error-on-fail", - "error-on-warn", - "create-issue", - "partial", - "", + +// dashdash converts dashes in option names to underscores in the parsed +// result (e.g. `--patch-dir` becomes `opts.patch_dir`). Absent options are +// simply omitted from the result object, so `"rebase" in opts` still tells us +// whether the flag was passed at all, and positional arguments end up in +// `opts._args`. +const parser = dashdash.createParser({ + options: [ + { names: ["help", "h"], type: "bool" }, + { names: ["version", "v"], type: "bool" }, + { name: "use-yarn", type: "bool" }, + { name: "case-sensitive-path-filtering", type: "bool" }, + { name: "reverse", type: "bool" }, + { name: "error-on-fail", type: "bool" }, + { name: "error-on-warn", type: "bool" }, + { name: "create-issue", type: "bool" }, + { name: "partial", type: "bool" }, + { name: "patch-dir", type: "string" }, + { name: "append", type: "string" }, + { name: "rebase", type: "string" }, + { name: "include", type: "string" }, + { name: "exclude", type: "string" }, ], - string: ["patch-dir", "append", "rebase"], }) -const packageNames = argv._ + +let argv: dashdash.Results +try { + argv = parser.parse(process.argv) +} catch (e) { + console.log(chalk.red((e as Error).message)) + process.exit(1) +} + +const packageNames = argv._args console.log( chalk.bold("patch-package"), @@ -37,12 +55,12 @@ console.log( require(join(__dirname, "../package.json")).version, ) -if (argv.version || argv.v) { +if (argv.version) { // noop -} else if (argv.help || argv.h) { +} else if (argv.help) { printHelp() } else { - const patchDir = slash(normalize((argv["patch-dir"] || "patches") + sep)) + const patchDir = slash(normalize((argv.patch_dir || "patches") + sep)) if (patchDir.startsWith("/")) { throw new Error("--patch-dir must be a relative path") } @@ -74,19 +92,19 @@ if (argv.version || argv.v) { argv.include, "include", /.*/, - argv["case-sensitive-path-filtering"], + argv.case_sensitive_path_filtering, ) const excludePaths = makeRegExp( argv.exclude, "exclude", /^package\.json$/, - argv["case-sensitive-path-filtering"], + argv.case_sensitive_path_filtering, ) const packageManager = detectPackageManager( appPath, - argv["use-yarn"] ? "yarn" : null, + argv.use_yarn ? "yarn" : null, ) - const createIssue = argv["create-issue"] + const createIssue = argv.create_issue packageNames.forEach((packagePathSpecifier: string) => { makePatch({ packagePathSpecifier, @@ -104,16 +122,16 @@ if (argv.version || argv.v) { }) } else { console.log("Applying patches...") - const reverse = !!argv["reverse"] + const reverse = !!argv.reverse // don't want to exit(1) on postinstall locally. // see https://github.com/ds300/patch-package/issues/86 const shouldExitWithError = - !!argv["error-on-fail"] || + !!argv.error_on_fail || (process.env.NODE_ENV === "production" && isCI) || (isCI && !process.env.PATCH_PACKAGE_INTEGRATION_TEST) || process.env.NODE_ENV === "test" - const shouldExitWithWarning = !!argv["error-on-warn"] + const shouldExitWithWarning = !!argv.error_on_warn applyPatchesForApp({ appPath, diff --git a/yarn.lock b/yarn.lock index fa92afe3..c462b1f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -339,6 +339,11 @@ dependencies: "@types/node" "*" +"@types/dashdash@^1.14.3": + version "1.14.3" + resolved "https://registry.yarnpkg.com/@types/dashdash/-/dashdash-1.14.3.tgz#670744b6165625affae070fd9a2302d6aae21a13" + integrity sha512-1BKd5kepSM4R+92c1SV1V0tcCletn2RDHh7QnuI9pTUVpVPwGJPi/3JPdaXR9l7TmwRlV9Zn24hiwxybjWR3Lw== + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -387,11 +392,6 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/minimist@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" - integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== - "@types/node@*", "@types/node@^12.0.0": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" @@ -1023,7 +1023,7 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" -dashdash@^1.12.0: +dashdash@^1.12.0, dashdash@^1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" dependencies: @@ -2852,11 +2852,6 @@ minimist@^1.1.1, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - mixin-deep@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"