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)
|
---|