Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and improve metro config merging #1770

Merged
merged 4 commits into from
Oct 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 43 additions & 31 deletions packages/engine-rn-macos/src/adapters/metroAdapter.ts
Original file line number Diff line number Diff line change
@@ -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;
};
98 changes: 64 additions & 34 deletions packages/engine-rn-tvos/src/adapters/metroAdapter.ts
Original file line number Diff line number Diff line change
@@ -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<Extract<InputConfig['cacheStores'], (...args: any[]) => 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;
};
83 changes: 48 additions & 35 deletions packages/engine-rn-windows/src/adapters/metroAdapter.ts
Original file line number Diff line number Diff line change
@@ -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;
};
61 changes: 39 additions & 22 deletions packages/engine-rn/src/adapters/metroAdapter.ts
Original file line number Diff line number Diff line change
@@ -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;
};
6 changes: 3 additions & 3 deletions packages/sdk-react-native/src/adapters.ts
Original file line number Diff line number Diff line change
@@ -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);
};