Skip to content

Commit

Permalink
feat(resolver): support package self-reference (#12682)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Apr 17, 2022
1 parent defbc05 commit 77b0f17
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -40,6 +40,7 @@
- `[jest-reporters]` Add GitHub Actions reporter ([#11320](https://github.com/facebook/jest/pull/11320), [#12658](https://github.com/facebook/jest/pull/12658)
- `[jest-reporters]` Pass `reporterContext` to custom reporter constructors as third argument ([#12657](https://github.com/facebook/jest/pull/12657))
- `[jest-resolve]` [**BREAKING**] Add support for `package.json` `exports` ([#11961](https://github.com/facebook/jest/pull/11961), [#12373](https://github.com/facebook/jest/pull/12373))
- `[jest-resolve]` Support package self-reference ([#12682](https://github.com/facebook/jest/pull/12682))
- `[jest-resolve, jest-runtime]` Add support for `data:` URI import and mock ([#12392](https://github.com/facebook/jest/pull/12392))
- `[jest-resolve, jest-runtime]` Add support for async resolver ([#11540](https://github.com/facebook/jest/pull/11540))
- `[jest-runner]` Allow `setupFiles` module to export an async function ([#12042](https://github.com/facebook/jest/pull/12042))
Expand Down
Empty file.
Empty file.
@@ -0,0 +1,3 @@
{
"name": "foo-no-exports"
}
Empty file.
@@ -0,0 +1,4 @@
{
"name": "foo-other-name",
"exports": "./file.js"
}
Empty file.
4 changes: 4 additions & 0 deletions packages/jest-resolve/src/__mocks__/self-ref/foo/package.json
@@ -0,0 +1,4 @@
{
"name": "foo",
"exports": "./file.js"
}
3 changes: 3 additions & 0 deletions packages/jest-resolve/src/__mocks__/self-ref/package.json
@@ -0,0 +1,3 @@
{
"name": "self-ref"
}
48 changes: 48 additions & 0 deletions packages/jest-resolve/src/__tests__/resolve.test.ts
Expand Up @@ -37,6 +37,8 @@ beforeEach(() => {
userResolver.mockClear();
userResolverAsync.async.mockClear();
mockResolveSync.mockClear();

Resolver.clearDefaultResolverCache();
});

describe('isCoreModule', () => {
Expand Down Expand Up @@ -251,6 +253,52 @@ describe('findNodeModule', () => {
);
});
});

describe('self-reference', () => {
const selfRefRoot = path.resolve(__dirname, '../__mocks__/self-ref');

test('supports self-reference', () => {
const result = Resolver.findNodeModule('foo', {
basedir: path.resolve(selfRefRoot, './foo/index.js'),
conditions: [],
});

expect(result).toEqual(path.resolve(selfRefRoot, './foo/file.js'));
});

test('supports nested self-reference', () => {
const result = Resolver.findNodeModule('foo', {
basedir: path.resolve(selfRefRoot, './foo/nested/index.js'),
conditions: [],
});

expect(result).toEqual(path.resolve(selfRefRoot, './foo/file.js'));
});

test('fails if own pkg.json with different name', () => {
const result = Resolver.findNodeModule('foo', {
basedir: path.resolve(
selfRefRoot,
'./foo/nested-with-own-pkg/index.js',
),
conditions: [],
});

expect(result).toEqual(null);
});

test('fails if own pkg.json with no exports', () => {
const result = Resolver.findNodeModule('foo-no-exports', {
basedir: path.resolve(
selfRefRoot,
'./foo/nested-with-no-exports/index.js',
),
conditions: [],
});

expect(result).toEqual(null);
});
});
});

describe('findNodeModuleAsync', () => {
Expand Down
36 changes: 32 additions & 4 deletions packages/jest-resolve/src/defaultResolver.ts
Expand Up @@ -14,6 +14,7 @@ import {
} from 'resolve.exports';
import {
PkgJson,
findClosestPackageJson,
isDirectory,
isFile,
readPackageCached,
Expand All @@ -36,7 +37,7 @@ interface ResolverOptions {
}

type UpstreamResolveOptionsWithConditions = UpstreamResolveOptions &
Pick<ResolverOptions, 'conditions'>;
Pick<ResolverOptions, 'basedir' | 'conditions'>;

export type SyncResolver = (path: string, options: ResolverOptions) => string;
export type AsyncResolver = (
Expand Down Expand Up @@ -112,6 +113,30 @@ function getPathInModule(
moduleName = `${moduleName}/${segments.shift()}`;
}

// self-reference
const closestPackageJson = findClosestPackageJson(options.basedir);
if (closestPackageJson) {
const pkg = readPackageCached(closestPackageJson);

if (pkg.name === moduleName && pkg.exports) {
const subpath = segments.join('/') || '.';

const resolved = resolveExports(
pkg,
subpath,
createResolveOptions(options.conditions),
);

if (!resolved) {
throw new Error(
'`exports` exists, but no results - this is a bug in Jest. Please report an issue',
);
}

return pathResolve(dirname(closestPackageJson), resolved);
}
}

let packageJsonPath = '';

try {
Expand All @@ -132,10 +157,13 @@ function getPathInModule(
createResolveOptions(options.conditions),
);

// TODO: should we throw if not?
if (resolved) {
return pathResolve(dirname(packageJsonPath), resolved);
if (!resolved) {
throw new Error(
'`exports` exists, but no results - this is a bug in Jest. Please report an issue',
);
}

return pathResolve(dirname(packageJsonPath), resolved);
}
}
}
Expand Down

0 comments on commit 77b0f17

Please sign in to comment.