├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .travis.yml ├── cli.js ├── index.js ├── license ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "jsx": true 11 | }, 12 | "sourceType": "module" 13 | }, 14 | "rules": { 15 | "no-const-assign": "warn", 16 | "no-this-before-super": "warn", 17 | "no-undef": "warn", 18 | "no-unreachable": "warn", 19 | "no-unused-vars": "warn", 20 | "constructor-super": "warn", 21 | "valid-typeof": "warn", 22 | "semi": ["warn", "never"] 23 | } 24 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '4' 5 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | var npmExec = require('./') 4 | var fs = require('fs') 5 | var path = require('path') 6 | var read = require('read') 7 | 8 | function handleExit(er) { 9 | if (!er) { 10 | console.log("Finished script execution") 11 | process.exit(0) 12 | } 13 | console.error("Error: Script execution failed:") 14 | console.error(er.message) 15 | if (process.env.NODE_ENV === "development") { 16 | console.error(er.stack) 17 | } 18 | process.exit(1) 19 | } 20 | 21 | fs.readFile(path.join(process.cwd(), 'package.json'), 'utf-8', function (er, res) { 22 | if (er) { 23 | handleExit(er) 24 | } 25 | var pkg 26 | try { 27 | pkg = JSON.parse(res) 28 | } catch (er) { 29 | handleExit(er) 30 | } 31 | if (!(pkg && pkg.scripts)) { 32 | handleExit(new Error("No package.json or scripts!")) 33 | } 34 | function exec(cmd, args) { 35 | npmExec(cmd, args, pkg, process.cwd(), handleExit) 36 | } 37 | if (process.argv[2] === '--help') { 38 | console.log([ 39 | 'Usage', 40 | 'npm-exec [name] [args...]', 41 | '', 42 | 'Arguments', 43 | ' name: Name of script to run or executable', 44 | ' If not provided, npm-exec will prompt you to give it a name', 45 | ' args: Arguments for the script [Default: None]', 46 | '', 47 | 'Examples', 48 | ' $ npm-exec', 49 | ' $ npm-exec test', 50 | ' $ npm-exec eslint *.js' 51 | ].join('\n')) 52 | }else if (process.argv[2]) { 53 | exec(process.argv[2], process.argv.slice(3)) 54 | } else { 55 | console.log("Possible scripts to run:") 56 | console.log(' ' + Object.keys(pkg.scripts).join('\n ')) 57 | read({ 58 | prompt: "What script to run? ", 59 | default: "Exit" 60 | }, function (err, result, isDefault) { 61 | if (err) { 62 | handleExit(err) 63 | } 64 | if (isDefault) { 65 | console.log("Exiting...") 66 | process.exit(0) 67 | } 68 | exec(result, []) 69 | }) 70 | } 71 | }) 72 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = exec 2 | 3 | var PATH = 'PATH' 4 | var path = require('path') 5 | var run = require('child_process').exec 6 | var fs = require('fs') 7 | var which = require('which') 8 | 9 | if (process.platform === 'win32') { 10 | PATH = 'Path' 11 | Object.keys(process.env).forEach(function (e) { 12 | if (e.match(/^PATH$/i)) { 13 | PATH = e 14 | } 15 | }) 16 | } 17 | 18 | function exec(stage, args, pkg, wd, cb) { 19 | if (typeof cb !== 'function') { 20 | cb = wd 21 | wd = process.cwd() 22 | } 23 | if (typeof cb !== 'function') { 24 | cb = pkg 25 | pkg = { 26 | scripts: {} 27 | } 28 | } 29 | if(typeof cb !== 'function') { 30 | cb = args 31 | args = [] 32 | } 33 | if (typeof cb !== 'function') { 34 | cb = stage 35 | stage = null 36 | } 37 | 38 | var env = makeEnv(pkg) 39 | env.npm_lifecycle_event = stage 40 | env.npm_node_execpath = env.NODE = env.NODE || process.execPath 41 | env.npm_execpath = require.main.filename 42 | 43 | var pathArr = [] 44 | var p = wd.split(/[\\\/]node_modules[\\\/]/) 45 | var acc = path.resolve(p.shift()) 46 | 47 | p.forEach(function (pp) { 48 | pathArr.unshift(path.join(acc, 'node_modules', '.bin')) 49 | acc = path.join(acc, 'node_modules', pp) 50 | }) 51 | pathArr.unshift(path.join(acc, 'node_modules', '.bin')) 52 | 53 | // we also unshift the bundled node-gyp-bin folder so that 54 | // the bundled one will be used for installing things. 55 | pathArr.unshift(path.join(__dirname, '..', '..', 'bin', 'node-gyp-bin')) 56 | 57 | if (shouldPrependCurrentNodeDirToPATH()) { 58 | // prefer current node interpreter in child scripts 59 | pathArr.push(path.dirname(process.execPath)) 60 | } 61 | 62 | if (env[PATH]) pathArr.push(env[PATH]) 63 | env[PATH] = pathArr.join(process.platform === 'win32' ? ';' : ':') 64 | 65 | env.npm_lifecycle_script = pkg.scripts[stage] 66 | 67 | if (pkg.scripts[stage]) { 68 | runCmd(pkg.scripts[stage], args, env, wd, cb) 69 | } else { 70 | fs.stat(path.join(wd, 'node_modules', '.bin', stage), function (er, stat) { 71 | if (er || !stat.isFile()) { 72 | return cb(new Error("Could not find script: " + stage)) 73 | } 74 | return runCmd(path.resolve(path.join(wd, 'node_modules', '.bin', stage)), args, env, wd, cb) 75 | }) 76 | } 77 | } 78 | 79 | function makeEnv(data, prefix, env) { 80 | prefix = prefix || 'npm_package_' 81 | if (!env) { 82 | env = {} 83 | for (var i in process.env) { 84 | if (!i.match(/^npm_/)) { 85 | env[i] = process.env[i] 86 | } 87 | } 88 | } else if (!data.hasOwnProperty('_lifecycleEnv')) { 89 | Object.defineProperty(data, '_lifecycleEnv', { 90 | value: env, 91 | enumerable: false 92 | }) 93 | } 94 | 95 | for (i in data) { 96 | if (i.charAt(0) !== '_') { 97 | var envKey = (prefix + i).replace(/[^a-zA-Z0-9_]/g, '_') 98 | if (i === 'readme') { 99 | continue 100 | } 101 | if (data[i] && typeof data[i] === 'object') { 102 | try { 103 | // quick and dirty detection for cyclical structures 104 | JSON.stringify(data[i]) 105 | makeEnv(data[i], envKey + '_', env) 106 | } catch (ex) { 107 | // usually these are package objects. 108 | // just get the path and basic details. 109 | var d = data[i] 110 | makeEnv({ 111 | name: d.name, 112 | version: d.version, 113 | path: d.path 114 | }, 115 | envKey + '_', 116 | env 117 | ) 118 | } 119 | } else { 120 | env[envKey] = String(data[i]) 121 | env[envKey] = env[envKey].indexOf('\n') !== -1 ? 122 | JSON.stringify(env[envKey]) : 123 | env[envKey] 124 | } 125 | } 126 | } 127 | 128 | if (prefix !== 'npm_package_') return env 129 | var pkgConfig = data.config || {} 130 | 131 | prefix = 'npm_package_config_' 132 | for (var i in pkgConfig) { 133 | var envKey = (prefix + i) 134 | env[envKey] = pkgConfig[i] 135 | } 136 | 137 | return env 138 | } 139 | 140 | function shouldPrependCurrentNodeDirToPATH() { 141 | var isWindows = process.platform === 'win32' 142 | try { 143 | var foundExecPath = which.sync(path.basename(process.execPath), { 144 | pathExt: isWindows ? ';' : ':' 145 | }) 146 | return process.execPath.toUpperCase() !== foundExecPath.toUpperCase() 147 | } catch (e) { 148 | return true 149 | } 150 | } 151 | 152 | function runCmd(cmd, args, env, wd, cb_) { 153 | function cb() { 154 | cb_.apply(null, arguments) 155 | } 156 | 157 | var conf = { 158 | cwd: wd, 159 | env: env, 160 | stdio: [0, 1, 2], 161 | shell: which.sync('sh') 162 | } 163 | 164 | var proc = run(cmd + ' ' + args.join(' '), conf) 165 | proc.on('error', procError) 166 | proc.on('close', function (code, signal) { 167 | if (signal) { 168 | process.kill(process.pid, signal) 169 | } else if (code) { 170 | var er = new Error('Exit status ' + code) 171 | } 172 | procError(er) 173 | }) 174 | 175 | process.stdin.pipe(proc.stdin) 176 | proc.stdout.pipe(process.stdout) 177 | proc.stderr.pipe(process.stderr) 178 | 179 | process.once('SIGTERM', procKill) 180 | 181 | function procError(er) { 182 | if (er) { 183 | er.message = '`' + cmd + args.join(' ') + '`\n' + 184 | er.message 185 | if (er.code !== 'EPERM') { 186 | er.code = 'ELIFECYCLE' 187 | } 188 | er.script = cmd 189 | } 190 | process.removeListener('SIGTERM', procKill) 191 | return cb(er) 192 | } 193 | 194 | function procKill() { 195 | proc.kill() 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) legodude17 (legodude17.github.io) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "standalone-npm-exec", 3 | "version": "1.0.0", 4 | "description": "A util for just running a npm script", 5 | "license": "MIT", 6 | "repository": "legodude17/npm-exec", 7 | "author": { 8 | "name": "legodude17", 9 | "email": "legodude17@users.noreply.github.com", 10 | "url": "legodude17.github.io" 11 | }, 12 | "bin": { 13 | "npm-exec": "cli.js" 14 | }, 15 | "scripts": { 16 | "test": "./cli.js lint && ./cli.js test:actual", 17 | "lint": "eslint *.js", 18 | "test:actual": "node test.js", 19 | "echo": "echo hi", 20 | "cat": "cat" 21 | }, 22 | "files": [ 23 | "index.js", 24 | "cli.js" 25 | ], 26 | "keywords": [ 27 | "cli-app", 28 | "cli", 29 | "npm", 30 | "exec" 31 | ], 32 | "dependencies": { 33 | "read": "^1.0.7", 34 | "which": "^1.2.12" 35 | }, 36 | "devDependencies": { 37 | "eslint": "^3.11.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # npm-exec 2 | 3 | > A util for just running a npm script 4 | 5 | 6 | 7 | * 1. [Install](#Install) 8 | * 2. [Usage](#Usage) 9 | * 3. [API](#API) 10 | * 3.1. [npmExec(name, argsForScript, pkgdata, wd, cb)](#npmExecnameargsForScriptpkgdatawdcb) 11 | * 3.1.1. [input](#input) 12 | * 3.1.2. [argsForScript](#argsForScript) 13 | * 3.1.3. [pkgdata](#pkgdata) 14 | * 3.1.4. [wd](#wd) 15 | * 3.1.5. [cb](#cb) 16 | * 4. [CLI](#CLI) 17 | * 5. [License](#License) 18 | 19 | 20 | 21 | ## 1. Install 22 | 23 | ``` 24 | $ npm install --save standalone-npm-exec 25 | ``` 26 | 27 | 28 | ## 2. Usage 29 | 30 | ```js 31 | const npmExec = require('npm-exec'); 32 | 33 | npmExec(script, args, pkgdata, wd, function (er) { 34 | if (er) { 35 | // Something bad happened 36 | } 37 | // Yay! 38 | }); 39 | ``` 40 | 41 | 42 | ## 3. API 43 | 44 | ### 3.1. npmExec(name, argsForScript, pkgdata, wd, cb) 45 | 46 | #### 3.1.1. input 47 | 48 | Type: `string` 49 | 50 | The script name to execute. Can either be a script defined in `pkgdata` or the name of a file in `wd/node_modules/.bin`. 51 | 52 | #### 3.1.2. argsForScript 53 | 54 | Type: `Array` 55 | 56 | Any args for the script you are running. 57 | 58 | #### 3.1.3. pkgdata 59 | 60 | Type: `Object` 61 | 62 | The parsed `package.json` data for where the script should be executed. 63 | 64 | #### 3.1.4. wd 65 | 66 | Type: `path` 67 | 68 | The working directory of where to find executables and where to execute the scripts. 69 | 70 | #### 3.1.5. cb 71 | 72 | Type: `function(err)` 73 | 74 | The function to be called when it is done. 75 | 76 | 77 | ## 4. CLI 78 | 79 | ``` 80 | $ npm install --global npm-exec 81 | ``` 82 | 83 | ``` 84 | $ npm-exec --help 85 | Usage 86 | npm-exec [name] [args...] 87 | 88 | Arguments 89 | name: Name of script to run or executable 90 | If not provided, npm-exec will prompt you to give it a name 91 | args: Arguments for the script [Default: None] 92 | 93 | Examples 94 | $ npm-exec 95 | $ npm-exec test 96 | $ npm-exec eslint *.js 97 | 98 | ``` 99 | 100 | 101 | ## 5. License 102 | 103 | MIT © [legodude17](https://legodude17.github.io) 104 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------