diff --git a/lib/linter/linter.js b/lib/linter/linter.js index 7b2d96b41e0a..78d58dc6e59e 100644 --- a/lib/linter/linter.js +++ b/lib/linter/linter.js @@ -595,22 +595,24 @@ function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper, language) { const disableDirectives = []; const problems = []; - const { - directives: directivesSources, - problems: directivesProblems - } = sourceCode.getDisableDirectives(); - - problems.push(...directivesProblems.map(directiveProblem => createLintingProblem({ - ...directiveProblem, - language - }))); - - directivesSources.forEach(directive => { - const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper); - - disableDirectives.push(...directives); - problems.push(...directiveProblems); - }); + if (sourceCode.getDisableDirectives) { + const { + directives: directivesSources, + problems: directivesProblems + } = sourceCode.getDisableDirectives(); + + problems.push(...directivesProblems.map(directiveProblem => createLintingProblem({ + ...directiveProblem, + language + }))); + + directivesSources.forEach(directive => { + const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper); + + disableDirectives.push(...directives); + problems.push(...directiveProblems); + }); + } return { problems, @@ -1131,7 +1133,7 @@ function runRules( const eventGenerator = new NodeEventGenerator(emitter, { visitorKeys: sourceCode.visitorKeys, fallback: Traverser.getKeys, - matchClass: language.matchesSelectorClass, + matchClass: language.matchesSelectorClass ?? (() => false), nodeTypeKey: language.nodeTypeKey }); @@ -1706,7 +1708,7 @@ class Linter { * this is primarily about adding variables into the global scope * to account for ecmaVersion and configured globals. */ - sourceCode.applyLanguageOptions(languageOptions); + sourceCode.applyLanguageOptions?.(languageOptions); const mergedInlineConfig = { rules: {} @@ -1723,147 +1725,151 @@ class Linter { // if inline config should warn then add the warnings if (options.warnInlineConfig) { - sourceCode.getInlineConfigNodes().forEach(node => { - inlineConfigProblems.push(createLintingProblem({ - ruleId: null, - message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`, - loc: node.loc, - severity: 1, - language: config.language - })); + if (sourceCode.getInlineConfigNodes) { + sourceCode.getInlineConfigNodes().forEach(node => { + inlineConfigProblems.push(createLintingProblem({ + ruleId: null, + message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`, + loc: node.loc, + severity: 1, + language: config.language + })); - }); + }); + } } else { - const inlineConfigResult = sourceCode.applyInlineConfig(); - - inlineConfigProblems.push( - ...inlineConfigResult.problems - .map(problem => createLintingProblem({ ...problem, language: config.language })) - .map(problem => { - problem.fatal = true; - return problem; - }) - ); + const inlineConfigResult = sourceCode.applyInlineConfig?.(); + + if (inlineConfigResult) { + inlineConfigProblems.push( + ...inlineConfigResult.problems + .map(problem => createLintingProblem({ ...problem, language: config.language })) + .map(problem => { + problem.fatal = true; + return problem; + }) + ); + + // next we need to verify information about the specified rules + const ruleValidator = new RuleValidator(); + + for (const { config: inlineConfig, node } of inlineConfigResult.configs) { + + Object.keys(inlineConfig.rules).forEach(ruleId => { + const rule = getRuleFromConfig(ruleId, config); + const ruleValue = inlineConfig.rules[ruleId]; + + if (!rule) { + inlineConfigProblems.push(createLintingProblem({ + ruleId, + loc: node.loc, + language: config.language + })); + return; + } - // next we need to verify information about the specified rules - const ruleValidator = new RuleValidator(); + if (Object.hasOwn(mergedInlineConfig.rules, ruleId)) { + inlineConfigProblems.push(createLintingProblem({ + message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`, + loc: node.loc, + language: config.language + })); + return; + } - for (const { config: inlineConfig, node } of inlineConfigResult.configs) { + try { - Object.keys(inlineConfig.rules).forEach(ruleId => { - const rule = getRuleFromConfig(ruleId, config); - const ruleValue = inlineConfig.rules[ruleId]; + let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue]; - if (!rule) { - inlineConfigProblems.push(createLintingProblem({ - ruleId, - loc: node.loc, - language: config.language - })); - return; - } + assertIsRuleSeverity(ruleId, ruleOptions[0]); - if (Object.hasOwn(mergedInlineConfig.rules, ruleId)) { - inlineConfigProblems.push(createLintingProblem({ - message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`, - loc: node.loc, - language: config.language - })); - return; - } + /* + * If the rule was already configured, inline rule configuration that + * only has severity should retain options from the config and just override the severity. + * + * Example: + * + * { + * rules: { + * curly: ["error", "multi"] + * } + * } + * + * /* eslint curly: ["warn"] * / + * + * Results in: + * + * curly: ["warn", "multi"] + */ - try { + let shouldValidateOptions = true; - let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue]; + if ( - assertIsRuleSeverity(ruleId, ruleOptions[0]); + /* + * If inline config for the rule has only severity + */ + ruleOptions.length === 1 && - /* - * If the rule was already configured, inline rule configuration that - * only has severity should retain options from the config and just override the severity. - * - * Example: - * - * { - * rules: { - * curly: ["error", "multi"] - * } - * } - * - * /* eslint curly: ["warn"] * / - * - * Results in: - * - * curly: ["warn", "multi"] - */ + /* + * And the rule was already configured + */ + config.rules && Object.hasOwn(config.rules, ruleId) + ) { - let shouldValidateOptions = true; + /* + * Then use severity from the inline config and options from the provided config + */ + ruleOptions = [ + ruleOptions[0], // severity from the inline config + ...config.rules[ruleId].slice(1) // options from the provided config + ]; - if ( + // if the rule was enabled, the options have already been validated + if (config.rules[ruleId][0] > 0) { + shouldValidateOptions = false; + } + } - /* - * If inline config for the rule has only severity - */ - ruleOptions.length === 1 && + if (shouldValidateOptions) { + ruleValidator.validate({ + plugins: config.plugins, + rules: { + [ruleId]: ruleOptions + } + }); + } - /* - * And the rule was already configured - */ - config.rules && Object.hasOwn(config.rules, ruleId) - ) { + mergedInlineConfig.rules[ruleId] = ruleOptions; + } catch (err) { /* - * Then use severity from the inline config and options from the provided config + * If the rule has invalid `meta.schema`, throw the error because + * this is not an invalid inline configuration but an invalid rule. */ - ruleOptions = [ - ruleOptions[0], // severity from the inline config - ...config.rules[ruleId].slice(1) // options from the provided config - ]; - - // if the rule was enabled, the options have already been validated - if (config.rules[ruleId][0] > 0) { - shouldValidateOptions = false; + if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") { + throw err; } - } - - if (shouldValidateOptions) { - ruleValidator.validate({ - plugins: config.plugins, - rules: { - [ruleId]: ruleOptions - } - }); - } - - mergedInlineConfig.rules[ruleId] = ruleOptions; - } catch (err) { - /* - * If the rule has invalid `meta.schema`, throw the error because - * this is not an invalid inline configuration but an invalid rule. - */ - if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") { - throw err; - } + let baseMessage = err.message.slice( + err.message.startsWith("Key \"rules\":") + ? err.message.indexOf(":", 12) + 1 + : err.message.indexOf(":") + 1 + ).trim(); - let baseMessage = err.message.slice( - err.message.startsWith("Key \"rules\":") - ? err.message.indexOf(":", 12) + 1 - : err.message.indexOf(":") + 1 - ).trim(); + if (err.messageTemplate) { + baseMessage += ` You passed "${ruleValue}".`; + } - if (err.messageTemplate) { - baseMessage += ` You passed "${ruleValue}".`; + inlineConfigProblems.push(createLintingProblem({ + ruleId, + message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`, + loc: node.loc, + language: config.language + })); } - - inlineConfigProblems.push(createLintingProblem({ - ruleId, - message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`, - loc: node.loc, - language: config.language - })); - } - }); + }); + } } } } @@ -1880,7 +1886,7 @@ class Linter { let lintingProblems; - sourceCode.finalize(); + sourceCode.finalize?.(); try { lintingProblems = runRules(