| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import { EOL } from 'node:os';
- import { DEFAULT_SEVERITY, RULE_NAME_ALL } from './constants.mjs';
- import { DEFAULT_CONFIGURATION_COMMENT } from './utils/configurationComment.mjs';
- import assignDisabledRanges from './assignDisabledRanges.mjs';
- import { emitDeprecationWarning } from './utils/emitWarning.mjs';
- import { fork } from 'css-tree';
- import getStylelintRule from './utils/getStylelintRule.mjs';
- import reportUnknownRuleNames from './reportUnknownRuleNames.mjs';
- import rules from './rules/index.mjs';
- import timing from './timing.mjs';
- /** @import {Config, LinterOptions, PostcssResult} from 'stylelint' */
- /**
- * @param {LinterOptions} stylelintOptions
- * @param {PostcssResult} postcssResult
- * @param {Config} config
- * @returns {Promise<any>}
- */
- export default async function lintPostcssResult(stylelintOptions, postcssResult, config) {
- postcssResult.stylelint.stylelintError = false;
- postcssResult.stylelint.stylelintWarning = false;
- postcssResult.stylelint.quiet = config.quiet;
- postcssResult.stylelint.quietDeprecationWarnings = stylelintOptions.quietDeprecationWarnings;
- postcssResult.stylelint.config = config;
- const postcssDoc = postcssResult.root;
- if (!('type' in postcssDoc)) {
- throw new Error('Unexpected Postcss root object!');
- }
- const newlineMatch = postcssDoc.source?.input.css.match(/\r?\n/);
- const newline = newlineMatch ? newlineMatch[0] : EOL;
- const configurationComment = config.configurationComment || DEFAULT_CONFIGURATION_COMMENT;
- const ctx = { configurationComment, newline };
- assignDisabledRanges(postcssDoc, postcssResult);
- const postcssRoots = /** @type {import('postcss').Root[]} */ (
- postcssDoc && postcssDoc.constructor.name === 'Document' ? postcssDoc.nodes : [postcssDoc]
- );
- // Promises for the rules. Although the rule code runs synchronously now,
- // the use of Promises makes it compatible with the possibility of async
- // rules down the line.
- /** @type {Array<Promise<any>>} */
- const performRules = [];
- const rulesOrder = Object.keys(rules);
- const ruleNames = config.rules
- ? Object.keys(config.rules).sort((a, b) => rulesOrder.indexOf(a) - rulesOrder.indexOf(b))
- : [];
- for (const ruleName of ruleNames) {
- const ruleFunction = await getStylelintRule(ruleName, config);
- if (ruleFunction === undefined) {
- performRules.push(
- Promise.all(
- postcssRoots.map((postcssRoot) =>
- reportUnknownRuleNames(ruleName, postcssRoot, postcssResult),
- ),
- ),
- );
- continue;
- }
- const ruleSettings = config.rules?.[ruleName];
- if (ruleSettings === null || ruleSettings[0] === null) continue;
- if (ruleFunction.meta?.deprecated && !stylelintOptions.quietDeprecationWarnings) {
- warnDeprecatedRule(postcssResult, ruleName);
- }
- const primaryOption = ruleSettings[0];
- const secondaryOptions = ruleSettings[1];
- // Log the rule's severity in the PostCSS result
- const defaultSeverity = config.defaultSeverity || DEFAULT_SEVERITY;
- postcssResult.stylelint.ruleSeverities[ruleName] =
- (secondaryOptions && secondaryOptions.severity) || defaultSeverity;
- postcssResult.stylelint.customMessages[ruleName] = secondaryOptions && secondaryOptions.message;
- postcssResult.stylelint.customUrls[ruleName] = secondaryOptions && secondaryOptions.url;
- postcssResult.stylelint.ruleMetadata[ruleName] = ruleFunction.meta || {};
- const shouldWarn = ruleFunction.meta?.fixable && !stylelintOptions.quietDeprecationWarnings;
- const disableFix = secondaryOptions?.disableFix === true;
- const fix = !disableFix && config.fix && isFixCompatible(postcssResult, ruleName);
- const lexer = getCachedLexer(config);
- const context = {
- ...ctx,
- lexer,
- // context.fix is unlikely to be removed in the foreseeable future
- // due to the sheer number of rules in the wild that rely on it
- get fix() {
- if (shouldWarn) {
- emitDeprecationWarning(
- '`context.fix` is being deprecated.',
- 'CONTEXT_FIX',
- `Please pass a \`fix\` callback to the \`report\` utility of "${ruleName}" instead.`,
- );
- }
- return fix;
- },
- };
- const ruleFn = ruleFunction(primaryOption, secondaryOptions, context);
- /**
- * @param {import('postcss').Root} postcssRoot
- */
- async function runRule(postcssRoot) {
- if (timing.enabled) {
- return timing.time(ruleName, () => ruleFn(postcssRoot, postcssResult))();
- }
- return ruleFn(postcssRoot, postcssResult);
- }
- performRules.push(Promise.all(postcssRoots.map(runRule)));
- }
- return Promise.all(performRules);
- }
- /**
- * using context.fix instead of the fix callback has the drawback
- * of not honouring the configuration comments in subtle ways
- * @see file://./../docs/user-guide/options.md#fix for details
- * @param {PostcssResult} postcssResult
- * @param {string} name
- * @returns {boolean}
- */
- function isFixCompatible({ stylelint: { disabledRanges } }, name) {
- return !disabledRanges[RULE_NAME_ALL]?.length && !disabledRanges[name];
- }
- /**
- * @param {PostcssResult} result
- * @param {string} ruleName
- * @returns {void}
- */
- function warnDeprecatedRule(result, ruleName) {
- const message = `The "${ruleName}" rule is deprecated.`;
- emitDeprecationWarning(
- message,
- 'RULE',
- `Please be aware that the "${ruleName}" rule will soon be either removed or renamed.`,
- );
- result.warn(message, { stylelintType: 'deprecation' });
- }
- const lexerCache = new Map();
- /**
- * @param {Config} config
- * @returns {import('css-tree').Lexer}
- * */
- function getCachedLexer(config) {
- const cacheKey = JSON.stringify(config.languageOptions?.syntax || {});
- if (lexerCache.has(cacheKey)) {
- return lexerCache.get(cacheKey);
- }
- const newLexer = fork({
- atrules: config.languageOptions?.syntax?.atRules || {},
- properties: config.languageOptions?.syntax?.properties || {},
- types: config.languageOptions?.syntax?.types || {},
- cssWideKeywords: config.languageOptions?.syntax?.cssWideKeywords || [],
- }).lexer;
- lexerCache.set(cacheKey, newLexer);
- return newLexer;
- }
|