This lesson preview is part of the Bundling and Automation in Monorepos course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Bundling and Automation in Monorepos, plus 90+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Bundling and Automation in Monorepos
  • [00:00 - 00:19] We're going to explore a feature of pnpm that has helped me solve problems numerous times in the past - the ability to patch a dependency. It is often that fixing things upstream takes longer, and we just need to be able to immediately fix problems for our projects.

    [00:20 - 00:26] Let's go to an example. I'm going to create a folder, then write a minimal package.json file.

    [00:27 - 01:00] Write a minimal .gitignore file that just ignores node_modules, add the "mysql2" package at version 3.3.2, which I know exhibits a problem, and make this a git repository and create an initial commit that adds all files. We can look at what was in this commit and it just has our .gitignore file package.json that has automatically set the "packageManager" to pnpm, and a small lock file.

    [01:01 - 01:38] Now, the problem with the package that we installed, the problem with the package that we installed is that it defines an "exports" field. We're going to go into more detail into what "exports" are later in the course, but suffice to say that when a package has an "exports" field, only the files enumerated in this "exports" field are available for other tools to load. In this case, there is a default export that exports index.js and "/promise" and "/promise.js", which exports the "promise.js" file.

    [01:39 - 01:56] Critically, this list of exports is missing the package.json of "mysql2". Every time you write an "exports" field, you must include your own package.json, because if you don't, other tools cannot reliably load it.

    [01:57 - 02:19] That means they cannot read necessary information from it, and I run into a problem where I needed to use this package. And because it was missing the package.json export in the "exports" fields, it was not working. Now, opening a ticket is always an option when you run into a problem, but we also want to be moving with velocity.

    [02:21 - 02:33] So I'm going to show you how we can fix this for ourselves without forking the package, because creating forks comes with its own set of complications. We can use a tool in pnpm called patch.

    [02:34 - 02:57] Patch basically allows you to define the package, one of your dependencies, and it will be copied into an edit directory where you would be able to change it and fix any problems with it. So let's say "pnpm patch mysql2".

    [02:58 - 03:09] It created a folder in our project folder "node_modules/.pnpm_patches". Let's quickly check the structure of our node_modules.

    [03:10 - 03:28] I'm going to write `tree node_modules -a", which means show hidden files "-L 2", which means show only the top two levels. We can see that node_modules has "mysql2" as we would expect, and it also has the hidden ".pnpm" store folder.

    [03:29 - 03:51] And now it has the hidden ".pnpm_patches" folder that contains mysql2 at version 3.3.2 that we can patch. To do that, I'm just going to directly open it - "node_modules/.pnpm_patches/[email protected]". And I'm going to open the package.json.

    [03:52 - 04:10] And in here I can directly add my "./package.json" export, which just then points back to "./package.json". It's not a complicated change, but again, without this existing, other tools cannot read the package.json file.

    [04:11 - 04:33] An "exports" field in ESM defines a strict set of files that are accessible to all other tools. And if you omit the package.json file, no other tool can read it. Let's close this. And once we have done the change in the copy of the package, we can now commit it. I'm going to copy this whole line from here.

    [04:34 - 05:10] "pnpm patch-commit", which will take the folder that we made our changes in. And now I'm going to "git add" everything and look at the diff "git diff --cached". So in package.json, pnpm added another field called "pnpm > patchedDependencies", and then for mysql2 it points to "patches/mysql2.patch" and indeed, in the "patches/mysql2.patch" file, we can find our change.

    [05:11 - 05:37] Finally, the lock file for pnpm changed. It lists the hash of our patch and adds that patch hash to our dependencies internally, so that pnpm can track if we change the patch in some way, and it will warn us that it has one version in the lock file with a specific hash, but we made a change it didn't know about.

    [05:38 - 05:52] I'm going to commit this as "Patch mysql2". And this is the whole process of patching a dependency and fixing a bug directly by ourselves without forking a package.

    [05:53 - 05:55] I will see you in the next lesson.