UNPKG

4.39 kBJavaScriptView Raw
1#!/usr/bin/env node
2/* eslint-disable import/order */
3'use strict';
4const debug = require('debug')('xo');
5
6// Prefer the local installation of XO.
7const resolveCwd = require('resolve-cwd');
8const hasFlag = require('has-flag');
9
10const localCLI = resolveCwd('xo/cli');
11
12if (!hasFlag('no-local') && localCLI && localCLI !== __filename) {
13 debug('Using local install of XO.');
14 require(localCLI);
15 return;
16}
17
18const path = require('path');
19const spawn = require('child_process').spawn;
20const updateNotifier = require('update-notifier');
21const getStdin = require('get-stdin');
22const meow = require('meow');
23const formatterPretty = require('eslint-formatter-pretty');
24const xo = require('./');
25
26const 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
77updateNotifier({pkg: cli.pkg}).notify();
78
79const input = cli.input;
80const opts = cli.flags;
81
82const log = report => {
83 // legacy
84 // TODO: remove in 1.0.0
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
95const 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.
105Fix 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 // Sublime Text and Atom support opening file at exact position
120 if (['subl', 'atom'].indexOf(executableName) >= 0) {
121 args.push(file.filePath + ':' + lineColumn(file.messages[0]));
122 return;
123 }
124
125 // WebStorm supports opening file on a specific line (no column support)
126 if (executableName === 'wstorm') {
127 args.push(file.filePath + ':' + file.messages[0].line);
128 return;
129 }
130
131 // TextMate requires a `--line` option
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// `xo -` => `xo --stdin`
147if (input[0] === '-') {
148 opts.stdin = true;
149 input.shift();
150}
151
152if (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}