上一篇文章讲解了如何找到源码以及npm config命令的源码解读,今天来看一下npm install命令。若是不知道怎么找源码能够参考上一篇文章【npm源码】config get命令大扒皮,一件衣服都不留node
首先在源码中找到Installer构造函数:react
/lib/install.js
function Installer (where, dryrun, args, opts) {
validate('SBA|SBAO', arguments)
if (!opts) opts = {}
this.where = where
this.dryrun = dryrun
this.args = args
// fakechildren are children created from the lockfile and lack relationship data
// the only exist when the tree does not match the lockfile
// this is fine when doing full tree installs/updates but not ok when modifying only
// a few deps via `npm install` or `npm uninstall`.
this.currentTree = null
this.idealTree = null
this.differences = []
this.todo = []
this.progress = {}
this.noPackageJsonOk = !!args.length
this.topLevelLifecycles = !args.length
this.autoPrune = npm.config.get('package-lock')
const dev = npm.config.get('dev')
const only = npm.config.get('only')
const onlyProd = /^prod(uction)?$/.test(only)
const onlyDev = /^dev(elopment)?$/.test(only)
const prod = npm.config.get('production')
this.dev = opts.dev != null ? opts.dev : dev || (!onlyProd && !prod) || onlyDev
this.prod = opts.prod != null ? opts.prod : !onlyDev
this.packageLockOnly = opts.packageLockOnly != null
? opts.packageLockOnly : npm.config.get('package-lock-only')
this.rollback = opts.rollback != null ? opts.rollback : npm.config.get('rollback')
this.link = opts.link != null ? opts.link : npm.config.get('link')
this.saveOnlyLock = opts.saveOnlyLock
this.global = opts.global != null ? opts.global : this.where === path.resolve(npm.globalDir, '..')
this.audit = npm.config.get('audit') && !this.global
this.started = Date.now()
}
复制代码
再继续找,找到安装过程:npm
/lib/install.js
Installer.prototype.run = function (_cb) {
//进行一些校验
...
//将安装的步骤放入队列里面
var installSteps = []
var postInstallSteps = []
if (!this.dryrun) {
installSteps.push(
[this.newTracker(log, 'runTopLevelLifecycles', 2)],
[this, this.runPreinstallTopLevelLifecycles])
}
installSteps.push(
[this.newTracker(log, 'loadCurrentTree', 4)],
[this, this.loadCurrentTree],
[this, this.finishTracker, 'loadCurrentTree'],
[this.newTracker(log, 'loadIdealTree', 12)],
[this, this.loadIdealTree],
[this, this.finishTracker, 'loadIdealTree'],
[this, this.debugTree, 'currentTree', 'currentTree'],
[this, this.debugTree, 'idealTree', 'idealTree'],
[this.newTracker(log, 'generateActionsToTake')],
[this, this.generateActionsToTake],
[this, this.finishTracker, 'generateActionsToTake'],
[this, this.debugActions, 'diffTrees', 'differences'],
[this, this.debugActions, 'decomposeActions', 'todo'],
[this, this.startAudit]
)
if (this.packageLockOnly) {
postInstallSteps.push(
[this, this.saveToDependencies])
} else if (!this.dryrun) {
installSteps.push(
[this.newTracker(log, 'executeActions', 8)],
[this, this.executeActions],
[this, this.finishTracker, 'executeActions'])
var node_modules = path.resolve(this.where, 'node_modules')
var staging = path.resolve(node_modules, '.staging')
postInstallSteps.push(
[this.newTracker(log, 'rollbackFailedOptional', 1)],
[this, this.rollbackFailedOptional, staging, this.todo],
[this, this.finishTracker, 'rollbackFailedOptional'],
[this, this.commit, staging, this.todo],
[this, this.runPostinstallTopLevelLifecycles],
[this, this.finishTracker, 'runTopLevelLifecycles']
)
if (getSaveType()) {
postInstallSteps.push(
[this, (next) => { computeMetadata(this.idealTree); next() }],
[this, this.pruneIdealTree],
[this, this.debugLogicalTree, 'saveTree', 'idealTree'],
[this, this.saveToDependencies])
}
}
postInstallSteps.push(
[this, this.printWarnings],
[this, this.printInstalled])
var self = this
//到这里才真正开始执行
chain(installSteps, function (installEr) {
if (installEr) self.failing = true
chain(postInstallSteps, function (postInstallEr) {
if (installEr && postInstallEr) {
var msg = errorMessage(postInstallEr)
msg.summary.forEach(function (logline) {
log.warn.apply(log, logline)
})
msg.detail.forEach(function (logline) {
log.verbose.apply(log, logline)
})
}
cb(installEr || postInstallEr, self.getInstalledModules(), self.idealTree)
})
})
return result
}
复制代码
这里就是安装模块的整个流程了,如今来分析一下。json
installStep: 安装步骤数组
postInstallSteps: 安装完成以后的步骤bash
判断是不是干运行app
若是不是干运行,则运行 preinstall 钩子(若是工程定义了的话)ide
若是是干运行,则进入下一步函数
这一步会执行两个动做:post
这一步会执行三个动做,
调用cloneCurrentTree函数 把currentTree复制给idealTree
调用loadShrinkwrap函数 ,该函数会依次读取npm-shrinkwrap.json、package-lock.json、package.json ,肯定完整的依赖树,注意这时候的依赖树只是逻辑上的依赖树
调用loadAllDepsIntoIdealTree函数 遍历上一步获得的idealTree,添加任何缺乏的依赖项,在不破坏原来的module的状况下,依赖项会尽量的放到顶层。
这一步会对上面生成的currentTree和idealTree进行一些处理
返回带有unicode管道字符的obj字符串表示形式 并打印在控制台,效果相似于npm ls
实际调用了 archy 方法 ,详细信息点这里
这阶段会有五个动做
调用validateTree方法 对idealTree进行校验,校验的内容有是否须要peer dependencies,node版本校验,要安装的包是否在dev和prod环境中都存在。
调用diffTrees方法找到由currentTree转为idelTree的不一样动做并放到 differences队列中
调用 computeLinked 方法计算局部包如何连接到合适的全局包
调用checkPermissions方法检查differnces里的anction操做是否有权限,好比是否有写文件的权限等。
调用decomposeActions方法,把diffrence队列里的action按照add、update、move、remove进行分解并放到todo队列中
这个阶段只作一件事就是打印每一个action的信息
若是packageLockOnly为true
.npmrc中对应的配置项为 package-lock-only
packageLockOnly的意思就是只将要安装的模块写到package.json的dependences里面,而不执行任何其余动做
若是packageLockOnly为false 而且dryrun(干运行)为true
.npmrc中对应的配置项为 dry-run
这个时候不执行任何动做只是打印信息
若是packageLockOnly为false,而且dryrun(干运行)为false,这个时候开始了真正安装阶段。
这个阶段调用executeActions方法,去执行todo里面的内容
/lib/install.js
steps.push(
[doSerialActions, 'global-install', staging, todo, trackLifecycle.newGroup('global-install')],
[lock, node_modules, '.staging'],
[rimraf, staging],
[doParallelActions, 'extract', staging, todo, cg.newGroup('extract', 100)],
[doReverseSerialActions, 'unbuild', staging, todo, cg.newGroup('unbuild')],
[doSerialActions, 'remove', staging, todo, cg.newGroup('remove')],
[doSerialActions, 'move', staging, todo, cg.newGroup('move')],
[doSerialActions, 'finalize', staging, todo, cg.newGroup('finalize')],
[doParallelActions, 'refresh-package-json', staging, todo, cg.newGroup('refresh-package-json')],
[doParallelActions, 'preinstall', staging, todo, trackLifecycle.newGroup('preinstall')],
[doSerialActions, 'build', staging, todo, trackLifecycle.newGroup('build')],
[doSerialActions, 'global-link', staging, todo, trackLifecycle.newGroup('global-link')],
[doParallelActions, 'update-linked', staging, todo, trackLifecycle.newGroup('update-linked')],
[doSerialActions, 'install', staging, todo, trackLifecycle.newGroup('install')],
[doSerialActions, 'postinstall', staging, todo, trackLifecycle.newGroup('postinstall')])
复制代码
在这个阶段会有三个步骤
检测安装是否失败,若是失败就进行回滚操做
执行全部的生命周期函数(若是定义了的话)
在控制台打印警告信息和安装了那些模块信息