<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes https://github.com/nrwl/nx/issues/26853
240 lines
6.0 KiB
TypeScript
240 lines
6.0 KiB
TypeScript
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 '@nx/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[],
|
|
exportsConditionNames: string[] = [],
|
|
mainFields: string[] = []
|
|
) {
|
|
return function (
|
|
_context: any,
|
|
realModuleName: string,
|
|
platform: string | null
|
|
) {
|
|
const debug = process.env.NX_REACT_NATIVE_DEBUG === 'true';
|
|
|
|
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,
|
|
exportsConditionNames,
|
|
mainFields
|
|
);
|
|
if (resolvedPath) {
|
|
return resolvedPath;
|
|
}
|
|
if (debug) {
|
|
console.log(
|
|
chalk.red(`[Nx] Unable to resolve with any resolver: ${realModuleName}`)
|
|
);
|
|
}
|
|
throw new Error(`Cannot resolve ${chalk.bold(realModuleName)}`);
|
|
};
|
|
}
|
|
|
|
function resolveRequestFromContext(
|
|
resolveRequest: Function,
|
|
context: any,
|
|
realModuleName: string,
|
|
platform: string | null,
|
|
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 | null,
|
|
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,
|
|
exportsConditionNames: string[] = [],
|
|
mainFields: string[] = []
|
|
) {
|
|
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 | null,
|
|
debug: boolean
|
|
) {
|
|
try {
|
|
const tsConfigPathMatcher = getMatcher(debug);
|
|
const match = tsConfigPathMatcher(
|
|
realModuleName,
|
|
undefined,
|
|
undefined,
|
|
extensions.map((ext) => `.${ext}`)
|
|
);
|
|
return metroResolver.resolve(context, match, platform);
|
|
} catch {
|
|
if (debug) {
|
|
console.log(
|
|
chalk.cyan(`[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<string, string[]>;
|
|
|
|
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[],
|
|
exportsConditionNames: string[] = [],
|
|
mainFields: 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',
|
|
'react-native',
|
|
'node',
|
|
...exportsConditionNames,
|
|
],
|
|
mainFields: ['react-native', 'browser', 'main', ...mainFields],
|
|
aliasFields: ['browser'],
|
|
});
|
|
}
|
|
return resolver;
|
|
}
|