Skip to content

Commit

Permalink
Merge pull request #44 from nfischer/export-command
Browse files Browse the repository at this point in the history
Adding export command
  • Loading branch information
dthree committed Feb 29, 2016
2 parents e4a3d1b + 991b60e commit f11a47e
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 2 deletions.
2 changes: 2 additions & 0 deletions bin/export.js
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./parser')(process.argv, 'export');
2 changes: 2 additions & 0 deletions commands.json
Expand Up @@ -5,6 +5,7 @@
"clear",
"cp",
"echo",
"export",
"kill",
"ls",
"mkdir",
Expand All @@ -22,6 +23,7 @@
"alias",
"cd",
"echo",
"export",
"less",
"sort",
"unalias"
Expand Down
72 changes: 72 additions & 0 deletions dist/commands/export.js
@@ -0,0 +1,72 @@
'use strict';

var interfacer = require('./../util/interfacer');
var preparser = require('./../preparser');

var _export = {
exec: function exec(args, options) {
args = args || [];
options = options || {};

if (args.length < 1) {
options.p = true;
}

if (typeof args === 'string' || args instanceof String) {
args = [args];
}

// Parse similarly to how `alias` does
var id = args.join(' ');
var value = '';
if (String(id).indexOf('=') > -1) {
var parts = String(id).trim().split('=');
id = parts[0];
value = parts[1] || value;
if (value.match(/^".*"$/)) {
value = JSON.parse(value);
} else {
var regMatch = value.match(/^'(.*)'$/);
if (regMatch && regMatch[1]) {
value = regMatch[1];
}
}
} else {
var parts = String(id).trim().split(' ');
id = parts.shift();
value = parts.join(' ') || null;
}

var validIdRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
if (options.p) {
for (var name in process.env) {
if (process.env.hasOwnProperty(name)) {
this.log('declare -x ' + String(name) + '=' + JSON.stringify(process.env[name]).replace(/\$/g, '\\$'));
}
}
} else if (id.match(validIdRegex)) {
process.env[id] = value !== null ? value : process.env[id];
} else {
this.log('-cash: export: `' + id + '\': not a valid identifier');
return 1;
}

return 0;
}
};

module.exports = function (vorpal) {
if (vorpal === undefined) {
return _export;
}
vorpal.api.export = _export;
vorpal.command('export [name...]').parse(preparser).option('-p', 'print all defined aliases in a reusable format').action(function (args, callback) {
args.options = args.options || {};
return interfacer.call(this, {
command: _export,
args: args.name,
options: args.options,
callback: callback
});
});
};
2 changes: 1 addition & 1 deletion dist/help.js
Expand Up @@ -2,7 +2,7 @@

var pad = require('./util/pad');

var commands = ['alias [-p] [name=[value]]', 'cat [-AbeEnstTv] [files ...]', 'cd [dir]', 'clear', 'cp [-fnr] source ... dest', 'echo [eE] [arg ...]', 'grep [-bHhinqsvw] [-m max] [--silent] [--include pattern] pattern [files ...]', 'kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l', 'less [files ...]', 'ls [-aAdFhilQrRStUwx1] [paths ...]', 'mkdir [-pv] [directories ...]', 'mv [-fnv] source ... dest', 'pwd [files ...]', 'rm [-frR] [files ...]', 'sort [-chMnrR] [-o file] [files ...]', 'touch [-acm] [-d date] [-r ref] [--time word] file ...', 'unalias [-a] name [names ...]'];
var commands = ['alias [-p] [name=[value]]', 'cat [-AbeEnstTv] [files ...]', 'cd [dir]', 'clear', 'cp [-fnr] source ... dest', 'echo [-eE] [arg ...]', 'export [-p][id=[value]]', 'grep [-bHhinqsvw] [-m max] [--silent] [--include pattern] pattern [files ...]', 'kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l', 'less [files ...]', 'ls [-aAdFhilQrRStUwx1] [paths ...]', 'mkdir [-pv] [directories ...]', 'mv [-fnv] source ... dest', 'pwd [files ...]', 'rm [-frR] [files ...]', 'sort [-chMnrR] [-o file] [files ...]', 'touch [-acm] [-d date] [-r ref] [--time word] file ...', 'unalias [-a] name [names ...]'];

function chop(str, len) {
var res = String(str).slice(0, len - 2);
Expand Down
3 changes: 3 additions & 0 deletions dist/help/export.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions src/commands/export.js
@@ -0,0 +1,78 @@
'use strict';

const interfacer = require('./../util/interfacer');
const preparser = require('./../preparser');

const _export = {

exec(args, options) {
args = args || [];
options = options || {};

if (args.length < 1) {
options.p = true;
}

if (typeof args === 'string' || args instanceof String) {
args = [args];
}

// Parse similarly to how `alias` does
let id = args.join(' ');
let value = '';
if (String(id).indexOf('=') > -1) {
const parts = String(id).trim().split('=');
id = parts[0];
value = parts[1] || value;
if (value.match(/^".*"$/)) {
value = JSON.parse(value);
} else {
const regMatch = value.match(/^'(.*)'$/);
if (regMatch && regMatch[1]) {
value = regMatch[1];
}
}
} else {
const parts = String(id).trim().split(' ');
id = parts.shift();
value = parts.join(' ') || null;
}

const validIdRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
if (options.p) {
for (const name in process.env) {
if (process.env.hasOwnProperty(name)) {
this.log(`declare -x ${String(name)}=${JSON.stringify(process.env[name])
.replace(/\$/g, '\\$')}`);
}
}
} else if (id.match(validIdRegex)) {
process.env[id] = value !== null ? value : process.env[id];
} else {
this.log(`-cash: export: \`${id}': not a valid identifier`);
return 1;
}

return 0;
}
};

module.exports = function (vorpal) {
if (vorpal === undefined) {
return _export;
}
vorpal.api.export = _export;
vorpal
.command('export [name...]')
.parse(preparser)
.option('-p', 'print all defined aliases in a reusable format')
.action(function (args, callback) {
args.options = args.options || {};
return interfacer.call(this, {
command: _export,
args: args.name,
options: args.options,
callback
});
});
};
3 changes: 2 additions & 1 deletion src/help.js
Expand Up @@ -8,7 +8,8 @@ const commands = [
'cd [dir]',
'clear',
'cp [-fnr] source ... dest',
'echo [eE] [arg ...]',
'echo [-eE] [arg ...]',
'export [-p][id=[value]]',
'grep [-bHhinqsvw] [-m max] [--silent] [--include pattern] pattern [files ...]',
'kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l',
'less [files ...]',
Expand Down
20 changes: 20 additions & 0 deletions src/help/export.js
@@ -0,0 +1,20 @@
module.exports = `
Usage: export [OPTION] [name[=value]]
Export variables into the environment
Without arguments, \`export' prints the list of environnmental variables in the
form \`declare -x NAME="VALUE"' on standard output. This is the same as the
behavior if \`-p' is given.
Otherwise, the variable is exported to the environment. If the variable has
already been defined in the environment (ex. \`PATH'), then this will either
redefine its value or do nothing (if no value is passed in). If the variable is
not already in the environment, it will be added with the \`VALUE' given
(defaults to the empty string).
-p print all exported variables in a reusable format
--help display this help and exit
Report export bugs to <https://github.com/dthree/cash>
Cash home page: <http://cash.js.org/>
`;
105 changes: 105 additions & 0 deletions test/export.js
@@ -0,0 +1,105 @@
'use strict';

require('assert');
const should = require('should');
const cash = require('../dist/index.js');

let oldProcessEnv;

describe('export', function () {
before(function () {
oldProcessEnv = process.env;
});

beforeEach(function () {
process.env = {};
});

after(function () {
process.env = oldProcessEnv;
});

it('should exist and be a function', function () {
should.exist(cash.export);
});

it('should create an export with an equal symbol', function () {
(function () {
cash.export(['foo=bar']);
}).should.not.throw();
cash('echo $foo').should.equal('bar\n');
});

it('should accept a string argument', function () {
(function () {
cash.export('foo=bar');
}).should.not.throw();
cash('echo $foo').should.equal('bar\n');
});

it('should print msg when reading an invalid export', function () {
cash.export(['1lalalala']).should.equal('-cash: export: `1lalalala\': not a valid identifier\n');
cash.export(['la@lalala']).should.equal('-cash: export: `la@lalala\': not a valid identifier\n');
});

it('should reassign an export', function () {
(function () {
cash.export(['foo=cows']);
cash.export(['foo=dogs']);
}).should.not.throw();
cash('echo $foo').should.equal('dogs\n');
});

it('should do nothing if already exported', function () {
(function () {
cash.export(['PATH=/usr/bin']);
cash.export(['PATH']);
}).should.not.throw();
cash('echo $PATH').should.equal(`${process.env.PATH}\n`);
cash('echo $PATH').should.equal('/usr/bin\n');
});

it('should work without surrounding quotes', function () {
cash.export(['foo=bar']);
cash('echo $foo').should.equal('bar\n');
});

it('should deal with surrounding single quotes', function () {
cash.export(['foo=\'bar tender nice to meet you\'']);
cash('echo $foo').should.equal('bar tender nice to meet you\n');
});

it('should deal with surrounding double quotes', function () {
cash.export(['foo="bar tender nice to meet you"']);
cash('echo $foo').should.equal('bar tender nice to meet you\n');
});

it('should handle multiple exports', function () {
(function () {
cash.export(['a="A"']);
cash.export(['b=\'B\'']);
cash.export(['c="C"']);
}).should.not.throw();
cash('echo $a $b $c').should.equal('A B C\n');
});

it('should list all registered aliases', function () {
(function () {
cash.export(['a="A"']);
cash.export(['b=\'B\'']);
cash.export(['c="C"']);
}).should.not.throw();
cash.export().should.equal('declare -x a="A"\ndeclare -x b="B"\ndeclare -x c="C"\n');
});

describe('-p', function () {
it('should list all registered exports', function () {
(function () {
cash.export(['a="A"']);
cash.export(['b=\'B\'']);
cash.export(['c="C"']);
}).should.not.throw();
cash.export(undefined, {p: true}).should.equal('declare -x a="A"\ndeclare -x b="B"\ndeclare -x c="C"\n');
});
});
});
8 changes: 8 additions & 0 deletions test/preparser.js
Expand Up @@ -112,6 +112,14 @@ describe('preparser', function () {
cash('cp $FOO dest').should.equal(`cp: cannot stat ${process.env.FOO}: No such file or directory\n`);
});

it('should work for export', function () {
(function () {
cash('export BAR=$FOO').should.equal('');
}).should.not.throw();
cash('echo $BAR').should.equal(`${process.env.FOO}\n`);
delete process.env.BAR;
});

it('should work for ls', function () {
cash('ls $FOO').should.equal('');
cash('ls $NOSUCHENVVAR').should.equal(cash('ls'));
Expand Down

0 comments on commit f11a47e

Please sign in to comment.