Skip to content
This repository has been archived by the owner on Jul 28, 2021. It is now read-only.

Commit

Permalink
feat(prepare): support install scripts and native builds
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Dec 19, 2018
1 parent 57e13c2 commit 11fc0d6
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 23 deletions.
2 changes: 1 addition & 1 deletion lib/ensure-package.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class CacacheUnpacker extends tar.Parse {
}

const EnsurePkgOpts = figgyPudding({
restore: { default: true }
restore: { default: false }
})

module.exports = ensurePackage
Expand Down
100 changes: 79 additions & 21 deletions lib/installer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const BB = require('bluebird')

const {
linkBin: binLink,
extract,
getPrefix,
log: npmlog,
logicalTree: buildLogicalTree,
Expand All @@ -28,8 +29,8 @@ const rimraf = BB.promisify(require('rimraf'))
const { spawn } = require('child_process')

const readFileAsync = BB.promisify(fs.readFile)
const readdirAsync = BB.promisify(fs.readdir)
const realpathAsync = BB.promisify(fs.realpath)
const statAsync = BB.promisify(fs.stat)
const symlinkAsync = BB.promisify(fs.symlink)
const writeFileAsync = BB.promisify(fs.writeFile)

Expand Down Expand Up @@ -73,6 +74,7 @@ class Installer {
if (!this.validLockHash || this.force) {
this.log('info', 'Generating new package map')
await this.timedStage('fetchTree', this.tree)
await this.timedStage('updateJson', this.tree)
await this.timedStage('buildTree', this.tree)
await this.tinkifyBins()
await this.timedStage('runScript', 'prepublish', this.pkg, this.prefix)
Expand Down Expand Up @@ -250,6 +252,28 @@ class Installer {
warn: msg => this.opts.log('warn', msg)
})
)
const pkg = await readJson(dep.path(this.prefix), 'package.json')
if (
(
pkg.scripts && (
pkg.scripts.preinstall ||
pkg.scripts.install ||
pkg.scripts.postinstall
)
) || (
pkg.bundleDependencies ||
pkg.bundledDependencies
)
) {
await extract(
npa.resolve(dep.name, dep.version),
dep.path(this.prefix),
this.opts.concat({
integrity: dep.integrity,
resolved: dep.resolved
})
)
}
// cg.completeWork(1)
this.pkgCount++
await next()
Expand All @@ -274,6 +298,56 @@ class Installer {
return (dep.dev && includeDev) || (!dep.dev && includeProd)
}

async updateJson (tree) {
this.log('verbose', 'updateJson', 'checking for native builds')
const pkgJsons = new Map()
await tree.forEachAsync(async (dep, next) => {
if (!this.checkDepEnv(dep)) { return }
const depPath = dep.path(this.prefix)
await next()
const pkg = await readJson(depPath, 'package.json')
await this.updateInstallScript(dep, pkg)
pkgJsons.set(dep, pkg)
}, { concurrency: 100, Promise: BB })
this.pkgJsons = pkgJsons
return pkgJsons
}

async updateInstallScript (dep, pkg) {
const depPath = dep.path(this.prefix)
if (!pkg.scripts || !pkg.scripts.install) {
const files = await readdirAsync(depPath)
if (files.find(f => /\.gyp$/i.test(f))) {
if (!pkg.scripts) {
pkg.scripts = {}
}
pkg.scripts.install = 'node-gyp rebuild'
}
}
let modified
if (pkg.scripts) {
if (pkg.scripts.preinstall) {
const old = pkg.scripts.preinstall
pkg.scripts.preinstall = pkg.scripts.preinstall.replace('node', 'tish')
modified = pkg.scripts.preinstall === old
}
if (pkg.scripts.install) {
const old = pkg.scripts.install
pkg.scripts.install = pkg.scripts.install.replace('node', 'tish')
modified = pkg.scripts.install === old
}
if (pkg.scripts.postinstall) {
const old = pkg.scripts.postinstall
pkg.scripts.postinstall = pkg.scripts.postinstall.replace('node', 'tish')
modified = pkg.scripts.postinstall === old
}
if (modified) {
await writeFileAsync(path.join(depPath, 'package.json'), JSON.stringify(pkg, null, 2))
}
}
return pkg
}

async buildTree (tree) {
this.log('verbose', 'buildTree', 'finalizing tree and running scripts')
await tree.forEachAsync(async (dep, next) => {
Expand All @@ -282,13 +356,13 @@ class Installer {
const spec = npa.resolve(dep.name, dep.version)
const depPath = dep.path(this.prefix)
this.log('silly', 'buildTree', `linking ${spec}`)
const pkg = await readPkgJson(path.join(depPath, 'package.json'))
const pkg = this.pkgJsons.get(dep)
await this.runScript('preinstall', pkg, depPath)
await next() // build children between preinstall and binLink
// Don't link root bins
if (
dep.isRoot ||
!(pkg.bin || pkg.man || (pkg.directories && pkg.directories.bin))
!(pkg.bin || pkg.man || (pkg.directories && pkg.directories.bin) || (pkg.scripts && (pkg.scripts.install || pkg.scripts.postinstall)))
) {
// We skip the relatively expensive readPkgJson if there's no way
// we'll actually be linking any bins or mans
Expand Down Expand Up @@ -329,23 +403,6 @@ class Installer {
}, { concurrency: 50, Promise: BB })
}

async updateInstallScript (dep, pkg) {
const depPath = dep.path(this.prefix)
let stat
try {
stat = statAsync(path.join(depPath, 'binding.gyp'))
} catch (err) {
if (err.code !== 'ENOENT') { throw err }
}
if (stat) {
if (!pkg.scripts) {
pkg.scripts = {}
}
pkg.scripts.install = 'node-gyp rebuild'
}
return pkg
}

// A cute little mark-and-sweep collector!
async garbageCollect (tree) {
if (!this.failedDeps.size) { return }
Expand All @@ -365,7 +422,8 @@ class Installer {
pkg._id = pkg.name + '@' + pkg.version
const ret = await runScript(pkg, stage, pkgPath, {
dir: this.prefix,
log: npmlog
log: npmlog,
config: this.opts
})
this.timings.scripts += Date.now() - start
return ret
Expand Down
2 changes: 1 addition & 1 deletion lib/lock-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ async function main (cache, integrity, pkg, opts) {
if (res && !res.pkgCount) { throw new Error('no packages installed') }
} catch (err) {
if (!ensurePkg) { ensurePkg = require('./ensure-package.js') }
await ensurePkg(cache, pkg.name, pkg, opts)
await ensurePkg(cache, pkg.name, pkg, opts.concat({restore: true}))
}
}

0 comments on commit 11fc0d6

Please sign in to comment.