UNPKG

5.65 kBJavaScriptView Raw
1var path = require('path');
2var util = require('util');
3var fs = require('fs');
4var assert = require('assert');
5
6var glob = require('glob');
7var resolve = require('resolve');
8
9var utils = require('../utils');
10var Configuration = require('./configuration');
11var configFinder = require('../cli-config');
12
13var OVERRIDE_OPTIONS = [
14 'fix',
15 'preset',
16 'maxErrors',
17 'errorFilter',
18 'es3',
19 'extract'
20];
21
22function req(entity, dir) {
23 return require(
24 resolve.sync(entity, { basedir: dir })
25 );
26}
27
28/**
29 * nodejs-compatible configuration module.
30 *
31 * @name NodeConfiguration
32 * @augments Configuration
33 * @constructor
34 */
35function NodeConfiguration() {
36 Configuration.call(this);
37 this._basePath = process.cwd();
38}
39
40util.inherits(NodeConfiguration, Configuration);
41
42/**
43 * Overrides configuration with options specified by the CLI
44 *
45 * @param {Object} program
46 */
47NodeConfiguration.prototype.overrideFromCLI = function(program) {
48 var overrides = {};
49
50 OVERRIDE_OPTIONS.forEach(function(option) {
51 if (option in program) {
52 overrides[option] = program[option];
53 }
54 });
55
56 this.override(overrides);
57};
58
59/**
60 * Load external module.
61 *
62 * @param {String|null} external - path (relative or absolute) or name to the external module
63 * @param {String} type - type of the module
64 * @param {String} [config = _basePath] - path to config relative to which external entities will be loaded
65 * @returns {Module|null}
66 * @protected
67 */
68NodeConfiguration.prototype.loadExternal = function(external, type, config) {
69 assert(
70 typeof external === 'string' || external === null,
71 '"' + type + '" option requires a string or null value'
72 );
73
74 if (external === null) {
75 return null;
76 }
77
78 var dir = config ? path.dirname(config) : this._basePath;
79 var get = function(prefix, postfix) {
80 prefix = prefix || '';
81 postfix = postfix || '';
82
83 try {
84 return finder(
85 utils.normalizePath(prefix + external + postfix, dir),
86 dir
87 );
88 } catch (e) {}
89
90 return null;
91 }.bind(this);
92
93 var finder;
94 if (type === 'preset') {
95 finder = configFinder.getContent;
96
97 } else {
98 finder = req;
99 }
100
101 var content;
102
103 if (external.indexOf('jscs-') !== 0) {
104 content = get('jscs-');
105
106 if (!content && type === 'preset') {
107 content = get('jscs-preset-') || get('jscs-config-');
108
109 if (!content && external.indexOf('/') !== -1 && !external.split('.')[1]) {
110 content = get('jscs-', '.json') ||
111 get('jscs-', '.js') ||
112 get('jscs-preset-', '.json') ||
113 get('jscs-config-', '.json') ||
114 get('jscs-preset-', '.js') ||
115 get('jscs-config-', '.js');
116 }
117 }
118 }
119
120 return content || get();
121};
122
123/**
124 * Loads plugin data.
125 *
126 * @param {String|function(Configuration)} plugin
127 * @param {String} [config] - path to config relative to which plugin will be loaded
128 * @protected
129 */
130NodeConfiguration.prototype._loadPlugin = function(plugin, config) {
131 if (typeof plugin !== 'function') {
132 plugin = this.loadExternal(plugin, 'plugin', config);
133 }
134
135 return Configuration.prototype._loadPlugin.call(this, plugin);
136};
137
138/**
139 * Loads preset.
140 *
141 * @param {String|null} preset
142 * @param {String} config - path to config relative to which plugin will be loaded
143 * @protected
144 */
145NodeConfiguration.prototype._loadPreset = function(preset, config) {
146 var name = path.basename(preset).split('.')[0];
147
148 try {
149 this.registerPreset(name, this.loadExternal(preset, 'preset', config));
150 } catch (e) {
151 var registeredPresets = this.getRegisteredPresets();
152
153 if (preset in registeredPresets) {
154 Configuration.prototype._loadPreset.call(this, preset);
155 return;
156 }
157 }
158
159 // If preset is an external module, error will be thrown by the caller
160 Configuration.prototype._loadPreset.call(this, name);
161};
162
163/**
164 * Loads an error filter module.
165 *
166 * @param {String|null} filter
167 * @param {String} config - path to config relative to which plugin will be loaded
168 * @protected
169 */
170NodeConfiguration.prototype._loadErrorFilter = function(filter, config) {
171 Configuration.prototype._loadErrorFilter.call(
172 this,
173 this.loadExternal(filter, 'errorFilter', config)
174 );
175};
176
177/**
178 * Loads additional rule.
179 *
180 * @param {String|Rule} additionalRule
181 * @param {String} config - path to config relative to which plugin will be loaded
182 * @private
183 */
184NodeConfiguration.prototype._loadAdditionalRule = function(additionalRule, config) {
185 config = config || this._basePath;
186
187 if (typeof additionalRule === 'string') {
188 if (glob.hasMagic(additionalRule)) {
189
190 // In some cases there might not be a config
191 // like if options are defined through direct initialization (grunt plugin case)
192 config = fs.statSync(config).isDirectory() ? config : path.dirname(config);
193
194 glob.sync(path.resolve(config, additionalRule)).forEach(function(p) {
195 var Rule = require(p);
196 Configuration.prototype._loadAdditionalRule.call(this, new Rule());
197 }, this);
198 } else {
199 var Rule = this.loadExternal(additionalRule, 'rule', config);
200 Configuration.prototype._loadAdditionalRule.call(this, new Rule());
201 }
202 } else {
203 Configuration.prototype._loadAdditionalRule.call(this, additionalRule);
204 }
205};
206
207module.exports = NodeConfiguration;