UNPKG

10.7 kBJavaScriptView Raw
1(function() {
2 /*
3 CoffeeLint
4
5 Copyright (c) 2011 Matthew Perpick.
6 CoffeeLint is freely distributable under the MIT license.
7 */
8 var Cache, CoffeeScript, coffeelint, config, configfinder, coreReporters, data, deprecatedReporter, errorReport, findCoffeeScripts, fs, getAllExtention, getFallbackConfig, glob, ignore, jsonIndentation, lintFiles, lintSource, loadConfig, log, logConfig, optimist, options, os, path, paths, read, readConfigFile, ref, reportAndExit, resolve, ruleLoader, scripts, stdin, stripComments, thisdir, userConfig;
9
10 resolve = require('resolve').sync;
11
12 path = require('path');
13
14 fs = require('fs');
15
16 os = require('os');
17
18 glob = require('glob');
19
20 optimist = require('optimist');
21
22 ignore = require('ignore');
23
24 stripComments = require('strip-json-comments');
25
26 thisdir = path.dirname(fs.realpathSync(__filename));
27
28 coffeelint = require(path.join(thisdir, 'coffeelint'));
29
30 configfinder = require(path.join(thisdir, 'configfinder'));
31
32 ruleLoader = require(path.join(thisdir, 'ruleLoader'));
33
34 Cache = require(path.join(thisdir, 'cache'));
35
36 CoffeeScript = require('coffeescript');
37
38 CoffeeScript.register();
39
40 log = function() {
41 // coffeelint: disable=no_debugger
42 return console.log(...arguments);
43 };
44
45 // coffeelint: enable=no_debugger
46 jsonIndentation = 2;
47
48 logConfig = function(config) {
49 var filter;
50 filter = function(k, v) {
51 if (k !== 'message' && k !== 'description' && k !== 'name') {
52 return v;
53 }
54 };
55 return log(JSON.stringify(config, filter, jsonIndentation));
56 };
57
58 // Return the contents of the given file synchronously.
59 read = function(path) {
60 var realPath;
61 realPath = fs.realpathSync(path);
62 return fs.readFileSync(realPath).toString();
63 };
64
65 // build all extentions to search
66 getAllExtention = function(extension) {
67 if (extension != null) {
68 extension = ['coffee'].concat(extension != null ? extension.split(',') : void 0);
69 return `@(${extension.join('|')})`;
70 } else {
71 return 'coffee';
72 }
73 };
74
75 // Return a list of CoffeeScript's in the given paths.
76 findCoffeeScripts = function(paths, extension) {
77 var allExtention, files, i, len, p;
78 files = [];
79 allExtention = getAllExtention(extension);
80 for (i = 0, len = paths.length; i < len; i++) {
81 p = paths[i];
82 if (fs.statSync(p).isDirectory()) {
83 // The glob library only uses forward slashes.
84 files = files.concat(glob.sync(`${p}/**/*.${allExtention}`));
85 } else {
86 files.push(p);
87 }
88 }
89 // Normalize paths, converting './test/fixtures' to 'test/fixtures'.
90 // Ignore pattern 'test/fixtures' does NOT match './test/fixtures',
91 // because if there is a slash(/) in the pattern, the pattern will not
92 // act as a glob pattern.
93 // Use `path.join()` instead of `path.normalize()` for better compatibility.
94 return files.map(function(p) {
95 return path.join(p);
96 });
97 };
98
99 // Return an error report from linting the given paths.
100 lintFiles = function(files, config) {
101 var errorReport, file, fileConfig, i, len, literate, source;
102 errorReport = new coffeelint.getErrorReport();
103 for (i = 0, len = files.length; i < len; i++) {
104 file = files[i];
105 source = read(file);
106 literate = CoffeeScript.helpers.isLiterate(file);
107 fileConfig = config ? config : getFallbackConfig(file);
108 errorReport.lint(file, source, fileConfig, literate);
109 }
110 return errorReport;
111 };
112
113 // Return an error report from linting the given coffeescript source.
114 lintSource = function(source, config, literate = false) {
115 var errorReport;
116 errorReport = new coffeelint.getErrorReport();
117 config || (config = getFallbackConfig());
118 errorReport.lint('stdin', source, config, literate);
119 return errorReport;
120 };
121
122 // Load a config file given a path/filename
123 readConfigFile = function(path) {
124 var text;
125 text = read(path);
126 try {
127 jsonIndentation = text.split('\n')[1].match(/^\s+/)[0].length;
128 } catch (error) {}
129 return JSON.parse(stripComments(text));
130 };
131
132 loadConfig = function(options) {
133 var config;
134 config = null;
135 if (!options.argv.noconfig) {
136 if (options.argv.f) {
137 config = readConfigFile(options.argv.f);
138 // If -f was specifying a package.json, extract the config
139 if (config.coffeelintConfig) {
140 config = config.coffeelintConfig;
141 }
142 }
143 }
144 return config;
145 };
146
147 // Get fallback configuration. With the -F flag found configs in standard places
148 // will be used for each file being linted. Standard places are package.json or
149 // coffeelint.json in a project's root folder or the user's home folder.
150 getFallbackConfig = function(filename = null) {
151 if (!options.argv.noconfig) {
152 return configfinder.getConfig(filename);
153 }
154 };
155
156 // These reporters are usually parsed by other software, so I can't just echo a
157 // warning. Creating a fake file is my best attempt.
158 deprecatedReporter = function(errorReport, reporter) {
159 var base;
160 if ((base = errorReport.paths)['coffeelint_fake_file.coffee'] == null) {
161 base['coffeelint_fake_file.coffee'] = [];
162 }
163 errorReport.paths['coffeelint_fake_file.coffee'].push({
164 'level': 'warn',
165 'rule': 'commandline',
166 'message': `parameter --${reporter} is deprecated. Use --reporter ${reporter} instead`,
167 'lineNumber': 0
168 });
169 return reporter;
170 };
171
172 coreReporters = {
173 default: require(path.join(thisdir, 'reporters', 'default')),
174 csv: require(path.join(thisdir, 'reporters', 'csv')),
175 jslint: require(path.join(thisdir, 'reporters', 'jslint')),
176 checkstyle: require(path.join(thisdir, 'reporters', 'checkstyle')),
177 raw: require(path.join(thisdir, 'reporters', 'raw'))
178 };
179
180 // Publish the error report and exit with the appropriate status.
181 reportAndExit = function(errorReport, options) {
182 var SelectedReporter, base, colorize, ref, reporter, strReporter;
183 strReporter = options.argv.jslint ? deprecatedReporter(errorReport, 'jslint') : options.argv.csv ? deprecatedReporter(errorReport, 'csv') : options.argv.checkstyle ? deprecatedReporter(errorReport, 'checkstyle') : options.argv.reporter;
184 if (strReporter == null) {
185 strReporter = 'default';
186 }
187 SelectedReporter = (ref = coreReporters[strReporter]) != null ? ref : (function() {
188 var reporterPath;
189 try {
190 reporterPath = resolve(strReporter, {
191 basedir: process.cwd(),
192 extensions: ['.js', '.coffee', '.litcoffee', '.coffee.md']
193 });
194 } catch (error) {
195 reporterPath = strReporter;
196 }
197 return require(reporterPath);
198 })();
199 if ((base = options.argv).color == null) {
200 base.color = options.argv.nocolor ? 'never' : 'auto';
201 }
202 colorize = (function() {
203 switch (options.argv.color) {
204 case 'always':
205 return true;
206 case 'never':
207 return false;
208 default:
209 return process.stdout.isTTY;
210 }
211 })();
212 reporter = new SelectedReporter(errorReport, {
213 colorize: colorize,
214 quiet: options.argv.q
215 });
216 reporter.publish();
217 return process.on('exit', function() {
218 return process.exit(errorReport.getExitCode());
219 });
220 };
221
222 // Declare command line options.
223 options = optimist.usage('Usage: coffeelint [options] source [...]').alias('f', 'file').alias('h', 'help').alias('v', 'version').alias('s', 'stdin').alias('q', 'quiet').alias('c', 'cache').describe('f', 'Specify a custom configuration file.').describe('rules', 'Specify a custom rule or directory of rules.').describe('makeconfig', 'Prints a default config file').describe('trimconfig', 'Compares your config with the default and prints a minimal configuration').describe('noconfig', 'Ignores any config file.').describe('h', 'Print help information.').describe('v', 'Print current version number.').describe('r', '(not used, but left for backward compatibility)').describe('reporter', 'built in reporter (default, csv, jslint, checkstyle, raw), or module, or path to reporter file.').describe('csv', '[deprecated] use --reporter csv').describe('jslint', '[deprecated] use --reporter jslint').describe('nocolor', '[deprecated] use --color=never').describe('checkstyle', '[deprecated] use --reporter checkstyle').describe('color=<when>', 'When to colorize the output. <when> can be one of always, never , or auto.').describe('s', 'Lint the source from stdin').describe('q', 'Only print errors.').describe('literate', 'Used with --stdin to process as Literate CoffeeScript').describe('c', 'Cache linting results').describe('ext', 'Specify an additional file extension, separated by comma.').boolean('csv').boolean('jslint').boolean('checkstyle').boolean('nocolor').boolean('noconfig').boolean('makeconfig').boolean('trimconfig').boolean('literate').boolean('r').boolean('s').boolean('q', 'Print errors only.').boolean('c');
224
225 if (options.argv.v) {
226 log(coffeelint.VERSION);
227 process.exit(0);
228 } else if (options.argv.h) {
229 options.showHelp();
230 process.exit(0);
231 } else if (options.argv.trimconfig) {
232 userConfig = (ref = loadConfig(options)) != null ? ref : getFallbackConfig();
233 logConfig(coffeelint.trimConfig(userConfig));
234 } else if (options.argv.makeconfig) {
235 logConfig(coffeelint.getRules());
236 } else if (options.argv._.length < 1 && !options.argv.s) {
237 options.showHelp();
238 process.exit(1);
239 } else {
240 // Initialize cache, if enabled
241 if (options.argv.cache) {
242 coffeelint.setCache(new Cache(path.join(os.tmpdir(), 'coffeelint')));
243 }
244 // Load configuration.
245 config = loadConfig(options);
246 if (options.argv.rules) {
247 ruleLoader.loadRule(coffeelint, options.argv.rules);
248 }
249 if (options.argv.s) {
250 // Lint from stdin
251 data = '';
252 stdin = process.openStdin();
253 stdin.on('data', function(buffer) {
254 if (buffer) {
255 return data += buffer.toString();
256 }
257 });
258 stdin.on('end', function() {
259 var errorReport;
260 errorReport = lintSource(data, config, options.argv.literate);
261 return reportAndExit(errorReport, options);
262 });
263 } else {
264 // Find scripts to lint.
265 paths = options.argv._;
266 scripts = findCoffeeScripts(paths, options.argv.ext);
267 if (fs.existsSync('.coffeelintignore')) {
268 scripts = ignore().add(fs.readFileSync('.coffeelintignore').toString()).filter(scripts);
269 }
270 // Lint the code.
271 errorReport = lintFiles(scripts, config, options.argv.literate);
272 reportAndExit(errorReport, options);
273 }
274 }
275
276}).call(this);