1414 * You should have received a copy of the Sonar Source-Available License
1515 * along with this program; if not, see https://sonarsource.com/license/ssal/
1616 */
17- import type { LinterOptions } from 'stylelint' ;
1817import type { CssAnalysisInput , CssAnalysisOutput } from './analysis.js' ;
1918import { linter } from '../linter/wrapper.js' ;
20- import { createStylelintConfig } from '../linter/config.js' ;
2119import { computeMetrics } from './metrics.js' ;
2220import { computeHighlighting } from './highlighting.js' ;
2321import { APIError } from '../../../shared/src/errors/error.js' ;
2422import {
2523 toProjectFailureResult ,
2624 type ProjectFailureResult ,
2725} from '../../../shared/src/errors/project-analysis.js' ;
28- import { error , warn } from '../../../shared/src/helpers/logging.js' ;
26+ import { warn } from '../../../shared/src/helpers/logging.js' ;
2927import type { CssIssue } from '../linter/issues/issue.js' ;
30- import {
31- shouldIgnoreFile ,
32- type ShouldIgnoreFileParams ,
33- } from '../../../shared/src/helpers/filter/filter.js' ;
34- import { isAlsoCssFile } from '../../../shared/src/helpers/configuration.js' ;
3528
3629/**
3730 * Analyzes a CSS analysis input
@@ -42,73 +35,80 @@ import { isAlsoCssFile } from '../../../shared/src/helpers/configuration.js';
4235 *
4336 * The input must be fully sanitized (all fields required) before calling this function.
4437 *
45- * When `input.rules` is provided (bridge per-request path), a fresh Stylelint
46- * config is created from them. When absent (analyzeProject path), the linter's
47- * pre-initialized config is used instead (see `LinterWrapper.initialize()`).
38+ * Stylelint config comes from the pre-initialized linter
39+ * (`LinterWrapper.initialize()`). For TEST files, rules are overridden to an
40+ * empty config to suppress issues while preserving parsing/highlighting behavior.
41+ * The CSS linter must be initialized before calling this function.
42+ *
43+ * Behavior:
44+ *
45+ * CSS in HTML/Vue MAIN: linted for issues, no CSS metrics.
46+ * CSS in HTML/Vue TEST: not linted (noMetrics + TEST early return), so no CSS issues.
47+ * Pure CSS MAIN: issues + metrics/highlighting.
48+ * Pure CSS TEST: no issues/metrics, highlighting only
4849 *
4950 * @param input the sanitized CSS analysis input to analyze
50- * @param shouldIgnoreParams parameters needed to determine whether a file should be ignored
51+ * @param includeMetrics whether to include metrics calculation
5152 * @returns a promise of the CSS analysis output
5253 */
5354export async function analyzeCSS (
5455 input : CssAnalysisInput ,
55- shouldIgnoreParams : ShouldIgnoreFileParams ,
56+ includeMetrics = true ,
5657) : Promise < CssAnalysisOutput > {
57- const { filePath, fileContent, rules, fileType } = input ;
58- if ( await shouldIgnoreFile ( { filePath, fileContent } , shouldIgnoreParams ) ) {
59- return { issues : [ ] } ;
60- }
58+ const { filePath, fileContent, fileType } = input ;
6159
6260 const isTestFile = fileType === 'TEST' ;
63- const sanitizedCode = fileContent . replaceAll ( / [ \u2000 - \u200F ] / g, ' ' ) ;
64-
65- // If rules are provided explicitly (bridge path), create a fresh config.
66- // Otherwise, use the linter's pre-initialized config (analyzeProject path).
67- const config = rules ? createStylelintConfig ( rules ) : undefined ;
6861
69- const options : LinterOptions = {
70- code : sanitizedCode ,
71- codeFilename : filePath ,
72- ...( config && { config } ) ,
73- } ;
62+ // Mixed-file CSS analysis (HTML/Vue) runs with noMetrics=true. For TEST files,
63+ // preserve legacy behavior by skipping CSS linting entirely.
64+ if ( isTestFile && ! includeMetrics ) {
65+ return { issues : [ ] } ;
66+ }
67+ const sanitizedCode = fileContent . replaceAll ( / [ \u2000 - \u200F ] / g, ' ' ) ;
7468
75- // TEST files only get highlighting (matching old CssMetricSensor behavior).
76- // Old CssRuleSensor never analyzed TEST files for issues, but we still
77- // need to run Stylelint to get the PostCSS root for highlighting.
78- const { issues, root } = await linter . lint ( filePath , options ) . catch ( err => {
79- error ( `Linter failed to parse file ${ filePath } : ${ err } ` ) ;
80- throw APIError . linterError ( `Linter failed to parse file ${ filePath } : ${ err } ` ) ;
81- } ) ;
82- throwIfCssParsingError ( issues ) ;
69+ // TEST files keep highlighting (parity with old CssMetricSensor), but issues remain suppressed.
70+ const lintResult = await linter . lint ( filePath , sanitizedCode , fileType ) ;
71+ const { root, issues : lintingIssues } = lintResult ;
72+ throwIfCssParsingError ( lintingIssues ) ;
73+ const issues = isTestFile ? [ ] : lintingIssues ;
8374
84- // Skip metrics and highlighting in SonarLint mode and for non-pure-CSS files
75+ // Skip metrics and highlighting for non-pure-CSS files
8576 // (HTML/Vue files are handled by their own analyzers for metrics)
86- if ( input . sonarlint || isAlsoCssFile ( filePath ) || ! root ) {
87- return { issues : isTestFile ? [ ] : issues } ;
77+ if ( ! includeMetrics || ! root ) {
78+ return { issues } ;
8879 }
8980
9081 try {
91- const highlights = computeHighlighting ( root , sanitizedCode ) ;
92- if ( isTestFile ) {
93- return { issues : [ ] , highlights } ;
82+ if ( input . sonarlint ) {
83+ const metrics = computeMetrics ( root ) ;
84+ // In SonarLint context, keep only NOSONAR lines (parity with JS/TS and old sensors).
85+ return {
86+ issues,
87+ metrics : { nosonarLines : metrics . nosonarLines } ,
88+ } ;
89+ } else {
90+ const highlights = computeHighlighting ( root , sanitizedCode ) ;
91+ if ( isTestFile ) {
92+ return { issues, highlights } ;
93+ }
94+ return {
95+ issues,
96+ highlights,
97+ metrics : computeMetrics ( root ) ,
98+ } ;
9499 }
95- return {
96- issues,
97- highlights,
98- metrics : computeMetrics ( root ) ,
99- } ;
100100 } catch ( err ) {
101101 warn ( `Failed to compute metrics/highlighting for ${ filePath } : ${ err } ` ) ;
102- return { issues : isTestFile ? [ ] : issues } ;
102+ return { issues } ;
103103 }
104104}
105105
106106export async function analyzeCSSProject (
107107 input : CssAnalysisInput ,
108- shouldIgnoreParams : ShouldIgnoreFileParams ,
108+ includeMetrics = true ,
109109) : Promise < CssAnalysisOutput | ProjectFailureResult > {
110110 try {
111- return await analyzeCSS ( input , shouldIgnoreParams ) ;
111+ return await analyzeCSS ( input , includeMetrics ) ;
112112 } catch ( err ) {
113113 return toProjectFailureResult ( err , 'css' ) ;
114114 }
0 commit comments