.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / lintSource.js
1 /* @flow */
2 "use strict";
3
4 const _ = require("lodash");
5 const assignDisabledRanges = require("./assignDisabledRanges");
6 const configurationError = require("./utils/configurationError");
7 const getOsEol = require("./utils/getOsEol");
8 const path = require("path");
9 const ruleDefinitions = require("./rules");
10
11 /*:: type postcssResultT = {
12   processor: {
13     version: string,
14     plugins: Array<Object>,
15   },
16   messages: Array<any>,
17   root: {
18     raws: {
19       semicolon?: boolean,
20       after: string,
21     },
22     type: string,
23     nodes: Array<Object>,
24     source: {
25       input: Object,
26       start: Object,
27     },
28     lastEach?: number,
29     indexes?: Object,
30     toResult: Function,
31   },
32   opts: {
33     configFile?: string,
34     defaultSeverity?: string,
35     ignoreFiles?: Array<string>,
36     rules?: Object,
37     from?: ?string,
38     syntax?: ?{
39       parse: Function,
40       stringify: Function,
41     },
42   },
43   css: ?string,
44   map: ?any,
45   lastPlugin?: {
46     postcssPlugin: string,
47     postcssVersion: string,
48   },
49   stylelint: {
50     customMessages: Object,
51     ignored?: boolean,
52     ignoreDisables?: boolean,
53     ruleSeverities: Object,
54     quiet?: boolean,
55   },
56 }*/
57
58 /*:: type emptyPostcssResultT = {
59   root: {
60     source: {
61       input: {
62         file: ?string,
63       },
64     },
65   },
66   messages: Array<any>,
67   stylelint: Object,
68 }*/
69
70 // Run stylelint on a PostCSS Result, either one that is provided
71 // or one that we create
72 module.exports = function lintSource(
73   stylelint /*: stylelint$internalApi*/,
74   options /*: {
75     code?: string,
76     codeFilename?: string, // Must be an absolute file path
77     filePath?: string, // Must be an absolute file path
78     existingPostcssResult?: Object,
79   }*/
80 ) /*: Promise<Object>*/ {
81   options = options || {};
82
83   if (
84     !options.filePath &&
85     options.code === undefined &&
86     !options.existingPostcssResult
87   ) {
88     return Promise.reject(
89       new Error("You must provide filePath, code, or existingPostcssResult")
90     );
91   }
92
93   const isCodeNotFile = options.code !== undefined;
94
95   const inputFilePath = isCodeNotFile ? options.codeFilename : options.filePath;
96   if (inputFilePath !== undefined && !path.isAbsolute(inputFilePath)) {
97     if (isCodeNotFile) {
98       return Promise.reject(new Error("codeFilename must be an absolute path"));
99     } else {
100       return Promise.reject(new Error("filePath must be an absolute path"));
101     }
102   }
103
104   const getIsIgnored = stylelint.isPathIgnored(inputFilePath).catch(err => {
105     if (isCodeNotFile && err.code === "ENOENT") return false;
106     throw err;
107   });
108
109   return getIsIgnored.then(isIgnored => {
110     if (isIgnored) {
111       const postcssResult /*: Object*/ =
112         options.existingPostcssResult ||
113         createEmptyPostcssResult(inputFilePath);
114       postcssResult.stylelint = postcssResult.stylelint || {};
115       postcssResult.stylelint.ignored = true;
116       postcssResult.standaloneIgnored = true; // TODO: remove need for this
117       return postcssResult;
118     }
119
120     const configSearchPath = stylelint._options.configFile || inputFilePath;
121
122     const getConfig = stylelint
123       .getConfigForFile(configSearchPath)
124       .catch(err => {
125         if (isCodeNotFile && err.code === "ENOENT")
126           return stylelint.getConfigForFile(process.cwd());
127         throw err;
128       });
129
130     return getConfig.then(result => {
131       const config /*: stylelint$config*/ = result.config;
132       const existingPostcssResult = options.existingPostcssResult;
133
134       if (existingPostcssResult) {
135         return lintPostcssResult(stylelint, existingPostcssResult, config).then(
136           () => existingPostcssResult
137         );
138       }
139
140       return stylelint
141         ._getPostcssResult({
142           code: options.code,
143           codeFilename: options.codeFilename,
144           filePath: inputFilePath,
145           codeProcessors: config.codeProcessors
146         })
147         .then(postcssResult => {
148           return lintPostcssResult(stylelint, postcssResult, config).then(
149             () => postcssResult
150           );
151         });
152     });
153   });
154 };
155
156 function lintPostcssResult(
157   stylelint /*: stylelint$internalApi*/,
158   postcssResult /*: postcssResultT*/,
159   config /*: stylelint$config*/
160 ) /*: Promise<Array<*>>*/ {
161   postcssResult.stylelint = postcssResult.stylelint || {};
162   postcssResult.stylelint.ruleSeverities = {};
163   postcssResult.stylelint.customMessages = {};
164   postcssResult.stylelint.quiet = config.quiet;
165
166   const newlineMatch = postcssResult.root
167     .toResult({
168       stringifier: postcssResult.opts.syntax
169     })
170     .css.match(/\r?\n/);
171   const newline = newlineMatch ? newlineMatch[0] : getOsEol();
172
173   const postcssRoot = postcssResult.root;
174   assignDisabledRanges(postcssRoot, postcssResult);
175   if (
176     stylelint._options.reportNeedlessDisables ||
177     stylelint._options.ignoreDisables
178   ) {
179     postcssResult.stylelint.ignoreDisables = true;
180   }
181
182   // Promises for the rules. Although the rule code runs synchronously now,
183   // the use of Promises makes it compatible with the possibility of async
184   // rules down the line.
185   const performRules = [];
186
187   const rules = config.rules ? Object.keys(config.rules) : [];
188
189   rules.forEach(ruleName => {
190     const ruleFunction =
191       ruleDefinitions[ruleName] || _.get(config, ["pluginFunctions", ruleName]);
192
193     if (ruleFunction === undefined) {
194       throw configurationError(`Undefined rule ${ruleName}`);
195     }
196
197     const ruleSettings = _.get(config, ["rules", ruleName]);
198     if (ruleSettings === null || ruleSettings[0] === null) {
199       return;
200     }
201
202     const primaryOption = ruleSettings[0];
203     const secondaryOptions = ruleSettings[1];
204
205     // Log the rule's severity in the PostCSS result
206     const defaultSeverity = config.defaultSeverity || "error";
207     postcssResult.stylelint.ruleSeverities[ruleName] = _.get(
208       secondaryOptions,
209       "severity",
210       defaultSeverity
211     );
212     postcssResult.stylelint.customMessages[ruleName] = _.get(
213       secondaryOptions,
214       "message"
215     );
216
217     const performRule = Promise.resolve().then(() => {
218       return ruleFunction(primaryOption, secondaryOptions, {
219         fix: stylelint._options.fix,
220         newline
221       })(postcssRoot, postcssResult);
222     });
223     performRules.push(performRule);
224   });
225
226   return Promise.all(performRules);
227 }
228
229 function createEmptyPostcssResult(
230   filePath /*:: ?: string*/
231 ) /*: emptyPostcssResultT*/ {
232   return {
233     root: {
234       source: {
235         input: { file: filePath }
236       }
237     },
238     messages: [],
239     stylelint: { stylelintError: null }
240   };
241 }