forked from STRML/JSXHint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cli.js
executable file
·195 lines (173 loc) · 6.45 KB
/
cli.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env node
/**
* JSXHint CLI tool
*
* Copyright 2013 (c) Condé Nast
*
* Please see LICENSE for details
*
*/
'use strict';
var jsxhint = require('./jsxhint');
var jshintcli = require('jshint/src/cli');
var fork = require('child_process').fork;
var through = require('through');
var rimraf = require('rimraf');
var glob = require('glob-all');
var isWin = /^win/.test(process.platform);
// The jshint call will throw an error if it encounters an option that it does
// not recognize. Therefore, we need to filter out jsxhint options before
// calling jshint. Because jsxhint is run in part of a callback of jshint after
// this check, we need to store jsxhint options someplace globally so we can
// access them inside the callback.
var acceptedJSXHintOptions = ['--jsx-only', '--babel', '--babel-experimental', '--harmony'];
var jsxhintOptions = {};
/**
* Intercept -h to show jshint help.
*/
function showHelp(){
var jshint_proc = fork(require.resolve('jshint/bin/jshint'), ['-h'], {silent: true});
var ts = through(function write(chunk){
this.queue(chunk.toString().replace(/(jshint)\b/g, 'jsxhint'));
}, function end() {
// This feels like a hack. There might be a better way to do this.
this.queue('\nThe above options are native to JSHint, which JSXHint extends.\n');
this.queue('\x1b[1m'); // bold
this.queue('\nJSXHint Options:\n');
this.queue('\x1b[0m');
this.queue(' --jsx-only Only transform files with the .jsx extension.\n' +
' Will run somewhat faster.\n');
this.queue(' --babel Use babel (6to5) instead of react esprima.\n' +
' Useful if you are using es6-module, etc. You must \n' +
' install the module `babel` manually with npm.\n');
this.queue(' --babel-experimental Use babel with experimental support for ES7.\n' +
' Useful if you are using es7-async, etc.\n');
this.queue(' --harmony Use react esprima with ES6 transformation support.\n' +
' Useful if you are using both es6-class and react.\n');
});
jshint_proc.stderr.pipe(ts).pipe(process.stderr);
}
/**
* Intercept -v, shows jsxhint and jshint versions.
*/
function showVersion(){
var jshint_proc = fork(__dirname + '/node_modules/jshint/bin/jshint', ['-v'], {silent: true});
var ts = through(function write(chunk){
this.queue("JSXHint v" + require('./package.json').version + " (" +
chunk.toString().replace("\n", "") + ")\n");
});
jshint_proc.stderr.pipe(ts).pipe(process.stderr);}
/**
* Proxy run function. Reaches out to jsxhint to transform
* incoming stream or read files & transform.
* @param {Object} opts Opts as created by JSHint.
* @param {Function} cb Callback.
*/
function run(opts, cb){
opts.extensions = opts.extensions ? opts.extensions + ',.jsx' : '.jsx';
runGlob(opts.args, {nodir: true}, function(err, globbed) {
if (err) return cb(err);
// Reassign the globbed files back to opts.args, so it looks like we just
// fed jshint a big list of files.
opts.args = globbed;
var files = jshintcli.gather(opts);
if (opts.useStdin) {
jsxhint.transformStream(process.stdin, jsxhintOptions, cb);
} else {
jsxhint.transformFiles(files, jsxhintOptions, cb);
}
});
}
/**
* Collects files. If running on Windows, runs glob; otherwise, simply calls back with
* the files names.
*
* Glob is for cmd.exe, the bane of my existence.
* We use glob-all so we can support multiple patterns.
*
* @param {Array} files File names / glob patterns.
* @param {Object} opts Glob options.
* @param {Function} cb Callback.
*/
function runGlob(files, opts, cb) {
if (isWin) return glob(files, opts, cb);
// Not needed on unixy systems, they glob on their own
cb(null, files);
}
/**
* Intercept configured reporter and change file names so it looks
* like nothing happened.
* @param {Reporter} reporter JSHint reporter
* @param {Object} filesMap Map related transformed files to original file paths.
* @return {Function} Wrapper around configured reporter. Same arity as reporter.
*/
function interceptReporter(reporter, filesMap){
if(!reporter) reporter = require('jshint/src/reporters/default').reporter;
return function(results, data, opts){
if (filesMap) {
results.forEach(function(result){
result.file = filesMap[result.file];
});
}
return reporter(results, data, opts);
};
}
/**
* Unlink temporary files created by JSX processor.
*/
function unlinkTemp(){
try {
rimraf.sync(jsxhint.tmpdir);
} catch(e) {
// ignore
}
}
// Run program. Intercept JSHintCLI.run to process JSX files.
try {
if (process.argv.indexOf('-h') !== -1 || process.argv.indexOf('--help') !== -1){
showHelp();
} else if (process.argv.indexOf('-v') !== -1 || process.argv.indexOf('--version') !== -1){
showVersion();
} else {
jshintcli.originalRun = jshintcli.run;
jshintcli.run = function(opts, cb){
// Files can either be string data (from stdin), or an object
// where keys are the original file name and values are the temporary file
// name where the transformed source is written.
run(opts, function(err, files){
opts.reporter = interceptReporter(opts.reporter, files);
// always false, stdin is never going to be usable as we may have read from it for the
// transform.
opts.useStdin = false;
if (err) {
opts.reporter([err], {}, opts);
return process.exit(1);
}
opts.args = Object.keys(files);
// Weird sync/async function, jshint oddity
var done = function(passed){
if (passed == null) return;
unlinkTemp();
cb(passed);
};
done(jshintcli.originalRun(opts, done));
});
};
var argv = process.argv.filter(function(value) {
if (acceptedJSXHintOptions.indexOf(value) > -1) {
// Store the jsxhint specific option globally so we can access it when
// we run the transformation.
jsxhintOptions[value] = true;
// Need to filter out jsxhint specific options so jshint doesn't throw
// an unknown option error.
return false;
}
return true;
});
jshintcli.interpret(argv);
}
} catch (e){
console.log(e.message.replace(/cli\.js/, 'jsxhint'));
unlinkTemp();
process.exit(1);
}