patch-package: Not able to include package.json in the patch

I was commenting on here as we are (for some unknown reason so far) not able to download the latest beta version which seem to fix the issue about the error after applying a patch.

@ds300 let me explain out situation so maybe you can point the way to go. We are having this issue: a third party package we are using has not properly set the folder directories to point to the /lib and /es folders.

We opened this PR for them to merge but even if that happens then the other package we want to use rc-slider. So in total this means waiting on two external PRs.

As a workaround we wanted to modify or local packages, patch them, to allow this changes which already make our build succesful but we are not able to include the package.jsonfile. We tried yarn patch-package rc-util --use-yarn --exclude /$.^/ as a void regexp to reenable the disbales package.json tracking for the patch but with no success.

As another workaround we modified the /es files to patch the package but the patch-package is presenting the whitespace error This is usually caused by inconsistent whitespace in the patch file.

TL,DR: What would be the way to include package.jsonin the patch?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 16
  • Comments: 23 (5 by maintainers)

Commits related to this issue

Most upvoted comments

The --include and --exclude options are there - but they’re used when creating patches for other packages, not during postinstall (where you’re applying patches to the various npm packages). You also need to be providing the --exclude option to override the default setting if you want to include changes to the package.json in the patch.

So to use it, you would first:

  • Modify the files inside the node_modules/package-to-be-patched so that they contained what you needed, then
  • Create the patch for the package using:
patch-package --exclude 'nothing' package-to-be-patched
  • Check that the patch file generated contains what you expected it to. If not, return to step 1 to update the contents of the package that you want to patch so that it reflects the correct information and redo the patch creation again.
  • Then set up postinstall so it applies the generated patches after each installation:
"postinstall": "patch-package && jetify"

You won’t be able to change the dependencies in another project’s package.json, because the patch is only applied after the dependencies have been installed. But you can do things like add a typescript type definition file to a project and update its package.json file to point to your new types.

If you can add an example for disabling the default exclude, that’d be fantastic.

The other time that being able to patch package.json is desirable is when adding typescript type definitions to a package that doesn’t have its own type definitions, so that you can add the “types” field to the package’s package.json file pointing to your new type definition file.

Edit: It doesn’t seem to matter what the exclude regex is, package.json is hardcoded to be excluded in the diff inside makePatch at: https://github.com/ds300/patch-package/blob/10c0a51d8f73fe67d414ef998a3eec6a001c14ba/src/makePatch.ts#L123-L126 and: https://github.com/ds300/patch-package/blob/10c0a51d8f73fe67d414ef998a3eec6a001c14ba/src/makePatch.ts#L135-L138

At present, the only way to be able to include package.json in the diff is to first patch patch-package to comment out those two lines in the compiled code. There’s a bit of humour there in using patch-package to patch patch-package itself…

Patch: patch-package+5.1.1.patch - to allow enabling patching of package.json if you also use a regex that doesn’t include it (e.g. --exclude '\*.txt').

patch-package
--- a/node_modules/patch-package/dist/makePatch.js
+++ b/node_modules/patch-package/dist/makePatch.js
@@ -66,14 +66,14 @@ function makePatch(packageName, appPath, packageManager, includePaths, excludePa
         fs.writeFileSync(path.join(tmpRepo.name, ".gitignore"), "!/node_modules\n\n");
         tmpExec_1("git", ["init"]);
         // don't commit package.json though
-        fs.unlinkSync(path.join(tmpRepo.name, "node_modules", packageName, "package.json"));
+        // fs.unlinkSync(path.join(tmpRepo.name, "node_modules", packageName, "package.json"));
         tmpExec_1("git", ["add", "-f", slash(path.join("node_modules", packageName))]);
         tmpExec_1("git", ["commit", "-m", "init"]);
         // replace package with user's version
         rimraf.sync(tmpRepoPackagePath);
         fsExtra.copySync(packagePath, tmpRepoPackagePath, { recursive: true });
         // remove package.json again
-        fs.unlinkSync(path.join(tmpRepo.name, "node_modules", packageName, "package.json"));
+        // fs.unlinkSync(path.join(tmpRepo.name, "node_modules", packageName, "package.json"));
         // stage all files
         tmpExec_1("git", ["add", "-f", slash(path.join("node_modules", packageName))]);
         // unstage any ignored files so they don't show up in the diff

I think --exclude 'nothing' should be the default setting

I think the biggest case for --exclude 'nothing' is that one common requirement is to modify the exports field of a package.json file - due to many of them lacking exports for esm modules yet

To really exclude everything without choosing a filename that doesn’t exist (which could lead to weird unexpected edge cases), use a regular expression pattern containing an empty string to exclude everything:

$ yarn patch-package react  --exclude '^$'

@dawnmist why not remove the default for --exclude? If we don’t make changes to the package.json it wouldn’t be picked up anyway, right?

@DominikGuzei You’d probably be better asking the project maintainer that question. I am just someone who uses patch-package (and highly recommends it for certain tasks) who also had to work out how to make changes to the package.json work (for adding typescript types to a few packages).

That said, my suspicion is that it’s because any patch you apply to the npm package’s package.json is only applied after all its dependencies have already been installed - so most changes you could potentially make to the npm package’s package.json file would be applied too late for them to actually do what you intend them to do. If those changes were included by default, many people would try to use them in situations where they do not work and the maintainer would forever be answering issues about things like “I modified the package’s dependencies using patch-package, but npm/yarn are still installing the old ones!?” due to people not understanding that the modification only gets applied after all packages are installed.

Hi @brunolemos 👋 !

Unfortunately what you want to do is not possible 😢 This is a common question about patch-package. I don’t know of a way to get absolute control over which transient dependencies get installed. The best thing I’ve found is manually editing your own lock file, but that’s only useful for making sure a specific version gets installed.

I don’t think anyone mentioned this idea yet But as typically we have 1000’s of npm packages, this means most individual npm packages are not that huge.

If we tweak our gitignore to ignore all node_module sub-folders like so:

node_nodules/*

We can then INCLUDE a certain node_module sub-folder in our git repo:

!node_modules/rc-slider

I think this may altogether be a better approach than patch-package, unless the package you are touching is huge. By simply checking the node module into git directly, there’s a lot of nice simple benefits

Just published a fix for this in patch-package@6.0.0-4 – Thanks for pointing this out @dawnmist and @davidlampon 🙇 ❤️

I’ll close this once v6 leaves beta.

@dawnmist why not remove the default for --exclude? If we don’t make changes to the package.json it wouldn’t be picked up anyway, right?

@nerdaliciousCH try making up a string that might be a filename but isn’t. e.g. --exclude 'IAmNotARealFile.txt'

The ‘$’ in a regexp string means “at the end of the line” (or “end of the string” for something like filename strings). By having nothing before the $, you’re actually matching every filename because every filename will have an end of the string.

@dawnmist makes sense, thanks for the detailed explanation! 👍 sorry for the mistake 😅