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");
11 /*:: type postcssResultT = {
14 plugins: Array<Object>,
34 defaultSeverity?: string,
35 ignoreFiles?: Array<string>,
46 postcssPlugin: string,
47 postcssVersion: string,
50 customMessages: Object,
52 ignoreDisables?: boolean,
53 ruleSeverities: Object,
58 /*:: type emptyPostcssResultT = {
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*/,
76 codeFilename?: string, // Must be an absolute file path
77 filePath?: string, // Must be an absolute file path
78 existingPostcssResult?: Object,
80 ) /*: Promise<Object>*/ {
81 options = options || {};
85 options.code === undefined &&
86 !options.existingPostcssResult
88 return Promise.reject(
89 new Error("You must provide filePath, code, or existingPostcssResult")
93 const isCodeNotFile = options.code !== undefined;
95 const inputFilePath = isCodeNotFile ? options.codeFilename : options.filePath;
96 if (inputFilePath !== undefined && !path.isAbsolute(inputFilePath)) {
98 return Promise.reject(new Error("codeFilename must be an absolute path"));
100 return Promise.reject(new Error("filePath must be an absolute path"));
104 const getIsIgnored = stylelint.isPathIgnored(inputFilePath).catch(err => {
105 if (isCodeNotFile && err.code === "ENOENT") return false;
109 return getIsIgnored.then(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;
120 const configSearchPath = stylelint._options.configFile || inputFilePath;
122 const getConfig = stylelint
123 .getConfigForFile(configSearchPath)
125 if (isCodeNotFile && err.code === "ENOENT")
126 return stylelint.getConfigForFile(process.cwd());
130 return getConfig.then(result => {
131 const config /*: stylelint$config*/ = result.config;
132 const existingPostcssResult = options.existingPostcssResult;
134 if (existingPostcssResult) {
135 return lintPostcssResult(stylelint, existingPostcssResult, config).then(
136 () => existingPostcssResult
143 codeFilename: options.codeFilename,
144 filePath: inputFilePath,
145 codeProcessors: config.codeProcessors
147 .then(postcssResult => {
148 return lintPostcssResult(stylelint, postcssResult, config).then(
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;
166 const newlineMatch = postcssResult.root
168 stringifier: postcssResult.opts.syntax
171 const newline = newlineMatch ? newlineMatch[0] : getOsEol();
173 const postcssRoot = postcssResult.root;
174 assignDisabledRanges(postcssRoot, postcssResult);
176 stylelint._options.reportNeedlessDisables ||
177 stylelint._options.ignoreDisables
179 postcssResult.stylelint.ignoreDisables = true;
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 = [];
187 const rules = config.rules ? Object.keys(config.rules) : [];
189 rules.forEach(ruleName => {
191 ruleDefinitions[ruleName] || _.get(config, ["pluginFunctions", ruleName]);
193 if (ruleFunction === undefined) {
194 throw configurationError(`Undefined rule ${ruleName}`);
197 const ruleSettings = _.get(config, ["rules", ruleName]);
198 if (ruleSettings === null || ruleSettings[0] === null) {
202 const primaryOption = ruleSettings[0];
203 const secondaryOptions = ruleSettings[1];
205 // Log the rule's severity in the PostCSS result
206 const defaultSeverity = config.defaultSeverity || "error";
207 postcssResult.stylelint.ruleSeverities[ruleName] = _.get(
212 postcssResult.stylelint.customMessages[ruleName] = _.get(
217 const performRule = Promise.resolve().then(() => {
218 return ruleFunction(primaryOption, secondaryOptions, {
219 fix: stylelint._options.fix,
221 })(postcssRoot, postcssResult);
223 performRules.push(performRule);
226 return Promise.all(performRules);
229 function createEmptyPostcssResult(
230 filePath /*:: ?: string*/
231 ) /*: emptyPostcssResultT*/ {
235 input: { file: filePath }
239 stylelint: { stylelintError: null }