Skip to content

Commit

Permalink
Pass original dependency specifier into the resolver
Browse files Browse the repository at this point in the history
Summary:
Changelog:
* **[Breaking]**: `DependencyGraph.resolveDependency` now takes a dependency object instead of a string.
* **[Feature]**: Custom resolvers may access the original dependency descriptor for diagnostic purposes.

Makes the full `TransformResultDependency` object ( = dependency descriptor extracted by `collectDependencies`) available to Metro's resolver, including custom resolvers. For the time being, this infrastructure will be used for improved diagnostics and explicitly *not* for any change to resolution semantics. We will likely loosen this restriction in the future (e.g. to introduce Node-compatible ESM/CJS resolution, more compliant `exports`, etc).

The improved diagnostics themselves are coming in a separate diff for ease of review.

Reviewed By: GijsWeterings

Differential Revision: D47453640

fbshipit-source-id: a743875ec1238c9fd50a14bd250aeae2973c12ab
  • Loading branch information
motiz88 authored and facebook-github-bot committed Aug 4, 2023
1 parent fe23a67 commit fbdd295
Show file tree
Hide file tree
Showing 29 changed files with 434 additions and 249 deletions.
26 changes: 26 additions & 0 deletions docs/Resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,32 @@ When calling the default resolver with a non-null `resolveRequest` function, it

Inside a custom resolver, `resolveRequest` is set to the default resolver function, for easy chaining and customization.

#### `dependency: ?Dependency`

A dependency descriptor corresponding to the current resolution request. This is provided for diagnostic purposes *only* and may not be used for semantic purposes. See the [Caching](#caching) section for more information.

```flow
type Dependency = {
// The literal name provided to a require or import call. For example 'foo' in
// case of `require('foo')`.
name: string,
data: {
// A locally unique key for this dependency within the origin module.
key: string,
// Source locations from the Babel AST, relative to the origin module, where
// this dependency was encountered. This may be an empty array.
locs: $ReadOnlyArray<BabelSourceLocation>,
asyncType: 'async' | 'prefetch' | 'weak' | null,
// Other properties are considered internal and may change in the future.
...
},
};
```

## Caching

Resolver results may be cached under the following conditions:
Expand Down
3 changes: 3 additions & 0 deletions packages/metro-resolver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@
"license": "MIT",
"engines": {
"node": ">=18"
},
"devDependencies": {
"metro": "0.77.0"
}
}
2 changes: 1 addition & 1 deletion packages/metro-resolver/src/PackageExportsResolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
* @oncall react_native
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-resolver/src/PackageResolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
* @oncall react_native
*/
Expand Down
7 changes: 6 additions & 1 deletion packages/metro-resolver/src/createDefaultContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* @oncall react_native
*/

import type {TransformResultDependency} from 'metro/src/DeltaBundler/types.flow';
import type {ResolutionContext} from './types';

import {redirectModulePath} from './PackageResolve';
Expand All @@ -23,10 +24,14 @@ type PartialContext = $ReadOnly<{
* As context values can be overridden by callers, this occurs externally to
* `resolve.js`.
*/
function createDefaultContext(context: PartialContext): ResolutionContext {
function createDefaultContext(
context: PartialContext,
dependency: TransformResultDependency,
): ResolutionContext {
return {
redirectModulePath: (modulePath: string) =>
redirectModulePath(context, modulePath),
dependency,
...context,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
* @oncall react_native
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-resolver/src/errors/InvalidPackageError.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
* @oncall react_native
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-resolver/src/errors/formatFileCandidates.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
* @oncall react_native
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-resolver/src/resolveAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
* @oncall react_native
*/
Expand Down
14 changes: 12 additions & 2 deletions packages/metro-resolver/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
* @format
* @oncall react_native
*/

'use strict';

import type {TransformResultDependency} from 'metro/src/DeltaBundler/types.flow';

export type Result<+TResolution, +TCandidates> =
| {+type: 'resolved', +resolution: TResolution}
| {+type: 'failed', +candidates: TCandidates};
Expand Down Expand Up @@ -123,6 +125,13 @@ export type ResolutionContext = $ReadOnly<{
*/
getPackageForModule: (modulePath: string) => ?PackageInfo,

/**
* The dependency descriptor, within the origin module, corresponding to the
* current resolution request. This is provided for diagnostic purposes ONLY
* and may not be used for resolution purposes.
*/
dependency?: TransformResultDependency,

/**
* The ordered list of fields to read in `package.json` to resolve a main
* entry point based on the "browser" field spec.
Expand All @@ -131,7 +140,8 @@ export type ResolutionContext = $ReadOnly<{

/**
* Full path of the module that is requiring or importing the module to be
* resolved.
* resolved. This may not be the only place this dependency was found,
* as resolutions can be cached.
*/
originModulePath: string,

Expand Down
12 changes: 11 additions & 1 deletion packages/metro-resolver/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
* @oncall react_native
*/

import {TransformResultDependency} from 'metro';

export type Result<TResolution, TCandidates> =
| {readonly type: 'resolved'; readonly resolution: TResolution}
| {readonly type: 'failed'; readonly candidates: TCandidates};
Expand Down Expand Up @@ -102,6 +104,13 @@ export interface ResolutionContext {
*/
readonly getPackageForModule: (modulePath: string) => PackageInfo | null;

/**
* The dependency descriptor, within the origin module, corresponding to the
* current resolution request. This is provided for diagnostic purposes ONLY
* and may not be used for resolution purposes.
*/
readonly dependency?: TransformResultDependency;

/**
* The ordered list of fields to read in `package.json` to resolve a main
* entry point based on the "browser" field spec.
Expand All @@ -110,7 +119,8 @@ export interface ResolutionContext {

/**
* Full path of the module that is requiring or importing the module to be
* resolved.
* resolved. This may not be the only place this dependency was found,
* as resolutions can be cached.
*/
readonly originModulePath: string;

Expand Down
2 changes: 1 addition & 1 deletion packages/metro/src/DeltaBundler/Graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ export class Graph<T = MixedOutput> {
} else {
try {
resolvedDep = {
absolutePath: options.resolve(parentPath, dep.name).filePath,
absolutePath: options.resolve(parentPath, dep).filePath,
data: dep,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

'use strict';

import type {TransformResultDependency} from '../types.flow';

jest.mock('../DeltaCalculator');

const DeltaBundler = require('../../DeltaBundler');
Expand All @@ -33,7 +35,7 @@ describe('DeltaBundler', () => {
unstable_enablePackageExports: false,
lazy: false,
onProgress: null,
resolve: (from: string, to: string) => {
resolve: (from: string, dependency: TransformResultDependency) => {
throw new Error('Never called');
},
shallow: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

'use strict';

import type {Options} from '../types.flow';
import type {Options, TransformResultDependency} from '../types.flow';
import type {Result} from '../Graph';
import CountingSet from '../../lib/CountingSet';
import {Graph} from '../Graph';
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('DeltaCalculator + require.context', () => {
unstable_enablePackageExports: false,
lazy: false,
onProgress: null,
resolve: (from: string, to: string) => {
resolve: (from: string, to: TransformResultDependency) => {
throw new Error('Never called');
},
shallow: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
* @oncall react_native
*/

import type {Module, Options, Dependency} from '../types.flow';
import type {
Module,
Options,
Dependency,
TransformResultDependency,
} from '../types.flow';
import type {Graph as GraphType, Result} from '../Graph';

import path from 'path';
Expand All @@ -34,7 +39,7 @@ describe.each(['linux', 'win32'])('DeltaCalculator (%s)', osPlatform => {
unstable_enablePackageExports: true,
lazy: false,
onProgress: null,
resolve: (from: string, to: string) => {
resolve: (from: string, to: TransformResultDependency) => {
throw new Error('Never called');
},
shallow: false,
Expand Down
4 changes: 2 additions & 2 deletions packages/metro/src/DeltaBundler/__tests__/Graph-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,9 @@ beforeEach(async () => {
unstable_enablePackageExports: false,
lazy: false,
onProgress: null,
resolve: (from: string, to: string) => {
resolve: (from: string, to: TransformResultDependency) => {
const deps = getMockDependency(from);
const {path} = deps.filter(dep => dep.name === to)[0];
const {path} = deps.filter(dep => dep.name === to.name)[0];

if (!mockedDependencies.has(path)) {
throw new Error(`Dependency not found: ${from} -> ${path}`);
Expand Down
Loading

0 comments on commit fbdd295

Please sign in to comment.