最近把我的项目中积淀进去的一些工具函数整理出来编写成一个代码仓库,筹备先在本地把每个函数都调试一遍之后再公布,须要应用yarn link 或者npm link 在测试的我的项目中link 指定的本地依赖库,在这过程中遇到一些问题

问题

在React Native 我的项目下无奈应用yarn link 或者npm link 来link 本地的依赖,起因是RN 应用的打包工具Metro 不反对symlinks

逛了一番issues 后,找到了一个可行的解决方案

解决方案

应用第三方库metro-symlinked-deps

这个库能够用来自定义metro 的打包配置,应用办法能够参考metro-symlinked-deps 的文档

这里贴一下metro-symlinked-deps 给出的例子

// metro.config.jsconst {    applyConfigForLinkedDependencies,} = require('@carimus/metro-symlinked-deps');module.exports = applyConfigForLinkedDependencies(    {        transformer: {            getTransformOptions: async () => ({                transform: {                    experimentalImportSupport: false,                    inlineRequires: false,                },            }),        },    },    {        projectRoot: __dirname,        blacklistLinkedModules: ['react-native'],    },);

然而在应用这个库的时候也遇到了一些问题

另一个问题

一开始应用这个库并没有解决yarn link 有效的问题。确定本人配置没错之后,就开始寻找问题。因为看到有人说这个库有用,有人说没用。就开始看这个库的源码,看看它到底干了啥

于是,发现这个库会拿到我的项目下node_modules 外面应用symlink 的依赖的实在地址。

那么按理来说我用yarn link 是合乎他的这个逻辑的, 并且我应用 ls -l 命令查看我link 的本地依赖也是正确返回了软链的地址。于是带着疑难我查看了这个库用来收集link 的依赖的办法,最终发现这个库应用了get-dev-paths 来查看link 的依赖

/** * Resolve all detected linked directories to unique absolute paths without a trailing slash. * * @param {string} projectRoot */function resolveDevPaths(projectRoot) {    return Array.from(        new Set(            getDevPaths(projectRoot)                .map((linkPath) => {                    return `${fs.realpathSync(linkPath)}`.replace(/\/+$/, '');                })                .filter((absLinkPath) => !!absLinkPath),        ),    );}

所以眼光来到了get-dev-paths

首先看看它是如何返回link 的依赖的

addPath = function(dep) {  var target;  if (!fs.lstatSync(dep).isSymbolicLink()) {    return;  }  try {    target = realpath.sync(dep);  } catch (error) {    err = error;    return typeof opts.onError === "function" ? opts.onError(err) : void 0;  }  // Skip target paths with "/node_modules/" in them.  if (nodeModulesRE.test(target)) {    return typeof opts.onError === "function" ? opts.onError(new Error(`Symlink leads to nothing: '${dep}'`)) : void 0;  }  if (opts.preserveLinks) {    paths.push(dep);  }  if (!visited.has(target)) {    visited.add(target);    if (!opts.preserveLinks) {      paths.push(target);    }    queue.push(dep);  }};

addPath 办法通过判断一个文件门路是否是软链,并文件的实在门路增加最终的输入后果中

那么addPath 在哪调用的呢,在这个库的源码里发现上面这段代码

return fs.readdirSync(depsDir).forEach(function(name) {        var scope;        if (name[0] === '.') { // Skip hidden directories.          return;        }        if (name[0] === '@') {          scope = name;          fs.readdirSync(path.join(depsDir, name)).forEach(function(name) {            if (deps[name = scope + '/' + name]) {              return addPath(path.join(depsDir, name));            }          });        } else if (deps[name]) {          addPath(path.join(depsDir, name));        }      });

下面代码中deps[name]deps是获取了我的项目目录下的package.json中的dependencies 里的依赖,并且与fs.readdirSync办法获取的文件名来比对,如果文件名呈现在了dependencies 就执行addPath办法来获取实在门路并增加到最终后果中。

于是我发现,我应用yarn link 的依赖的名字并没有呈现在我的package.json中的dependencies 里,于是我在package.json 中的dependencies 里增加了本地依赖的名字

"dependencies": {    "myPackageName" : "*"},

再次尝试react-native start ,终于编译胜利。

One more thing

到这里就能够开始测试本人的本地依赖了,然而还有一个小小问题,可能会报错。本地依赖的依赖找不到了。这时候须要改一下配置

// metro.config.jsconst {    applyConfigForLinkedDependencies,} = require('@carimus/metro-symlinked-deps');module.exports = applyConfigForLinkedDependencies(    {        transformer: {            getTransformOptions: async () => ({                transform: {                    experimentalImportSupport: false,                    inlineRequires: false,                },            }),        },    },    {        projectRoot: __dirname,        blacklistLinkedModules: ['react-native'],        // 新增这个          resolveNodeModulesAtRoot: true     },);

终于能够在React Native 我的项目中调试本地依赖了~

最初

以上是我寻找在React Native 我的项目中应用yarn link的解决办法的全过程。

参考

  • metro-symlinked-deps
  • get-dev-paths
本文作者: Roy Luo

本文链接: 在react-native 我的项目中应用yarn link