diff --git a/index.js b/index.js index 11aee40..9061ec4 100644 --- a/index.js +++ b/index.js @@ -243,6 +243,10 @@ class Installer { ? pkg : this.updateFromField(dep, pkg).then(() => pkg) ) + .then(pkg => (pkg.scripts && pkg.scripts.install) + ? pkg + : this.updateInstallScript(dep, pkg).then(() => pkg) + ) .tap(pkg => { pkgJsons.set(dep, pkg) }) }, {concurrency: 100, Promise: BB}) .then(() => pkgJsons) @@ -311,6 +315,21 @@ class Installer { .then(pkg) } + updateInstallScript (dep, pkg) { + const depPath = dep.path(this.prefix) + return statAsync(path.join(depPath, 'binding.gyp')) + .catch(err => { if (err.code !== 'ENOENT') { throw err } }) + .then(stat => { + if (stat) { + if (!pkg.scripts) { + pkg.scripts = {} + } + pkg.scripts.install = 'node-gyp rebuild' + } + }) + .then(pkg) + } + // A cute little mark-and-sweep collector! garbageCollect (tree) { if (!this.failedDeps.size) { return } diff --git a/test/specs/index.js b/test/specs/index.js index 7677ee7..5c01473 100644 --- a/test/specs/index.js +++ b/test/specs/index.js @@ -20,6 +20,9 @@ const pkgVersion = '1.0.0' const writeEnvScript = process.platform === 'win32' ? 'echo %npm_lifecycle_event% > %npm_lifecycle_event%' : 'echo $npm_lifecycle_event > $npm_lifecycle_event' +const binarySuffix = process.platform === 'win32' + ? '.exe' + : '' const prefix = require('../lib/test-dir')(__filename) @@ -654,6 +657,78 @@ test('skips lifecycle scripts with ignoreScripts is set', t => { }) }) +test('adds install script when binding.gyp is present', t => { + const originalConsoleLog = console.log + console.log = () => {} + + const fixture = new Tacks(Dir({ + 'package.json': File({ + name: pkgName, + version: pkgVersion, + dependencies: { + a: '^1', + b: '^1' + } + }), + 'package-lock.json': File({ + dependencies: { + a: { version: '1.0.0' }, + b: { version: '1.0.0' } + }, + lockfileVersion: 1 + }) + })) + fixture.create(prefix) + + extract = (name, child, childPath, opts) => { + const pkg = (child.name === 'a') + ? { + name: 'a', + version: '1.0.0' + } + : { + name: 'b', + version: '1.0.0', + scripts: { + install: 'exit 0' + } + } + const files = new Tacks(Dir({ + 'package.json': File(pkg), + 'binding.gyp': File({ + 'targets': [ + { + 'target_name': 'hello', + 'type': 'executable', + 'sources': [ + 'hello.cc' + ] + } + ] + }), + 'hello.cc': File( + '#include \n' + + 'int main() {\n' + + 'std::cout << "hello";\n' + + 'return 0;\n' + + '}\n' + ) + })) + files.create(childPath) + } + + return run().then(details => { + t.equal(details.pkgCount, 2) + t.ok(fs.statSync(path.join(prefix, 'node_modules', 'a', 'build', 'Release', 'hello' + binarySuffix)), 'dep a binary is built') + t.throws(() => { + fs.statSync(path.join(prefix, 'node_modules', 'b', 'build', 'Release', 'hello' + binarySuffix)) + }, 'dep b binary is not built') + + fixtureHelper.teardown() + console.log = originalConsoleLog + }) +}) + test('handles JSON docs that contain a BOM', t => { t.plan(2) const Installer = requireInject('../../index.js', {/* just don't want to cache */})