Skip to content

Commit c6548f7

Browse files
huntiefacebook-github-bot
authored andcommitted
Add assetExts to ResolutionContext, remove isAssetFile
Summary: This commit removes the `isAssetFile` API used both for the module resolver and by `transformHelpers` — both based on `resolver.assetExts`, but the former being user-overridable in the resolver via `ResolutionContext.isAssetFile`. Motivation: - Correctness: While `ResolutionContext.isAssetFile` is respected by the resolver, the current `node-haste` implementation in Metro discards this selection, coercing the resolution to a `sourceFile`. With this change, a common `isAssetFile` implementation is now shared by both layers, leading to consistent behaviour when customised. - Simplicity: `assetExts` is sufficiently expressive for most use cases — and can be bailed out from using `ResolutionContext.resolveRequest`. Changelog: **[Breaking]** Remove `isAssetFile` from custom resolver context, add `assetExts` Reviewed By: motiz88 Differential Revision: D43735610 fbshipit-source-id: 77d4aa7e17b27b24d1fae87a162753beb1501d92
1 parent 6442685 commit c6548f7

File tree

11 files changed

+44
-33
lines changed

11 files changed

+44
-33
lines changed

docs/Resolution.md

+5-7
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ These are the rules that Metro's default resolver follows. Refer to [`metro-reso
6262

6363
2. Otherwise, try to resolve _moduleName_ as a relative or absolute path:
6464
1. If the path is relative, convert it to an absolute path by prepending the current directory (i.e. parent of [`context.originModulePath`](#originmodulepath-string)).
65-
2. If the path refers to an [asset](#isassetfile-string--boolean):
65+
2. If the path refers to an [asset](#assetexts-readonlysetstring):
6666

6767
1. Use [`context.resolveAsset`](#resolveasset-dirpath-string-assetname-string-extension-string--readonlyarraystring) to collect all asset variants.
6868
2. Return an [asset resolution](#asset-files) containing the collected asset paths.
@@ -120,18 +120,16 @@ These are the rules that Metro's default resolver follows. Refer to [`metro-reso
120120

121121
### Resolution context
122122

123+
#### `assetExts: $ReadOnlySet<string>`
124+
125+
The set of file extensions used to identify asset files. Defaults to [`resolver.assetExts`](./Configuration.md#assetexts).
126+
123127
#### `doesFileExist: string => boolean`
124128

125129
Returns `true` if the file with the given path exists, or `false` otherwise.
126130

127131
By default, Metro implements this by consulting an in-memory map of the filesystem that has been prepared in advance. This approach avoids disk I/O during module resolution.
128132

129-
#### `isAssetFile: string => boolean`
130-
131-
Returns `true` if the given path represents an asset file, or `false` otherwise.
132-
133-
By default, Metro implements this by checking the file's extension against [`resolver.assetExts`](./Configuration.md#assetexts).
134-
135133
#### `nodeModulesPaths: $ReadOnlyArray<string>`
136134

137135
A list of paths to check for modules after looking through all `node_modules` directories.

packages/metro-resolver/src/PackageExportsResolve.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import InvalidModuleSpecifierError from './errors/InvalidModuleSpecifierError';
1616
import InvalidPackageConfigurationError from './errors/InvalidPackageConfigurationError';
1717
import PackagePathNotExportedError from './errors/PackagePathNotExportedError';
1818
import resolveAsset from './resolveAsset';
19+
import isAssetFile from './utils/isAssetFile';
1920
import toPosixPath from './utils/toPosixPath';
2021

2122
/**
@@ -99,7 +100,7 @@ export function resolvePackageTargetFromExports(
99100
patternMatch != null ? target.replace('*', patternMatch) : target,
100101
);
101102

102-
if (context.isAssetFile(filePath)) {
103+
if (isAssetFile(filePath, context.assetExts)) {
103104
const assetResult = resolveAsset(context, filePath);
104105

105106
if (assetResult != null) {

packages/metro-resolver/src/__tests__/package-exports-test.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,6 @@ describe('with package exports resolution enabled', () => {
771771

772772
describe('asset resolutions', () => {
773773
const assetResolutions = ['1', '1.5', '2', '3', '4'];
774-
const isAssetFile = (filePath: string) => filePath.endsWith('.png');
775774

776775
const baseContext = {
777776
...createResolutionContext({
@@ -786,7 +785,7 @@ describe('with package exports resolution enabled', () => {
786785
'/root/node_modules/test-pkg/assets/icons/metro@2x.png': '',
787786
'/root/node_modules/test-pkg/assets/icons/metro@3x.png': '',
788787
}),
789-
isAssetFile,
788+
assetExts: new Set(['png']),
790789
originModulePath: '/root/src/main.js',
791790
unstable_enablePackageExports: true,
792791
};

packages/metro-resolver/src/__tests__/utils.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ export function createResolutionContext(
3333
): $Diff<ResolutionContext, {originModulePath: string}> {
3434
return {
3535
allowHaste: true,
36+
assetExts: new Set(['jpg', 'png']),
3637
customResolverOptions: {},
3738
disableHierarchicalLookup: false,
3839
extraNodeModules: null,
39-
isAssetFile: () => false,
4040
mainFields: ['browser', 'main'],
4141
nodeModulesPaths: [],
4242
preferNativePlatform: false,

packages/metro-resolver/src/index.js

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ export type {
2121
FileCandidates,
2222
FileResolution,
2323
GetRealPath,
24-
IsAssetFile,
2524
ResolutionContext,
2625
Resolution,
2726
ResolveAsset,

packages/metro-resolver/src/resolve.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import formatFileCandidates from './errors/formatFileCandidates';
3030
import {getPackageEntryPoint} from './PackageResolve';
3131
import {resolvePackageTargetFromExports} from './PackageExportsResolve';
3232
import resolveAsset from './resolveAsset';
33+
import isAssetFile from './utils/isAssetFile';
3334

3435
function resolve(
3536
context: ResolutionContext,
@@ -369,7 +370,7 @@ function resolveFile(
369370
fileName: string,
370371
platform: string | null,
371372
): Result<Resolution, FileCandidates> {
372-
if (context.isAssetFile(fileName)) {
373+
if (isAssetFile(fileName, context.assetExts)) {
373374
const assetResolutions = resolveAsset(
374375
context,
375376
path.join(dirPath, fileName),

packages/metro-resolver/src/types.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ export type PackageInfo = $ReadOnly<{
7171
*/
7272
export type DoesFileExist = (filePath: string) => boolean;
7373
export type GetRealPath = (path: string) => ?string;
74-
export type IsAssetFile = (fileName: string) => boolean;
7574

7675
/**
7776
* Given a directory path and the base asset name, return a list of all the
@@ -87,6 +86,7 @@ export type ResolveAsset = (
8786

8887
export type ResolutionContext = $ReadOnly<{
8988
allowHaste: boolean,
89+
assetExts: $ReadOnlySet<string>,
9090
customResolverOptions: CustomResolverOptions,
9191
disableHierarchicalLookup: boolean,
9292
doesFileExist: DoesFileExist,
@@ -103,8 +103,6 @@ export type ResolutionContext = $ReadOnly<{
103103
*/
104104
getPackageForModule: (modulePath: string) => ?PackageInfo,
105105

106-
isAssetFile: IsAssetFile,
107-
108106
/**
109107
* The ordered list of fields to read in `package.json` to resolve a main
110108
* entry point based on the "browser" field spec.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
* @format
9+
* @oncall react_native
10+
*/
11+
12+
import path from 'path';
13+
14+
/**
15+
* Determine if a file path should be considered an asset file based on the
16+
* given `assetExts`.
17+
*/
18+
export default function isAssetFile(
19+
filePath: string,
20+
assetExts: $ReadOnlySet<string>,
21+
): boolean {
22+
return assetExts.has(path.extname(filePath).slice(1));
23+
}

packages/metro/src/lib/transformHelpers.js

+5-8
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import type {Type} from 'metro-transform-worker';
2323
import type {RequireContext} from './contextModule';
2424

2525
import {getContextModuleTemplate} from './contextModuleTemplates';
26+
import isAssetFile from 'metro-resolver/src/utils/isAssetFile';
2627

27-
const path = require('path');
2828
import type {ResolverInputOptions} from '../shared/types.flow';
2929

3030
type InlineRequiresRaw = {+blockList: {[string]: true, ...}, ...} | boolean;
@@ -143,6 +143,7 @@ async function getTransformFn(
143143
options,
144144
resolverOptions,
145145
);
146+
const assetExts = new Set(config.resolver.assetExts);
146147

147148
return async (modulePath: string, requireContext: ?RequireContext) => {
148149
let templateBuffer: Buffer;
@@ -173,11 +174,7 @@ async function getTransformFn(
173174
modulePath,
174175
{
175176
...transformOptions,
176-
type: getType(
177-
transformOptions.type,
178-
modulePath,
179-
config.resolver.assetExts,
180-
),
177+
type: getType(transformOptions.type, modulePath, assetExts),
181178
inlineRequires: removeInlineRequiresBlockListFromOptions(
182179
modulePath,
183180
inlineRequires,
@@ -191,13 +188,13 @@ async function getTransformFn(
191188
function getType(
192189
type: string,
193190
filePath: string,
194-
assetExts: $ReadOnlyArray<string>,
191+
assetExts: $ReadOnlySet<string>,
195192
): Type {
196193
if (type === 'script') {
197194
return type;
198195
}
199196

200-
if (assetExts.indexOf(path.extname(filePath).slice(1)) !== -1) {
197+
if (isAssetFile(filePath, assetExts)) {
201198
return 'asset';
202199
}
203200

packages/metro/src/node-haste/DependencyGraph.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ function getOrCreateMap<T>(
5353
}
5454

5555
class DependencyGraph extends EventEmitter {
56-
_assetExtensions: Set<string>;
5756
_config: ConfigT;
5857
_haste: MetroFileMap;
5958
_fileSystem: FileSystem;
@@ -89,9 +88,6 @@ class DependencyGraph extends EventEmitter {
8988
super();
9089

9190
this._config = config;
92-
this._assetExtensions = new Set(
93-
config.resolver.assetExts.map(asset => '.' + asset),
94-
);
9591

9692
const {hasReducedPerformance, watch} = options ?? {};
9793
const initializingMetroLogEntry = log(
@@ -176,6 +172,7 @@ class DependencyGraph extends EventEmitter {
176172

177173
_createModuleResolver() {
178174
this._moduleResolver = new ModuleResolver({
175+
assetExts: new Set(this._config.resolver.assetExts),
179176
dirExists: (filePath: string) => {
180177
try {
181178
return fs.lstatSync(filePath).isDirectory();
@@ -191,7 +188,6 @@ class DependencyGraph extends EventEmitter {
191188
this._hasteModuleMap.getModule(name, platform, true),
192189
getHastePackagePath: (name, platform) =>
193190
this._hasteModuleMap.getPackage(name, platform, true),
194-
isAssetFile: file => this._assetExtensions.has(path.extname(file)),
195191
mainFields: this._config.resolver.resolverMainFields,
196192
moduleCache: this._moduleCache,
197193
nodeModulesPaths: this._config.resolver.nodeModulesPaths,

packages/metro/src/node-haste/DependencyGraph/ModuleResolution.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import type {
1616
DoesFileExist,
1717
FileCandidates,
1818
GetRealPath,
19-
IsAssetFile,
2019
Resolution,
2120
ResolveAsset,
2221
} from 'metro-resolver';
@@ -55,14 +54,14 @@ export type ModuleishCache<TPackage> = interface {
5554
};
5655

5756
type Options<TPackage> = $ReadOnly<{
57+
assetExts: $ReadOnlySet<string>,
5858
dirExists: DirExistsFn,
5959
disableHierarchicalLookup: boolean,
6060
doesFileExist: DoesFileExist,
6161
emptyModulePath: string,
6262
extraNodeModules: ?Object,
6363
getHasteModulePath: (name: string, platform: ?string) => ?string,
6464
getHastePackagePath: (name: string, platform: ?string) => ?string,
65-
isAssetFile: IsAssetFile,
6665
mainFields: $ReadOnlyArray<string>,
6766
moduleCache: ModuleishCache<TPackage>,
6867
nodeModulesPaths: $ReadOnlyArray<string>,
@@ -126,10 +125,10 @@ class ModuleResolver<TPackage: Packageish> {
126125
resolverOptions: ResolverInputOptions,
127126
): BundlerResolution {
128127
const {
128+
assetExts,
129129
disableHierarchicalLookup,
130130
doesFileExist,
131131
extraNodeModules,
132-
isAssetFile,
133132
mainFields,
134133
nodeModulesPaths,
135134
preferNativePlatform,
@@ -146,10 +145,10 @@ class ModuleResolver<TPackage: Packageish> {
146145
const result = Resolver.resolve(
147146
createDefaultContext({
148147
allowHaste,
148+
assetExts,
149149
disableHierarchicalLookup,
150150
doesFileExist,
151151
extraNodeModules,
152-
isAssetFile,
153152
mainFields,
154153
nodeModulesPaths,
155154
preferNativePlatform,

0 commit comments

Comments
 (0)