import * as metroResolver from 'metro-resolver'; import type { MatchPath } from 'tsconfig-paths'; import { createMatchPath, loadConfig } from 'tsconfig-paths'; import * as chalk from 'chalk'; import { CachedInputFileSystem, ResolverFactory } from 'enhanced-resolve'; import { dirname, join } from 'path'; import * as fs from 'fs'; import { workspaceRoot } from '@nrwl/devkit'; /* * Use tsconfig to resolve additional workspace libs. * * This resolve function requires projectRoot to be set to * workspace root in order modules and assets to be registered and watched. */ export function getResolveRequest(extensions: string[]) { return function ( _context: any, realModuleName: string, platform: string | null ) { const debug = process.env.NX_REACT_NATIVE_DEBUG === 'true'; if (debug) console.log(chalk.cyan(`[Nx] Resolving: ${realModuleName}`)); const { resolveRequest, ...context } = _context; const resolvedPath = resolveRequestFromContext( resolveRequest, _context, realModuleName, platform, debug ) || defaultMetroResolver(context, realModuleName, platform, debug) || tsconfigPathsResolver( context, extensions, realModuleName, platform, debug ) || pnpmResolver(extensions, context, realModuleName, debug); if (resolvedPath) { return resolvedPath; } throw new Error(`Cannot resolve ${chalk.bold(realModuleName)}`); }; } function resolveRequestFromContext( resolveRequest: Function, context: any, realModuleName: string, platform: string, debug: boolean ) { try { return resolveRequest(context, realModuleName, platform); } catch { if (debug) console.log( chalk.cyan( `[Nx] Unable to resolve with default resolveRequest: ${realModuleName}` ) ); } } /** * This function try to resolve path using metro's default resolver * @returns path if resolved, else undefined */ function defaultMetroResolver( context: any, realModuleName: string, platform: string, debug: boolean ) { try { return metroResolver.resolve(context, realModuleName, platform); } catch { if (debug) console.log( chalk.cyan( `[Nx] Unable to resolve with default Metro resolver: ${realModuleName}` ) ); } } /** * This resolver try to resolve module for pnpm. * @returns path if resolved, else undefined * This pnpm resolver is inspired from https://github.com/vjpr/pnpm-react-native-example/blob/main/packages/pnpm-expo-helper/util/make-resolver.js */ function pnpmResolver( extensions: string[], context: any, realModuleName: string, debug: boolean ) { try { const pnpmResolve = getPnpmResolver(extensions); const lookupStartPath = dirname(context.originModulePath); const filePath = pnpmResolve.resolveSync( {}, lookupStartPath, realModuleName ); if (filePath) { return { type: 'sourceFile', filePath }; } } catch { if (debug) console.log( chalk.cyan( `[Nx] Unable to resolve with default PNPM resolver: ${realModuleName}` ) ); } } /** * This function try to resolve files that are specified in tsconfig's paths * @returns path if resolved, else undefined */ function tsconfigPathsResolver( context: any, extensions: string[], realModuleName: string, platform: string, debug: boolean ) { const tsConfigPathMatcher = getMatcher(debug); const match = tsConfigPathMatcher( realModuleName, undefined, undefined, extensions.map((ext) => `.${ext}`) ); if (match) { return metroResolver.resolve(context, match, platform); } else { if (debug) { console.log( chalk.red(`[Nx] Failed to resolve ${chalk.bold(realModuleName)}`) ); console.log( chalk.cyan( `[Nx] The following tsconfig paths was used:\n:${chalk.bold( JSON.stringify(paths, null, 2) )}` ) ); } } } let matcher: MatchPath; let absoluteBaseUrl: string; let paths: Record; function getMatcher(debug: boolean) { if (!matcher) { const result = loadConfig(); if (result.resultType === 'success') { absoluteBaseUrl = result.absoluteBaseUrl; paths = result.paths; if (debug) { console.log( chalk.cyan(`[Nx] Located tsconfig at ${chalk.bold(absoluteBaseUrl)}`) ); console.log( chalk.cyan( `[Nx] Found the following paths:\n:${chalk.bold( JSON.stringify(paths, null, 2) )}` ) ); } matcher = createMatchPath(absoluteBaseUrl, paths); } else { console.log(chalk.cyan(`[Nx] Failed to locate tsconfig}`)); throw new Error(`Could not load tsconfig for project`); } } return matcher; } /** * This function returns resolver for pnpm. * It is inspired form https://github.com/vjpr/pnpm-expo-example/blob/main/packages/pnpm-expo-helper/util/make-resolver.js. */ let resolver; function getPnpmResolver(extensions: string[]) { if (!resolver) { const fileSystem = new CachedInputFileSystem(fs, 4000); resolver = ResolverFactory.createResolver({ fileSystem, extensions: extensions.map((extension) => '.' + extension), useSyncFileSystemCalls: true, modules: [join(workspaceRoot, 'node_modules'), 'node_modules'], conditionNames: ['native', 'browser', 'require', 'default'], }); } return resolver; }