diff --git a/code-pushup.config.ts b/code-pushup.config.ts index 1b95aca77..5437fc60f 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -37,7 +37,8 @@ export default mergeConfigs( { plugins: [ await stylelintPlugin({ - configFile: 'packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json', + configFile: + 'packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json', files: 'packages/plugin-stylelint/mocks/fixtures/basic/**/*.css', // Adjust the path to your CSS files }), ], diff --git a/packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json b/packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json index 02b59d06a..9c791327a 100644 --- a/packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json +++ b/packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json @@ -1,6 +1,6 @@ { "extends": "stylelint-config-standard", "rules": { - "block-no-empty": null + "block-no-empty": "impossibleValue" } } diff --git a/packages/plugin-stylelint/src/lib/runner/index.ts b/packages/plugin-stylelint/src/lib/runner/index.ts index 7703ece47..3898557e7 100644 --- a/packages/plugin-stylelint/src/lib/runner/index.ts +++ b/packages/plugin-stylelint/src/lib/runner/index.ts @@ -1,11 +1,14 @@ import { type LinterOptions } from 'stylelint'; -import type { RunnerFunction } from '@code-pushup/models'; +import type { Audit, RunnerFunction } from '@code-pushup/models'; import { lintStyles } from './stylelint-runner.js'; import { mapStylelintResultsToAudits } from './utils.js'; -export function createRunnerFunction(opt: LinterOptions): RunnerFunction { +export function createRunnerFunction( + opt: LinterOptions, + expectedAudits: Audit[], +): RunnerFunction { return async () => { const report = await lintStyles(opt); - return mapStylelintResultsToAudits(report); + return mapStylelintResultsToAudits(report, expectedAudits); }; } diff --git a/packages/plugin-stylelint/src/lib/runner/model.ts b/packages/plugin-stylelint/src/lib/runner/model.ts new file mode 100644 index 000000000..c472f13ab --- /dev/null +++ b/packages/plugin-stylelint/src/lib/runner/model.ts @@ -0,0 +1,3 @@ +export type NormalizedStyleLintConfig = { + config: { rules: Record }; +}; diff --git a/packages/plugin-stylelint/src/lib/runner/normalize-config.integration.test.ts b/packages/plugin-stylelint/src/lib/runner/normalize-config.integration.test.ts new file mode 100644 index 000000000..fbd7c35bf --- /dev/null +++ b/packages/plugin-stylelint/src/lib/runner/normalize-config.integration.test.ts @@ -0,0 +1,28 @@ +import path from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { getNormalizedConfigForFile } from './normalize-config'; + +describe('getNormalizedConfigForFile', () => { + it('should load config from specified configFile and respect the value of specifically set rules', async () => { + const configFile = path.join( + process.cwd(), + 'packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json', + ); + + const parsed = await getNormalizedConfigForFile({ configFile }); + + expect(parsed.config.rules['block-no-empty']).toStrictEqual([ + 'impossibleValue', + ]); // The default value is [ true ], so having the not even valid value from the config file is correct + }); + + it('should load config from specified configFile and add default rules', async () => { + const configFile = path.join( + process.cwd(), + 'packages/plugin-stylelint/mocks/fixtures/basic/.stylelintrc.json', + ); + + const parsed = await getNormalizedConfigForFile({ configFile }); + expect(Object.keys(parsed.config.rules).length).toBeGreaterThan(1); + }); +}); diff --git a/packages/plugin-stylelint/src/lib/runner/normalize-config.ts b/packages/plugin-stylelint/src/lib/runner/normalize-config.ts index 793101b8d..f36b1461f 100644 --- a/packages/plugin-stylelint/src/lib/runner/normalize-config.ts +++ b/packages/plugin-stylelint/src/lib/runner/normalize-config.ts @@ -1,10 +1,14 @@ -import stylelint, {getConfigForFile, type LinterOptions} from "stylelint"; -import path from "node:path"; -import * as process from "node:process"; +import path from 'node:path'; +import * as process from 'node:process'; +import stylelint, { type LinterOptions, getConfigForFile } from 'stylelint'; +import type { NormalizedStyleLintConfig } from './model.js'; -export function getNormalizedConfigForFile(options: LinterOptions) { +export function getNormalizedConfigForFile( + options: LinterOptions, +): NormalizedStyleLintConfig { const _linter = stylelint._createLinter(options); - const configFile = options.configFile ?? path.join(options?.cwd ?? process.cwd(), '.stylelintrc.json'); + const configFile = + options.configFile ?? + path.join(options?.cwd ?? process.cwd(), '.stylelintrc.json'); return getConfigForFile(_linter, configFile); } - diff --git a/packages/plugin-stylelint/src/lib/runner/stylelint-runner.ts b/packages/plugin-stylelint/src/lib/runner/stylelint-runner.ts index 255e6d2eb..ecf85d373 100644 --- a/packages/plugin-stylelint/src/lib/runner/stylelint-runner.ts +++ b/packages/plugin-stylelint/src/lib/runner/stylelint-runner.ts @@ -1,7 +1,10 @@ import stylelint, { type LinterOptions } from 'stylelint'; // Run Stylelint Programmatically -export async function lintStyles({config, ...options}: Omit) { +export async function lintStyles({ + config, + ...options +}: Omit) { try { // eslint-disable-next-line functional/immutable-data globalThis.console.assert = globalThis.console.assert || (() => {}); diff --git a/packages/plugin-stylelint/src/lib/runner/utils.ts b/packages/plugin-stylelint/src/lib/runner/utils.ts index 22a9ea256..a9a2fe5b7 100644 --- a/packages/plugin-stylelint/src/lib/runner/utils.ts +++ b/packages/plugin-stylelint/src/lib/runner/utils.ts @@ -1,7 +1,21 @@ import stylelint, { type LintResult } from 'stylelint'; import type { Audit, AuditReport } from '@code-pushup/models'; -export function mapStylelintResultsToAudits(results: LintResult[]): Audit[] { +export function mapStylelintResultsToAudits( + results: LintResult[], + expectedAudits: Audit[], +): Audit[] { + const initialAuditMap = expectedAudits.reduce((map, audit) => { + return map.set(audit.slug, { + ...audit, + score: 1, + value: 0, + details: { + issues: [], + }, + }); + }, new Map()); + const auditMap = results.reduce((map, result) => { const { source, warnings } = result; @@ -12,15 +26,10 @@ export function mapStylelintResultsToAudits(results: LintResult[]): Audit[] { return warnings.reduce((innerMap, warning) => { const { rule, severity, line, text } = warning; - const existingAudit: AuditReport = innerMap.get(rule) || { - slug: rule, - title: '', - score: 1, - value: 0, - details: { - issues: [], - }, - }; + const existingAudit = innerMap.get(rule); + if (!existingAudit) { + return innerMap; + } const updatedAudit: AuditReport = { ...existingAudit, @@ -28,7 +37,7 @@ export function mapStylelintResultsToAudits(results: LintResult[]): Audit[] { value: existingAudit.value + 1, details: { issues: [ - ...(existingAudit?.details?.issues ?? []), + ...(existingAudit.details?.issues ?? []), { severity, message: text, @@ -43,9 +52,11 @@ export function mapStylelintResultsToAudits(results: LintResult[]): Audit[] { }, }; - return new Map(innerMap).set(rule, updatedAudit); + return innerMap.set(rule, updatedAudit); }, map); - }, new Map()); + }, initialAuditMap); + + console.log('auditMap: ', auditMap); return [...auditMap.values()]; } diff --git a/packages/plugin-stylelint/src/lib/stylelint-plugin.ts b/packages/plugin-stylelint/src/lib/stylelint-plugin.ts index 3a67cb8a0..b7dd82a61 100644 --- a/packages/plugin-stylelint/src/lib/stylelint-plugin.ts +++ b/packages/plugin-stylelint/src/lib/stylelint-plugin.ts @@ -1,13 +1,15 @@ -import {createRequire} from 'node:module'; -import type {LinterOptions} from 'stylelint'; -import type {Audit, PluginConfig} from '@code-pushup/models'; -import {createRunnerFunction} from './runner/index.js'; -import {getNormalizedConfigForFile} from "./runner/normalize-config.js"; +import { createRequire } from 'node:module'; +import type { LinterOptions } from 'stylelint'; +import type { PluginConfig } from '@code-pushup/models'; +import { createRunnerFunction } from './runner/index.js'; +import { getAudits } from './utils.js'; - -export type StylelintPluginConfig = Pick & { - onlyAudits?: string[] -} +export type StylelintPluginConfig = Pick< + LinterOptions, + 'configFile' | 'files' +> & { + onlyAudits?: string[]; +}; /** * Instantiates Code PushUp code stylelint plugin for core config. @@ -36,7 +38,7 @@ export async function stylelintPlugin( '../../package.json', ) as typeof import('../../package.json'); - console.log('getNormalizedConfigForFile: ', await getNormalizedConfigForFile(options ?? {})); + const audits = await getAudits(options ?? {}); return { slug: 'stylelint', @@ -46,23 +48,17 @@ export async function stylelintPlugin( docsUrl: 'https://www.npmjs.com/package/@code-pushup/stylelint-plugin/', packageName: packageJson.name, version: packageJson.version, - audits: Object.keys(options?.config?.rules ?? { - 'color-no-invalid-hex': true, - }).map(slug => ({ - slug, - title: slug, - docsUrl: `https://stylelint.io/user-guide/rules/${slug}`, - })), - runner: createRunnerFunction(options ?? {}), + audits, + runner: createRunnerFunction(options ?? {}, audits), }; } -async function getAudits(options: StylelintPluginConfig): Promise { - const {onlyAudits = [], ...rawCfg} = options; - const config = await getNormalizedConfigForFile(rawCfg); - return Object.keys(config.rules).filter(rule => onlyAudits.length > 0 && config.rules[rule] !== false).map(rule => ({ - slug: rule, - title: rule, - docsUrl: `https://stylelint.io/user-guide/rules/${rule}`, - })); -} +// async function getAudits(options: StylelintPluginConfig): Promise { +// const {onlyAudits = [], ...rawCfg} = options; +// const config = await getNormalizedConfigForFile(rawCfg); +// return Object.keys(config.rules).filter(rule => onlyAudits.length > 0 && config.rules[rule] !== false).map(rule => ({ +// slug: rule, +// title: rule, +// docsUrl: `https://stylelint.io/user-guide/rules/${rule}`, +// })); +// } diff --git a/packages/plugin-stylelint/src/lib/utils.ts b/packages/plugin-stylelint/src/lib/utils.ts index fd8bab7c1..c284067bd 100644 --- a/packages/plugin-stylelint/src/lib/utils.ts +++ b/packages/plugin-stylelint/src/lib/utils.ts @@ -1,6 +1,8 @@ import type { Config, ConfigRuleSettings } from 'stylelint'; import type { Audit, Group } from '@code-pushup/models'; import { truncateDescription, truncateTitle } from '@code-pushup/utils'; +import { getNormalizedConfigForFile } from './runner/normalize-config'; +import type { StylelintPluginConfig } from './stylelint-plugin'; export async function listAuditsAndGroups( rules: Config['rules'] = [], @@ -35,3 +37,18 @@ export function ruleToAudit(rule: ConfigRuleSettings): Audit { }), }; } + +export function auditSlugToFullAudit(slug: string): Audit { + return { + slug, + title: slug, + docsUrl: `https://stylelint.io/user-guide/rules/${slug}`, + }; +} + +export async function getAudits( + options: StylelintPluginConfig, +): Promise { + const normalizedConfig = await getNormalizedConfigForFile(options); + return Object.keys(normalizedConfig.config.rules).map(auditSlugToFullAudit); +} diff --git a/packages/plugin-stylelint/src/scripts/postinstall/bin.ts b/packages/plugin-stylelint/src/scripts/postinstall/bin.ts index 08676f6eb..e19d396bb 100644 --- a/packages/plugin-stylelint/src/scripts/postinstall/bin.ts +++ b/packages/plugin-stylelint/src/scripts/postinstall/bin.ts @@ -1,6 +1,6 @@ -import {patchStylelint} from "./index.js"; +import { patchStylelint } from './index.js'; (async () => { await patchStylelint(); - console.log("stylelint patched!"); -})() + console.log('stylelint patched!'); +})(); diff --git a/packages/plugin-stylelint/src/scripts/postinstall/index.ts b/packages/plugin-stylelint/src/scripts/postinstall/index.ts index dd76d3160..2e52b77a0 100644 --- a/packages/plugin-stylelint/src/scripts/postinstall/index.ts +++ b/packages/plugin-stylelint/src/scripts/postinstall/index.ts @@ -3,7 +3,7 @@ import { resolve } from 'node:path'; const absolutesStylelintPath = resolve( process.cwd(), - 'node_modules/stylelint/lib/index.mjs' + 'node_modules/stylelint/lib/index.mjs', ); export async function patchStylelint(stylelintPath = absolutesStylelintPath) {