1 | #!/usr/bin/env node
|
2 |
|
3 | 'use strict';
|
4 | const debug = require('debug')('xo');
|
5 |
|
6 |
|
7 | const resolveCwd = require('resolve-cwd');
|
8 | const hasFlag = require('has-flag');
|
9 |
|
10 | const localCLI = resolveCwd('xo/cli');
|
11 |
|
12 | if (!hasFlag('no-local') && localCLI && localCLI !== __filename) {
|
13 | debug('Using local install of XO.');
|
14 | require(localCLI);
|
15 | return;
|
16 | }
|
17 |
|
18 | const path = require('path');
|
19 | const spawn = require('child_process').spawn;
|
20 | const updateNotifier = require('update-notifier');
|
21 | const getStdin = require('get-stdin');
|
22 | const meow = require('meow');
|
23 | const formatterPretty = require('eslint-formatter-pretty');
|
24 | const xo = require('./');
|
25 |
|
26 | const cli = meow(`
|
27 | Usage
|
28 | $ xo [<file|glob> ...]
|
29 |
|
30 | Options
|
31 | --init Add XO to your project
|
32 | --fix Automagically fix issues
|
33 | --reporter Reporter to use
|
34 | --env Environment preset [Can be set multiple times]
|
35 | --global Global variable [Can be set multiple times]
|
36 | --ignore Additional paths to ignore [Can be set multiple times]
|
37 | --space Use space indent instead of tabs [Default: 2]
|
38 | --no-semicolon Prevent use of semicolons
|
39 | --plugin Include third-party plugins [Can be set multiple times]
|
40 | --extend Extend defaults with a custom config [Can be set multiple times]
|
41 | --open Open files with issues in your editor
|
42 | --quiet Show only errors and no warnings
|
43 | --extension Additional extension to lint [Can be set multiple times]
|
44 | --no-esnext Don't enforce ES2015+ rules
|
45 | --stdin Validate/fix code from stdin
|
46 | --stdin-filename Specify a filename for the --stdin option
|
47 |
|
48 | Examples
|
49 | $ xo
|
50 | $ xo index.js
|
51 | $ xo *.js !foo.js
|
52 | $ xo --space
|
53 | $ xo --env=node --env=mocha
|
54 | $ xo --init --space
|
55 | $ xo --plugin=react
|
56 | $ xo --plugin=html --extension=html
|
57 | $ echo 'const x=true' | xo --stdin --fix
|
58 |
|
59 | Tips
|
60 | Put options in package.json instead of using flags so other tools can read it.
|
61 | `, {
|
62 | string: [
|
63 | '_'
|
64 | ],
|
65 | boolean: [
|
66 | 'init',
|
67 | 'compact',
|
68 | 'stdin',
|
69 | 'fix',
|
70 | 'open'
|
71 | ],
|
72 | alias: {
|
73 | 'stdin-filename': 'filename'
|
74 | }
|
75 | });
|
76 |
|
77 | updateNotifier({pkg: cli.pkg}).notify();
|
78 |
|
79 | const input = cli.input;
|
80 | const opts = cli.flags;
|
81 |
|
82 | const log = report => {
|
83 |
|
84 |
|
85 | if (opts.compact) {
|
86 | opts.reporter = 'compact';
|
87 | }
|
88 |
|
89 | const reporter = opts.reporter ? xo.getFormatter(opts.reporter) : formatterPretty;
|
90 |
|
91 | process.stdout.write(reporter(report.results));
|
92 | process.exit(report.errorCount === 0 ? 0 : 1);
|
93 | }
|
94 |
|
95 | const open = report => {
|
96 | if (report.errorCount === 0) {
|
97 | return;
|
98 | }
|
99 |
|
100 | const editor = process.env.EDITOR;
|
101 |
|
102 | if (!editor) {
|
103 | console.log(`
|
104 | \`open\` option was used, but your $EDITOR environment variable is empty.
|
105 | Fix it by setting path to your editor of choice in ~/.bashrc or ~/.zshrc:
|
106 |
|
107 | export EDITOR=atom
|
108 | `);
|
109 | return;
|
110 | }
|
111 |
|
112 | const executableName = editor.split(path.sep).pop();
|
113 | const lineColumn = message => `${message.line}:${message.column}`;
|
114 | const args = [];
|
115 |
|
116 | report.results
|
117 | .filter(file => file.errorCount > 0)
|
118 | .forEach(file => {
|
119 |
|
120 | if (['subl', 'atom'].indexOf(executableName) >= 0) {
|
121 | args.push(file.filePath + ':' + lineColumn(file.messages[0]));
|
122 | return;
|
123 | }
|
124 |
|
125 |
|
126 | if (executableName === 'wstorm') {
|
127 | args.push(file.filePath + ':' + file.messages[0].line);
|
128 | return;
|
129 | }
|
130 |
|
131 |
|
132 | if (executableName === 'mate') {
|
133 | args.push('--line', lineColumn(file.messages[0]), file.filePath);
|
134 | return;
|
135 | }
|
136 |
|
137 | args.push(file.filePath);
|
138 | });
|
139 |
|
140 | spawn(editor, args, {
|
141 | detached: true,
|
142 | stdio: 'ignore'
|
143 | }).unref();
|
144 | }
|
145 |
|
146 |
|
147 | if (input[0] === '-') {
|
148 | opts.stdin = true;
|
149 | input.shift();
|
150 | }
|
151 |
|
152 | if (opts.init) {
|
153 | require('xo-init')();
|
154 | } else if (opts.stdin) {
|
155 | getStdin().then(str => {
|
156 | if (opts.fix) {
|
157 | console.log(xo.lintText(str, opts).results[0].output);
|
158 | return;
|
159 | }
|
160 |
|
161 | if (opts.open) {
|
162 | console.error('The `open` option is not supported on stdin');
|
163 | process.exit(1);
|
164 | }
|
165 |
|
166 | log(xo.lintText(str, opts));
|
167 | });
|
168 | } else {
|
169 | xo.lintFiles(input, opts).then(report => {
|
170 | if (opts.fix) {
|
171 | xo.outputFixes(report);
|
172 | }
|
173 |
|
174 | if (opts.open) {
|
175 | open(report);
|
176 | }
|
177 |
|
178 | log(report);
|
179 | });
|
180 | }
|