Skip to content

Commit

Permalink
[jest-resolve] Add support for packageFilter for custom resolvers (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
scinos committed Aug 12, 2020
1 parent cf07d11 commit 0fa9552
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,8 @@

### Features

- `[jest-resolve]` Add support for `packageFilter` on custom resolver ([#10393](https://github.com/facebook/jest/pull/10393))

### Fixes

- `[pretty-format]` Handle `tagName` not being a string ([#10397](https://github.com/facebook/jest/pull/10397))
Expand Down
31 changes: 31 additions & 0 deletions docs/Configuration.md
Expand Up @@ -693,6 +693,7 @@ This option allows the use of a custom resolver. This resolver must be a node mo
"extensions": [string],
"moduleDirectory": [string],
"paths": [string],
"packageFilter": "function(pkg, pkgdir)",
"rootDir": [string]
}
```
Expand All @@ -712,6 +713,36 @@ For example, if you want to respect Browserify's [`"browser"` field](https://git
}
```

By combining `defaultResolver` and `packageFilter` we can implement a `package.json` "pre-processor" that allows us to change how the default resolver will resolve modules. For example, imagine we want to use the field `"module"` if it is present, otherwise fallback to `"main"`:

```json
{
...
"jest": {
"resolver": "my-module-resolve"
}
}
```

```js
// my-module-resolve package

module.exports = (request, options) => {
// Call the defaultResolver, so we leverage its cache, error handling, etc.
return options.defaultResolver(request, {
...options,
// Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb)
packageFilter: pkg => {
return {
...pkg,
// Alter the value of `main` before resolving the package
main: pkg.module || pkg.main,
};
},
});
};
```

### `restoreMocks` [boolean]

Default: `false`
Expand Down
37 changes: 37 additions & 0 deletions packages/jest-resolve/src/__tests__/resolve.test.ts
Expand Up @@ -9,6 +9,7 @@
import * as path from 'path';
import * as fs from 'graceful-fs';
import {ModuleMap} from 'jest-haste-map';
import {sync as resolveSync} from 'resolve';
import Resolver = require('../');
import userResolver from '../__mocks__/userResolver';
import nodeModulesPaths from '../nodeModulesPaths';
Expand All @@ -17,8 +18,23 @@ import type {ResolverConfig} from '../types';

jest.mock('../__mocks__/userResolver');

// Do not fully mock `resolve` because it is used by Jest. Doing it will crash
// in very strange ways. Instead just spy on the method `sync`.
jest.mock('resolve', () => {
const originalModule = jest.requireActual('resolve');
return {
...originalModule,
sync: jest.spyOn(originalModule, 'sync'),
};
});

const mockResolveSync = <
jest.Mock<ReturnType<typeof resolveSync>, Parameters<typeof resolveSync>>
>resolveSync;

beforeEach(() => {
userResolver.mockClear();
mockResolveSync.mockClear();
});

describe('isCoreModule', () => {
Expand Down Expand Up @@ -93,6 +109,27 @@ describe('findNodeModule', () => {
rootDir: undefined,
});
});

it('passes packageFilter to the resolve module when using the default resolver', () => {
const packageFilter = jest.fn();

// A resolver that delegates to defaultResolver with a packageFilter implementation
userResolver.mockImplementation((request, opts) =>
opts.defaultResolver(request, {...opts, packageFilter}),
);

Resolver.findNodeModule('test', {
basedir: '/',
resolver: require.resolve('../__mocks__/userResolver'),
});

expect(mockResolveSync).toHaveBeenCalledWith(
'test',
expect.objectContaining({
packageFilter,
}),
);
});
});

describe('resolveModule', () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/jest-resolve/src/defaultResolver.ts
Expand Up @@ -6,7 +6,7 @@
*/

import * as fs from 'graceful-fs';
import {sync as resolveSync} from 'resolve';
import {Opts as ResolveOpts, sync as resolveSync} from 'resolve';
import pnpResolver from 'jest-pnp-resolver';
import {tryRealpath} from 'jest-util';
import type {Config} from '@jest/types';
Expand All @@ -20,6 +20,7 @@ type ResolverOptions = {
moduleDirectory?: Array<string>;
paths?: Array<Config.Path>;
rootDir?: Config.Path;
packageFilter?: ResolveOpts['packageFilter'];
};

declare global {
Expand All @@ -45,6 +46,7 @@ export default function defaultResolver(
isDirectory,
isFile,
moduleDirectory: options.moduleDirectory,
packageFilter: options.packageFilter,
paths: options.paths,
preserveSymlinks: false,
realpathSync,
Expand Down

0 comments on commit 0fa9552

Please sign in to comment.