diff --git a/README.md b/README.md index 0353f942..565017eb 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,14 @@ Run `patch-package` without arguments to apply all patches in your project. Specify the name for the directory in which the patch files are located +- `--ignore-missing` + + Ignores patches for packages that are not present in node_modules. + This is useful when working with monorepos and wanting to install sub-packages + separately from the root package, with pruned dependencies. + + See https://github.com/ds300/patch-package/issues/339 for background. + #### Notes To apply patches individually, you may use `git`: diff --git a/integration-tests/ignore-missing/__snapshots__/ignore-missing.test.ts.snap b/integration-tests/ignore-missing/__snapshots__/ignore-missing.test.ts.snap new file mode 100644 index 00000000..a1b3ff33 --- /dev/null +++ b/integration-tests/ignore-missing/__snapshots__/ignore-missing.test.ts.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test ignore-missing: patch-package returns 1 on missing package 1`] = ` +"SNAPSHOT: patch-package returns 1 on missing package +Error: Patch file found for package missing-package which is not present at node_modules/missing-package +--- +patch-package finished with 1 error(s). +END SNAPSHOT" +`; + +exports[`Test ignore-missing: adding --ignore-missing forces patch-package to return 0 1`] = ` +"SNAPSHOT: adding --ignore-missing forces patch-package to return 0 +patch-package 0.0.0 +Applying patches... +left-pad@1.1.3 ✔ +Skipping missing missing-package@1.0.0 ✔ +END SNAPSHOT" +`; + +exports[`Test ignore-missing: setting PATCH_PACKAGE_IGNORE_MISSING=1 forces patch-package to return 0 1`] = ` +"SNAPSHOT: setting PATCH_PACKAGE_IGNORE_MISSING=1 forces patch-package to return 0 +patch-package 0.0.0 +Applying patches... +left-pad@1.1.3 ✔ +Skipping missing missing-package@1.0.0 ✔ +END SNAPSHOT" +`; diff --git a/integration-tests/ignore-missing/ignore-missing.sh b/integration-tests/ignore-missing/ignore-missing.sh new file mode 100755 index 00000000..b44f60f5 --- /dev/null +++ b/integration-tests/ignore-missing/ignore-missing.sh @@ -0,0 +1,22 @@ +# make sure errors stop the script +set -e + +echo "add patch-package" +yarn add $1 +alias patch-package=./node_modules/.bin/patch-package + +(>&2 echo "SNAPSHOT: patch-package returns 1 on missing package") +if patch-package; +then + exit 1 +fi +(>&2 echo "END SNAPSHOT") + +echo "SNAPSHOT: adding --ignore-missing forces patch-package to return 0" +patch-package --ignore-missing; +echo "END SNAPSHOT" + +export PATCH_PACKAGE_IGNORE_MISSING=1 +echo "SNAPSHOT: setting PATCH_PACKAGE_IGNORE_MISSING=1 forces patch-package to return 0" +patch-package; +echo "END SNAPSHOT" diff --git a/integration-tests/ignore-missing/ignore-missing.test.ts b/integration-tests/ignore-missing/ignore-missing.test.ts new file mode 100644 index 00000000..5b5b8717 --- /dev/null +++ b/integration-tests/ignore-missing/ignore-missing.test.ts @@ -0,0 +1,5 @@ +import { runIntegrationTest } from "../runIntegrationTest" +runIntegrationTest({ + projectName: "ignore-missing", + shouldProduceSnapshots: true, +}) diff --git a/integration-tests/ignore-missing/package.json b/integration-tests/ignore-missing/package.json new file mode 100644 index 00000000..287cde3b --- /dev/null +++ b/integration-tests/ignore-missing/package.json @@ -0,0 +1,11 @@ +{ + "name": "ignore-missing", + "version": "1.0.0", + "description": "integration test for patch-package", + "main": "index.js", + "author": "", + "license": "ISC", + "dependencies": { + "left-pad": "1.1.3" + } +} diff --git a/integration-tests/ignore-missing/patches/left-pad+1.1.3.patch b/integration-tests/ignore-missing/patches/left-pad+1.1.3.patch new file mode 100644 index 00000000..0600ef9f --- /dev/null +++ b/integration-tests/ignore-missing/patches/left-pad+1.1.3.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/left-pad/index.js b/node_modules/left-pad/index.js +index 26f73ff..60f3f56 100644 +--- a/node_modules/left-pad/index.js ++++ b/node_modules/left-pad/index.js +@@ -4,7 +4,7 @@ + * To Public License, Version 2, as published by Sam Hocevar. See + * http://www.wtfpl.net/ for more details. */ + 'use strict'; +-module.exports = leftPad; ++module.exports = patch-package; + + var cache = [ + '', diff --git a/integration-tests/ignore-missing/patches/missing-package+1.0.0.patch b/integration-tests/ignore-missing/patches/missing-package+1.0.0.patch new file mode 100644 index 00000000..e294d98b --- /dev/null +++ b/integration-tests/ignore-missing/patches/missing-package+1.0.0.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/left-pad/index.js b/node_modules/left-pad/index.js +index 26f73ff..60f3f56 100644 +--- a/node_modules/left-pad/index.js ++++ b/node_modules/left-pad/index.js +@@ -7,7 +7,7 @@ + module.exports = leftPad; + + var cache = [ +- '', ++ "", + ' ', + ' ', + ' ', \ No newline at end of file diff --git a/integration-tests/ignore-missing/yarn.lock b/integration-tests/ignore-missing/yarn.lock new file mode 100644 index 00000000..392d269a --- /dev/null +++ b/integration-tests/ignore-missing/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +left-pad@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.1.3.tgz#612f61c033f3a9e08e939f1caebeea41b6f3199a" + integrity sha1-YS9hwDPzqeCOk58crr7qQbbzGZo= diff --git a/src/applyPatches.ts b/src/applyPatches.ts index 2bc4aba3..28e483e6 100644 --- a/src/applyPatches.ts +++ b/src/applyPatches.ts @@ -33,18 +33,23 @@ function getInstalledPackageVersion({ pathSpecifier, isDevOnly, patchFilename, + ignoreMissing, }: { appPath: string path: string pathSpecifier: string isDevOnly: boolean patchFilename: string + ignoreMissing: boolean }): null | string { const packageDir = join(appPath, path) if (!existsSync(packageDir)) { if (process.env.NODE_ENV === "production" && isDevOnly) { return null } + if (ignoreMissing) { + return null + } let err = `${chalk.red("Error:")} Patch file found for package ${posix.basename( @@ -85,12 +90,14 @@ export function applyPatchesForApp({ patchDir, shouldExitWithError, shouldExitWithWarning, + ignoreMissing, }: { appPath: string reverse: boolean patchDir: string shouldExitWithError: boolean shouldExitWithWarning: boolean + ignoreMissing: boolean }): void { const patchesDirectory = join(appPath, patchDir) const files = findPatchFiles(patchesDirectory) @@ -133,11 +140,12 @@ export function applyPatchesForApp({ (process.env.NODE_ENV === "production" && packageIsDevDependency({ appPath, packageDetails })), patchFilename, + ignoreMissing, }) if (!installedPackageVersion) { - // it's ok we're in production mode and this is a dev only package + // it's ok we're ignoring missing packages OR in production mode and this is a dev only package console.log( - `Skipping dev-only ${chalk.bold( + `Skipping ${ignoreMissing ? "missing" : "dev-only"} ${chalk.bold( pathSpecifier, )}@${version} ${chalk.blue("✔")}`, ) diff --git a/src/index.ts b/src/index.ts index 6278f04a..df47e879 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,6 +23,7 @@ const argv = minimist(process.argv.slice(2), { "error-on-fail", "error-on-warn", "create-issue", + "ignore-missing", ], string: ["patch-dir"], }) @@ -85,12 +86,16 @@ if (argv.version || argv.v) { const shouldExitWithWarning = !!argv["error-on-warn"] + const ignoreMissing = + !!argv["ignore-missing"] || !!process.env.PATCH_PACKAGE_IGNORE_MISSING + applyPatchesForApp({ appPath, reverse, patchDir, shouldExitWithError, shouldExitWithWarning, + ignoreMissing, }) } } @@ -147,6 +152,21 @@ Usage: and patch file updates (https://github.com/ds300/patch-package/issues/37), but might be useful in other contexts too. + ${chalk.bold("--ignore-missing")} + + Ignores patches for packages that are not present in node_modules. + This is useful when working with monorepos and wanting to install sub-packages + separately from the root package, with pruned dependencies. + + This option is can also be set via the environment variable + PATCH_PACKAGE_IGNORE_MISSING=1. Setting this env variable during the deployment + build process is recommended instead of using the --ignore-missing command option + when we want to ignore missing packages during deployment (of a monorepo with + pruned dependencies), but ensure we fail loudly during development if any + dependencies are moved or removed without updating the patches accordingly. + + See https://github.com/ds300/patch-package/issues/339 for background. + 2. Creating patch files =======================