npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

patch-module

v0.1.0

Published

Monkey patch npm modules.

Downloads

205

Readme

patch-module

npm version Build Status Coverage Status

Monkey patch npm modules.

Have you ever looked through a module you are using and wished it was written just a little bit different?

Knowing the hassle of forking a repo, changing the code, submitting a Pull Request (which may not get accepted) and then losing track of updates in the original module; I built Patch Module.

 Patch Module allows you to change the source code of another module before it is required. It mimics node's require functionality, making it familiar to use whilst avoiding the use of eval. Patch Module will load a module from the file system, apply the patches you wish to make and then compiles the module.

Patch Module includes a couple of optional safety features too. You can define the version of the module you are patching as well as how many times the regex for the patch should match. If either of those operations don't match your settings, Patch Module will alert you letting you know that the module you are trying to patch has been updated and you should check your patch is still necessary.

Install

$ npm install patch-module --save

Usage

We will use the module is-it-friday as an example of a module to patch. is-it-friday is simply one line module.exports = "Probably not...";

Default is-it-friday behaviour:

// file: index.js
var isItFriday = require('is-it-friday');
console.log(isItFriday); // Probably not...

Patching as a require:

// file: index.js
var patch = require('patch-module');
var isItFriday = patch('./node_modules/is-it-friday/index.js', {
	version: '0.1.0',
	package: './node_modules/is-it-friday/package.json'
}, [
	{find: '"Probably not..."', replace: '(new Date()).getDay() === 5 ? "Yes" : "No"', expect: 1}
]);
console.log(isItFriday); // "Yes" or "No" depending if it's Friday


Here we create our patched isItFriday module so we can require it elsewhere in our project whilst making it easy to revert to the original module later if needed:

// file: index.js
var isItFriday = require('./patch/is-it-friday.js');
console.log(isItFriday); // "Yes" or "No" depending if it's Friday
 
// file: patch/is-it-friday.js
var patch = require('patch-module');
module.exports = patch('./node_modules/is-it-friday/index.js', {
	version: '0.1.0',
	package: './node_modules/is-it-friday/package.json'
}, [
	{find: '"Probably not..."', replace: '(new Date()).getDay() === 5 ? "Yes" : "No"', expect: 1}
]);

Advanced Usage

To show an example of patching multiple files in a module we will use jugglingdb as an example. Let's patch the way jugglingdb handles connection errors when trying to connect to a Mongo database that is not available, by adding the ability to listen to a new error event and handle it instead of it throwing an uncatchable error.

In jugglingdb if a full path is specified for a Schema it will load that instead of looking in node_modules so we can point it directly to our modified jugglingdb-mongodb module.

// file: index.js
var jugglingdb = require('./patch/jugglingdb.js');
var Schema = jugglingdb.Schema;
var db = new Schema(__dirname + '/patch/jugglingdb-mongodb.js', {
	url: process.env.MONGO_URL || 'mongodb://localhost:27017/test'
});
db.on('connected', function () {
	console.log('connected!');
});
db.on('disconnected', function () {
	console.log('disconnected!');
});
// This is new functionality
db.on('error', function (err) {
	console.log('error!', err.message);
});

We need to modify the Schema class, which is loaded from the modules index.js.

Important: If you are modifying a relative require (require(./) you need to replace the new path with the full path.

// file: patch/jugglingdb.js
var patch = require('patch-module');
module.exports = patch('./node_modules/jugglingdb/index.js', {
	version: '2.0.0-rc8',
	package: './node_modules/jugglingdb/package.json'
}, [
	{find: 'var Schema = exports.Schema = require(\'./lib/schema\').Schema;', replace: 'var Schema = exports.Schema = require(\'' + __dirname + '/patch/jugglingdb-schema.js\').Schema;', expect: 1}
]);

Here we are modifying the jugglingdb-mongodb connector module and changing the throw to calling the callback and passing the error on, that way we can choose how to handle it. We expect to make two replacements, so we've set expect to 2.

// file: patch/jugglingdb-mongodb.js
var patch = require('patch-module');
module.exports = patch('./node_modules/jugglingdb-mongodb/lib/mongodb.js', {
	version: '0.2.0',
	package: './node_modules/jugglingdb-mongodb/package.json'
}, [
	{find: /if \(err\) throw err;/mg, replace: 'if (err) return callback(err);', expect: 2},
]);

When jugglingdb connects to a database, it creates a client variable that holds the connection to the database in the connector and passes it back. However it doesn't check if the client variable actually has a valid connection that emits the connected event. So we are checking if the client variable has a valid connection to a database and only emitting the connected event then, if not we will emit a new error event with the error.

// file: patch/jugglingdb-schema.js
var patch = require('patch-module');
module.exports = patch('./node_modules/jugglingdb/lib/schema.js', {
	version: '2.0.0-rc8',
	package: './node_modules/jugglingdb/package.json'
}, [
	{find: 'adapter.initialize(this, function () {', replace: 'adapter.initialize(this, function (err) {', expect: 1},
	{find: /this\.connecting = false;\s+this\.connected = true;\s+this\.emit\('connected'\);/m, replace: `if (this.client) {
			this.connecting = false;
			this.connected = true;
			this.emit('connected');
		} else {
			this.emit('error', err);
		}`, expect: 1}
]);

patch(filePath, options = {}, replacements = [])

options Object

  • version - Version that we expect from the package file for the module we are modifying
  • package - Package file to look in when version is defined
  • dontReplaceRelativeRequires - Don't automatically replace require(./ with require(path/to/file/you/are/changing. The module is set to replace relative requires from a module back to the original path by default as a convenience
  • callback - callback Function, see callback Function below
  • returnAsString - Useful for debugging your modifications, will return the source as a string which you can write to a file or console.log

replacements Array

An Array of replacement Objects with keys:

  • find - String or RegExp to look for
  • replace - String to replace what we find with
  • expect - (Optional) Explicitly define an Integer for how many occurrences of find we expect. This helps with module updates and old patches

callback Function

A Function that takes a string function (str) { /* ...modify str... */ return str; }, modifies it and returns it to be compiled. If you have a callback Function and replacements Array the callback Function modifies the source first than the replacements are applied.

Tests

To run the test suite, first install the dependencies, then run npm test:

$ npm install
$ npm test

License

MIT