diff options
author | Scott Santucci <ScottFreeCode@users.noreply.github.com> | 2017-05-15 23:48:50 +0300 |
---|---|---|
committer | Rebecca Turner <me@re-becca.org> | 2017-08-17 21:20:05 +0300 |
commit | 0ef320cb40222693b7367b97c60ddffabc2d58c5 (patch) | |
tree | 7ed5eebef6c50e7971caffc00587329a6cfd208d /lib | |
parent | a267ab4309883012a9d55934533c5915e9842277 (diff) |
build: Check if bin script starts with hashbang
PR-URL: https://github.com/npm/npm/pull/16634
Fixes: #12371
Fixes: #13203
Credit: @ScottFreeCode
Reviewed-By: @iarna
Diffstat (limited to 'lib')
-rw-r--r-- | lib/build.js | 21 | ||||
-rw-r--r-- | lib/utils/convert-line-endings.js | 49 | ||||
-rw-r--r-- | lib/utils/is-hashbang-file.js | 19 |
3 files changed, 82 insertions, 7 deletions
diff --git a/lib/build.js b/lib/build.js index 44ac40a00..6a788bc85 100644 --- a/lib/build.js +++ b/lib/build.js @@ -18,6 +18,8 @@ var link = require('./utils/link.js') var linkIfExists = link.ifExists var cmdShim = require('cmd-shim') var cmdShimIfExists = cmdShim.ifExists +var isHashbangFile = require('./utils/is-hashbang-file.js') +var dos2Unix = require('./utils/convert-line-endings.js').dos2Unix var asyncMap = require('slide').asyncMap var ini = require('ini') var writeFile = require('write-file-atomic') @@ -187,13 +189,18 @@ function linkBins (pkg, folder, parent, gtop, cb) { if (er && er.code === 'ENOENT' && npm.config.get('ignore-scripts')) { return cb() } - if (er || !gtop) return cb(er) - var dest = path.resolve(binRoot, b) - var out = npm.config.get('parseable') - ? dest + '::' + src + ':BINFILE' - : dest + ' -> ' + src - if (!npm.config.get('json') && !npm.config.get('parseable')) output(out) - cb() + if (er) return cb(er) + isHashbangFile(src).then((isHashbang) => { + if (isHashbang) return dos2Unix(src) + }).then(() => { + if (!gtop) return cb() + var dest = path.resolve(binRoot, b) + var out = npm.config.get('parseable') + ? dest + '::' + src + ':BINFILE' + : dest + ' -> ' + src + if (!npm.config.get('json') && !npm.config.get('parseable')) output(out) + cb() + }).catch(cb) }) } ) diff --git a/lib/utils/convert-line-endings.js b/lib/utils/convert-line-endings.js new file mode 100644 index 000000000..b05d328aa --- /dev/null +++ b/lib/utils/convert-line-endings.js @@ -0,0 +1,49 @@ +'use strict' + +const Transform = require('stream').Transform +const Bluebird = require('bluebird') +const fs = require('graceful-fs') +const stat = Bluebird.promisify(fs.stat) +const chmod = Bluebird.promisify(fs.chmod) +const fsWriteStreamAtomic = require('fs-write-stream-atomic') + +module.exports.dos2Unix = dos2Unix + +function dos2Unix (file) { + return stat(file).then((stats) => { + let previousChunkEndedInCR = false + return new Bluebird((resolve, reject) => { + fs.createReadStream(file) + .on('error', reject) + .pipe(new Transform({ + transform: function (chunk, encoding, done) { + let data = chunk.toString() + if (previousChunkEndedInCR) { + data = '\r' + data + } + if (data[data.length - 1] === '\r') { + data = data.slice(0, -1) + previousChunkEndedInCR = true + } else { + previousChunkEndedInCR = false + } + done(null, data.replace(/\r\n/g, '\n')) + }, + flush: function (done) { + if (previousChunkEndedInCR) { + this.push('\r') + } + done() + } + })) + .on('error', reject) + .pipe(fsWriteStreamAtomic(file)) + .on('error', reject) + .on('finish', function () { + resolve(chmod(file, stats.mode)) + }) + }) + }) +} + +// could add unix2Dos and legacy Mac functions if need be diff --git a/lib/utils/is-hashbang-file.js b/lib/utils/is-hashbang-file.js new file mode 100644 index 000000000..f1677381f --- /dev/null +++ b/lib/utils/is-hashbang-file.js @@ -0,0 +1,19 @@ +'use strict' +const Bluebird = require('bluebird') +const fs = require('graceful-fs') +const open = Bluebird.promisify(fs.open) +const close = Bluebird.promisify(fs.close) + +module.exports = isHashbangFile + +function isHashbangFile (file) { + return open(file, 'r').then((fileHandle) => { + return new Bluebird((resolve, reject) => { + fs.read(fileHandle, new Buffer(new Array(2)), 0, 2, 0, function (err, bytesRead, buffer) { + close(fileHandle).then(() => { + resolve(!err && buffer.toString() === '#!') + }).catch(reject) + }) + }) + }) +} |