forked from tabatkins/railroad-diagrams
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrrdlint.js
120 lines (108 loc) · 3.36 KB
/
rrdlint.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import globSync from 'tiny-glob/sync';
import { readFileSync } from 'fs';
import { join, extname } from 'path';
import { safeLoad } from 'js-yaml';
import rr, { Diagram } from 'railroad-diagrams';
const args = process.argv;
let verbose = false;
const patterns = [];
let forcedInputType;
function usage () {
console.log(`Checks the syntax of railroad diagrams in JSON, YAML or JavaScript.
Usage: rrdlint [option...] [pattern...]
Options:
-i|--input <type> read input from json, yaml or javascript. defaults to json
-v|--verbose print checked file names and error stacktrace
-V|--version print version number
-h|--help print usage instructions
If no file name pattern is provided, standard input will be read.
If no input type is provided, it will be inferred from the file extension:
".json" -> json, ".yaml" or ".yml" -> yaml, ".js" -> javascript.
Examples:
cat foo.yaml | rrdlint -i yaml
rrdlint diagrams/*`);
process.exit(0);
}
if (!args.length) usage();
for (let i = 2, l = args.length; i < l; ++i) {
const arg = args[i];
let match;
if ((match = /^(?:-|--)(?:(no)-)?(\w+)$/.exec(arg))) {
switch (match[2]) {
case 'v': case 'verbose':
verbose = true;
continue;
case 'i': case 'input':
forcedInputType = args[++i];
if (forcedInputType !== 'json' && forcedInputType !== 'yaml' && forcedInputType !== 'javascript') {
console.error(`Invalid input type: "${forcedInputType}".`);
process.exit(2);
}
continue;
case 'V': case 'version':
console.log(JSON.parse(readFileSync(join(
__dirname, '../package.json'), 'utf-8')).version);
process.exit(0);
continue;
case 'h': case 'help':
usage();
}
console.error(`Unknown option: "${match[0]}".`);
process.exit(2);
}
patterns.push(arg);
}
if (patterns.length) {
for (const pattern of patterns) {
const names = globSync(pattern, { filesOnly: true });
for (const name of names) {
let inputType;
if (forcedInputType === undefined) {
const ext = extname(name);
switch (ext) {
case '.json': inputType = 'json'; break;
case '.yml': case '.yaml': inputType = 'yaml'; break;
case '.js': inputType = 'javascript';
}
} else {
inputType = forcedInputType;
}
run({ name, code: readFileSync(name, 'utf-8') }, inputType);
}
}
}
function run (source, inputType) {
try {
switch (inputType) {
case 'javascript':
fromJavaScript(source.code); break;
case 'yaml':
Diagram.fromJSON(safeLoad(source.code)); break;
case 'json':
default:
Diagram.fromJSON(JSON.parse(source.code));
}
if (verbose) console.log(`${source.name}: OK`);
} catch (error) {
console.error(`${source.name}: ${error.message}`);
if (verbose) console.log(error.stack);
process.exitCode = 1;
}
}
function fromJavaScript (input) {
global.rr = rr;
const diagramFunctions = `const { ${Object.keys(rr).join(', ')} } = rr;`;
const createDiagram = new Function(`${diagramFunctions}
${input}`);
createDiagram();
}
if (!patterns.length) {
let input = '';
process.stdin.setEncoding('utf8');
process.stdin
.on('data', chunk => (input += chunk))
.on('end', () => {
run({ name: 'snippet', code: input }, forcedInputType);
})
.resume();
}