From f321cffa73dac3cd407a0865462e62e25aa531f4 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Fri, 31 Mar 2023 08:57:15 -0700 Subject: [PATCH] Add support for "exports" root subpath array shorthand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Adds support for the `"exports"` root value being an array of strings, an edge-case legacy usage that technically remains in the Node.js 19.x spec — see [`"exports"` field (Type)](https://nodejs.org/docs/latest-v19.x/api/packages.html#exports). Behaviour: ```js "exports": [ "./lib/index.js" ], ``` expands to ```js "exports": { "./lib/index.js": "./lib/index.js" }, ``` Changelog: **[Experimental]** Add compatibility with legacy Node.js "exports" array formats Reviewed By: jacdebug Differential Revision: D44338043 fbshipit-source-id: e9e719358c2610f4ae6d120d8312fcc647727da4 --- .../src/PackageExportsResolve.js | 21 ++++++++++++++--- .../src/__tests__/package-exports-test.js | 23 +++++++++++++++++++ packages/metro-resolver/src/types.js | 4 +++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/metro-resolver/src/PackageExportsResolve.js b/packages/metro-resolver/src/PackageExportsResolve.js index cf6883980f..84a371defb 100644 --- a/packages/metro-resolver/src/PackageExportsResolve.js +++ b/packages/metro-resolver/src/PackageExportsResolve.js @@ -9,7 +9,12 @@ * @oncall react_native */ -import type {ExportMap, FileResolution, ResolutionContext} from './types'; +import type { + ExportMap, + ExportsField, + FileResolution, + ResolutionContext, +} from './types'; import path from 'path'; import InvalidModuleSpecifierError from './errors/InvalidModuleSpecifierError'; @@ -46,7 +51,7 @@ export function resolvePackageTargetFromExports( * to a package-relative subpath for comparison. */ modulePath: string, - exportsField: ExportMap | string, + exportsField: ExportsField, platform: string | null, ): FileResolution { const raiseConfigError = (reason: string) => { @@ -142,13 +147,23 @@ function getExportsSubpath(packagePath: string, modulePath: string): string { * See https://nodejs.org/docs/latest-v19.x/api/packages.html#exports-sugar. */ function normalizeExportsField( - exportsField: ExportMap | string, + exportsField: ExportsField, raiseConfigError: (reason: string) => void, ): ExportMap { if (typeof exportsField === 'string') { return {'.': exportsField}; } + if (Array.isArray(exportsField)) { + return exportsField.reduce( + (result, subpath) => ({ + ...result, + [subpath]: subpath, + }), + {}, + ); + } + const firstLevelKeys = Object.keys(exportsField); const subpathKeys = firstLevelKeys.filter(subpathOrCondition => subpathOrCondition.startsWith('.'), diff --git a/packages/metro-resolver/src/__tests__/package-exports-test.js b/packages/metro-resolver/src/__tests__/package-exports-test.js index fa18a9ca91..276b93ff0a 100644 --- a/packages/metro-resolver/src/__tests__/package-exports-test.js +++ b/packages/metro-resolver/src/__tests__/package-exports-test.js @@ -333,6 +333,29 @@ describe('with package exports resolution enabled', () => { }); }); + test('should expand array of strings as subpath mapping (root shorthand)', () => { + const logWarning = jest.fn(); + const context = { + ...baseContext, + ...createPackageAccessors({ + '/root/node_modules/test-pkg/package.json': JSON.stringify({ + exports: ['./index.js', './foo.js'], + }), + }), + unstable_logWarning: logWarning, + }; + + expect(Resolver.resolve(context, 'test-pkg/index.js', null)).toEqual({ + type: 'sourceFile', + filePath: '/root/node_modules/test-pkg/index.js', + }); + expect(Resolver.resolve(context, 'test-pkg/foo.js', null)).toEqual({ + type: 'sourceFile', + filePath: '/root/node_modules/test-pkg/foo.js', + }); + expect(logWarning).not.toHaveBeenCalled(); + }); + describe('should resolve "exports" target directly', () => { test('without expanding `sourceExts`', () => { expect(Resolver.resolve(baseContext, 'test-pkg/foo.js', null)).toEqual({ diff --git a/packages/metro-resolver/src/types.js b/packages/metro-resolver/src/types.js index b5ad29ab0a..0881f3505b 100644 --- a/packages/metro-resolver/src/types.js +++ b/packages/metro-resolver/src/types.js @@ -54,10 +54,12 @@ export type ExportMap = $ReadOnly<{ [subpathOrCondition: string]: ExportMap | string | null, }>; +export type ExportsField = string | $ReadOnlyArray | ExportMap; + export type PackageJson = $ReadOnly<{ name?: string, main?: string, - exports?: string | ExportMap, + exports?: ExportsField, ... }>;