From 89986a340e1944e7ac008019118b78526852d7fd Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:35:42 +0100 Subject: [PATCH] metro config test --- .github/workflows/pr.yml | 16 ++++-- Apps/BRNPlayground/metro.config.js | 82 +++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 29ee8382e..01ce8f4b1 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -23,6 +23,11 @@ jobs: - name: NPM Install (Playground) run: npm install working-directory: ./Apps/BRNPlayground + + - name: Build Windows Bundle + run: npm run build:android + working-directory: ./Apps/BRNPlayground + - name: NPM Install (Binary Package) run: npm install working-directory: ./Package @@ -48,6 +53,11 @@ jobs: - name: NPM Install (Playground) run: npm install working-directory: ./Apps/BRNPlayground + + - name: Build Windows Bundle + run: npm run build:ios + working-directory: ./Apps/BRNPlayground + - name: NPM Install (Binary Package) run: npm install working-directory: ./Package @@ -78,9 +88,9 @@ jobs: run: npm install working-directory: ./Apps/BRNPlayground -# - name: Build Windows Bundle -# run: npm run build:windows -# working-directory: ./Apps/BRNPlayground + - name: Build Windows Bundle + run: npm run build:windows + working-directory: ./Apps/BRNPlayground - name: NPM Install Package run: npm install diff --git a/Apps/BRNPlayground/metro.config.js b/Apps/BRNPlayground/metro.config.js index b32f67da0..ce3a87e4e 100644 --- a/Apps/BRNPlayground/metro.config.js +++ b/Apps/BRNPlayground/metro.config.js @@ -1,4 +1,66 @@ const path = require("path"); +const fs = require('fs'); +const appendExclusions = require('metro-config/src/defaults/blacklist'); + +// Escape function taken from the MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping +function escapeRegExp(string) { + return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +// NOTE: The Metro bundler does not support symlinks (see https://github.com/facebook/metro/issues/1), which NPM uses for local packages. +// To work around this, we supplement the logic to follow symbolic links. + +// Create a mapping of package ids to linked directories. +function processModuleSymLinks() { + const nodeModulesPath = path.resolve(__dirname, 'node_modules'); + let moduleMappings = {}; + let moduleExclusions = []; + + function findPackageDirs(directory) { + fs.readdirSync(directory).forEach(item => { + const itemPath = path.resolve(directory, item); + const itemStat = fs.lstatSync(itemPath); + if (itemStat.isSymbolicLink()) { + let linkPath = fs.readlinkSync(itemPath); + // Sym links are relative in Unix, absolute in Windows. + if (!path.isAbsolute(linkPath)) { + linkPath = path.resolve(directory, linkPath); + } + const linkStat = fs.statSync(linkPath); + if (linkStat.isDirectory()) { + const packagePath = path.resolve(linkPath, "package.json"); + if (fs.existsSync(packagePath)) { + const packageId = path.relative(nodeModulesPath, itemPath); + moduleMappings[packageId] = linkPath; + + const packageInfoData = fs.readFileSync(packagePath); + const packageInfo = JSON.parse(packageInfoData); + + const dependencies = packageInfo.dependencies ? Object.keys(packageInfo.dependencies) : []; + const peerDependencies = packageInfo.peerDependencies ? Object.keys(packageInfo.peerDependencies) : []; + const devDependencies = packageInfo.devDependencies ? Object.keys(packageInfo.devDependencies) : []; + + // Exclude dependencies that appear in devDependencies or peerDependencies but not in dependencies. Otherwise, + // the metro bundler will package those devDependencies/peerDependencies as unintended copies. + for (const devDependency of devDependencies.concat(peerDependencies).filter(dependency => !dependencies.includes(dependency))) { + moduleExclusions.push(new RegExp(escapeRegExp(path.join(linkPath, "node_modules", devDependency)) + "\/.*")); + } + } + } + } else if (itemStat.isDirectory()) { + findPackageDirs(itemPath); + } + }); + } + + findPackageDirs(nodeModulesPath); + + return [moduleMappings, moduleExclusions]; +} + +const [moduleMappings, moduleExclusions] = processModuleSymLinks(); +console.log("Mapping the following sym linked packages:"); +console.log(moduleMappings); const { makeMetroConfig } = require("@rnx-kit/metro-config"); module.exports = makeMetroConfig({ @@ -10,9 +72,27 @@ module.exports = makeMetroConfig({ }, }), }, + resolver: { + // Register an "extra modules proxy" for resolving modules outside of the normal resolution logic. + extraNodeModules: new Proxy( + // Provide the set of known local package mappings. + moduleMappings, + { + // Provide a mapper function, which uses the above mappings for associated package ids, + // otherwise fall back to the standard behavior and just look in the node_modules directory. + get: (target, name) => name in target ? target[name] : path.join(__dirname, `node_modules/${name}`), + }, + ), + blacklistRE: appendExclusions(moduleExclusions), + }, + + projectRoot: path.resolve(__dirname), + + watchFolders: Object.values(moduleMappings), +/* watchFolders: [ path.join(__dirname, "node_modules", "@babylonjs/react-native"), path.join(__dirname, "node_modules", "@babylonjs/react-native-iosandroid"), path.join(__dirname, "node_modules", "@babylonjs/react-native-windows"), - ], + ],*/ });