From 48fd8118057a19830e154231991a9acdb1a5610d Mon Sep 17 00:00:00 2001 From: isaacs Date: Sun, 26 Jan 2020 23:12:51 -0800 Subject: [PATCH] promisification --- README.md | 22 ++++-------- index.js | 94 ++++++++++++++++++++------------------------------- test/basic.js | 64 +++++++---------------------------- 3 files changed, 55 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 2b8b2f4..60e6625 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ On Unix systems, you should use a symbolic link instead. [![Build Status](https://img.shields.io/travis/npm/cmd-shim/master.svg)](https://travis-ci.org/npm/cmd-shim) [![Dependency Status](https://img.shields.io/david/npm/cmd-shim.svg)](https://david-dm.org/npm/cmd-shim) -[![NPM version](https://img.shields.io/npm/v/cmd-shim.svg)](https://www.npmjs.com/package/cmd-shim) +[![npm version](https://img.shields.io/npm/v/cmd-shim.svg)](https://www.npmjs.com/package/cmd-shim) ## Installation @@ -17,28 +17,18 @@ npm install cmd-shim ## API -### cmdShim(from, to, cb) +### cmdShim(from, to) -> Promise Create a cmd shim at `to` for the command line program at `from`. e.g. ```javascript var cmdShim = require('cmd-shim'); -cmdShim(__dirname + '/cli.js', '/usr/bin/command-name', function (err) { - if (err) throw err; -}); +cmdShim(__dirname + '/cli.js', '/usr/bin/command-name').then(() => { + // shims are created! +}) ``` -### cmdShim.ifExists(from, to, cb) +### cmdShim.ifExists(from, to) -> Promise The same as above, but will just continue if the file does not exist. -Source: - -```javascript -function cmdShimIfExists (from, to, cb) { - fs.stat(from, function (er) { - if (er) return cb() - cmdShim(from, to, cb) - }) -} -``` diff --git a/index.js b/index.js index 3efee56..71fbc36 100644 --- a/index.js +++ b/index.js @@ -8,64 +8,55 @@ // Write a binroot/pkg.bin + ".cmd" file that has this line in it: // @ %dp0% %* +const {promisify} = require('util') const fs = require('fs') +const writeFile = promisify(fs.writeFile) +const readFile = promisify(fs.readFile) +const chmod = promisify(fs.chmod) +const stat = promisify(fs.stat) +const unlink = promisify(fs.unlink) +const {dirname, relative} = require('path') const mkdir = require('mkdirp') -const path = require('path') const toBatchSyntax = require('./lib/to-batch-syntax') const shebangExpr = /^#\!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+=[^ \t]+\s+)*\s*([^ \t]+)(.*)$/ -const cmdShimIfExists = (from, to, cb) => { - fs.stat(from, er => { - if (er) return cb() - cmdShim(from, to, cb) - }) -} +const cmdShimIfExists = (from, to) => + stat(from).then(() => cmdShim(from, to), () => {}) // Try to unlink, but ignore errors. // Any problems will surface later. -const rm = (path, cb) => fs.unlink(path, () => cb()) - -const cmdShim = (from, to, cb) => { - fs.stat(from, (er, stat) => { - if (er) - return cb(er) +const rm = path => unlink(path).catch(() => {}) - cmdShim_(from, to, cb) - }) -} +const cmdShim = (from, to) => + stat(from).then(() => cmdShim_(from, to)) -const cmdShim_ = (from, to, cb) => { - const next = () => writeShim(from, to, cb) - const then = times(3, next, cb) - - rm(to, then) - rm(to + '.cmd', then) - rm(to + '.ps1', then) -} +const cmdShim_ = (from, to) => Promise.all([ + rm(to), + rm(to + '.cmd'), + rm(to + '.ps1'), +]).then(() => writeShim(from, to)) -const writeShim = (from, to, cb) => { +const writeShim = (from, to) => // make a cmd file and a sh script // First, check if the bin is a #! of some sort. // If not, then assume it's something that'll be compiled, or some other // sort of script, and just call it directly. - mkdir(path.dirname(to)).then(() => { - fs.readFile(from, 'utf8', (er, data) => { - if (er) return writeShim_(from, to, null, null, null, cb) + mkdir(dirname(to)) + .then(() => readFile(from, 'utf8')) + .then(data => { const firstLine = data.trim().split(/\r*\n/)[0] const shebang = firstLine.match(shebangExpr) - if (!shebang) return writeShim_(from, to, null, null, null, cb) + if (!shebang) return writeShim_(from, to) const vars = shebang[1] || '' const prog = shebang[2] const args = shebang[3] || '' - return writeShim_(from, to, prog, args, vars, cb) - }) - }, cb) -} + return writeShim_(from, to, prog, args, vars) + }, er => writeShim_(from, to)) -const writeShim_ = (from, to, prog, args, variables, cb) => { - let shTarget = path.relative(path.dirname(to), from) +const writeShim_ = (from, to, prog, args, variables) => { + let shTarget = relative(dirname(to), from) let target = shTarget.split('/').join('\\') let longProg let shProg = prog && prog.split('\\').join('/') @@ -225,31 +216,18 @@ const writeShim_ = (from, to, prog, args, variables, cb) => { + 'exit $LASTEXITCODE\n' } - const next = () => chmodShim(to, cb) - const then = times(3, next, cb) - fs.writeFile(to + '.ps1', pwsh, 'utf8', then) - fs.writeFile(to + '.cmd', cmd, 'utf8', then) - fs.writeFile(to, sh, 'utf8', then) -} - -const chmodShim = (to, cb) => { - const then = times(3, cb, cb) - fs.chmod(to, 0o755, then) - fs.chmod(to + '.cmd', 0o755, then) - fs.chmod(to + '.ps1', 0o755, then) + return Promise.all([ + writeFile(to + '.ps1', pwsh, 'utf8'), + writeFile(to + '.cmd', cmd, 'utf8'), + writeFile(to, sh, 'utf8'), + ]).then(() => chmodShim(to)) } -const times = (n, ok, cb) => { - let errState = null - return er => { - if (!errState) { - if (er) - cb(errState = er) - else if (--n === 0) - ok() - } - } -} +const chmodShim = to => Promise.all([ + chmod(to, 0o755), + chmod(to + '.cmd', 0o755), + chmod(to + '.ps1', 0o755), +]) module.exports = cmdShim cmdShim.ifExists = cmdShimIfExists diff --git a/test/basic.js b/test/basic.js index 928fc02..a4ef523 100755 --- a/test/basic.js +++ b/test/basic.js @@ -12,22 +12,17 @@ var cmdShim = require('../') test('no shebang', function (t) { var from = path.resolve(fixtures, 'from.exe') var to = path.resolve(fixtures, 'exe.shim') - cmdShim(from, to, function(er) { - if (er) - throw er + return cmdShim(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') - t.end() }) }) test('if exists (it does exist)', function (t) { var from = path.resolve(fixtures, 'from.exe') var to = path.resolve(fixtures, 'exe.shim') - cmdShim.ifExists(from, to, function(er) { - if (er) - throw er + return cmdShim.ifExists(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') @@ -38,9 +33,7 @@ test('if exists (it does exist)', function (t) { test('if exists (it does not exist)', function (t) { var from = path.resolve(fixtures, 'argle bargle we like to sparkle') var to = path.resolve(fixtures, 'argle-bargle-shim') - cmdShim.ifExists(from, to, function(er) { - if (er) - throw er + return cmdShim.ifExists(from, to).then(() => { t.throws(() => fs.statSync(to)) t.throws(() => fs.statSync(to + '.cmd')) t.throws(() => fs.statSync(to + '.ps1')) @@ -51,112 +44,81 @@ test('if exists (it does not exist)', function (t) { test('fails if from doesnt exist', t => { var from = path.resolve(fixtures, 'argle bargle we like to sparkle') var to = path.resolve(fixtures, 'argle-bargle-shim') - cmdShim(from, to, function(er) { - t.match(er, { code: 'ENOENT' }) - t.end() - }) + return t.rejects(cmdShim(from, to), { code: 'ENOENT' }) }) test('fails if mkdir fails', t => { var from = path.resolve(fixtures, 'from.env') var to = path.resolve(fixtures, 'from.env/a/b/c') - cmdShim(from, to, er => { - t.match(er, { code: /^(ENOTDIR|EEXIST)$/ }) - t.end() - }) + return t.rejects(cmdShim(from, to), { code: /^(ENOTDIR|EEXIST)$/ }) }) test('fails if to is a dir', t => { var from = path.resolve(fixtures, 'from.env') var to = path.resolve(fixtures) - cmdShim(from, to, er => { - t.match(er, { code: 'EISDIR' }) + t.teardown(() => { rimraf.sync(to + '.cmd') rimraf.sync(to + '.ps1') - t.end() }) + return t.rejects(cmdShim(from, to), { code: 'EISDIR' }) }) test('just proceed if reading fails', t => { var from = fixtures var to = path.resolve(fixtures, 'env.shim') - cmdShim(from, to, er => { - if (er) - throw er - + return cmdShim(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') - t.end() }) }) test('env shebang', function (t) { var from = path.resolve(fixtures, 'from.env') var to = path.resolve(fixtures, 'env.shim') - cmdShim(from, to, function(er) { - if (er) - throw er - + return cmdShim(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') - t.end() }) }) test('env shebang with args', function (t) { var from = path.resolve(fixtures, 'from.env.args') var to = path.resolve(fixtures, 'env.args.shim') - cmdShim(from, to, function(er) { - if (er) - throw er - + return cmdShim(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') - t.end() }) }) test('env shebang with variables', function (t) { var from = path.resolve(fixtures, 'from.env.variables') var to = path.resolve(fixtures, 'env.variables.shim') - cmdShim(from, to, function(er) { - if (er) - throw er - + return cmdShim(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') - t.end() }) }) test('explicit shebang', function (t) { var from = path.resolve(fixtures, 'from.sh') var to = path.resolve(fixtures, 'sh.shim') - cmdShim(from, to, function(er) { - if (er) - throw er - + return cmdShim(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') - t.end() }) }) test('explicit shebang with args', function (t) { var from = path.resolve(fixtures, 'from.sh.args') var to = path.resolve(fixtures, 'sh.args.shim') - cmdShim(from, to, function(er) { - if (er) - throw er - + return cmdShim(from, to).then(() => { matchSnapshot(t, fs.readFileSync(to, 'utf8'), 'shell') matchSnapshot(t, fs.readFileSync(to + '.cmd', 'utf8'), 'cmd') matchSnapshot(t, fs.readFileSync(to + '.ps1', 'utf8'), 'ps1') - t.end() }) })