diff --git a/packages/devkit/src/utils/config-utils.ts b/packages/devkit/src/utils/config-utils.ts index a116472c5dae1..fdee82126b8c1 100644 --- a/packages/devkit/src/utils/config-utils.ts +++ b/packages/devkit/src/utils/config-utils.ts @@ -1,4 +1,4 @@ -import { dirname, extname, join } from 'path'; +import { dirname, extname, join, sep } from 'path'; import { existsSync, readdirSync } from 'fs'; import { requireNx } from '../../nx'; import { pathToFileURL } from 'node:url'; @@ -54,15 +54,26 @@ export function getRootTsConfigFileName(): string | null { return null; } +const packageInstallationDirectories = [ + `${sep}node_modules${sep}`, + `${sep}.yarn${sep}`, +]; + +export function clearRequireCache(): void { + for (const k of Object.keys(require.cache)) { + if (!packageInstallationDirectories.some((dir) => k.includes(dir))) { + delete require.cache[k]; + } + } +} + /** * Load the module after ensuring that the require cache is cleared. */ async function load(path: string): Promise { // Clear cache if the path is in the cache if (require.cache[path]) { - for (const k of Object.keys(require.cache)) { - delete require.cache[k]; - } + clearRequireCache(); } try { diff --git a/packages/expo/plugins/plugin.ts b/packages/expo/plugins/plugin.ts index 9e96549f4808a..62ffcc692a3f3 100644 --- a/packages/expo/plugins/plugin.ts +++ b/packages/expo/plugins/plugin.ts @@ -15,6 +15,7 @@ import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; import { existsSync, readdirSync } from 'fs'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory'; +import { loadConfigFile } from '@nx/devkit/src/utils/config-utils'; export interface ExpoPluginOptions { startTargetName?: string; @@ -59,7 +60,7 @@ export const createDependencies: CreateDependencies = () => { export const createNodes: CreateNodes = [ '**/app.{json,config.js}', - (configFilePath, options, context) => { + async (configFilePath, options, context) => { options = normalizeOptions(options); const projectRoot = dirname(configFilePath); @@ -71,7 +72,7 @@ export const createNodes: CreateNodes = [ ) { return {}; } - const appConfig = getAppConfig(configFilePath, context); + const appConfig = await getAppConfig(configFilePath, context); // if appConfig.expo is not defined if (!appConfig.expo) { return {}; @@ -152,11 +153,10 @@ function buildExpoTargets( function getAppConfig( configFilePath: string, context: CreateNodesContext -): any { +): Promise { const resolvedPath = join(context.workspaceRoot, configFilePath); - let module = load(resolvedPath); - return module.default ?? module; + return loadConfigFile(resolvedPath); } function getInputs( @@ -180,21 +180,6 @@ function getOutputs(projectRoot: string, dir: string) { } } -/** - * Load the module after ensuring that the require cache is cleared. - */ -function load(path: string): any { - // Clear cache if the path is in the cache - if (require.cache[path]) { - for (const k of Object.keys(require.cache)) { - delete require.cache[k]; - } - } - - // Then require - return require(path); -} - function normalizeOptions(options: ExpoPluginOptions): ExpoPluginOptions { options ??= {}; options.startTargetName ??= 'start'; diff --git a/packages/jest/src/plugins/plugin.ts b/packages/jest/src/plugins/plugin.ts index fd375291dd442..74b5b6b743a7e 100644 --- a/packages/jest/src/plugins/plugin.ts +++ b/packages/jest/src/plugins/plugin.ts @@ -16,6 +16,7 @@ import { existsSync, readdirSync, readFileSync } from 'fs'; import { readConfig } from 'jest-config'; import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; +import { clearRequireCache } from '@nx/devkit/src/utils/config-utils'; import { getGlobPatternsFromPackageManagerWorkspaces } from 'nx/src/plugins/package-json-workspaces'; import { combineGlobPatterns } from 'nx/src/utils/globs'; import { minimatch } from 'minimatch'; @@ -109,12 +110,6 @@ export const createNodes: CreateNodes = [ }, ]; -const jestValidatePath = dirname( - require.resolve('jest-validate/package.json', { - paths: [dirname(require.resolve('jest-config'))], - }) -); - async function buildJestTargets( configFilePath: string, projectRoot: string, @@ -124,13 +119,7 @@ async function buildJestTargets( const absConfigFilePath = resolve(context.workspaceRoot, configFilePath); if (require.cache[absConfigFilePath]) { - for (const k of Object.keys(require.cache)) { - // Only delete the cache outside of jest-validate - // jest-validate has a Symbol which is important for jest config validation which breaks if the require cache is broken - if (relative(jestValidatePath, k).startsWith('../')) { - delete require.cache[k]; - } - } + clearRequireCache(); } const config = await readConfig( diff --git a/packages/next/src/plugins/plugin.ts b/packages/next/src/plugins/plugin.ts index af4ac374e72a2..61f18394498c9 100644 --- a/packages/next/src/plugins/plugin.ts +++ b/packages/next/src/plugins/plugin.ts @@ -16,6 +16,7 @@ import { existsSync, readdirSync } from 'fs'; import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; import { getLockFileName } from '@nx/js'; +import { loadConfigFile } from '@nx/devkit/src/utils/config-utils'; export interface NextPluginOptions { buildTargetName?: string; @@ -194,19 +195,13 @@ async function getOutputs(projectRoot, nextConfig) { } } -async function getNextConfig( +function getNextConfig( configFilePath: string, context: CreateNodesContext ): Promise { const resolvedPath = join(context.workspaceRoot, configFilePath); - let module; - if (extname(configFilePath) === '.mjs') { - module = await loadEsmModule(resolvedPath); - } else { - module = load(resolvedPath); - } - return module.default ?? module; + return loadConfigFile(resolvedPath); } function normalizeOptions(options: NextPluginOptions): NextPluginOptions { @@ -230,36 +225,3 @@ function getInputs( }, ]; } - -const packageInstallationDirectories = ['node_modules', '.yarn']; -/** - * Load the module after ensuring that the require cache is cleared. - */ -function load(path: string): any { - // Clear cache if the path is in the cache - if (require.cache[path]) { - for (const key of Object.keys(require.cache)) { - if (!packageInstallationDirectories.some((dir) => key.includes(dir))) { - delete require.cache[key]; - } - } - } - - // Then require - return require(path); -} - -/** - * Lazily compiled dynamic import loader function. - */ -let dynamicLoad: ((modulePath: string | URL) => Promise) | undefined; - -export function loadEsmModule(modulePath: string | URL): Promise { - const modulePathWithCacheBust = `${modulePath}?version=${Date.now()}`; - dynamicLoad ??= new Function( - 'modulePath', - `return import(modulePath);` - ) as Exclude; - - return dynamicLoad(modulePathWithCacheBust); -} diff --git a/packages/react-native/plugins/plugin.ts b/packages/react-native/plugins/plugin.ts index b44142c269bbf..6cdc96d764959 100644 --- a/packages/react-native/plugins/plugin.ts +++ b/packages/react-native/plugins/plugin.ts @@ -15,6 +15,7 @@ import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs'; import { existsSync, readdirSync } from 'fs'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory'; +import { loadConfigFile } from '@nx/devkit/src/utils/config-utils'; export interface ReactNativePluginOptions { startTargetName?: string; @@ -59,7 +60,7 @@ export const createDependencies: CreateDependencies = () => { export const createNodes: CreateNodes = [ '**/app.{json,config.js}', - (configFilePath, options, context) => { + async (configFilePath, options, context) => { options = normalizeOptions(options); const projectRoot = dirname(configFilePath); @@ -71,7 +72,7 @@ export const createNodes: CreateNodes = [ ) { return {}; } - const appConfig = getAppConfig(configFilePath, context); + const appConfig = await getAppConfig(configFilePath, context); if (appConfig.expo) { return {}; } @@ -164,11 +165,10 @@ function buildReactNativeTargets( function getAppConfig( configFilePath: string, context: CreateNodesContext -): any { +): Promise { const resolvedPath = join(context.workspaceRoot, configFilePath); - let module = load(resolvedPath); - return module.default ?? module; + return loadConfigFile(resolvedPath); } function getInputs( @@ -192,21 +192,6 @@ function getOutputs(projectRoot: string, dir: string) { } } -/** - * Load the module after ensuring that the require cache is cleared. - */ -function load(path: string): any { - // Clear cache if the path is in the cache - if (require.cache[path]) { - for (const k of Object.keys(require.cache)) { - delete require.cache[k]; - } - } - - // Then require - return require(path); -} - function normalizeOptions( options: ReactNativePluginOptions ): ReactNativePluginOptions { diff --git a/packages/webpack/src/utils/webpack/resolve-user-defined-webpack-config.ts b/packages/webpack/src/utils/webpack/resolve-user-defined-webpack-config.ts index ee4c0fd490cba..6696c43f2f2df 100644 --- a/packages/webpack/src/utils/webpack/resolve-user-defined-webpack-config.ts +++ b/packages/webpack/src/utils/webpack/resolve-user-defined-webpack-config.ts @@ -1,4 +1,5 @@ import { registerTsProject } from '@nx/js/src/internal'; +import { clearRequireCache } from '@nx/devkit/src/utils/config-utils'; export function resolveUserDefinedWebpackConfig( path: string, @@ -10,9 +11,7 @@ export function resolveUserDefinedWebpackConfig( // Clear cache if the path is in the cache if (require.cache[path]) { // Clear all entries because config may import other modules - for (const k of Object.keys(require.cache)) { - delete require.cache[k]; - } + clearRequireCache(); } }