【问题标题】:React native couldn't resolve local module after noHoist has been added to project将 noHoist 添加到项目后,React Native 无法解析本地模块
【发布时间】:2021-11-09 23:13:50
【问题描述】:

我有这个带有 yarn 工作区和 lerna 的 monorepo js 设置

/package.json
/packages
   /common (js shared code)
     /package.json 
   /mobile (react native - metro)
     /package.json
   /web (CRA)
     /package.json

移动和网络包在 package.json 中导入通用包,如下所示

"dependencies": {
    "common": "*",
} 

我必须在根 package.json 中添加 noHoist 选项,以便移动原生依赖项不会被提升,因此构建脚本仍然可以正常运行

"workspaces": {
    "packages": [
      "packages/*"
    ],
    "nohoist": [
      "**/react-native",
      "**/react-native/**"
    ]
  }

在添加 noHoist 选项之前和之后,Web 都可以正常工作

添加 noHoist 后 React 本机 Metro 捆绑开始失败 .. 它显示

"Error: Unable to resolve module .. could not be found within the project or in these directories:
  node_modules
  ../../node_modules" 

但是根 node_modules 下确实存在通用包? 看起来像是某种链接问题! (确实尝试手动链接它/同样的问题).. 请注意,我没有在 noHoist 下添加 common 包

我的 Metro 配置如下所示

const path= require('path');

const watchFolders = [   
  path.resolve(`${__dirname}`), // Relative path to package node_modules   
  path.resolve(`${__dirname}/../../node_modules`), // Relative path to root node_modules ];

module.exports = {   
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),},
  maxWorkers: 2,
  watchFolders, };

任何想法? ????

【问题讨论】:

    标签: react-native monorepo lerna yarn-workspaces metro-bundler


    【解决方案1】:

    原来问题出在捆绑中,通过编辑 metro.config.js 以包含阻止列表和 extraNodeModules 来解决

    const path = require('path');
    const exclusionList = require('metro-config/src/defaults/exclusionList');
    const getWorkspaces = require('get-yarn-workspaces');
    
    function generateAssetsPath(depth, subpath) {
      return `/assets`.concat(
        Array.from({ length: depth })
          // eslint-disable-next-line no-unused-vars
          .map((_, i) => `/${subpath}`)
          .join(''),
      );
    }
    
    function getMetroAndroidAssetsResolutionFix(params = {}) {
      const { depth = 3 } = params;
      let publicPath = generateAssetsPath(depth, 'dir');
      const applyMiddleware = (middleware) => (req, res, next) => {
        // eslint-disable-next-line no-plusplus
        for (let currentDepth = depth; currentDepth >= 0; currentDepth--) {
          const pathToReplace = generateAssetsPath(currentDepth, 'dir');
          const replacementPath = generateAssetsPath(depth - currentDepth, '..');
          if (currentDepth === depth) {
            publicPath = pathToReplace;
          }
          if (req.url.startsWith(pathToReplace)) {
            req.url = req.url.replace(pathToReplace, replacementPath);
            break;
          }
        }
        return middleware(req, res, next);
      };
      return {
        publicPath,
        applyMiddleware,
      };
    }
    
    function getNohoistedPackages() {
      // eslint-disable-next-line global-require
      const monorepoRootPackageJson = require('../../package.json');
      const nohoistedPackages = monorepoRootPackageJson.workspaces.nohoist
        .filter((packageNameGlob) => !packageNameGlob.endsWith('**'))
        .map((packageNameGlob) => packageNameGlob.substring(3));
      return nohoistedPackages;
    }
    
    function getMetroNohoistSettings({
      dir,
      workspaceName,
      reactNativeAlias,
    } = {}) {
      const nohoistedPackages = getNohoistedPackages();
      const blockList = [];
      const extraNodeModules = {};
      nohoistedPackages.forEach((packageName) => {
        extraNodeModules[packageName] =
          reactNativeAlias && packageName === 'react-native'
            ? path.resolve(dir, `./node_modules/${reactNativeAlias}`)
            : path.resolve(dir, `./node_modules/${packageName}`);
        const regexSafePackageName = packageName.replace('/', '\\/');
        blockList.push(
          new RegExp(
            `^((?!${workspaceName}).)*\\/node_modules\\/${regexSafePackageName}\\/.*$`,
          ),
        );
      });
      return { extraNodeModules, blockList };
    }
    
    const workspaces = getWorkspaces(__dirname);
    
    const androidAssetsResolutionFix = getMetroAndroidAssetsResolutionFix({
      depth: 3,
    });
    
    const nohoistSettings = getMetroNohoistSettings({
      dir: __dirname,
      workspaceName: 'mobile',
    });
    
    module.exports = {
      transformer: {
        // Apply the Android assets resolution fix to the public path...
        // publicPath: androidAssetsResolutionFix.publicPath,
        getTransformOptions: async () => ({
          transform: {
            experimentalImportSupport: false,
            inlineRequires: true,
          },
        }),
      },
      // server: {
      //   // ...and to the server middleware.
      //   enhanceMiddleware: (middleware) =>
      //     androidAssetsResolutionFix.applyMiddleware(middleware),
      // },
      // Add additional Yarn workspace package roots to the module map.
      // This allows importing importing from all the project's packages.
      watchFolders: [
        path.resolve(__dirname, '../../node_modules'),
        ...workspaces.filter((workspaceDir) => !(workspaceDir === __dirname)),
      ],
      maxWorkers: 2,
      resolver: {
        // Ensure we resolve nohoisted packages from this directory.
        blockList: exclusionList(nohoistSettings.blockList),
        extraNodeModules: nohoistSettings.extraNodeModules,
      },
    };
    

    您可以查看this universal CRA/RN mono-repo that uses such metro configs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-11
      • 2016-03-23
      • 1970-01-01
      • 2016-07-24
      • 2018-11-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      相关资源
      最近更新 更多