Skip to content

Commit

Permalink
feat: support . in exports field
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Oct 15, 2021
1 parent 3674bbf commit da3ab0f
Show file tree
Hide file tree
Showing 26 changed files with 119 additions and 72 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Features

- `[jest-resolver]` Support default export (`.`) in `exports` field _if_ `main` is missing ([#11919](https://github.com/facebook/jest/pull/11919))

### Fixes

- `[jest-runtime]` Ensure absolute paths can be resolved within test modules ([11943](https://github.com/facebook/jest/pull/11943))
Expand Down
5 changes: 0 additions & 5 deletions e2e/__tests__/resolveConditions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,10 @@

import {resolve} from 'path';
import {onNodeVersions} from '@jest/test-utils';
import {runYarnInstall} from '../Utils';
import runJest from '../runJest';

const dir = resolve(__dirname, '..', 'resolve-conditions');

beforeAll(() => {
runYarnInstall(dir);
});

// The versions where vm.Module exists and commonjs with "exports" is not broken
onNodeVersions('>=12.16.0', () => {
test('resolves package exports correctly with custom resolver', () => {
Expand Down
2 changes: 1 addition & 1 deletion e2e/resolve-conditions/__tests__/browser.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @jest-environment <rootDir>/browser-env.js
*/

import {fn} from '../fake-dual-dep';
import {fn} from 'fake-dual-dep';

test('returns correct message', () => {
expect(fn()).toEqual('hello from browser');
Expand Down
2 changes: 1 addition & 1 deletion e2e/resolve-conditions/__tests__/node.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @jest-environment <rootDir>/node-env.js
*/

import {fn} from '../fake-dual-dep';
import {fn} from 'fake-dual-dep';

test('returns correct message', () => {
expect(fn()).toEqual('hello from node');
Expand Down
2 changes: 1 addition & 1 deletion e2e/resolve-conditions/__tests__/resolveCjs.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

const {fn} = require('../fake-dep');
const {fn} = require('fake-dep');

test('returns correct message', () => {
expect(fn()).toEqual('hello from CJS');
Expand Down
2 changes: 1 addition & 1 deletion e2e/resolve-conditions/__tests__/resolveEsm.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {fn} from '../fake-dep';
import {fn} from 'fake-dep';

test('returns correct message', () => {
expect(fn()).toEqual('hello from ESM');
Expand Down
File renamed without changes.
File renamed without changes.
4 changes: 0 additions & 4 deletions e2e/resolve-conditions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@
"mjs",
"json"
],
"resolver": "<rootDir>/resolver.js",
"testMatch": [
"<rootDir>/**/*.test.*"
],
"transform": {}
},
"dependencies": {
"resolve.exports": "^1.1.0"
}
}
30 changes: 0 additions & 30 deletions e2e/resolve-conditions/resolver.js

This file was deleted.

21 changes: 0 additions & 21 deletions e2e/resolve-conditions/yarn.lock

This file was deleted.

1 change: 1 addition & 0 deletions packages/jest-resolve/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"jest-util": "^27.2.5",
"jest-validate": "^27.2.5",
"resolve": "^1.20.0",
"resolve.exports": "^1.1.0",
"slash": "^3.0.0"
},
"devDependencies": {
Expand Down
Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions packages/jest-resolve/src/__mocks__/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "__mocks__",
"version": "1.0.0",
"dependencies": {
}
}
51 changes: 43 additions & 8 deletions packages/jest-resolve/src/__tests__/resolve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,61 @@ describe('findNodeModule', () => {
});
});

it('passes packageFilter to the resolve module when using the default resolver', () => {
it('wraps passed 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.findNodeModule('./test', {
basedir: path.resolve(__dirname, '../__mocks__/'),
resolver: require.resolve('../__mocks__/userResolver'),
});

expect(mockResolveSync).toHaveBeenCalledWith(
'test',
expect.objectContaining({
packageFilter,
}),
expect(packageFilter).toHaveBeenCalledWith(
expect.objectContaining({name: '__mocks__'}),
expect.any(String),
);
});

describe('conditions', () => {
const conditionsRoot = path.resolve(__dirname, '../__mocks__/conditions');

test('resolves without exports, just main', () => {
const result = Resolver.findNodeModule('main', {
basedir: conditionsRoot,
conditions: ['require'],
});

expect(result).toEqual(
path.resolve(conditionsRoot, './node_modules/main/file.js'),
);
});

test('resolves with import', () => {
const result = Resolver.findNodeModule('import', {
basedir: conditionsRoot,
conditions: ['import'],
});

expect(result).toEqual(
path.resolve(conditionsRoot, './node_modules/import/file.js'),
);
});

test('resolves with require', () => {
const result = Resolver.findNodeModule('require', {
basedir: conditionsRoot,
conditions: ['require'],
});

expect(result).toEqual(
path.resolve(conditionsRoot, './node_modules/require/file.js'),
);
});
});
});

describe('resolveModule', () => {
Expand Down
39 changes: 39 additions & 0 deletions packages/jest-resolve/src/defaultResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import * as fs from 'graceful-fs';
import pnpResolver from 'jest-pnp-resolver';
import {Opts as ResolveOpts, sync as resolveSync} from 'resolve';
import {
Options as ResolveExportsOptions,
resolve as resolveExports,
} from 'resolve.exports';
import type {Config} from '@jest/types';
import {tryRealpath} from 'jest-util';

Expand Down Expand Up @@ -43,6 +47,10 @@ export default function defaultResolver(
...options,
isDirectory,
isFile,
packageFilter: createPackageFilter(
options.conditions,
options.packageFilter,
),
preserveSymlinks: false,
readPackageSync,
realpathSync,
Expand Down Expand Up @@ -149,3 +157,34 @@ function realpathSync(file: Config.Path): Config.Path {
function readPackageSync(_: unknown, file: Config.Path): PkgJson {
return readPackageCached(file);
}

function createPackageFilter(
conditions?: Array<string>,
userFilter?: ResolverOptions['packageFilter'],
): ResolverOptions['packageFilter'] {
function attemptExportsFallback(pkg: PkgJson) {
const options: ResolveExportsOptions = conditions
? {conditions, unsafe: true}
: // no conditions were passed - let's assume this is Jest internal and it should be `require`
{browser: false, require: true};

try {
return resolveExports(pkg, '.', options);
} catch {
return undefined;
}
}

return function packageFilter(pkg: PkgJson, ...rest) {
let filteredPkg = pkg;

if (userFilter) {
filteredPkg = userFilter(filteredPkg, ...rest);
}

return {
...filteredPkg,
main: filteredPkg.main ?? attemptExportsFallback(filteredPkg),
};
};
}
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13027,6 +13027,7 @@ fsevents@^1.2.7:
jest-util: ^27.2.5
jest-validate: ^27.2.5
resolve: ^1.20.0
resolve.exports: ^1.1.0
slash: ^3.0.0
languageName: unknown
linkType: soft
Expand Down Expand Up @@ -18945,6 +18946,13 @@ react-native@0.64.0:
languageName: node
linkType: hard

"resolve.exports@npm:^1.1.0":
version: 1.1.0
resolution: "resolve.exports@npm:1.1.0"
checksum: d04d2ce651fac14fe6ba13b377690f790cbbe91e6211b8fbec97ee08282e278875c74073a9b6243143a64e33d95eefb479e1dd4965664edc73b28b712100b36c
languageName: node
linkType: hard

"resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.15.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2":
version: 1.20.0
resolution: "resolve@npm:1.20.0"
Expand Down

0 comments on commit da3ab0f

Please sign in to comment.