I'm also not aware of a platform agnostic way to loop in the shell.
However, a platform agnostic solution to running a single npm-script on multiple files with jshint, as per your example, is to:
- Utilize cli-glob to find the
.js
files.
- Pipe the results/paths from the globbing pattern to a custom utility node script.
- Then within the node script:
- Read the paths piped to
stdin
using nodes readline
module.
- Create an
Array
of each path and subsequently convert that to a String
.
- Run the
jshint
executable, (including the String
of all paths), using nodes child_process.exec()
module.
Whilst this solution is not particularly "simple", the following gists demonstrate this process:
npm-script
"scripts": {
"jshint": "glob \"src/js/**/*.js\" | node .scripts/jshint.js"
},
Note cli-glob, (added to package.json), obtains the paths and pipes them to jshint.js.
jshint.js
#!/usr/bin/env node
'use strict';
var path = require('path');
var readline = require('readline');
var exec = require('child_process').exec;
var rl = readline.createInterface({
input: process.stdin,
output: null,
terminal: false
});
// Normalise path to local jshint executable.
var jshintExec = ['.', 'node_modules', '.bin', 'jshint '].join(path.sep);
var paths = [];
function jshint(paths) {
var command = [jshintExec, paths].join('');
exec(command, function(error, stdout, stderr) {
if (stdout) {
console.log(stdout);
}
if (stderr) {
console.log(stderr);
}
});
}
rl.on('line', function(srcPath) {
paths.push(srcPath);
});
rl.on('close', function() {
jshint(paths.join(' '));
});
Note
Line 16 reading:
var jshintExec = ['.', 'node_modules', '.bin', 'jshint '].join(path.sep);
The script assumes jshint
has been installed locally and added to the "devDependencies": {}
section of the package.json
. I.e. Its pointing to the local jhint
executable found in the node_modules/.bin
folder and not the globally installed one.
If your preference is to run the globally installed jshint
then change line 16 to:
var jshintExec = 'jshint ';
Personally, having it installed locally is the preferred option IMHO for this scenario!
Multiple globbing patterns
Your example provided includes multiple glob patterns.
"lint": "jshint *.js **/*.js"
One limitation of cli-glob is that it doesn't accept multiple glob patterns. So one workaround is to do something like this:
"scripts": {
"jshint": "npm run jshint-a && npm run jshint-b",
"jshint-a": "glob \"*.js\" | node .scripts/jshint.js",
"jshint-b": "glob \"**/*.js\" | node .scripts/jshint.js"
},
Yeah, not particularly terse - but works!