-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(metro-resolver-symlinks): add lib -> src remapper util (#905)
- Refactored resolvers to conform to a single interface - Added a utility for remapping import paths (akin to babel-plugin-import-path-remapper) - Updated README
- Loading branch information
Showing
30 changed files
with
510 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@rnx-kit/tools-react-native": minor | ||
--- | ||
|
||
Added functions for retrieving platform extensions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@rnx-kit/metro-resolver-symlinks": patch | ||
--- | ||
|
||
- Refactored resolvers to conform to a single interface | ||
- Added a utility for remapping import paths (akin to babel-plugin-import-path-remapper) | ||
- Updated README |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import type { ResolutionContext as MetroResolutionContext } from "metro-resolver"; | ||
|
||
export type MetroResolver = typeof import("metro-resolver").resolve; | ||
|
||
export type ResolutionContext = Pick< | ||
MetroResolutionContext, | ||
"originModulePath" | ||
>; | ||
|
||
export type ModuleResolver = ( | ||
context: ResolutionContext, | ||
moduleName: string, | ||
platform: string | ||
) => string; | ||
|
||
export type Options = { | ||
remapModule?: ModuleResolver; | ||
}; |
136 changes: 136 additions & 0 deletions
136
packages/metro-resolver-symlinks/src/utils/remapImportPath.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import type { PackageModuleRef } from "@rnx-kit/tools-node"; | ||
import { isFileModuleRef, parseModuleRef } from "@rnx-kit/tools-node"; | ||
import { expandPlatformExtensions } from "@rnx-kit/tools-react-native"; | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import type { ModuleResolver, ResolutionContext } from "../types"; | ||
|
||
type Resolver = (fromDir: string, moduleId: string) => string; | ||
|
||
type ResolverOptions = { | ||
extensions: string[]; | ||
mainFields: string[]; | ||
}; | ||
|
||
type Options = Partial<ResolverOptions> & { | ||
test: (moduleName: string) => boolean; | ||
}; | ||
|
||
const DEFAULT_OPTIONS = { | ||
extensions: [".ts", ".tsx"], | ||
mainFields: ["module", "main"], | ||
}; | ||
|
||
export function remapLibToSrc( | ||
{ originModulePath }: ResolutionContext, | ||
ref: PackageModuleRef, | ||
resolver: Resolver | ||
): string | undefined { | ||
const pattern = /^(.\/)?lib\b/; | ||
const { name, scope, path: modulePath } = ref; | ||
if (!modulePath || !pattern.test(modulePath)) { | ||
return undefined; | ||
} | ||
|
||
const fromDir = originModulePath | ||
? path.dirname(originModulePath) | ||
: process.cwd(); | ||
const packageName = scope ? `${scope}/${name}` : name; | ||
return resolver( | ||
fromDir, | ||
`${packageName}/${modulePath.replace(pattern, "src")}` | ||
); | ||
} | ||
|
||
export function resolveModule( | ||
fromDir: string, | ||
moduleId: string, | ||
resolver: Resolver, | ||
{ mainFields }: ResolverOptions | ||
): string { | ||
const manifestPath = resolver(fromDir, moduleId + "/package.json"); | ||
const content = fs.readFileSync(manifestPath, { encoding: "utf-8" }); | ||
const manifest = JSON.parse(content); | ||
for (const mainField of mainFields) { | ||
const main = manifest[mainField]; | ||
if (main) { | ||
return main; | ||
} | ||
} | ||
|
||
throw new Error( | ||
`A main field (e.g. ${mainFields.join(", ")}) is missing for '${moduleId}'` | ||
); | ||
} | ||
|
||
export function resolveModulePath( | ||
{ originModulePath }: ResolutionContext, | ||
ref: PackageModuleRef, | ||
resolver: Resolver, | ||
options: ResolverOptions | ||
): PackageModuleRef { | ||
if (ref.path) { | ||
return ref; | ||
} | ||
|
||
const fromDir = originModulePath | ||
? path.dirname(originModulePath) | ||
: process.cwd(); | ||
const { name, scope } = ref; | ||
const packageName = scope ? `${scope}/${name}` : name; | ||
const modulePath = resolveModule(fromDir, packageName, resolver, options); | ||
|
||
return { | ||
name, | ||
scope, | ||
path: modulePath.replace(/\.jsx?$/, ""), | ||
}; | ||
} | ||
|
||
export function remapImportPath(pluginOptions: Options): ModuleResolver { | ||
if (!pluginOptions) { | ||
throw new Error("A test function is required for this plugin"); | ||
} | ||
|
||
const { test, ...options } = pluginOptions; | ||
if (typeof test !== "function") { | ||
throw new Error( | ||
"Expected option `test` to be a function `(moduleId: string) => boolean`" | ||
); | ||
} | ||
|
||
const resolverOptions = { | ||
...DEFAULT_OPTIONS, | ||
...options, | ||
}; | ||
|
||
const getResolver = (() => { | ||
const { extensions, mainFields } = resolverOptions; | ||
let resolve: Resolver; | ||
return (platform: string) => { | ||
if (!resolve) { | ||
resolve = require("enhanced-resolve").create.sync({ | ||
extensions: expandPlatformExtensions(platform, extensions), | ||
mainFields, | ||
}); | ||
} | ||
return resolve; | ||
}; | ||
})(); | ||
|
||
return (context, moduleId, platform) => { | ||
const ref = parseModuleRef(moduleId); | ||
if (isFileModuleRef(ref) || !test(moduleId)) { | ||
return moduleId; | ||
} | ||
|
||
const resolve = getResolver(platform); | ||
const resolvedRef = resolveModulePath( | ||
context, | ||
ref, | ||
resolve, | ||
resolverOptions | ||
); | ||
return remapLibToSrc(context, resolvedRef, resolve) || moduleId; | ||
}; | ||
} |
5 changes: 5 additions & 0 deletions
5
...r-symlinks/test/__fixtures__/remap-import-path/node_modules/@contoso/example/package.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Empty file.
5 changes: 5 additions & 0 deletions
5
...er-symlinks/test/__fixtures__/remap-import-path/node_modules/@contoso/exotic/package.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
5 changes: 5 additions & 0 deletions
5
...-symlinks/test/__fixtures__/remap-import-path/node_modules/@contoso/relative/package.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Empty file.
Oops, something went wrong.