packages/jest-resolve/src/utils.ts (152 lines of code) (raw):

/** * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import * as path from 'path'; import chalk = require('chalk'); import {ValidationError} from 'jest-validate'; import Resolver from './resolver'; const BULLET: string = chalk.bold('\u25cf '); const DOCUMENTATION_NOTE = ` ${chalk.bold('Configuration Documentation:')} https://jestjs.io/docs/configuration `; const createValidationError = (message: string) => new ValidationError(`${BULLET}Validation Error`, message, DOCUMENTATION_NOTE); const replaceRootDirInPath = (rootDir: string, filePath: string): string => { if (!/^<rootDir>/.test(filePath)) { return filePath; } return path.resolve( rootDir, path.normalize(`./${filePath.substr('<rootDir>'.length)}`), ); }; const resolveWithPrefix = ( resolver: string | undefined | null, { filePath, humanOptionName, optionName, prefix, requireResolveFunction, rootDir, }: { filePath: string; humanOptionName: string; optionName: string; prefix: string; requireResolveFunction: (moduleName: string) => string; rootDir: string; }, ): string => { const fileName = replaceRootDirInPath(rootDir, filePath); let module = Resolver.findNodeModule(`${prefix}${fileName}`, { basedir: rootDir, resolver: resolver || undefined, }); if (module) { return module; } try { return requireResolveFunction(`${prefix}${fileName}`); } catch {} module = Resolver.findNodeModule(fileName, { basedir: rootDir, resolver: resolver || undefined, }); if (module) { return module; } try { return requireResolveFunction(fileName); } catch {} throw createValidationError( ` ${humanOptionName} ${chalk.bold( fileName, )} cannot be found. Make sure the ${chalk.bold( optionName, )} configuration option points to an existing node module.`, ); }; /** * Finds the test environment to use: * * 1. looks for jest-environment-<name> relative to project. * 1. looks for jest-environment-<name> relative to Jest. * 1. looks for <name> relative to project. * 1. looks for <name> relative to Jest. */ export const resolveTestEnvironment = ({ rootDir, testEnvironment: filePath, requireResolveFunction, }: { rootDir: string; testEnvironment: string; requireResolveFunction: (moduleName: string) => string; }): string => { try { return resolveWithPrefix(undefined, { filePath, humanOptionName: 'Test environment', optionName: 'testEnvironment', prefix: 'jest-environment-', requireResolveFunction, rootDir, }); } catch (error: any) { if (filePath === 'jsdom' || filePath === 'jest-environment-jsdom') { error.message += '\n\nAs of Jest 28 "jest-environment-jsdom" is no longer shipped by default, make sure to install it separately.'; } throw error; } }; /** * Finds the watch plugins to use: * * 1. looks for jest-watch-<name> relative to project. * 1. looks for jest-watch-<name> relative to Jest. * 1. looks for <name> relative to project. * 1. looks for <name> relative to Jest. */ export const resolveWatchPlugin = ( resolver: string | undefined | null, { filePath, rootDir, requireResolveFunction, }: { filePath: string; rootDir: string; requireResolveFunction: (moduleName: string) => string; }, ): string => resolveWithPrefix(resolver, { filePath, humanOptionName: 'Watch plugin', optionName: 'watchPlugins', prefix: 'jest-watch-', requireResolveFunction, rootDir, }); /** * Finds the runner to use: * * 1. looks for jest-runner-<name> relative to project. * 1. looks for jest-runner-<name> relative to Jest. * 1. looks for <name> relative to project. * 1. looks for <name> relative to Jest. */ export const resolveRunner = ( resolver: string | undefined | null, { filePath, rootDir, requireResolveFunction, }: { filePath: string; rootDir: string; requireResolveFunction: (moduleName: string) => string; }, ): string => resolveWithPrefix(resolver, { filePath, humanOptionName: 'Jest Runner', optionName: 'runner', prefix: 'jest-runner-', requireResolveFunction, rootDir, }); export const resolveSequencer = ( resolver: string | undefined | null, { filePath, rootDir, requireResolveFunction, }: { filePath: string; rootDir: string; requireResolveFunction: (moduleName: string) => string; }, ): string => resolveWithPrefix(resolver, { filePath, humanOptionName: 'Jest Sequencer', optionName: 'testSequencer', prefix: 'jest-sequencer-', requireResolveFunction, rootDir, });