Skip to content

Commit 6d65a32

Browse files
huntiefacebook-github-bot
authored andcommitted
Fix selection of assets when assetExts include a multipart extension
Summary: Resolves #937. Previously, the matching of `resolver.assetExts` in the resolve and transform steps was based on `path.extname()`, which would only consider the final segment in a path split by a `'.'`. This meant the inability to configure and match multipart extensions in `assetExts`, such as `'.asset.json'`. The new implementation iterates over all possible file extensions. Validated against: - Internal test app. - Sample repo with relevant Metro config. Changelog: **[Fix]** `resolver.assetExts` will now match asset files for extension values that include a dot (`.`) Reviewed By: motiz88 Differential Revision: D43737453 fbshipit-source-id: 150c3aaf3bd58c24cd1670cc1a46e8531c24acf7
1 parent 439f0d0 commit 6d65a32

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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-local
8+
* @format
9+
* @oncall react_native
10+
*/
11+
12+
'use strict';
13+
14+
import path from 'path';
15+
import Resolver from '../index';
16+
import {createResolutionContext} from './utils';
17+
18+
describe('asset resolutions', () => {
19+
const baseContext = {
20+
...createResolutionContext({
21+
'/root/project/index.js': '',
22+
'/root/project/src/data.json': '',
23+
'/root/project/assets/example.asset.json': '',
24+
'/root/project/assets/icon.png': '',
25+
'/root/project/assets/[email protected]': '',
26+
}),
27+
originModulePath: '/root/project/index.js',
28+
};
29+
const assetResolutions = ['1', '2'];
30+
const resolveAsset = (
31+
dirPath: string,
32+
assetName: string,
33+
extension: string,
34+
) => {
35+
const basePath = dirPath + path.sep + assetName;
36+
let assets = [
37+
basePath + extension,
38+
...assetResolutions.map(
39+
resolution => basePath + '@' + resolution + 'x' + extension,
40+
),
41+
];
42+
43+
assets = assets.filter(candidate => baseContext.doesFileExist(candidate));
44+
45+
return assets.length ? assets : null;
46+
};
47+
48+
test('should resolve a path as an asset when matched against `assetExts`', () => {
49+
const context = {
50+
...baseContext,
51+
assetExts: new Set(['png']),
52+
resolveAsset,
53+
};
54+
55+
expect(Resolver.resolve(context, './assets/icon.png', null)).toEqual({
56+
type: 'assetFiles',
57+
filePaths: [
58+
'/root/project/assets/icon.png',
59+
'/root/project/assets/[email protected]',
60+
],
61+
});
62+
});
63+
64+
test('should resolve a path as an asset when matched against `assetExts` (overlap with `sourceExts`)', () => {
65+
const context = {
66+
...baseContext,
67+
assetExts: new Set(['asset.json']),
68+
resolveAsset,
69+
sourceExts: ['js', 'json'],
70+
};
71+
72+
// Source file matching `sourceExts`
73+
expect(Resolver.resolve(context, './src/data.json', null)).toEqual({
74+
type: 'sourceFile',
75+
filePath: '/root/project/src/data.json',
76+
});
77+
78+
// Asset file matching more specific asset ext
79+
expect(
80+
Resolver.resolve(context, './assets/example.asset.json', null),
81+
).toEqual({
82+
type: 'assetFiles',
83+
filePaths: ['/root/project/assets/example.asset.json'],
84+
});
85+
});
86+
});

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,17 @@ export default function isAssetFile(
1919
filePath: string,
2020
assetExts: $ReadOnlySet<string>,
2121
): boolean {
22-
return assetExts.has(path.extname(filePath).slice(1));
22+
const baseName = path.basename(filePath);
23+
24+
for (let i = baseName.length - 1; i >= 0; i--) {
25+
if (baseName[i] === '.') {
26+
const ext = baseName.slice(i + 1);
27+
28+
if (assetExts.has(ext)) {
29+
return true;
30+
}
31+
}
32+
}
33+
34+
return false;
2335
}

0 commit comments

Comments
 (0)