DEV Community

Cover image for How to Use Patch-Package to Fix Broken Node Modules Instantly! 🚀🔗
Kunal Ukey
Kunal Ukey

Posted on

How to Use Patch-Package to Fix Broken Node Modules Instantly! 🚀🔗


When working with third-party NPM packages, you may find bugs, missing features, or specific needs that require modifying the package code. Instead of waiting for the package maintainers to merge a pull request (which can take weeks or months), you can directly modify the package in your node_modules and make your changes persist using patch-package.

In this blog, we'll go through the steps to modify an NPM package, create a patch, and ensure the changes are applied even after reinstalling dependencies.

Why Modify an NPM Package?

  • Fix Bugs: If a package has a bug that affects your project, you don't have to wait for the maintainer to fix it.

  • Add Features: Sometimes, packages lack features you need, and modifying them is quicker than waiting for an official update.

  • Avoid Forking: Instead of forking and maintaining a separate package, you can patch the existing one directly.

  • No Need for PR Approval: Pull requests can take weeks or months to get merged. With patch-package, you can apply changes immediately.

Step 1: Install patch-package

  • First, install patch-package as a development dependency:
npm install patch-package postinstall-postinstall --save-dev
Enter fullscreen mode Exit fullscreen mode
  • Also, add a postinstall script in your package.json to ensure patches are reapplied after installing dependencies:
"scripts": {
  "postinstall": "patch-package"
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Modify the Package Code

  • Locate the package inside node_modules and make the necessary changes. For example, if you're modifying @kunalukey/react-image, edit the file:

node_modules/@kunalukey/react-image/main/LazyImage.js

import React, { useEffect, useRef, useState } from "react";
const LazyImage = ({
  placeholderSrc,
  placeholderClassName,
  placeholderStyle,
  src,
  alt,
  className,
  style
}) => {
  const [isLoading, setIsLoading] = useState(true);
  const [isPlaceholder, setIsPlaceholder] = useState(false);
  const [view, setView] = useState("");
  const placeholderRef = useRef();
  const imageRef = useRef();
  useEffect(() => {
    if (document.readyState === "complete") {
      const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
          setView(src);
          observer.unobserve(placeholderRef.current);
        }
      });
      if (placeholderRef && placeholderRef.current && isPlaceholder) {
        observer.observe(placeholderRef.current);
      }
    }
  }, [src, isPlaceholder]);
  return /*#__PURE__*/React.createElement(React.Fragment, null, isLoading && /*#__PURE__*/React.createElement("img", {
    src: placeholderSrc,
    alt: "",
    className: placeholderClassName,
    style: placeholderStyle,
    ref: placeholderRef,
    onLoad: () => setIsPlaceholder(true)
  }), /*#__PURE__*/React.createElement("img", {
    src: view,
    alt: alt,
    className: className,
    onLoad: () => setIsLoading(false),
    style: isLoading ? {
      display: "none"
    } : style,
    ref: imageRef
  }));
};
export default LazyImage;
Enter fullscreen mode Exit fullscreen mode

to

import React, { useEffect, useRef, useState } from "react";
import defaultPlaceholder from "./path-to-svg-file"; // import placeholder file

const LazyImage = ({
  placeholderSrc = defaultPlaceholder, // default placeholder
  placeholderClassName,
  placeholderStyle,
  src,
  alt,
  className,
  style
}) => {
  const [isLoading, setIsLoading] = useState(true);
  const [isPlaceholder, setIsPlaceholder] = useState(false);
  const [view, setView] = useState("");
  const placeholderRef = useRef();
  const imageRef = useRef();
  useEffect(() => {
    if (document.readyState === "complete") {
      const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
          setView(src);
          observer.unobserve(placeholderRef.current);
        }
      });
      if (placeholderRef && placeholderRef.current && isPlaceholder) {
        observer.observe(placeholderRef.current);
      }
    }
  }, [src, isPlaceholder]);
  return /*#__PURE__*/React.createElement(React.Fragment, null, isLoading && /*#__PURE__*/React.createElement("img", {
    src: placeholderSrc,
    alt: "",
    className: placeholderClassName,
    style: placeholderStyle,
    ref: placeholderRef,
    onLoad: () => setIsPlaceholder(true)
  }), /*#__PURE__*/React.createElement("img", {
    src: view,
    alt: alt,
    className: className,
    onLoad: () => setIsLoading(false),
    style: isLoading ? {
      display: "none"
    } : style,
    ref: imageRef
  }));
};
export default LazyImage;
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Patch File

  • Once you've made your changes, run the following command to create a patch:
npx patch-package @kunalukey/react-image
Enter fullscreen mode Exit fullscreen mode
  • This will generate a patch file inside a patches/ directory:

The patch file contains a diff of the changes you made.

Step 4: Handle Binary Files (SVG, GIF, etc.)

  • patch-package works best on text-based files like .svg and may not work perfectly on .gif or .mp4 files.

Step 5: Apply Patches After Installing Packages

  • The postinstall script will automatically apply patches whenever you run npm install. If needed, you can manually reapply them with:
npx patch-package name-of-package
Enter fullscreen mode Exit fullscreen mode

Conclusion:

Using patch-package, you can quickly fix bugs, add features, and modify NPM packages without waiting for upstream changes. This is a powerful tool for improving your workflow while keeping your project up-to-date with third-party dependencies.

PS - I am open for freelance work or a remote job 🤓.
Contact me: [email protected]

Top comments (1)

Collapse
 
kunalukey profile image
Kunal Ukey • Edited

I am open to freelance or remote work 🤓: Portfolio