gsvg
Version:
Git-Friendly SVG
224 lines (190 loc) • 6.71 kB
JavaScript
;
var fs = require('fs');
var Promise = require('bluebird');
var getStdin = require('get-stdin');
var meow = require('meow');
var validateArgs = require('./lib/validate-cli-args');
var gsvg = require('./');
// eslint-disable-next-line no-use-extend-native/no-use-extend-native
fs = Promise.promisifyAll(fs);
// is this module imported or is it run as a CLI?
var isImportedModule = require.main !== module;
// validateArgs uses this
var validationInfo = {
cliAliases: {
// meow will decamelize any camelCase options
h: 'help',
v: 'version',
i: 'inPlace',
s: 'shiftwidth',
a: 'attrExtraIndent'
},
boolFlags: [
'help',
'version',
'inPlace'
],
indentFlags: [
'shiftwidth',
'attrExtraIndent'
]
};
var minimistOptions = {
alias: validationInfo.cliAliases
};
var meowOptionsTemplate = {
inferType: true,
help: `
Usage
$ gsvg [--help|--version]
$ gsvg [flags/options] [<infile> [<outfile>]]
$ echo "<svg></svg>" | gsvg [flags/options] [<outfile>]
<infile> Input filename
If a string is piped to GSVG, that will be used as the input and
the first positional argument will be treated as <outfile> instead.
<outfile> Output filename
The file will be overwritten if it exists.
If <outfile> is omitted, output will be piped to stdout.
Flags
-h, --help, Display this message
-v, --version Display version number
-i, --in-place Change file in place
Requires: <infile>
Excludes: piping, <outfile>
Options
-s, --shiftwidth <integer|string> Shiftwidth used for indentation
<integer> (Default: 2) Number of spaces (0 or more)
<string> Spaces (" ") or "t" (converted to "\\t")
-a, --attr-extra-indent <integer|string> Added to indent for attributes
<integer> (Default: 1) Number of spaces (0 or more)
<string> Spaces (" ") or "t" (converted to "\\t")
Examples
$ gsvg --shiftwidth t sample.svg
$ gsvg -i sample.svg
$ cat sample.svg | gsvg -a 0 > sample.gsvg.svg
`
};
/* TODO check for these error codes in catch block of promise chain
var getFileContents = function (filePath) {
filePath = path.normalize(filePath);
return fs.readFileAsync(filePath, 'utf8')
.catch(function (err) {
Object.keys(err).forEach((k) => console.log(`${k}: ${err[k]}`));
if (err.code === 'ENOENT') {
throw new Error(' File does not exist');
} else if (err.code === 'EISDIR') {
throw new Error(' Path is a directory, not a file');
} else if (err.code === 'EACCES') {
throw new Error(' Permission denied to access file');
} else if (err.code === 'EPERM') {
throw new Error(' Elevated permissions required to read file');
}
});
};
*/
module.exports = function (cliArgs, testStdin) {
// `cliArgs` and `testStdin` are optional and only used for tests
var meowOptions = Object.assign({}, meowOptionsTemplate);
// if we're running tests, use passed in args instead of process.argv
if (isImportedModule) {
meowOptions.argv = cliArgs || [];
}
var cli = meow(meowOptions, minimistOptions);
var result = {
flags: cli.flags,
files: cli.input
};
// use bluebird promise instead of the one returned by getStdin()
return Promise.resolve().then(function () {
// only check for real stdin if this file is run directly
return isImportedModule ?
testStdin :
getStdin();
}).then(function (stdin) {
// get input from stdin or input file
if (stdin) {
result.stdin = stdin;
return stdin;
}
if (cli.input[0]) {
result.infile = cli.input[0];
return fs.readFileAsync(result.infile, 'utf8');
}
}).tap(function () {
// set up filenames
// it's OK if cli.input[x] is undefined, that's still falsy
if (result.stdin) {
result.outfile = cli.input[1];
} else {
result.infile = cli.input[0];
result.outfile = cli.input[1];
}
// change 't' to '\t' for cli flags which set indent
validationInfo.indentFlags.forEach(function (flag) {
if (cli.flags[flag] === 't') {
cli.flags[flag] = '\t';
}
});
}).tap(function () {
// this will throw errors if any input is invalid
return validateArgs(cli, validationInfo, result);
}).then(function (input) {
// process the input
return gsvg(input, cli.flags);
}).then(function (output) {
// write to a file if the user asked for that
if (result.outfile) {
result.stdout = '';
// TODO prompt user first if file exists and !cli.flags.inPlace
return fs.writeFileAsync(result.outfile, output, 'utf8');
}
return output;
}).catch(function (err) {
// catch errors and return error messages for humans
result.exitCode = 1;
if (err.message === 'NO INPUT') {
return cli.help;
}
if (err.name === 'Error') {
return ` GSVG: ${err.message}`;
}
console.error(`name: ${err.name}`);
console.error(`code: ${err.code}`);
console.error(`syscall: ${err.syscall}`);
console.error(`path: ${err.path}`);
console.error(` GSVG: ${err.message}`);
// name: Error
// code: ENOENT
// syscall: open
// path: D:\Harper\js\gsvg\a
// ENOENT: no such file or directory, open 'D:\Harper\js\gsvg\a'
}).then(function (output) {
// collect and return all our data
if (result.exitCode >= 1) {
result.stdout = '';
result.stderr = output + '\n';
} else {
result.exitCode = 0;
result.stdout = output || '';
result.stderr = '';
}
// result: (object)
// - stdin (string || undefined)
// - stdout (string)
// - stderr (string)
// - exitCode (number)
// - infile (string || undefined)
// - outfile (string || undefined)
// - flags (object)
// - input (array) filenames which were sorted into infile and outfile
return result;
});
};
if (!isImportedModule) {
module.exports().then(function (result) {
process.stdout.write(result.stdout);
process.stderr.write(result.stderr);
process.exitCode = result.exitCode;
});
}