diff --git a/docs/Resolution.md b/docs/Resolution.md index 8cb6341c9b..f041c6ca27 100644 --- a/docs/Resolution.md +++ b/docs/Resolution.md @@ -168,6 +168,10 @@ The ordered list of fields in `package.json` that should be read to resolve a pa Given the path to a `package.json` file, returns the parsed file contents. +#### `getPackageForModule: (modulePath: string) => ?PackageInfo` + +Given a module path that may exist under an npm package, locates and returns the package root path and parsed `package.json` contents. + #### `resolveHasteModule: string => ?string` Resolves a Haste module name to an absolute path. Returns `null` if no such module exists. diff --git a/packages/metro-resolver/src/__tests__/utils.js b/packages/metro-resolver/src/__tests__/utils.js index 7baad385b4..fe4425af70 100644 --- a/packages/metro-resolver/src/__tests__/utils.js +++ b/packages/metro-resolver/src/__tests__/utils.js @@ -33,6 +33,7 @@ export function createResolutionContext( extraNodeModules: null, getPackage: (packageJsonPath: string) => JSON.parse(fileMap[packageJsonPath]), + getPackageForModule: () => null, isAssetFile: () => false, mainFields: ['browser', 'main'], nodeModulesPaths: [], diff --git a/packages/metro-resolver/src/types.js b/packages/metro-resolver/src/types.js index e41cda4e1b..b8ba05edc8 100644 --- a/packages/metro-resolver/src/types.js +++ b/packages/metro-resolver/src/types.js @@ -50,6 +50,11 @@ export type PackageJson = $ReadOnly<{ ... }>; +export type PackageInfo = $ReadOnly<{ + packageJson: PackageJson, + rootPath: string, +}>; + /** * Check existence of a single file. */ @@ -91,6 +96,12 @@ export type FileOrDirContext = $ReadOnly<{ * Get the parsed contents of the specified `package.json` file. */ getPackage: (packageJsonPath: string) => ?PackageJson, + + /** + * Get the package information and parsed `package.json` file for for a given + * module path, if it is contained within an npm package. + */ + getPackageForModule: (modulePath: string) => ?PackageInfo, }>; export type HasteContext = $ReadOnly<{ diff --git a/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js b/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js index d2e3dc0ae4..e1827a9382 100644 --- a/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js +++ b/packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js @@ -20,7 +20,7 @@ import type { ResolveAsset, } from 'metro-resolver'; import type {ResolverInputOptions} from '../../shared/types.flow'; -import type {PackageJson} from 'metro-resolver/src/types'; +import type {PackageInfo, PackageJson} from 'metro-resolver/src/types'; const {codeFrameColumns} = require('@babel/code-frame'); const fs = require('fs'); @@ -220,6 +220,7 @@ class ModuleResolver { resolveHastePackage: (name: string) => this._options.getHastePackagePath(name, platform), getPackage: this._getPackage, + getPackageForModule: this._getPackageForModule, }, moduleName, platform, @@ -282,6 +283,24 @@ class ModuleResolver { return null; }; + _getPackageForModule = (modulePath: string): ?PackageInfo => { + let pkg; + + try { + pkg = this._options.moduleCache.getPackageOf(modulePath); + } catch (e) { + // Do nothing. The standard module cache does not trigger any error, but + // the ModuleGraph one does, if the module does not exist. + } + + return pkg != null + ? { + rootPath: path.dirname(pkg.path), + packageJson: pkg.read(), + } + : null; + }; + /** * TODO: Return Resolution instead of coercing to BundlerResolution here */