Skip to content
Browse files

allow just using a string for a volofile command or an array of strin…

…gs, and use shell-quote to expand that to an array usable by v.spawn. Also allow running a local node command via an n prefix on the command.
  • Loading branch information...
1 parent 129cd19 commit d988c0d6a31411d5eba318db884d3e6ceef9fac4 @jrburke jrburke committed
Showing with 169 additions and 11 deletions.
  1. +26 −1 lib/commands.js
  2. +46 −4 lib/v.js
  3. +9 −5 lib/volofile.js
  4. +33 −0 node_modules/shell-quote/index.js
  5. +53 −0 node_modules/shell-quote/package.json
  6. +2 −1 package.json
View
27 lib/commands.js
@@ -61,7 +61,7 @@
run: function (command, venv, namedArgs /*other args can be passed*/) {
var d = q.defer(),
- args;
+ shellCommand, args;
if (!venv) {
venv = v(path.resolve('.')).env;
@@ -75,6 +75,31 @@
command = {
run: command
};
+ } else if (typeof command === 'string' ||
+ Array.isArray(command)) {
+ command = {
+ run: command
+ };
+ }
+
+ //Now convert run to a function if it is not.
+ //This allows a command to have depends and
+ //a run property that is just a shell string or array
+ //of shell strings
+ if (typeof command.run === 'string') {
+ shellCommand = command.run;
+ command.run = function (d, v, namedArgs) {
+ d.resolve(v.shell(shellCommand, {
+ useConsole: true
+ }));
+ };
+ } else if (Array.isArray(command.run)) {
+ shellCommand = command.run;
+ command.run = function (d, v, namedArgs) {
+ d.resolve(v.sequence(shellCommand, {
+ useConsole: true
+ }));
+ };
}
args = [].slice.call(arguments, 2);
View
50 lib/v.js
@@ -13,13 +13,15 @@ var path = require('path'),
q = require('q'),
spawn = require('child_process').spawn,
exec = require('child_process').exec,
+ shellQuote = require('shell-quote'),
file = require('./file'),
template = require('./template'),
qutil = require('./qutil'),
tty = require('tty'),
voloMainPath = path.join(__dirname, '..', 'volo'),
defaultEncoding = 'utf8',
- lineRegExp = /(\r)?\n$/;
+ lineRegExp = /(\r)?\n$/,
+ nodeModulesBinPrefix = 'node_modules/.bin/';
function execToConsole(value) {
console.log(value.replace(lineRegExp, ''));
@@ -165,6 +167,14 @@ function v(dirName) {
return d.promise;
},
+
+ //Runs a command in the shell. Similar to spawn(),
+ //but does not require constructing string arrays,
+ //just type in the command with its args as a string.
+ shell: function (cmd, options) {
+ return instance.env.spawn.apply(instance.env, shellQuote.parse(cmd).concat(options));
+ },
+
//Spawns a command in the shell via child_process.spawn.
//If options.useConsole is true, then data sent to stdout,
//stderr will be sent to console as soon as it is received.
@@ -172,10 +182,39 @@ function v(dirName) {
//the command from the args passed to it.
spawn: function (cmd, args, options) {
var d = q.defer(),
- spawned = spawn(cmd, args, options),
okResponse = '',
errResponse = '',
- onData, onErrData;
+ spawned, onData, onErrData;
+
+ if (typeof args === 'string') {
+ //If args is a string, then this is a variadic call,
+ //with the last one possibly being an options arg.
+ args = [].slice.call(arguments, 0);
+ cmd = args.shift();
+ if (typeof args[args.length - 1] !== 'string') {
+ options = args.pop();
+ } else {
+ options = {};
+ }
+ }
+
+ //Is this a command that should test for
+ //a local node_modules bin file?
+ if (cmd.indexOf('n!') === 0) {
+ cmd = cmd.substring(2);
+
+ if (path.existsSync(nodeModulesBinPrefix + cmd)) {
+ cmd = nodeModulesBinPrefix + cmd;
+
+ //Add the command back to the args,
+ //and use 'node' as the command,
+ //so that it works on windows.
+ args.unshift(cmd);
+ cmd = 'node';
+ }
+ }
+
+ spawned = spawn(cmd, args, options);
options = options || {};
@@ -216,13 +255,16 @@ function v(dirName) {
//the second array value is a method name and the rest of the
//array values are arguments. Example:
//v.sequence([
- // ['git', 'init'], //ends up with v.sequence('git', ['init'], options)
+ // ['git', 'init'], //ends up with v.spawn('git', ['init'], options)
// [v, 'rm', 'README.md'] //ends up calling v.rm.apply(v, ['README.md']);
//], options);
sequence: function (list, options) {
var result = q.resolve();
list.forEach(function (item) {
result = result.then(function () {
+ //Allow for string values that are just plain shell commands.
+ item = typeof item === 'string' ? shellQuote.parse(item) : item;
+
var start = item[0],
action = item[1],
useSpawn = typeof start === 'string',
View
14 lib/volofile.js
@@ -85,11 +85,15 @@ function volofile(basePath, callback, errback) {
contents = fs.readFileSync(volofilePath, 'utf8');
//Look for define, and the absence of amdefine use.
- if (parse.usesAmdOrRequireJs(volofilePath, contents) &&
- contents.indexOf('amdefine') === -1) {
- d.resolve(legacy01Exec(volofilePath, contents));
- } else {
- d.resolve(require(volofilePath));
+ try {
+ if (parse.usesAmdOrRequireJs(volofilePath, contents) &&
+ contents.indexOf('amdefine') === -1) {
+ d.resolve(legacy01Exec(volofilePath, contents));
+ } else {
+ d.resolve(require(volofilePath));
+ }
+ } catch (e) {
+ d.reject('Malformed volofile: ' + volofilePath + ': ' + e);
}
} else {
d.resolve();
View
33 node_modules/shell-quote/index.js
@@ -0,0 +1,33 @@
+exports.quote = function (xs) {
+ return xs.map(function (s) {
+ if (/["\s]/.test(s) && !/'/.test(s)) {
+ return "'" + s.replace(/(['\\])/g, '\\$1') + "'";
+ }
+ else if (/["'\s]/.test(s)) {
+ return '"' + s.replace(/(["\\$`(){}!#&*|])/g, '\\$1') + '"';
+ }
+ else {
+ return s.replace(/([\\$`(){}!#&*|])/g, '\\$1');
+ }
+ }).join(' ');
+};
+
+exports.parse = function (s) {
+ return s.match(/(['"])((\\\1|[^\1])*?)\1|(\\ |\S)+/g)
+ .map(function (s) {
+ if (/^'/.test(s)) {
+ return s
+ .replace(/^'|'$/g, '')
+ .replace(/\\(["'\\$`(){}!#&*|])/g, '$1');
+ ;
+ }
+ else if (/^"/.test(s)) {
+ return s
+ .replace(/^"|"$/g, '')
+ .replace(/\\(["'\\$`(){}!#&*|])/g, '$1');
+ ;
+ }
+ else return s.replace(/\\([ "'\\$`(){}!#&*|])/g, '$1');
+ })
+ ;
+};
View
53 node_modules/shell-quote/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "shell-quote",
+ "version": "0.0.1",
+ "description": "quote and parse shell commands",
+ "main": "index.js",
+ "directories": {
+ "example": "example",
+ "test": "test"
+ },
+ "devDependencies": {
+ "tap": "~0.2.5"
+ },
+ "scripts": {
+ "test": "tap test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/substack/node-shell-quote.git"
+ },
+ "keywords": [
+ "shell",
+ "command",
+ "quote",
+ "parse"
+ ],
+ "author": {
+ "name": "James Halliday",
+ "email": "mail@substack.net",
+ "url": "http://substack.net"
+ },
+ "license": "MIT",
+ "engine": {
+ "node": ">=0.4"
+ },
+ "_npmUser": {
+ "name": "jrburke",
+ "email": "jrburke@gmail.com"
+ },
+ "_id": "shell-quote@0.0.1",
+ "dependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "_engineSupported": true,
+ "_npmVersion": "1.1.21",
+ "_nodeVersion": "v0.6.17",
+ "_defaultsLoaded": true,
+ "dist": {
+ "shasum": "678b23fc66020d259b67e7682562e16b0628ed6e"
+ },
+ "_from": "shell-quote"
+}
View
3 package.json
@@ -27,7 +27,8 @@
"dependencies": {
"q": "0.8.5",
"zip": "0.0.6",
- "uglify-js": "1.2.6"
+ "uglify-js": "1.2.6",
+ "shell-quote": "0.0.1"
},
"main": "volo"

0 comments on commit d988c0d

Please sign in to comment.
Something went wrong with that request. Please try again.