# @contrast/dep-hooks

Intercept calls to `require` in order to modify or replace exports.

## Usage

### Class: `DepHooks`

#### Instantiation

```javascript
const DepHooks = require('./lib');
const depHooks = new DepHooks();
```

The `DepHooks` constructor accepts a [`pino`](https://github.com/pinojs/pino)
logger as an argument.

#### `.resolve(descriptor, ...handlers)`

Options:

- `descriptor`: This can be a string or an object describing the module you want
  to intercept. If a string is used, or if the version field of the descriptor
  isn't set, all versions of the described module will be matched. Descriptors
  can have a `name`, `version`, and `file` property.

- `handlers`: The remaning arguments are the handlers which will be invoked when
  the described module is `require`'d. Each handler is passed the exported
  module and metadata including the module's root directory and its name and
  version as seen in its `package.json` file. If a handler returns a truthy
  value, then that value will replace the return value of `require`.

_**Note:**_ Registered handlers run _once_ per unique instance of an export
matching a descriptor.

#### `.install()`

This will monkey-patch `Module.prototype.require` so that exports can be
intercepted. The monkey-patching will only happen once regardless of how many
times this is invoked.

#### `.uninstall()`

This will reset `Module.prototype.require` to its value before being
monkey-patched by the instance.

## Examples

**Use case:** For `express` versions greater than or equal to 4, intercept the
export of the package's `lib/view.js` file (relative to the package's base
directory) and apply a tag to the exported function.

```javascript
const DepHooks = require('./lib');
const depHooks = new DepHooks();
depHooks.resolve(
  {
    name: 'express',
    version: '>=4',
    file: 'lib/view.js',
  },
  (xport, metadata) => {
    // Read from the package.json:
    // - metadata.name
    // - metadata.version
    // Absolute path to file:
    // - metadata.packageDir

    // xport === function View() { /*...*/ }
    xport['I was intercepted'] = true;
  },
);
```

**Use case:** Intercept all versions of `body-parser` and replace the exported
functions.

```javascript
const DepHooks = require('./lib');
const depHooks = new DepHooks();
depHooks.resolve({ name: 'body-parser' }, (xport, metadata) => {
  // Read from the package.json:
  // - metadata.name
  // - metadata.version
  // Absolute path to file:
  // - metadata.packageDir

  // xport === function bodyParser() { /*...*/ }
  return function bodyParserReplacement() {
    /*...*/
  };
});
```
