diff --git a/README.md b/README.md index c3a910b0..1a01f25e 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,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 1a50d094..8627ff62 100644 --- a/src/applyPatches.ts +++ b/src/applyPatches.ts @@ -31,18 +31,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( @@ -98,6 +103,7 @@ export function applyPatchesForApp({ shouldExitWithError, shouldExitWithWarning, bestEffort, + ignoreMissing, }: { appPath: string reverse: boolean @@ -105,6 +111,7 @@ export function applyPatchesForApp({ shouldExitWithError: boolean shouldExitWithWarning: boolean bestEffort: boolean + ignoreMissing: boolean }): void { const patchesDirectory = join(appPath, patchDir) const groupedPatches = getGroupedPatches(patchesDirectory) @@ -128,6 +135,7 @@ export function applyPatchesForApp({ warnings, errors, bestEffort, + ignoreMissing, }) } @@ -170,6 +178,7 @@ export function applyPatchesForPackage({ warnings, errors, bestEffort, + ignoreMissing, }: { patches: PatchedPackageDetails[] appPath: string @@ -178,6 +187,7 @@ export function applyPatchesForPackage({ warnings: string[] errors: string[] bestEffort: boolean + ignoreMissing: boolean }) { const pathSpecifier = patches[0].pathSpecifier const state = patches.length > 1 ? getPatchApplicationState(patches[0]) : null @@ -245,11 +255,12 @@ export function applyPatchesForPackage({ patchDetails, })), 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 8ee449a9..b2ac0c24 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,7 @@ const argv = minimist(process.argv.slice(2), { "create-issue", "partial", "", + "ignore-missing", ], string: ["patch-dir", "append", "rebase"], }) @@ -115,6 +116,9 @@ 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, @@ -122,6 +126,7 @@ if (argv.version || argv.v) { shouldExitWithError, shouldExitWithWarning, bestEffort: argv.partial, + ignoreMissing, }) } } @@ -178,13 +183,28 @@ 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 ======================= ${chalk.bold("patch-package")} ${chalk.italic( - "[ ]", - )} + "[ ]", + )} When given package names as arguments, patch-package will create patch files based on any changes you've made to the versions installed by yarn/npm.