1 | 'use strict';
|
2 |
|
3 | const _ = require('lodash');
|
4 | const configurationError = require('./utils/configurationError');
|
5 | const dynamicRequire = require('./dynamicRequire');
|
6 | const getModulePath = require('./utils/getModulePath');
|
7 | const globjoin = require('globjoin');
|
8 | const normalizeRuleSettings = require('./normalizeRuleSettings');
|
9 | const path = require('path');
|
10 | const requireRule = require('./requireRule');
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | function augmentConfigBasic(stylelint, config, configDir, allowOverrides) {
|
31 | return Promise.resolve()
|
32 | .then(() => {
|
33 | if (!allowOverrides) return config;
|
34 |
|
35 | return _.merge(config, stylelint._options.configOverrides);
|
36 | })
|
37 | .then((augmentedConfig) => {
|
38 | return extendConfig(stylelint, augmentedConfig, configDir);
|
39 | })
|
40 | .then((augmentedConfig) => {
|
41 | return absolutizePaths(augmentedConfig, configDir);
|
42 | });
|
43 | }
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | function augmentConfigExtended(stylelint, cosmiconfigResult) {
|
54 | if (!cosmiconfigResult) return Promise.resolve(null);
|
55 |
|
56 | const configDir = path.dirname(cosmiconfigResult.filepath || '');
|
57 | const { ignoreFiles, ...cleanedConfig } = cosmiconfigResult.config;
|
58 |
|
59 | return augmentConfigBasic(stylelint, cleanedConfig, configDir).then((augmentedConfig) => {
|
60 | return {
|
61 | config: augmentedConfig,
|
62 | filepath: cosmiconfigResult.filepath,
|
63 | };
|
64 | });
|
65 | }
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | function augmentConfigFull(stylelint, cosmiconfigResult) {
|
73 | if (!cosmiconfigResult) return Promise.resolve(null);
|
74 |
|
75 | const config = cosmiconfigResult.config;
|
76 | const filepath = cosmiconfigResult.filepath;
|
77 |
|
78 | const configDir = stylelint._options.configBasedir || path.dirname(filepath || '');
|
79 |
|
80 | return augmentConfigBasic(stylelint, config, configDir, true)
|
81 | .then((augmentedConfig) => {
|
82 | return addPluginFunctions(augmentedConfig);
|
83 | })
|
84 | .then((augmentedConfig) => {
|
85 | return addProcessorFunctions(augmentedConfig);
|
86 | })
|
87 | .then((augmentedConfig) => {
|
88 | if (!augmentedConfig.rules) {
|
89 | throw configurationError(
|
90 | 'No rules found within configuration. Have you provided a "rules" property?',
|
91 | );
|
92 | }
|
93 |
|
94 | return normalizeAllRuleSettings(augmentedConfig);
|
95 | })
|
96 | .then((augmentedConfig) => {
|
97 | return {
|
98 | config: augmentedConfig,
|
99 | filepath: cosmiconfigResult.filepath,
|
100 | };
|
101 | });
|
102 | }
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 | function absolutizePaths(config, configDir) {
|
115 | if (config.ignoreFiles) {
|
116 | config.ignoreFiles = ([]).concat(config.ignoreFiles).map((glob) => {
|
117 | if (path.isAbsolute(glob.replace(/^!/, ''))) return glob;
|
118 |
|
119 | return globjoin(configDir, glob);
|
120 | });
|
121 | }
|
122 |
|
123 | if (config.plugins) {
|
124 | config.plugins = ([]).concat(config.plugins).map((lookup) => {
|
125 | return getModulePath(configDir, lookup);
|
126 | });
|
127 | }
|
128 |
|
129 | if (config.processors) {
|
130 | config.processors = absolutizeProcessors(config.processors, configDir);
|
131 | }
|
132 |
|
133 | return config;
|
134 | }
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | function absolutizeProcessors(processors, configDir) {
|
144 | const normalizedProcessors = Array.isArray(processors) ? processors : [processors];
|
145 |
|
146 | return normalizedProcessors.map((item) => {
|
147 | if (typeof item === 'string') {
|
148 | return getModulePath(configDir, item);
|
149 | }
|
150 |
|
151 | return [getModulePath(configDir, item[0]), item[1]];
|
152 | });
|
153 | }
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | function extendConfig(stylelint, config, configDir) {
|
162 | if (config.extends === undefined) return Promise.resolve(config);
|
163 |
|
164 | const normalizedExtends = Array.isArray(config.extends) ? config.extends : [config.extends];
|
165 | const { extends: configExtends, ...originalWithoutExtends } = config;
|
166 |
|
167 | const loadExtends = normalizedExtends.reduce((resultPromise, extendLookup) => {
|
168 | return resultPromise.then((resultConfig) => {
|
169 | return loadExtendedConfig(stylelint, resultConfig, configDir, extendLookup).then(
|
170 | (extendResult) => {
|
171 | if (!extendResult) return resultConfig;
|
172 |
|
173 | return mergeConfigs(resultConfig, extendResult.config);
|
174 | },
|
175 | );
|
176 | });
|
177 | }, Promise.resolve(originalWithoutExtends));
|
178 |
|
179 | return loadExtends.then((resultConfig) => {
|
180 | return mergeConfigs(resultConfig, originalWithoutExtends);
|
181 | });
|
182 | }
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 | function loadExtendedConfig(stylelint, config, configDir, extendLookup) {
|
192 | const extendPath = getModulePath(configDir, extendLookup);
|
193 |
|
194 | return stylelint._extendExplorer.load(extendPath);
|
195 | }
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 | function mergeConfigs(a, b) {
|
209 |
|
210 | const pluginMerger = {};
|
211 |
|
212 | if (a.plugins || b.plugins) {
|
213 | pluginMerger.plugins = [];
|
214 |
|
215 | if (a.plugins) {
|
216 | pluginMerger.plugins = pluginMerger.plugins.concat(a.plugins);
|
217 | }
|
218 |
|
219 | if (b.plugins) {
|
220 | pluginMerger.plugins = [...new Set(pluginMerger.plugins.concat(b.plugins))];
|
221 | }
|
222 | }
|
223 |
|
224 |
|
225 | const processorMerger = {};
|
226 |
|
227 | if (a.processors || b.processors) {
|
228 | processorMerger.processors = [];
|
229 |
|
230 | if (a.processors) {
|
231 | processorMerger.processors = processorMerger.processors.concat(a.processors);
|
232 | }
|
233 |
|
234 | if (b.processors) {
|
235 | processorMerger.processors = [...new Set(processorMerger.processors.concat(b.processors))];
|
236 | }
|
237 | }
|
238 |
|
239 | const rulesMerger = {};
|
240 |
|
241 | if (a.rules || b.rules) {
|
242 | rulesMerger.rules = { ...a.rules, ...b.rules };
|
243 | }
|
244 |
|
245 | const result = { ...a, ...b, ...processorMerger, ...pluginMerger, ...rulesMerger };
|
246 |
|
247 | return result;
|
248 | }
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 | function addPluginFunctions(config) {
|
255 | if (!config.plugins) return config;
|
256 |
|
257 | const normalizedPlugins = Array.isArray(config.plugins) ? config.plugins : [config.plugins];
|
258 |
|
259 | const pluginFunctions = normalizedPlugins.reduce(
|
260 | (result, pluginLookup) => {
|
261 | let pluginImport = dynamicRequire(pluginLookup);
|
262 |
|
263 |
|
264 | pluginImport = pluginImport.default || pluginImport;
|
265 |
|
266 |
|
267 |
|
268 | const normalizedPluginImport = Array.isArray(pluginImport) ? pluginImport : [pluginImport];
|
269 |
|
270 | normalizedPluginImport.forEach((pluginRuleDefinition) => {
|
271 | if (!pluginRuleDefinition.ruleName) {
|
272 | throw configurationError(
|
273 | 'stylelint v3+ requires plugins to expose a ruleName. ' +
|
274 | `The plugin "${pluginLookup}" is not doing this, so will not work ` +
|
275 | 'with stylelint v3+. Please file an issue with the plugin.',
|
276 | );
|
277 | }
|
278 |
|
279 | if (!pluginRuleDefinition.ruleName.includes('/')) {
|
280 | throw configurationError(
|
281 | 'stylelint v7+ requires plugin rules to be namespaced, ' +
|
282 | 'i.e. only `plugin-namespace/plugin-rule-name` plugin rule names are supported. ' +
|
283 | `The plugin rule "${pluginRuleDefinition.ruleName}" does not do this, so will not work. ` +
|
284 | 'Please file an issue with the plugin.',
|
285 | );
|
286 | }
|
287 |
|
288 | result[pluginRuleDefinition.ruleName] = pluginRuleDefinition.rule;
|
289 | });
|
290 |
|
291 | return result;
|
292 | },
|
293 | ({}),
|
294 | );
|
295 |
|
296 | config.pluginFunctions = pluginFunctions;
|
297 |
|
298 | return config;
|
299 | }
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 | function normalizeAllRuleSettings(config) {
|
306 |
|
307 | const normalizedRules = {};
|
308 |
|
309 | if (!config.rules) return config;
|
310 |
|
311 | Object.keys(config.rules).forEach((ruleName) => {
|
312 | const rawRuleSettings = _.get(config, ['rules', ruleName]);
|
313 |
|
314 | const rule = requireRule(ruleName) || _.get(config, ['pluginFunctions', ruleName]);
|
315 |
|
316 | if (!rule) {
|
317 | normalizedRules[ruleName] = [];
|
318 | } else {
|
319 | normalizedRules[ruleName] = normalizeRuleSettings(
|
320 | rawRuleSettings,
|
321 | ruleName,
|
322 | _.get(rule, 'primaryOptionArray'),
|
323 | );
|
324 | }
|
325 | });
|
326 |
|
327 | config.rules = normalizedRules;
|
328 |
|
329 | return config;
|
330 | }
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 | const processorCache = new Map();
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 | function addProcessorFunctions(config) {
|
352 | if (!config.processors) return config;
|
353 |
|
354 |
|
355 | const codeProcessors = [];
|
356 |
|
357 | const resultProcessors = [];
|
358 |
|
359 | ([])
|
360 | .concat(config.processors)
|
361 | .forEach((processorConfig) => {
|
362 | const processorKey = JSON.stringify(processorConfig);
|
363 |
|
364 | let initializedProcessor;
|
365 |
|
366 | if (processorCache.has(processorKey)) {
|
367 | initializedProcessor = processorCache.get(processorKey);
|
368 | } else {
|
369 | const processorLookup =
|
370 | typeof processorConfig === 'string' ? processorConfig : processorConfig[0];
|
371 | const processorOptions =
|
372 | typeof processorConfig === 'string' ? undefined : processorConfig[1];
|
373 | let processor = dynamicRequire(processorLookup);
|
374 |
|
375 | processor = processor.default || processor;
|
376 | initializedProcessor = processor(processorOptions);
|
377 | processorCache.set(processorKey, initializedProcessor);
|
378 | }
|
379 |
|
380 | if (initializedProcessor && initializedProcessor.code) {
|
381 | codeProcessors.push(initializedProcessor.code);
|
382 | }
|
383 |
|
384 | if (initializedProcessor && initializedProcessor.result) {
|
385 | resultProcessors.push(initializedProcessor.result);
|
386 | }
|
387 | });
|
388 |
|
389 | config.codeProcessors = codeProcessors;
|
390 | config.resultProcessors = resultProcessors;
|
391 |
|
392 | return config;
|
393 | }
|
394 |
|
395 | module.exports = { augmentConfigExtended, augmentConfigFull };
|