12
12
import Resolver from '../index' ;
13
13
import { createPackageAccessors , createResolutionContext } from './utils' ;
14
14
15
+ // Tests validating Package Exports resolution behaviour. See RFC0534:
16
+ // https://github.com/react-native-community/discussions-and-proposals/blob/master/proposals/0534-metro-package-exports-support.md
17
+ //
18
+ // '[nonstrict]' tests describe behaviour that is out-of-spec, but which Metro
19
+ // supports at feature launch for backwards compatibility. A future strict mode
20
+ // for exports will disable these features.
21
+
15
22
describe ( 'with package exports resolution disabled' , ( ) => {
16
23
test ( 'should ignore "exports" field for main entry point' , ( ) => {
17
24
const context = {
@@ -115,7 +122,7 @@ describe('with package exports resolution enabled', () => {
115
122
} ) ;
116
123
} ) ;
117
124
118
- test ( 'should fall back to "main" field resolution when file does not exist' , ( ) => {
125
+ test ( '[nonstrict] should fall back to "main" field resolution when file does not exist' , ( ) => {
119
126
const context = {
120
127
...baseContext ,
121
128
...createPackageAccessors ( {
@@ -134,7 +141,7 @@ describe('with package exports resolution enabled', () => {
134
141
// file missing message
135
142
} ) ;
136
143
137
- test ( 'should fall back to "main" field resolution when "exports" is an invalid subpath' , ( ) => {
144
+ test ( '[nonstrict] should fall back to "main" field resolution when "exports" is an invalid subpath' , ( ) => {
138
145
const context = {
139
146
...baseContext ,
140
147
...createPackageAccessors ( {
@@ -238,7 +245,7 @@ describe('with package exports resolution enabled', () => {
238
245
} ) ;
239
246
240
247
describe ( 'package encapsulation' , ( ) => {
241
- test ( 'should fall back to "browser" spec resolution and log inaccessible import warning' , ( ) => {
248
+ test ( '[nonstrict] should fall back to "browser" spec resolution and log inaccessible import warning' , ( ) => {
242
249
expect (
243
250
Resolver . resolve ( baseContext , 'test-pkg/private/bar' , null ) ,
244
251
) . toEqual ( {
@@ -269,6 +276,91 @@ describe('with package exports resolution enabled', () => {
269
276
} ) ;
270
277
} ) ;
271
278
279
+ describe ( 'subpath patterns' , ( ) => {
280
+ const baseContext = {
281
+ ...createResolutionContext ( {
282
+ '/root/src/main.js' : '' ,
283
+ '/root/node_modules/test-pkg/package.json' : JSON . stringify ( {
284
+ name : 'test-pkg' ,
285
+ main : 'index.js' ,
286
+ exports : {
287
+ './features/*.js' : './src/features/*.js' ,
288
+ './features/bar/*.js' : {
289
+ 'react-native' : null ,
290
+ } ,
291
+ './assets/*' : './assets/*' ,
292
+ } ,
293
+ } ) ,
294
+ '/root/node_modules/test-pkg/src/features/foo.js' : '' ,
295
+ '/root/node_modules/test-pkg/src/features/foo.js.js' : '' ,
296
+ '/root/node_modules/test-pkg/src/features/bar/Bar.js' : '' ,
297
+ '/root/node_modules/test-pkg/src/features/baz.native.js' : '' ,
298
+ '/root/node_modules/test-pkg/assets/Logo.js' : '' ,
299
+ } ) ,
300
+ originModulePath : '/root/src/main.js' ,
301
+ unstable_enablePackageExports : true ,
302
+ } ;
303
+
304
+ test ( 'should resolve subpath patterns in "exports" matching import specifier' , ( ) => {
305
+ for ( const [ importSpecifier , filePath ] of [
306
+ [
307
+ 'test-pkg/features/foo.js' ,
308
+ '/root/node_modules/test-pkg/src/features/foo.js' ,
309
+ ] ,
310
+ // Valid: Subpath patterns allow the match to be any substring between
311
+ // the pattern base and pattern trailer
312
+ [
313
+ 'test-pkg/features/foo.js.js' ,
314
+ '/root/node_modules/test-pkg/src/features/foo.js.js' ,
315
+ ] ,
316
+ [
317
+ 'test-pkg/features/bar/Bar.js' ,
318
+ '/root/node_modules/test-pkg/src/features/bar/Bar.js' ,
319
+ ] ,
320
+ ] ) {
321
+ expect ( Resolver . resolve ( baseContext , importSpecifier , null ) ) . toEqual ( {
322
+ type : 'sourceFile' ,
323
+ filePath,
324
+ } ) ;
325
+ }
326
+
327
+ expect ( ( ) =>
328
+ Resolver . resolve ( baseContext , 'test-pkg/features/foo' , null ) ,
329
+ ) . toThrowError ( ) ;
330
+ expect ( ( ) =>
331
+ Resolver . resolve ( baseContext , 'test-pkg/features/baz.js' , null ) ,
332
+ ) . toThrowError ( ) ;
333
+ } ) ;
334
+
335
+ test ( 'should use most specific pattern base' , ( ) => {
336
+ const context = {
337
+ ...baseContext ,
338
+ unstable_conditionNames : [ 'react-native' ] ,
339
+ } ;
340
+
341
+ // TODO(T145206395): Improve this error trace
342
+ expect ( ( ) =>
343
+ Resolver . resolve ( context , 'test-pkg/features/bar/Bar.js' , null ) ,
344
+ ) . toThrowErrorMatchingInlineSnapshot ( `
345
+ "Module does not exist in the Haste module map or in these directories:
346
+ /root/src/node_modules
347
+ /root/node_modules
348
+ /node_modules
349
+ "
350
+ ` ) ;
351
+ } ) ;
352
+
353
+ test ( '[nonstrict] should fall back to "browser" spec resolution and log inaccessible import warning' , ( ) => {
354
+ expect (
355
+ Resolver . resolve ( baseContext , 'test-pkg/assets/Logo.js' , null ) ,
356
+ ) . toEqual ( {
357
+ type : 'sourceFile' ,
358
+ filePath : '/root/node_modules/test-pkg/assets/Logo.js' ,
359
+ } ) ;
360
+ // TODO(T142200031): Assert inaccessible import warning is logged
361
+ } ) ;
362
+ } ) ;
363
+
272
364
describe ( 'conditional exports' , ( ) => {
273
365
const baseContext = {
274
366
...createResolutionContext ( {
0 commit comments