Skip to content

Repo sync #39742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions src/content-linter/scripts/lint-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,8 @@ function getMarkdownLintConfig(errorsOnly, runRules) {
continue
}

if (runRules && !runRules.includes(ruleName)) continue
// Check if the rule should be included based on user-specified rules
if (runRules && !shouldIncludeRule(ruleName, runRules)) continue

// Skip british-english-quotes rule in CI/PRs (only run in pre-commit)
if (ruleName === 'british-english-quotes' && !isPrecommit) continue
Expand Down Expand Up @@ -638,6 +639,29 @@ function getCustomRule(ruleName) {
return rule
}

// Check if a rule should be included based on user-specified rules
// Handles both short names (e.g., GHD053, MD001) and long names (e.g., header-content-requirement, heading-increment)
export function shouldIncludeRule(ruleName, runRules) {
// First check if the rule name itself is in the list
if (runRules.includes(ruleName)) {
return true
}

// For custom rules, check if any of the rule's names (short or long) are in the runRules list
const customRule = customRules.find((rule) => rule.names.includes(ruleName))
if (customRule) {
return customRule.names.some((name) => runRules.includes(name))
}

// For built-in markdownlint rules, check if any of the rule's names are in the runRules list
const builtinRule = allRules.find((rule) => rule.names.includes(ruleName))
if (builtinRule) {
return builtinRule.names.some((name) => runRules.includes(name))
}

return false
}

/*
The severity of the search-replace custom rule is embedded in
each individual search rule. This function returns the severity
Expand Down Expand Up @@ -681,9 +705,7 @@ function isOptionsValid() {
}

// rules should only contain existing, correctly spelled rules
const allRulesList = Object.values(allRules)
.map((rule) => rule.names)
.flat()
const allRulesList = [...allRules.map((rule) => rule.names).flat(), ...Object.keys(allConfig)]
const rules = program.opts().rules || []
for (const rule of rules) {
if (!allRulesList.includes(rule)) {
Expand Down
62 changes: 62 additions & 0 deletions src/content-linter/tests/unit/rule-filtering.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, test, expect, vi } from 'vitest'
import { shouldIncludeRule } from '../../scripts/lint-content.js'

// Mock the get-rules module to provide test data for rule definitions
vi.mock('../../lib/helpers/get-rules', () => ({
allRules: [
{
names: ['MD001', 'heading-increment'],
description: 'Heading levels should only increment by one level at a time',
},
{
names: ['MD002', 'first-heading-h1'],
description: 'First heading should be a top level heading',
},
],
customRules: [
{
names: ['GHD053', 'header-content-requirement'],
description: 'Headers must have content below them',
},
{
names: ['GHD030', 'code-fence-line-length'],
description: 'Code fence content should not exceed line length limit',
},
],
allConfig: {},
}))

describe('shouldIncludeRule', () => {
test('includes rule by long name', () => {
expect(shouldIncludeRule('heading-increment', ['heading-increment'])).toBe(true)
expect(shouldIncludeRule('header-content-requirement', ['header-content-requirement'])).toBe(
true,
)
})

test('includes built-in rule by short code', () => {
expect(shouldIncludeRule('heading-increment', ['MD001'])).toBe(true)
expect(shouldIncludeRule('first-heading-h1', ['MD002'])).toBe(true)
})

test('includes custom rule by short code', () => {
expect(shouldIncludeRule('header-content-requirement', ['GHD053'])).toBe(true)
expect(shouldIncludeRule('code-fence-line-length', ['GHD030'])).toBe(true)
})

test('excludes rule not in list', () => {
expect(shouldIncludeRule('heading-increment', ['MD002'])).toBe(false)
expect(shouldIncludeRule('header-content-requirement', ['GHD030'])).toBe(false)
})

test('handles multiple rules', () => {
const runRules = ['MD001', 'GHD053', 'some-other-rule']
expect(shouldIncludeRule('heading-increment', runRules)).toBe(true)
expect(shouldIncludeRule('header-content-requirement', runRules)).toBe(true)
expect(shouldIncludeRule('first-heading-h1', runRules)).toBe(false)
})

test('handles unknown rules gracefully', () => {
expect(shouldIncludeRule('non-existent-rule', ['MD001'])).toBe(false)
})
})
Loading