1 |
|
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 requireRule = require("./requireRule");
|
10 | const rulesOrder = require("./rules");
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | module.exports = function lintSource(
|
74 | stylelint /*: stylelint$internalApi*/,
|
75 | options /*: {
|
76 | code?: string,
|
77 | codeFilename?: string,
|
78 | filePath?: string,
|
79 | existingPostcssResult?: Object,
|
80 | }*/
|
81 | ) {
|
82 | options = options || {};
|
83 |
|
84 | if (
|
85 | !options.filePath &&
|
86 | options.code === undefined &&
|
87 | !options.existingPostcssResult
|
88 | ) {
|
89 | return Promise.reject(
|
90 | new Error("You must provide filePath, code, or existingPostcssResult")
|
91 | );
|
92 | }
|
93 |
|
94 | const isCodeNotFile = options.code !== undefined;
|
95 |
|
96 | const inputFilePath = isCodeNotFile ? options.codeFilename : options.filePath;
|
97 |
|
98 | if (inputFilePath !== undefined && !path.isAbsolute(inputFilePath)) {
|
99 | if (isCodeNotFile) {
|
100 | return Promise.reject(new Error("codeFilename must be an absolute path"));
|
101 | } else {
|
102 | return Promise.reject(new Error("filePath must be an absolute path"));
|
103 | }
|
104 | }
|
105 |
|
106 | const getIsIgnored = stylelint.isPathIgnored(inputFilePath).catch(err => {
|
107 | if (isCodeNotFile && err.code === "ENOENT") return false;
|
108 |
|
109 | throw err;
|
110 | });
|
111 |
|
112 | return getIsIgnored.then(isIgnored => {
|
113 | if (isIgnored) {
|
114 | const postcssResult =
|
115 | options.existingPostcssResult ||
|
116 | createEmptyPostcssResult(inputFilePath);
|
117 |
|
118 | postcssResult.stylelint = postcssResult.stylelint || {};
|
119 | postcssResult.stylelint.ignored = true;
|
120 | postcssResult.standaloneIgnored = true;
|
121 |
|
122 | return postcssResult;
|
123 | }
|
124 |
|
125 | const configSearchPath = stylelint._options.configFile || inputFilePath;
|
126 |
|
127 | const getConfig = stylelint
|
128 | .getConfigForFile(configSearchPath)
|
129 | .catch(err => {
|
130 | if (isCodeNotFile && err.code === "ENOENT")
|
131 | return stylelint.getConfigForFile(process.cwd());
|
132 |
|
133 | throw err;
|
134 | });
|
135 |
|
136 | return getConfig.then(result => {
|
137 | const config = result.config;
|
138 | const existingPostcssResult = options.existingPostcssResult;
|
139 |
|
140 | if (existingPostcssResult) {
|
141 | return lintPostcssResult(stylelint, existingPostcssResult, config).then(
|
142 | () => existingPostcssResult
|
143 | );
|
144 | }
|
145 |
|
146 | return stylelint
|
147 | ._getPostcssResult({
|
148 | code: options.code,
|
149 | codeFilename: options.codeFilename,
|
150 | filePath: inputFilePath,
|
151 | codeProcessors: config.codeProcessors
|
152 | })
|
153 | .then(postcssResult => {
|
154 | return lintPostcssResult(stylelint, postcssResult, config).then(
|
155 | () => postcssResult
|
156 | );
|
157 | });
|
158 | });
|
159 | });
|
160 | };
|
161 |
|
162 | function lintPostcssResult(
|
163 | stylelint /*: stylelint$internalApi*/,
|
164 | postcssResult /*: postcssResultT*/,
|
165 | config /*: stylelint$config*/
|
166 | ) /*: Promise<Array<*>>*/ {
|
167 | postcssResult.stylelint = postcssResult.stylelint || {};
|
168 | postcssResult.stylelint.ruleSeverities = {};
|
169 | postcssResult.stylelint.customMessages = {};
|
170 | postcssResult.stylelint.quiet = config.quiet;
|
171 |
|
172 | const postcssDoc = postcssResult.root;
|
173 | const newlineMatch = postcssDoc.source.input.css.match(/\r?\n/);
|
174 | const newline = newlineMatch ? newlineMatch[0] : getOsEol();
|
175 |
|
176 | assignDisabledRanges(postcssDoc, postcssResult);
|
177 |
|
178 | if (
|
179 | stylelint._options.reportNeedlessDisables ||
|
180 | stylelint._options.ignoreDisables
|
181 | ) {
|
182 | postcssResult.stylelint.ignoreDisables = true;
|
183 | }
|
184 |
|
185 | const postcssRoots =
|
186 | postcssDoc.constructor.name === "Document"
|
187 | ? postcssDoc.nodes
|
188 | : [postcssDoc];
|
189 |
|
190 |
|
191 |
|
192 |
|
193 | const performRules = [];
|
194 |
|
195 | const rules = config.rules
|
196 | ? Object.keys(config.rules).sort(
|
197 | (a, b) => rulesOrder.indexOf(a) - rulesOrder.indexOf(b)
|
198 | )
|
199 | : [];
|
200 |
|
201 | rules.forEach(ruleName => {
|
202 | const ruleFunction =
|
203 | requireRule(ruleName) || _.get(config, ["pluginFunctions", ruleName]);
|
204 |
|
205 | if (ruleFunction === undefined) {
|
206 | throw configurationError(`Undefined rule ${ruleName}`);
|
207 | }
|
208 |
|
209 | const ruleSettings = _.get(config, ["rules", ruleName]);
|
210 |
|
211 | if (ruleSettings === null || ruleSettings[0] === null) {
|
212 | return;
|
213 | }
|
214 |
|
215 | const primaryOption = ruleSettings[0];
|
216 | const secondaryOptions = ruleSettings[1];
|
217 |
|
218 |
|
219 | const defaultSeverity = config.defaultSeverity || "error";
|
220 |
|
221 | postcssResult.stylelint.ruleSeverities[ruleName] = _.get(
|
222 | secondaryOptions,
|
223 | "severity",
|
224 | defaultSeverity
|
225 | );
|
226 | postcssResult.stylelint.customMessages[ruleName] = _.get(
|
227 | secondaryOptions,
|
228 | "message"
|
229 | );
|
230 |
|
231 | performRules.push(
|
232 | Promise.all(
|
233 | postcssRoots.map(postcssRoot =>
|
234 | ruleFunction(primaryOption, secondaryOptions, {
|
235 | fix: stylelint._options.fix,
|
236 | newline
|
237 | })(postcssRoot, postcssResult)
|
238 | )
|
239 | )
|
240 | );
|
241 | });
|
242 |
|
243 | return Promise.all(performRules);
|
244 | }
|
245 |
|
246 | function createEmptyPostcssResult(
|
247 | filePath /*:: ?: string*/
|
248 | ) /*: emptyPostcssResultT*/ {
|
249 | return {
|
250 | root: {
|
251 | source: {
|
252 | input: { file: filePath }
|
253 | }
|
254 | },
|
255 | messages: [],
|
256 | stylelint: { stylelintError: null }
|
257 | };
|
258 | }
|