用depcheck来解决工程项目的灵异事件
前言
你是否遇到以下问题
-
GitHub
上面clone
的项目,运行报错…
-
- 去一家新公司,用新电脑运行项目各种报错…
-
- 明明这个项目在公司电脑上运行正常,用我自己的电脑运行就各种错误,运行不起来?
-
- 项目在别人那里运行的好好地,拿到自己这边就跑不起来了…
前端在项目开发中,多多少少会遇见 npm run serve
跑不起来项目的问题,本文用 depcheck
来帮助你进行项目依赖自检,让你不再过多的关心项目的灵异事件。项目 package.json
长这样:
场景
当我们 clone
目录下来,在本地 npm i
的之后,再去执行 npm run dev
,可能出现两种情况:
case1
case2
这种情况很常见,那么我们可以利用 depcheck
这个包来协助我们了。
使用
// 安装
npm i -g depcheck;
// 执行
depcheck;
结果如下:
Unused 表示没有使用的依赖包,Missing 表示使用到了但是没有在 json 文件中声明的依赖包
运行结果显示,没有用到的本地依赖,开发依赖,缺失的依赖。所以我们可以按照他的提示来安装一下依赖,就能够运行啦!
常用的参数
--skip-missing=[true | false] 默认false,表示是否检测Missing的依赖包
--ignore-bin-package=[true | false] 默认false,表示是否忽略包含bin条目的包
--json 表示所有包的检测结果以 json 格式输出,大概就是XX包在哪些文件使用了,{"包名": ["path1","path2"]}
--ignores="eslint,babel-" 表示要忽略的包名称(逗号分隔),比如 depcheck --ignores="eslint,@babel/,babel-*"
--ignore-path 表示要忽略的文件的模式的文件的路径,比如 depcheck --ignore-path=.eslintignore
--ignore-dirs 已经弃用,使用 --ignore-patterns 替代,表示要忽略的目录名,逗号分隔--ignore-dirs=dist,coverage.
--ignore-patterns 表示要忽略的用逗号分隔的模式描述文件,比如depcheck --ignore-patterns=build/Release,dist,coverage,*.log.
- --parsers
- --detectors
- --specials
高级的语法使用参考官方文档
ignore
可能这个包实现的是有一些问题,比如 prettier
包明显有使用到,为什么会被列入 Unused devDependencies
中呢?
那么这里的解决办法就是新建 .depcheckrc
文件,把这个写到里面去。
ignores: ["eslint", "babel-*", "@babel/*", "@vue/*", "prettier"]
skip-missing: true // 表示不呈现项目中使用到但是在package.json里面没有的包,默认为false
运行结果:
源码解读
- github仓库
- 描述:
- 全局安装
depcheck
可带可不带行内参数
入口文件
"bin": {
"depcheck": "bin/depcheck.js"
}
depcheck.js
// #!/usr/bin/env node
require('please-upgrade-node')(require('../package.json'));
/* eslint-disable no-console */
require('../dist/cli')(
process.argv.slice(2),
console.log,
console.error,
function exit(code) {
process.exitCode = code;
},
);
CommonJS
写法:
require('please-upgrade-node')(require('../package.json'))
, 拆分为:
const pleaseUpgradeNode = require('please-upgrade-node');
const pkg = require('../package.json');
pleaseUpgradeNode(pkg);
- 这个包把
package.json
传进去,就为了获取规定的nodejs
版本,与当前版本对比,给予及时更新node
版本的逻辑。 - 之后便是导入
dist
目录下的cli
,就是导入的cli
函数。
cli
async function cli(args, log, error, exit) {
try {
const opt =
await (0, _configurationReader.getConfiguration)(args, _package.name, _package.version);
const dir = opt._[0] || '.';
const rootDir = _path.default.resolve(dir); // 获取到的根路径 /Users/mac/Desktop/depCheck
await checkPathExist(rootDir, `Path ${dir} does not exist`); // 检查根目录是否存在
await checkPathExist(_path.default.resolve(rootDir, 'package.json'), `Path ${dir} does not contain a package.json file`); // 检查package.json文件是否存在于根目录
// 找到dependencies、devDependencies、invalidDirs、invalidFiles、missing、using
const depcheckResult = await (0, _index.default)(rootDir, {
ignoreBinPackage: opt.ignoreBinPackage,
ignorePath: opt.ignorePath,
ignoreMatches: opt.ignores || [],
ignoreDirs: opt.ignoreDirs || [],
ignorePatterns: opt.ignorePatterns || [],
parsers: getParsers(opt.parsers),
detectors: getDetectors(opt.detectors),
specials: getSpecials(opt.specials),
skipMissing: opt.skipMissing
});
print(depcheckResult, log, opt, rootDir); // 控制台输出 Unused、Missing等
exit(noIssue(depcheckResult) ? 0 : -1);
} catch (err) {
error(err);
exit(-1);
}
}
其中我们发现了这样的一段代码 (0, _configurationReader.getConfiguration)(args, _package.name, _package.version)
,我们简化成 (0, fn)(params)
(0, fn)(params)
0
的作用
- 1.绑定(重置)
this
指向,指向全局对象——最外层window
或globalThis
。 - 2.避免了有些源码中会修改
prototype
,导致原型链
上方法不可用的情况
上述代码最关键的就是opt,一切都是围绕着opt展开的,那我们来一起看看opt得到的是什么吧。
获得opt
exports.getCliArgs = getCliArgs;
exports.getRCFileConfiguration = getRCFileConfiguration;
exports.getConfiguration = getConfiguration;
...
async function getConfiguration(args, moduleName, version) {
// args: []
// moduleName: depcheck
// version: 0.0.1
const dir = args[0] || '.'; // dir 取相对路径
const cliConfig = getCliArgs(args, version); // 获得cliConfig
const rcConfig = await getRCFileConfiguration(moduleName, cliConfig.argv.config, dir);
return { ...rcConfig, // ignorerc文件
...cliConfig.argv // 暴露方法
};
}
opt.oneline 来记录在哪一行使用到了依赖导入,针对于Missing的依赖才记录,Unused的不记录。
得到的opt
长下面这样子:
其余的大概没有什么了,最后就是获取到的一些依赖进行打印。
function print(result, log, opt, rootDir) {
if (opt.json) { // json标识
log(JSON.stringify(result, (key, value) => _lodash.default.isError(value) ? value.stack : value));
} else if (noIssue(result)) {
log('No depcheck issue'); // 没有结果
} else { // 有depcheckResult 输出
// 组装deps
const deps = prettify('Unused dependencies', result.dependencies, opt.oneline);
// 组装devDeps
const devDeps = prettify('Unused devDependencies', result.devDependencies, opt.oneline);
// 组装Missing
const missing = prettify('Missing dependencies', mapMissing(result.missing, rootDir, opt.oneline), opt.oneline);
// 组装deps、 devDeps、 Missing 输出数组
const content = deps.concat(devDeps, missing).join('\n');
log(content);
}
return result; // ??? 这里为什么要返回result,有点多此一举的感觉
}
总结
depcheck
能够帮助解决前言中的问题,让你的项目不再出现灵异事件,用你专注于业务代码开发。
转载自:https://juejin.cn/post/7205489514360782905