UNPKG

6.02 kBJavaScriptView Raw
1/**
2 * Command line config helpers
3 */
4
5var fs = require('fs');
6var path = require('path');
7
8var stripJSONComments = require('strip-json-comments');
9var yaml = require('js-yaml');
10var parseJson = require('jsonlint').parse;
11var supportsColor = require('chalk').supportsColor;
12var glob = require('glob');
13var resolve = require('resolve');
14var stripBOM = require('strip-bom');
15
16// Configuration sources in priority order.
17var configs = ['package.json', '.jscsrc', '.jscs.json', '.jscs.yaml'];
18
19// Before, "findup-sync" package was used,
20// but it does not provide filter callback
21function findup(patterns, options, fn) {
22 /* jshint -W083 */
23
24 var lastpath;
25 var file;
26
27 options = Object.create(options);
28 options.maxDepth = 1;
29 options.cwd = path.resolve(options.cwd);
30
31 do {
32 file = patterns.filter(function(pattern) {
33 var configPath = glob.sync(pattern, options)[0];
34
35 if (configPath) {
36 return fn(path.join(options.cwd, configPath));
37 }
38 })[0];
39
40 if (file) {
41 return path.join(options.cwd, file);
42 }
43
44 lastpath = options.cwd;
45 options.cwd = path.resolve(options.cwd, '..');
46 } while (options.cwd !== lastpath);
47}
48
49/**
50 * Get content of the configuration file.
51 *
52 * @param {String} config - partial path to configuration file
53 * @param {String} directory - directory path which will be joined with config argument
54 * @return {Object}
55 */
56exports.getContent = function(config, directory) {
57 if (!config) {
58 return;
59 }
60
61 if (!directory) {
62 directory = process.cwd();
63 }
64
65 var configPath = path.resolve(directory, config);
66 var ext;
67 var data;
68 var content;
69 var requireConfigPath;
70
71 if (fs.existsSync(configPath)) {
72 config = path.basename(config);
73 ext = path.extname(configPath);
74
75 if (ext === '.js') {
76 content = require(configPath);
77 } else {
78 data = stripBOM(fs.readFileSync(configPath, 'utf8'));
79
80 if (ext === '.json') {
81 content = parseJson(stripJSONComments(data));
82 } else if (ext === '.yaml') {
83 content = yaml.safeLoad(data);
84 } else {
85 // try both JSON and YAML
86
87 try {
88 content = parseJson(stripJSONComments(data));
89 } catch (jsonError) {
90 try {
91 content = yaml.safeLoad(data);
92 } catch (yamlError) {
93 if (stripJSONComments(data).trim()[0] === '{') {
94 // the intention was probably JSON
95 throw jsonError;
96 } else {
97 // assume the intention was YAML
98 throw yamlError;
99 }
100 }
101 }
102 }
103 }
104 } else {
105 // Try to load it as a node module
106 try {
107 requireConfigPath = resolve.sync(config, { basedir: directory });
108 content = require(requireConfigPath);
109 } catch (e) {}
110 }
111
112 if (content) {
113 // Adding property via Object.defineProperty makes it
114 // non-enumerable and avoids warning for unsupported rules
115 Object.defineProperty(content, 'configPath', {
116 value: requireConfigPath || configPath
117 });
118 }
119
120 return content && config === 'package.json' ? content.jscsConfig : content;
121};
122
123/**
124 * Get content of the configuration file.
125 *
126 * @param {String} config - partial path to configuration file
127 * @param {String} [cwd = process.cwd()] - directory path which will be joined with config argument
128 * @return {Object|undefined}
129 */
130exports.load = function(config, cwd) {
131 var content;
132 var directory = cwd || process.cwd();
133
134 // If config option is given, attempt to load it
135 if (config) {
136 return this.getContent(config, directory);
137 }
138
139 content = this.getContent(
140 findup(configs, { nocase: true, cwd: directory }, function(configPath) {
141 if (path.basename(configPath) === 'package.json') {
142 return !!this.getContent(configPath);
143 }
144
145 return true;
146 }.bind(this))
147 );
148
149 if (content) {
150 return content;
151 }
152
153 // Try to load standard configs from home dir
154 var directoryArr = [process.env.USERPROFILE, process.env.HOMEPATH, process.env.HOME];
155 for (var i = 0, dirLen = directoryArr.length; i < dirLen; i++) {
156 if (!directoryArr[i]) {
157 continue;
158 }
159
160 for (var j = 0, len = configs.length; j < len; j++) {
161 content = this.getContent(configs[j], directoryArr[i]);
162
163 if (content) {
164 return content;
165 }
166 }
167 }
168};
169
170/**
171 * Get reporter function and path to it.
172 *
173 * @param {String} reporter - path or the name of one of the pre-defined reporters
174 * @param {Boolean} [colors = true] - should it be a colorful output?
175 * @return {{writer: Function, path: String}}
176 */
177exports.getReporter = function(reporter, colors) {
178 var writerPath;
179 var writer;
180
181 if (colors !== false) {
182 colors = true;
183 }
184
185 if (reporter) {
186 // ensure reporter is a string (and allow non-string types to be coerced)
187 reporter = reporter.toString();
188 writerPath = path.resolve(process.cwd(), reporter);
189
190 if (!fs.existsSync(writerPath)) {
191 writerPath = path.resolve(__dirname, './reporters/' + reporter);
192 }
193 } else {
194 writerPath = path.resolve(
195 __dirname, './reporters/', (colors && supportsColor ? 'console' : 'text')
196 );
197 }
198
199 try {
200 writer = require(writerPath);
201 } catch (e) {
202 writer = null;
203 }
204
205 if (!writer) {
206 try {
207 writer = require(reporter);
208 writerPath = reporter;
209 } catch (e) {}
210 }
211
212 return {
213 path: writerPath,
214 writer: writer
215 };
216};