diff --git a/README.md b/README.md index 3a5e05cd..d070258b 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ Simply specify the config in the same format as you would for a config file but "...": "... other standard package.json values", "nodemonConfig": { "ignore": ["test/*", "docs/*"], -"delay": "2500" + "delay": "2500" } } ``` @@ -324,12 +324,6 @@ Nodemon is not perfect, and CLI arguments has sprawled beyond where I'm complete See the [FAQ](https://github.com/remy/nodemon/blob/master/faq.md) and please add your own questions if you think they would help others. -## Contributors - -This project exists thanks to all the people who [contribute](https://github.com/remy/nodemon/blob/master/.github/CONTRIBUTING.md). -[![nodemon contributors](https://opencollective.com/nodemon/contributors.svg?width=890)](https://opencollective.com/nodemon#backer) - - ## Backers Thank you to all [our backers](https://opencollective.com/nodemon#backer)! 🙏 diff --git a/faq.md b/faq.md index ecb1d90d..efbd3497 100644 --- a/faq.md +++ b/faq.md @@ -26,6 +26,16 @@ $ nodemon app.js -- -L -opt2 -opt3 nodemon will ignore all script arguments after `--` and pass them to your script. +# Error: "process failed, unhandled exit code (2)" + +Nodemon will look for exit signals from the child process it runs. When the exit code is `2`, nodemon throws an error. Typically this is because the arguments are bad for the executing program, but it can also be due other reasons. + +For example, mocha@3.x will exit with `2` on failing tests. To handle the exit code in a way that nodemon can consume, manually exit the process, i.e.: + +```bash +nodemon -x 'mocha test/bad.test.js || exit 1' +``` + # Can't install nodemon: permission issue You may need to install nodemon using `sudo` (which isn't recommended, but I understand it's unavoidable in some environemnts). If the install fails with this appearing in the npm error log, then you need the following workaround. @@ -103,7 +113,7 @@ A workaround is to make sure that `node` binary exists in the `PATH`: sudo ln -s /usr/bin/nodejs /usr/local/bin/node ``` -Alternatively the `--exec nodejs` option can be used. +Alternatively the `--exec nodejs` option can be used. Fedora and Ubuntu pakage node as nodejs, because node.dpkg is diff --git a/lib/config/command.js b/lib/config/command.js index 2ce46346..9839b5c7 100644 --- a/lib/config/command.js +++ b/lib/config/command.js @@ -40,4 +40,4 @@ function command(settings) { executable: executable, args: args, }; -} \ No newline at end of file +} diff --git a/lib/config/exec.js b/lib/config/exec.js index 852186f6..3a4f7f7d 100644 --- a/lib/config/exec.js +++ b/lib/config/exec.js @@ -73,28 +73,43 @@ function exec(nodemonOptions, execMap) { execMap = {}; } + var options = utils.clone(nodemonOptions || {}); + var script; + + // if there's no script passed, try to get it from the first argument + if (!options.script && (options.args || []).length) { + script = expandScript(options.args[0], + options.ext && ('.' + (options.ext || 'js').split(',')[0])); + + // if the script was found, shift it off our args + if (script !== options.args[0]) { + options.script = script; + options.args.shift(); + } + } + // if there's no exec found yet, then try to read it from the local // package.json this logic used to sit in the cli/parse, but actually the cli // should be parsed first, then the user options (via nodemon.json) then // finally default down to pot shots at the directory via package.json - if (!nodemonOptions.exec && !nodemonOptions.script) { + if (!options.exec && !options.script) { var found = execFromPackage(); if (found !== null) { if (found.exec) { - nodemonOptions.exec = found.exec; + options.exec = found.exec; } - if (!nodemonOptions.script) { - nodemonOptions.script = found.script; + if (!options.script) { + options.script = found.script; } - if (Array.isArray(nodemonOptions.args) && - nodemonOptions.scriptPosition === null) { - nodemonOptions.scriptPosition = nodemonOptions.args.length; + if (Array.isArray(options.args) && + options.scriptPosition === null) { + options.scriptPosition = options.args.length; } } } - var options = utils.clone(nodemonOptions || {}); - var script = path.basename(options.script || ''); + // var options = utils.clone(nodemonOptions || {}); + script = path.basename(options.script || ''); var scriptExt = path.extname(script).slice(1); diff --git a/lib/config/load.js b/lib/config/load.js index 06ec8019..c4ce7758 100644 --- a/lib/config/load.js +++ b/lib/config/load.js @@ -9,6 +9,7 @@ var exec = require('./exec'); var defaults = require('./defaults'); module.exports = load; +module.exports.mutateExecOptions = mutateExecOptions; var existsSync = fs.existsSync || path.existsSync; @@ -72,23 +73,7 @@ function load(settings, options, config, callback) { } } - // work out the execOptions based on the final config we have - options.execOptions = exec({ - script: options.script, - exec: options.exec, - args: options.args, - scriptPosition: options.scriptPosition, - nodeArgs: options.nodeArgs, - execArgs: options.execArgs, - ext: options.ext, - env: options.env, - }, options.execMap); - - // clean up values that we don't need at the top level - delete options.scriptPosition; - delete options.script; - delete options.args; - delete options.ext; + mutateExecOptions(options); if (options.quiet) { utils.quiet(); @@ -229,3 +214,25 @@ function loadPackageJSON(config, ready) { ready(settings.nodemonConfig || {}); }); } + +function mutateExecOptions(options) { + // work out the execOptions based on the final config we have + options.execOptions = exec({ + script: options.script, + exec: options.exec, + args: options.args, + scriptPosition: options.scriptPosition, + nodeArgs: options.nodeArgs, + execArgs: options.execArgs, + ext: options.ext, + env: options.env, + }, options.execMap); + + // clean up values that we don't need at the top level + delete options.scriptPosition; + delete options.script; + delete options.args; + delete options.ext; + + return options; +} diff --git a/lib/monitor/run.js b/lib/monitor/run.js index b6b0e309..cc149131 100644 --- a/lib/monitor/run.js +++ b/lib/monitor/run.js @@ -143,8 +143,7 @@ function run(options) { if (code === 2) { // something wrong with parsed command - utils.log.error('failed to start process, possible issue with exec ' + - 'arguments'); + utils.log.error('process failed, unhandled exit code (2)'); bus.emit('error', code); process.exit(); } diff --git a/lib/nodemon.js b/lib/nodemon.js index c2f89734..c71e2e6e 100644 --- a/lib/nodemon.js +++ b/lib/nodemon.js @@ -138,6 +138,9 @@ function nodemon(settings) { if (!config.required) { const restartSignal = config.options.signal === 'SIGUSR2' ? 'SIGHUP' : 'SIGUSR2'; process.on(restartSignal, nodemon.restart); + utils.bus.on('error', () => { + utils.log.fail((new Error().stack)); + }); utils.log.detail((config.options.restartable ? 'or ' : '') + 'send ' + restartSignal + ' to ' + process.pid + ' to restart'); } diff --git a/package.json b/package.json index 158eab6c..d96deab4 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "clean": "rm -rf test/fixtures/test*.js test/fixtures/test*.md", "web": "node web", "semantic-release": "semantic-release pre && npm publish && semantic-release post", + "prepush": "npm run lint", "postinstall": "node -e \"console.log('\\u001b[32mLove nodemon? You can now support the project via the open collective:\\u001b[22m\\u001b[39m\\n > \\u001b[96m\\u001b[1mhttps://opencollective.com/nodemon/donate\\u001b[0m\\n')\"" }, "devDependencies": { diff --git a/test/cli/parse.test.js b/test/cli/parse.test.js index 15e16d78..636feb5f 100644 --- a/test/cli/parse.test.js +++ b/test/cli/parse.test.js @@ -7,6 +7,8 @@ var cli = require('../../lib/cli/'), command = require('../../lib/config/command'), utils = require('../../lib/utils'); +const mutateExecOptions = require('../../lib/config/load').mutateExecOptions; + function asCLI(cmd) { return ('node nodemon ' + (cmd || '')).trim(); } @@ -26,6 +28,7 @@ function parse(cmd) { }); return parsed; + return mutateExecOptions(cli.parse(cmd)); } function commandToString(command) { @@ -97,18 +100,6 @@ describe('nodemon CLI parser', function () { assert(cmd === 'node --harmony app.js', 'command is ' + cmd); }); - // it('should put the script at the end if found in package.scripts.start', function () { - // var pwd = process.cwd(); - // process.chdir('test/fixtures/packages/start'); // allows us to load text/fixtures/package.json - // var settings = parse(asCLI('--harmony')), - // cmd = commandToString(command(settings)); - - // process.chdir(pwd); - // console.log(settings, cmd); - - // assert(cmd === 'node --harmony app.js', 'command is ' + cmd); - // }); - it('should support default express4 format', function () { var pwd = process.cwd(); process.chdir('test/fixtures/packages/express4'); // allows us to load text/fixtures/package.json @@ -274,6 +265,27 @@ describe('nodemon respects custom "ext" and "execMap"', function () { }); }); +describe('nodemon should support implicit extensions', () => { + it('should expand script to script.js', () => { + const cwd = process.cwd(); + process.chdir('test/fixtures/'); + const settings = parse(asCLI('env')); + process.chdir(cwd); + var cmd = commandToString(command(settings)); + assert.equal(cmd, 'node env.js', 'implicit extension added'); + }); + + it('should support non-js', () => { + const cwd = process.cwd(); + process.chdir('test/fixtures/'); + const settings = parse(asCLI('hello --ext py')); + process.chdir(cwd); + var cmd = commandToString(command(settings)); + assert.equal(cmd, 'node hello.py', 'implicit extension added'); + }); + +}); + describe('nodemon should slurp properly', () => { it('should read quotes as a single entity', () => { const settings = parse(asCLI('notindex.js -- -b "hello - world"'));