diff --git a/packages/engine-rn-macos/src/adapters/metroAdapter.ts b/packages/engine-rn-macos/src/adapters/metroAdapter.ts index d27df9aec..9cdce8e29 100644 --- a/packages/engine-rn-macos/src/adapters/metroAdapter.ts +++ b/packages/engine-rn-macos/src/adapters/metroAdapter.ts @@ -1,9 +1,9 @@ import { Env } from '@rnv/core'; -import { InputConfig } from '@rnv/sdk-react-native'; +import { withMetroConfig, mergeConfig, InputConfig } from '@rnv/sdk-react-native'; const path = require('path'); -const sharedBlacklist = [ +const sharedExclusions = [ /node_modules\/react\/dist\/.*/, /website\/node_modules\/.*/, /heapCapture\/bundle\.js/, @@ -21,20 +21,22 @@ function escapeRegExp(pattern: RegExp | string) { } else if (Object.prototype.toString.call(pattern) === '[object RegExp]') { return pattern.source.replace(/\//g, path.sep); } - throw new Error(`Unexpected blacklist pattern: ${pattern}`); + throw new Error(`Unexpected exclusion pattern: ${pattern}`); } -function blacklist(additionalBlacklist: RegExp[]) { - return new RegExp(`(${(additionalBlacklist || []).concat(sharedBlacklist).map(escapeRegExp).join('|')})$`); +function exclusionList(additionalExclusions: RegExp[]) { + return [...additionalExclusions, ...sharedExclusions].map((regexp) => new RegExp(escapeRegExp(regexp))); } export const withRNVMetro = (config: InputConfig): InputConfig => { - const projectPath = process.env.RNV_PROJECT_ROOT || process.cwd(); + const projectPath = env.RNV_PROJECT_ROOT || process.cwd(); + + const defaultConfig = withMetroConfig(projectPath); const watchFolders = [path.resolve(projectPath, 'node_modules')]; if (env.RNV_IS_MONOREPO === 'true' || env.RNV_IS_MONOREPO === true) { - const monoRootPath = process.env.RNV_MONO_ROOT || projectPath; + const monoRootPath = env.RNV_MONO_ROOT || projectPath; watchFolders.push(path.resolve(monoRootPath, 'node_modules')); watchFolders.push(path.resolve(monoRootPath, 'packages')); } @@ -44,37 +46,47 @@ export const withRNVMetro = (config: InputConfig): InputConfig => { const exts: string = env.RNV_EXTENSIONS || ''; - const cnf = { - ...config, + const cnfRnv: InputConfig = { transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - // this defeats the RCTDeviceEventEmitter is not a registered callable module - inlineRequires: true, - }, - }), - ...(config?.transformer || {}), + getTransformOptions: async (entryPoints, options, getDependenciesOf) => { + const transformOptions = + (await config?.transformer?.getTransformOptions?.(entryPoints, options, getDependenciesOf)) || {}; + + return { + ...transformOptions, + transform: { + experimentalImportSupport: false, + // this defeats the RCTDeviceEventEmitter is not a registered callable module + inlineRequires: true, + ...(transformOptions?.transform || {}), + }, + }; + }, }, resolver: { - blacklistRE: blacklist([ - /platformBuilds\/.*/, - /buildHooks\/.*/, - /projectConfig\/.*/, - /website\/.*/, - /appConfigs\/.*/, - /renative.local.*/, - /metro.config.local.*/, - /.expo\/.*/, - /.rollup.cache\/.*/, - ]), - ...(config?.resolver || {}), + blockList: exclusionList( + [ + /platformBuilds\/.*/, + /buildHooks\/.*/, + /projectConfig\/.*/, + /website\/.*/, + /appConfigs\/.*/, + /renative.local.*/, + /metro.config.local.*/, + /.expo\/.*/, + /.rollup.cache\/.*/, + ] + .concat(config?.resolver?.blockList || []) + .concat(config?.resolver?.blacklistRE || []) + ), + blacklistRE: undefined, // must be reset to prevent it from being processed by metro sourceExts: [...(config?.resolver?.sourceExts || []), ...exts.split(',')], - extraNodeModules: config?.resolver?.extraNodeModules, }, watchFolders, - projectRoot: path.resolve(projectPath), + projectRoot: config?.projectRoot || path.resolve(projectPath), }; + const cnf = mergeConfig(defaultConfig, config, cnfRnv); + return cnf; }; diff --git a/packages/engine-rn-tvos/src/adapters/metroAdapter.ts b/packages/engine-rn-tvos/src/adapters/metroAdapter.ts index fddcdf2e6..e0f1d3f1c 100644 --- a/packages/engine-rn-tvos/src/adapters/metroAdapter.ts +++ b/packages/engine-rn-tvos/src/adapters/metroAdapter.ts @@ -5,7 +5,7 @@ const path = require('path'); const os = require('os'); const { doResolve } = require('@rnv/core'); -const sharedBlacklist = [ +const sharedExclusions = [ /node_modules\/react\/dist\/.*/, /website\/node_modules\/.*/, /heapCapture\/bundle\.js/, @@ -23,11 +23,11 @@ function escapeRegExp(pattern: RegExp | string) { } else if (Object.prototype.toString.call(pattern) === '[object RegExp]') { return pattern.source.replace(/\//g, path.sep); } - throw new Error(`Unexpected blacklist pattern: ${pattern}`); + throw new Error(`Unexpected exclusion pattern: ${pattern}`); } -function blacklist(additionalBlacklist: RegExp[]) { - return new RegExp(`(${(additionalBlacklist || []).concat(sharedBlacklist).map(escapeRegExp).join('|')})$`); +function exclusionList(additionalExclusions: RegExp[]) { + return [...additionalExclusions, ...sharedExclusions].map((regexp) => new RegExp(escapeRegExp(regexp))); } export const withRNVMetro = (config: InputConfig) => { @@ -53,20 +53,43 @@ export const withRNVMetro = (config: InputConfig) => { const exts: string = env.RNV_EXTENSIONS || ''; const cnfRnv: InputConfig = { - cacheStores: [ - new FileStore({ - root: path.join(os.tmpdir(), 'metro-cache-tvos'), - }), - ], + cacheStores: (metroCache) => { + let cacheStores: ReturnType any>> = []; + + if (typeof config?.cacheStores === 'function') { + cacheStores = config.cacheStores(metroCache); + } else if (config?.cacheStores?.length) { + // eslint-disable-next-line prefer-destructuring + cacheStores = config.cacheStores; + } + + cacheStores = [ + ...cacheStores, + new FileStore({ + root: path.join(os.tmpdir(), 'metro-cache-tvos'), + }), + ]; + + return cacheStores; + }, transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - // this defeats the RCTDeviceEventEmitter is not a registered callable module - inlineRequires: true, - }, - }), - assetRegistryPath: path.resolve(`${doResolve('react-native-tvos')}/Libraries/Image/AssetRegistry.js`), + getTransformOptions: async (entryPoints, options, getDependenciesOf) => { + const transformOptions = + (await config?.transformer?.getTransformOptions?.(entryPoints, options, getDependenciesOf)) || {}; + + return { + ...transformOptions, + transform: { + experimentalImportSupport: false, + // this defeats the RCTDeviceEventEmitter is not a registered callable module + inlineRequires: true, + ...(transformOptions?.transform || {}), + }, + }; + }, + assetRegistryPath: + config?.transformer?.assetRegistryPath || + path.resolve(`${doResolve('react-native-tvos')}/Libraries/Image/AssetRegistry.js`), }, resolver: { resolveRequest: (context, moduleName, platform) => { @@ -77,31 +100,38 @@ export const withRNVMetro = (config: InputConfig) => { platform ); } + + // Chain to the custom config resolver if provided. + if (typeof config?.resolver?.resolveRequest === 'function') { + return config.resolver.resolveRequest(context, moduleName, platform); + } + // Optionally, chain to the standard Metro resolver. return context.resolveRequest(context, moduleName, platform); }, - blacklistRE: blacklist([ - /platformBuilds\/.*/, - /buildHooks\/.*/, - /projectConfig\/.*/, - /website\/.*/, - /appConfigs\/.*/, - /renative.local.*/, - /metro.config.local.*/, - /.expo\/.*/, - /.rollup.cache\/.*/, - ]), - ...(config?.resolver || {}), + blockList: exclusionList( + [ + /platformBuilds\/.*/, + /buildHooks\/.*/, + /projectConfig\/.*/, + /website\/.*/, + /appConfigs\/.*/, + /renative.local.*/, + /metro.config.local.*/, + /.expo\/.*/, + /.rollup.cache\/.*/, + ] + .concat(config?.resolver?.blockList || []) + .concat(config?.resolver?.blacklistRE || []) + ), + blacklistRE: undefined, // must be reset to prevent it from being processed by metro sourceExts: [...(config?.resolver?.sourceExts || []), ...exts.split(',')], - extraNodeModules: config?.resolver?.extraNodeModules, }, watchFolders, - projectRoot: path.resolve(projectPath), + projectRoot: config?.projectRoot || path.resolve(projectPath), }; - const cnfWithRnv = mergeConfig(defaultConfig, cnfRnv); - - const cnf = mergeConfig(cnfWithRnv, config); + const cnf = mergeConfig(defaultConfig, config, cnfRnv); return cnf; }; diff --git a/packages/engine-rn-windows/src/adapters/metroAdapter.ts b/packages/engine-rn-windows/src/adapters/metroAdapter.ts index 20fae5ece..d2d5d6d54 100644 --- a/packages/engine-rn-windows/src/adapters/metroAdapter.ts +++ b/packages/engine-rn-windows/src/adapters/metroAdapter.ts @@ -1,9 +1,10 @@ -const path = require('path'); import { Env } from '@rnv/core'; import fs from 'fs'; -import { InputConfig } from '@rnv/sdk-react-native'; +import { withMetroConfig, mergeConfig, InputConfig } from '@rnv/sdk-react-native'; + +const path = require('path'); -const sharedBlacklist = [ +const sharedExclusions = [ /node_modules\/react\/dist\/.*/, /website\/node_modules\/.*/, /heapCapture\/bundle\.js/, @@ -21,21 +22,24 @@ function escapeRegExp(pattern: RegExp | string) { } else if (Object.prototype.toString.call(pattern) === '[object RegExp]') { return pattern.source.replace(/\//g, path.sep); } - throw new Error(`Unexpected blacklist pattern: ${pattern}`); + throw new Error(`Unexpected exclusion pattern: ${pattern}`); } -function blacklist(additionalBlacklist: RegExp[]) { - return new RegExp(`(${(additionalBlacklist || []).concat(sharedBlacklist).map(escapeRegExp).join('|')})$`); +function exclusionList(additionalExclusions: RegExp[]) { + return [...additionalExclusions, ...sharedExclusions].map((regexp) => new RegExp(escapeRegExp(regexp))); } export const withRNVMetro = (config: InputConfig): InputConfig => { - const projectPath = process.env.RNV_PROJECT_ROOT || process.cwd(); + const projectPath = env.RNV_PROJECT_ROOT || process.cwd(); + const rnwPath = fs.realpathSync(path.resolve(require.resolve('react-native-windows/package.json'), '..')); + const defaultConfig = withMetroConfig(projectPath); + const watchFolders = [path.resolve(projectPath, 'node_modules')]; if (env.RNV_IS_MONOREPO === 'true' || env.RNV_IS_MONOREPO === true) { - const monoRootPath = process.env.RNV_MONO_ROOT || projectPath; + const monoRootPath = env.RNV_MONO_ROOT || projectPath; watchFolders.push(path.resolve(monoRootPath, 'node_modules')); watchFolders.push(path.resolve(monoRootPath, 'packages')); } @@ -45,42 +49,51 @@ export const withRNVMetro = (config: InputConfig): InputConfig => { const exts: string = env.RNV_EXTENSIONS || ''; - const cnf = { - ...config, + const cnfRnv: InputConfig = { transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - // this defeats the RCTDeviceEventEmitter is not a registered callable module - inlineRequires: true, - }, - }), - ...(config?.resolver || {}), - sourceExts: [...(config?.resolver?.sourceExts || []), ...exts.split(',')], - extraNodeModules: config?.resolver?.extraNodeModules, - ...(config?.transformer || {}), + getTransformOptions: async (entryPoints, options, getDependenciesOf) => { + const transformOptions = + (await config?.transformer?.getTransformOptions?.(entryPoints, options, getDependenciesOf)) || {}; + + return { + ...transformOptions, + transform: { + experimentalImportSupport: false, + // this defeats the RCTDeviceEventEmitter is not a registered callable module + inlineRequires: true, + ...(transformOptions?.transform || {}), + }, + }; + }, }, resolver: { - blacklistRE: blacklist([ - /platformBuilds\/.*/, - /buildHooks\/.*/, - /projectConfig\/.*/, - /website\/.*/, - /appConfigs\/.*/, - /renative.local.*/, - /metro.config.local.*/, - /.expo\/.*/, - /.rollup.cache\/.*/, - ]), + blockList: exclusionList( + [ + /platformBuilds\/.*/, + /buildHooks\/.*/, + /projectConfig\/.*/, + /website\/.*/, + /appConfigs\/.*/, + /renative.local.*/, + /metro.config.local.*/, + /.expo\/.*/, + /.rollup.cache\/.*/, + ] + .concat(config?.resolver?.blockList || []) + .concat(config?.resolver?.blacklistRE || []) + ), + blacklistRE: undefined, // must be reset to prevent it from being processed by metro + sourceExts: [...(config?.resolver?.sourceExts || []), ...exts.split(',')], extraNodeModules: { - // Redirect react-native-windows to avoid symlink (metro doesn't like symlinks) + ...(config?.resolver?.extraNodeModules || {}), 'react-native-windows': rnwPath, }, }, watchFolders, - sourceExts: [...(config?.resolver?.sourceExts || []), ...exts.split(',')], - projectRoot: path.resolve(projectPath), + projectRoot: config?.projectRoot || path.resolve(projectPath), }; + const cnf = mergeConfig(defaultConfig, config, cnfRnv); + return cnf; }; diff --git a/packages/engine-rn/src/adapters/metroAdapter.ts b/packages/engine-rn/src/adapters/metroAdapter.ts index 8a2deb8ad..401e0cc83 100644 --- a/packages/engine-rn/src/adapters/metroAdapter.ts +++ b/packages/engine-rn/src/adapters/metroAdapter.ts @@ -4,7 +4,7 @@ import { withMetroConfig, mergeConfig, InputConfig } from '@rnv/sdk-react-native // TODO merge with packages/engine-rn-macos/src/adapters/metroAdapter.ts and place in @rnv/sdk-react-native const path = require('path'); -const sharedBlacklist = [ +const sharedExclusions = [ /node_modules\/react\/dist\/.*/, /website\/node_modules\/.*/, /heapCapture\/bundle\.js/, @@ -22,11 +22,11 @@ function escapeRegExp(pattern: RegExp | string) { } else if (Object.prototype.toString.call(pattern) === '[object RegExp]') { return pattern.source.replace(/\//g, path.sep); } - throw new Error(`Unexpected blacklist pattern: ${pattern}`); + throw new Error(`Unexpected exclusion pattern: ${pattern}`); } -function blacklist(additionalBlacklist: RegExp[]) { - return new RegExp(`(${(additionalBlacklist || []).concat(sharedBlacklist).map(escapeRegExp).join('|')})$`); +function exclusionList(additionalExclusions: RegExp[]) { + return [...additionalExclusions, ...sharedExclusions].map((regexp) => new RegExp(escapeRegExp(regexp))); } export const withRNVMetro = (config: InputConfig): InputConfig => { @@ -47,30 +47,47 @@ export const withRNVMetro = (config: InputConfig): InputConfig => { const exts: string = env.RNV_EXTENSIONS || ''; - const cnfRnv = { + const cnfRnv: InputConfig = { + transformer: { + getTransformOptions: async (entryPoints, options, getDependenciesOf) => { + const transformOptions = + (await config?.transformer?.getTransformOptions?.(entryPoints, options, getDependenciesOf)) || {}; + + return { + ...transformOptions, + transform: { + experimentalImportSupport: false, + // this defeats the RCTDeviceEventEmitter is not a registered callable module + inlineRequires: true, + ...(transformOptions?.transform || {}), + }, + }; + }, + }, resolver: { - blacklistRE: blacklist([ - /platformBuilds\/.*/, - /buildHooks\/.*/, - /projectConfig\/.*/, - /website\/.*/, - /appConfigs\/.*/, - /renative.local.*/, - /metro.config.local.*/, - /.expo\/.*/, - /.rollup.cache\/.*/, - ]), - ...(config?.resolver || {}), + blockList: exclusionList( + [ + /platformBuilds\/.*/, + /buildHooks\/.*/, + /projectConfig\/.*/, + /website\/.*/, + /appConfigs\/.*/, + /renative.local.*/, + /metro.config.local.*/, + /.expo\/.*/, + /.rollup.cache\/.*/, + ] + .concat(config?.resolver?.blockList || []) + .concat(config?.resolver?.blacklistRE || []) + ), + blacklistRE: undefined, // must be reset to prevent it from being processed by metro sourceExts: [...(config?.resolver?.sourceExts || []), ...exts.split(',')], - extraNodeModules: config?.resolver?.extraNodeModules, }, watchFolders, - projectRoot: path.resolve(projectPath), + projectRoot: config?.projectRoot || path.resolve(projectPath), }; - const cnfWithRnv = mergeConfig(defaultConfig, cnfRnv); - - const cnf = mergeConfig(cnfWithRnv, config); + const cnf = mergeConfig(defaultConfig, config, cnfRnv); return cnf; }; diff --git a/packages/sdk-react-native/src/adapters.ts b/packages/sdk-react-native/src/adapters.ts index 32b211dd7..2a0bb4e8c 100644 --- a/packages/sdk-react-native/src/adapters.ts +++ b/packages/sdk-react-native/src/adapters.ts @@ -128,7 +128,7 @@ export const withMetroConfig = (projectRoot: string): ConfigT => { ].join('|') ); - const config = { + const config: InputConfig = { resolver: { resolverMainFields: ['react-native', 'browser', 'main'], platforms: ['android', 'ios'], @@ -179,7 +179,7 @@ export const withMetroConfig = (projectRoot: string): ConfigT => { return mergeConfig(getDefaultConfig.getDefaultValues(projectRoot), config); }; -export const mergeConfig = (config1: ConfigT, config2: InputConfig) => { +export const mergeConfig = (defaultConfig: ConfigT, ...configs: InputConfig[]): ConfigT => { const mc = require('metro-config'); - return mc.mergeConfig(config1, config2); + return mc.mergeConfig(defaultConfig, ...configs); };