[d24f17c] | 1 | <p align="center">
|
---|
| 2 | <img src="https://ds300.github.io/patch-package/patch-package.svg" width="80%" alt="patch-package" />
|
---|
| 3 | </p>
|
---|
| 4 |
|
---|
| 5 | `patch-package` lets app authors instantly make and keep fixes to npm
|
---|
| 6 | dependencies. It's a vital band-aid for those of us living on the bleeding edge.
|
---|
| 7 |
|
---|
| 8 | ```sh
|
---|
| 9 | # fix a bug in one of your dependencies
|
---|
| 10 | vim node_modules/some-package/brokenFile.js
|
---|
| 11 |
|
---|
| 12 | # run patch-package to create a .patch file
|
---|
| 13 | npx patch-package some-package
|
---|
| 14 |
|
---|
| 15 | # commit the patch file to share the fix with your team
|
---|
| 16 | git add patches/some-package+3.14.15.patch
|
---|
| 17 | git commit -m "fix brokenFile.js in some-package"
|
---|
| 18 | ```
|
---|
| 19 |
|
---|
| 20 | Patches created by `patch-package` are automatically and gracefully applied when
|
---|
| 21 | you use `npm`(>=5) or `yarn`.
|
---|
| 22 |
|
---|
| 23 | No more waiting around for pull requests to be merged and published. No more
|
---|
| 24 | forking repos just to fix that one tiny thing preventing your app from working.
|
---|
| 25 |
|
---|
| 26 | ## Set-up
|
---|
| 27 |
|
---|
| 28 | In package.json
|
---|
| 29 |
|
---|
| 30 | ```diff
|
---|
| 31 | "scripts": {
|
---|
| 32 | + "postinstall": "patch-package"
|
---|
| 33 | }
|
---|
| 34 | ```
|
---|
| 35 |
|
---|
| 36 | Then
|
---|
| 37 |
|
---|
| 38 | ### npm
|
---|
| 39 |
|
---|
| 40 | npm i patch-package
|
---|
| 41 |
|
---|
| 42 | You can use `--save-dev` if you don't need to run npm in production, e.g. if
|
---|
| 43 | you're making a web frontend.
|
---|
| 44 |
|
---|
| 45 | ### yarn v1
|
---|
| 46 |
|
---|
| 47 | yarn add patch-package postinstall-postinstall
|
---|
| 48 |
|
---|
| 49 | You can use `--dev` if you don't need to run yarn in production, e.g. if you're
|
---|
| 50 | making a web frontend.
|
---|
| 51 |
|
---|
| 52 | To understand why yarn needs the `postinstall-postinstall` package see:
|
---|
| 53 | [Why use postinstall-postinstall](#why-use-postinstall-postinstall-with-yarn)
|
---|
| 54 |
|
---|
| 55 | ### yarn workspaces
|
---|
| 56 |
|
---|
| 57 | Same as for yarn ☝️ Note that if you want to patch un-hoisted packages you'll
|
---|
| 58 | need to repeat the setup process for the child package. Also make sure you're in
|
---|
| 59 | the child package directory when you run `patch-package` to generate the patch
|
---|
| 60 | files.
|
---|
| 61 |
|
---|
| 62 | ### yarn v2+
|
---|
| 63 |
|
---|
| 64 | yarn 2+ have native support for patching dependencies via
|
---|
| 65 | [`yarn patch`](https://yarnpkg.com/cli/patch). You do not need to use
|
---|
| 66 | patch-package on these projects.
|
---|
| 67 |
|
---|
| 68 | ### pnpm
|
---|
| 69 |
|
---|
| 70 | pnpm has native support for patching dependencies via
|
---|
| 71 | [`pnpm patch`](https://pnpm.io/cli/patch). You do not need to use patch-package
|
---|
| 72 | on these projects.
|
---|
| 73 |
|
---|
| 74 | ### Heroku
|
---|
| 75 |
|
---|
| 76 | For `patch-package` to work on Heroku applications, you must specify
|
---|
| 77 | [`NPM_CONFIG_PRODUCTION=false` or `YARN_PRODUCTION=false`](https://devcenter.heroku.com/articles/nodejs-support#package-installation).
|
---|
| 78 | See [this issue](https://github.com/ds300/patch-package/issues/130) for more
|
---|
| 79 | details.
|
---|
| 80 |
|
---|
| 81 | ### Docker and CI
|
---|
| 82 |
|
---|
| 83 | - If having errors about working directory ("cannot run in wd [...]") when
|
---|
| 84 | building in Docker, you might need to adjust configuration in `.npmrc`. See
|
---|
| 85 | [#185](https://github.com/ds300/patch-package/issues/185).
|
---|
| 86 | - In your `Dockerfile`, remember to copy over the patch files _before_ running
|
---|
| 87 | `[npm|yarn] install`
|
---|
| 88 | - If you cache `node_modules` rather than running `yarn install` every time,
|
---|
| 89 | make sure that the `patches` dir is included in your cache key somehow.
|
---|
| 90 | Otherwise if you update a patch then the change may not be reflected on
|
---|
| 91 | subsequent CI runs.
|
---|
| 92 |
|
---|
| 93 | ### CircleCI
|
---|
| 94 |
|
---|
| 95 | Create a hash of your patches before loading/saving your cache. If using a Linux
|
---|
| 96 | machine, run `md5sum patches/* > patches.hash`. If running on a macOS machine,
|
---|
| 97 | use `md5 patches/* > patches.hash`
|
---|
| 98 |
|
---|
| 99 | ```yaml
|
---|
| 100 | - run:
|
---|
| 101 | name: patch-package hash
|
---|
| 102 | command: md5sum patches/* > patches.hash
|
---|
| 103 | ```
|
---|
| 104 |
|
---|
| 105 | Then, update your hash key to include a checksum of that file:
|
---|
| 106 |
|
---|
| 107 | ```yaml
|
---|
| 108 | - restore_cache:
|
---|
| 109 | key:
|
---|
| 110 | app-node_modules-v1-{{ checksum "yarn.lock" }}-{{ checksum "patches.hash"
|
---|
| 111 | }}
|
---|
| 112 | ```
|
---|
| 113 |
|
---|
| 114 | As well as the save_cache
|
---|
| 115 |
|
---|
| 116 | ```yaml
|
---|
| 117 | - save_cache:
|
---|
| 118 | key:
|
---|
| 119 | app-node_modules-v1-{{ checksum "yarn.lock" }}-{{ checksum "patches.hash"
|
---|
| 120 | }}
|
---|
| 121 | paths:
|
---|
| 122 | - ./node_modules
|
---|
| 123 | ```
|
---|
| 124 |
|
---|
| 125 | ## Usage
|
---|
| 126 |
|
---|
| 127 | ### Making patches
|
---|
| 128 |
|
---|
| 129 | First make changes to the files of a particular package in your node_modules
|
---|
| 130 | folder, then run
|
---|
| 131 |
|
---|
| 132 | yarn patch-package package-name
|
---|
| 133 |
|
---|
| 134 | or use npx (included with `npm > 5.2`)
|
---|
| 135 |
|
---|
| 136 | npx patch-package package-name
|
---|
| 137 |
|
---|
| 138 | where `package-name` matches the name of the package you made changes to.
|
---|
| 139 |
|
---|
| 140 | If this is the first time you've used `patch-package`, it will create a folder
|
---|
| 141 | called `patches` in the root dir of your app. Inside will be a file called
|
---|
| 142 | `package-name+0.44.0.patch` or something, which is a diff between normal old
|
---|
| 143 | `package-name` and your fixed version. Commit this to share the fix with your
|
---|
| 144 | team.
|
---|
| 145 |
|
---|
| 146 | #### Options
|
---|
| 147 |
|
---|
| 148 | - `--create-issue`
|
---|
| 149 |
|
---|
| 150 | For packages whose source is hosted on GitHub this option opens a web browser
|
---|
| 151 | with a draft issue based on your diff.
|
---|
| 152 |
|
---|
| 153 | - `--use-yarn`
|
---|
| 154 |
|
---|
| 155 | By default, patch-package checks whether you use npm or yarn based on which
|
---|
| 156 | lockfile you have. If you have both, it uses npm by default. Set this option
|
---|
| 157 | to override that default and always use yarn.
|
---|
| 158 |
|
---|
| 159 | - `--exclude <regexp>`
|
---|
| 160 |
|
---|
| 161 | Ignore paths matching the regexp when creating patch files. Paths are relative
|
---|
| 162 | to the root dir of the package to be patched.
|
---|
| 163 |
|
---|
| 164 | Default value: `package\\.json$`
|
---|
| 165 |
|
---|
| 166 | - `--include <regexp>`
|
---|
| 167 |
|
---|
| 168 | Only consider paths matching the regexp when creating patch files. Paths are
|
---|
| 169 | relative to the root dir of the package to be patched.
|
---|
| 170 |
|
---|
| 171 | Default value: `.*`
|
---|
| 172 |
|
---|
| 173 | - `--case-sensitive-path-filtering`
|
---|
| 174 |
|
---|
| 175 | Make regexps used in --include or --exclude filters case-sensitive.
|
---|
| 176 |
|
---|
| 177 | - `--patch-dir`
|
---|
| 178 |
|
---|
| 179 | Specify the name for the directory in which to put the patch files.
|
---|
| 180 |
|
---|
| 181 | #### Nested packages
|
---|
| 182 |
|
---|
| 183 | If you are trying to patch a package at, e.g.
|
---|
| 184 | `node_modules/package/node_modules/another-package` you can just put a `/`
|
---|
| 185 | between the package names:
|
---|
| 186 |
|
---|
| 187 | npx patch-package package/another-package
|
---|
| 188 |
|
---|
| 189 | It works with scoped packages too
|
---|
| 190 |
|
---|
| 191 | npx patch-package @my/package/@my/other-package
|
---|
| 192 |
|
---|
| 193 | ### Updating patches
|
---|
| 194 |
|
---|
| 195 | Use exactly the same process as for making patches in the first place, i.e. make
|
---|
| 196 | more changes, run patch-package, commit the changes to the patch file.
|
---|
| 197 |
|
---|
| 198 | ### Applying patches
|
---|
| 199 |
|
---|
| 200 | Run `patch-package` without arguments to apply all patches in your project.
|
---|
| 201 |
|
---|
| 202 | #### Options
|
---|
| 203 |
|
---|
| 204 | - `--error-on-fail`
|
---|
| 205 |
|
---|
| 206 | Forces patch-package to exit with code 1 after failing.
|
---|
| 207 |
|
---|
| 208 | When running locally patch-package always exits with 0 by default. This
|
---|
| 209 | happens even after failing to apply patches because otherwise yarn.lock and
|
---|
| 210 | package.json might get out of sync with node_modules, which can be very
|
---|
| 211 | confusing.
|
---|
| 212 |
|
---|
| 213 | `--error-on-fail` is **switched on** by default on CI.
|
---|
| 214 |
|
---|
| 215 | See https://github.com/ds300/patch-package/issues/86 for background.
|
---|
| 216 |
|
---|
| 217 | - `--reverse`
|
---|
| 218 |
|
---|
| 219 | Un-applies all patches.
|
---|
| 220 |
|
---|
| 221 | Note that this will fail if the patched files have changed since being
|
---|
| 222 | patched. In that case, you'll probably need to re-install `node_modules`.
|
---|
| 223 |
|
---|
| 224 | This option was added to help people using CircleCI avoid
|
---|
| 225 | [an issue around caching and patch file updates](https://github.com/ds300/patch-package/issues/37)
|
---|
| 226 | but might be useful in other contexts too.
|
---|
| 227 |
|
---|
| 228 | - `--patch-dir`
|
---|
| 229 |
|
---|
| 230 | Specify the name for the directory in which the patch files are located
|
---|
| 231 |
|
---|
| 232 | #### Notes
|
---|
| 233 |
|
---|
| 234 | To apply patches individually, you may use `git`:
|
---|
| 235 |
|
---|
| 236 | git apply --ignore-whitespace patches/package-name+0.44.2.patch
|
---|
| 237 |
|
---|
| 238 | or `patch` in unixy environments:
|
---|
| 239 |
|
---|
| 240 | patch -p1 -i patches/package-name+0.44.2.patch
|
---|
| 241 |
|
---|
| 242 | ### Dev-only patches
|
---|
| 243 |
|
---|
| 244 | If you deploy your package to production (e.g. your package is a server) then
|
---|
| 245 | any patched `devDependencies` will not be present when patch-package runs in
|
---|
| 246 | production. It will happily ignore those patch files if the package to be
|
---|
| 247 | patched is listed directly in the `devDependencies` of your package.json. If
|
---|
| 248 | it's a transitive dependency patch-package can't detect that it is safe to
|
---|
| 249 | ignore and will throw an error. To fix this, mark patches for transitive dev
|
---|
| 250 | dependencies as dev-only by renaming from, e.g.
|
---|
| 251 |
|
---|
| 252 | package-name+0.44.0.patch
|
---|
| 253 |
|
---|
| 254 | to
|
---|
| 255 |
|
---|
| 256 | package-name+0.44.0.dev.patch
|
---|
| 257 |
|
---|
| 258 | This will allow those patch files to be safely ignored when
|
---|
| 259 | `NODE_ENV=production`.
|
---|
| 260 |
|
---|
| 261 | ### Creating multiple patches for the same package
|
---|
| 262 |
|
---|
| 263 | _💡 This is an advanced feature and is not recommended unless you really, really
|
---|
| 264 | need it._
|
---|
| 265 |
|
---|
| 266 | Let's say you have a patch for react-native called
|
---|
| 267 |
|
---|
| 268 | - `patches/react-native+0.72.0.patch`
|
---|
| 269 |
|
---|
| 270 | If you want to add another patch file to `react-native`, you can use the
|
---|
| 271 | `--append` flag while supplying a name for the patch.
|
---|
| 272 |
|
---|
| 273 | Just make you changes inside `node_modules/react-native` then run e.g.
|
---|
| 274 |
|
---|
| 275 | npx patch-package react-native --append 'fix-touchable-opacity'
|
---|
| 276 |
|
---|
| 277 | This will create a new patch file while renaming the old patch file so that you
|
---|
| 278 | now have:
|
---|
| 279 |
|
---|
| 280 | - `patches/react-native+0.72.0+001+initial.patch`
|
---|
| 281 | - `patches/react-native+0.72.0+002+fix-touchable-opacity.patch`
|
---|
| 282 |
|
---|
| 283 | The patches are ordered in a sequence, so that they can build on each other if
|
---|
| 284 | necessary. **Think of these as commits in a git history**.
|
---|
| 285 |
|
---|
| 286 | #### Updating a sequenced patch file
|
---|
| 287 |
|
---|
| 288 | If the patch file is the last one in the sequence, you can just make your
|
---|
| 289 | changes inside e.g. `node_modules/react-native` and then run
|
---|
| 290 |
|
---|
| 291 | npx patch-package react-native
|
---|
| 292 |
|
---|
| 293 | This will update the last patch file in the sequence.
|
---|
| 294 |
|
---|
| 295 | If the patch file is not the last one in the sequence **you need to use the
|
---|
| 296 | `--rebase` feature** to un-apply the succeeding patch files first.
|
---|
| 297 |
|
---|
| 298 | Using the example above, let's say you want to update the `001+initial` patch
|
---|
| 299 | but leave the other patch alone. You can run
|
---|
| 300 |
|
---|
| 301 | npx patch-package react-native --rebase patches/react-native+0.72.0+001+initial.patch
|
---|
| 302 |
|
---|
| 303 | This will undo the `002-fix-touchable-opacity` patch file. You can then make
|
---|
| 304 | your changes and run
|
---|
| 305 |
|
---|
| 306 | npx patch-package react-native
|
---|
| 307 |
|
---|
| 308 | to finish the rebase by updating the `001+initial` patch file and re-apply the
|
---|
| 309 | `002-fix-touchable-opacity` patch file, leaving you with all patches applied and
|
---|
| 310 | up-to-date.
|
---|
| 311 |
|
---|
| 312 | #### Inserting a new patch file in the middle of an existing sequence
|
---|
| 313 |
|
---|
| 314 | Using the above example, let's say you want to insert a new patch file between
|
---|
| 315 | the `001+initial` and `002+fix-touchable-opacity` patch files. You can run
|
---|
| 316 |
|
---|
| 317 | npx patch-package react-native --rebase patches/react-native+0.72.0+001+initial.patch
|
---|
| 318 |
|
---|
| 319 | This will undo the `002-fix-touchable-opacity` patch file. You can then make any
|
---|
| 320 | changes you want to insert in a new patch file and run
|
---|
| 321 |
|
---|
| 322 | npx patch-package react-native --append 'fix-console-warnings'
|
---|
| 323 |
|
---|
| 324 | This will create a new patch file while renaming any successive patches to
|
---|
| 325 | maintain the sequence order, leaving you with
|
---|
| 326 |
|
---|
| 327 | - `patches/react-native+0.72.0+001+initial.patch`
|
---|
| 328 | - `patches/react-native+0.72.0+002+fix-console-warnings.patch`
|
---|
| 329 | - `patches/react-native+0.72.0+003+fix-touchable-opacity.patch`
|
---|
| 330 |
|
---|
| 331 | To insert a new patch file at the start of the sequence, you can run
|
---|
| 332 |
|
---|
| 333 | npx patch-package react-native --rebase 0
|
---|
| 334 |
|
---|
| 335 | Which will un-apply all patch files in the sequence. Then follow the process
|
---|
| 336 | above to create a new patch file numbered `001`.
|
---|
| 337 |
|
---|
| 338 | #### Deleting a sequenced patch file
|
---|
| 339 |
|
---|
| 340 | To delete a sequenced patch file, just delete it, then remove and reinstall your
|
---|
| 341 | `node_modules` folder.
|
---|
| 342 |
|
---|
| 343 | If you deleted one of the patch files other than the last one, you don't need to
|
---|
| 344 | update the sequence numbers in the successive patch file names, but you might
|
---|
| 345 | want to do so to keep things tidy.
|
---|
| 346 |
|
---|
| 347 | #### Partially applying a broken patch file
|
---|
| 348 |
|
---|
| 349 | Normally patch application is atomic per patch file. i.e. if a patch file
|
---|
| 350 | contains an error anywhere then none of the changes in the patch file will be
|
---|
| 351 | applied and saved to disk.
|
---|
| 352 |
|
---|
| 353 | This can be problematic if you have a patch with many changes and you want to
|
---|
| 354 | keep some of them and update others.
|
---|
| 355 |
|
---|
| 356 | In this case you can use the `--partial` option. Patch-package will apply as
|
---|
| 357 | many of the changes as it can and then leave it to you to fix the rest.
|
---|
| 358 |
|
---|
| 359 | Any errors encountered will be written to a file `./patch-package-errors.log` to
|
---|
| 360 | help you keep track of what needs fixing.
|
---|
| 361 |
|
---|
| 362 | ## Benefits of patching over forking
|
---|
| 363 |
|
---|
| 364 | - Sometimes forks need extra build steps, e.g. with react-native for Android.
|
---|
| 365 | Forget that noise.
|
---|
| 366 | - Get told in big red letters when the dependency changed and you need to check
|
---|
| 367 | that your fix is still valid.
|
---|
| 368 | - Keep your patches colocated with the code that depends on them.
|
---|
| 369 | - Patches can be reviewed as part of your normal review process, forks probably
|
---|
| 370 | can't
|
---|
| 371 |
|
---|
| 372 | ## When to fork instead
|
---|
| 373 |
|
---|
| 374 | - The change is too consequential to be developed in situ.
|
---|
| 375 | - The change would be useful to other people as-is.
|
---|
| 376 | - You can afford to make a proper PR to upstream.
|
---|
| 377 |
|
---|
| 378 | ## Isn't this dangerous?
|
---|
| 379 |
|
---|
| 380 | Nope. The technique is quite robust. Here are some things to keep in mind
|
---|
| 381 | though:
|
---|
| 382 |
|
---|
| 383 | - It's easy to forget to run `yarn` or `npm` when switching between branches
|
---|
| 384 | that do and don't have patch files.
|
---|
| 385 | - Long lived patches can be costly to maintain if they affect an area of code
|
---|
| 386 | that is updated regularly and you want to update the package regularly too.
|
---|
| 387 | - Big semantic changes can be hard to review. Keep them small and obvious or add
|
---|
| 388 | plenty of comments.
|
---|
| 389 | - Changes can also impact the behaviour of other untouched packages. It's
|
---|
| 390 | normally obvious when this will happen, and often desired, but be careful
|
---|
| 391 | nonetheless.
|
---|
| 392 |
|
---|
| 393 | ## Why use postinstall-postinstall with Yarn?
|
---|
| 394 |
|
---|
| 395 | Most times when you do a `yarn`, `yarn add`, `yarn remove`, or `yarn install`
|
---|
| 396 | (which is the same as just `yarn`) Yarn will completely replace the contents of
|
---|
| 397 | your node_modules with freshly unpackaged modules. patch-package uses the
|
---|
| 398 | `postinstall` hook to modify these fresh modules, so that they behave well
|
---|
| 399 | according to your will.
|
---|
| 400 |
|
---|
| 401 | Yarn only runs the `postinstall` hook after `yarn` and `yarn add`, but not after
|
---|
| 402 | `yarn remove`. The `postinstall-postinstall` package is used to make sure your
|
---|
| 403 | `postinstall` hook gets executed even after a `yarn remove`.
|
---|
| 404 |
|
---|
| 405 | ## License
|
---|
| 406 |
|
---|
| 407 | MIT
|
---|
| 408 |
|
---|
| 409 | [](http://futurice.com/blog/sponsoring-free-time-open-source-activities?utm_source=github&utm_medium=spice&utm_campaign=patch-package)
|
---|