上一篇文章講解了如何找到源碼以及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')])
複製代碼
在這個階段會有三個步驟
檢測安裝是否失敗,若是失敗就進行回滾操做
執行全部的生命週期函數(若是定義了的話)
在控制檯打印警告信息和安裝了那些模塊信息