Skip to content

Commit bd52536

Browse files
heiskrCopilot
andauthored
Add content linter rule for versions frontmatter whitespace validation (GHD051) (#56131)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent e48b53c commit bd52536

File tree

5 files changed

+380
-1
lines changed

5 files changed

+380
-1
lines changed

data/reusables/contributing/content-linter-rules.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,5 @@
7272
| GHD046 | outdated-release-phase-terminology | Outdated release phase terminology should be replaced with current GitHub terminology | warning | terminology, consistency, release-phases |
7373
| GHD048 | british-english-quotes | Periods and commas should be placed inside quotation marks (American English style) | warning | punctuation, quotes, style, consistency |
7474
| GHD050 | multiple-emphasis-patterns | Do not use more than one emphasis/strong, italics, or uppercase for a string | warning | formatting, emphasis, style |
75-
| GHD049 | note-warning-formatting | Note and warning tags should be formatted according to style guide | warning | formatting, callouts, notes, warnings, style |
75+
| GHD049 | note-warning-formatting | Note and warning tags should be formatted according to style guide | warning | formatting, callouts, notes, warnings, style |
76+
| GHD051 | frontmatter-versions-whitespace | Versions frontmatter should not contain unnecessary whitespace | warning | frontmatter, versions |
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { addError } from 'markdownlint-rule-helpers'
2+
import { getFrontmatter } from '@/content-linter/lib/helpers/utils'
3+
4+
export const frontmatterVersionsWhitespace = {
5+
names: ['GHD051', 'frontmatter-versions-whitespace'],
6+
description: 'Versions frontmatter should not contain unnecessary whitespace',
7+
tags: ['frontmatter', 'versions'],
8+
function: (params, onError) => {
9+
const fm = getFrontmatter(params.lines)
10+
if (!fm || !fm.versions) return
11+
12+
const versionsObj = fm.versions
13+
if (typeof versionsObj !== 'object') return
14+
15+
// Find the frontmatter section in the file
16+
const fmStartIndex = params.lines.findIndex((line) => line.trim() === '---')
17+
if (fmStartIndex === -1) return
18+
19+
// Check each version entry for whitespace issues
20+
Object.entries(versionsObj).forEach(([key, value]) => {
21+
if (typeof value !== 'string') return
22+
23+
const hasUnwantedWhitespace = checkForUnwantedWhitespace(value)
24+
if (hasUnwantedWhitespace) {
25+
// Find the line containing this version key
26+
const versionLineIndex = params.lines.findIndex((line, index) => {
27+
return index > fmStartIndex && line.trim().startsWith(`${key}:`) && line.includes(value)
28+
})
29+
30+
if (versionLineIndex !== -1) {
31+
const line = params.lines[versionLineIndex]
32+
const lineNumber = versionLineIndex + 1
33+
const cleanedValue = getCleanedValue(value)
34+
35+
// Create fix info to remove unwanted whitespace
36+
const fixInfo = {
37+
editColumn: line.indexOf(value) + 1,
38+
deleteCount: value.length,
39+
insertText: cleanedValue,
40+
}
41+
42+
addError(
43+
onError,
44+
lineNumber,
45+
`Versions frontmatter should not contain leading or trailing whitespace. Found: '${value}', expected: '${cleanedValue}'`,
46+
line,
47+
[line.indexOf(value) + 1, value.length],
48+
fixInfo,
49+
)
50+
}
51+
}
52+
})
53+
},
54+
}
55+
56+
/**
57+
* Check if a version string has unwanted whitespace
58+
* Allows whitespace in complex expressions like '<3.6 >3.8'
59+
* but disallows leading/trailing whitespace
60+
*/
61+
function checkForUnwantedWhitespace(value) {
62+
// Don't flag if the value is just whitespace or empty
63+
if (!value || value.trim() === '') return false
64+
65+
// Check for leading or trailing whitespace
66+
if (value !== value.trim()) return true
67+
68+
// Allow whitespace around operators in complex expressions
69+
// This regex matches patterns like '<3.6 >3.8', '>=2.19', etc.
70+
const hasOperators = /[<>=]/.test(value)
71+
if (hasOperators) {
72+
// For operator expressions, we're more lenient about internal whitespace
73+
// Only flag if there's leading/trailing whitespace (already checked above)
74+
return false
75+
}
76+
77+
// For simple version strings (like 'fpt', 'ghec'), no internal whitespace should be allowed
78+
// This catches cases like 'f pt' where there's whitespace in the middle
79+
return /\s/.test(value)
80+
}
81+
82+
/**
83+
* Get the cleaned version of a value by removing appropriate whitespace
84+
*/
85+
function getCleanedValue(value) {
86+
// For values with operators, just trim leading/trailing whitespace
87+
const hasOperators = /[<>=]/.test(value)
88+
if (hasOperators) {
89+
return value.trim()
90+
}
91+
92+
// For simple version strings, remove all whitespace
93+
return value.replace(/\s/g, '')
94+
}

src/content-linter/lib/linting-rules/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { outdatedReleasePhaseTerminology } from '@/content-linter/lib/linting-ru
5050
import { britishEnglishQuotes } from '@/content-linter/lib/linting-rules/british-english-quotes'
5151
import { multipleEmphasisPatterns } from '@/content-linter/lib/linting-rules/multiple-emphasis-patterns'
5252
import { noteWarningFormatting } from '@/content-linter/lib/linting-rules/note-warning-formatting'
53+
import { frontmatterVersionsWhitespace } from '@/content-linter/lib/linting-rules/frontmatter-versions-whitespace'
5354

5455
const noDefaultAltText = markdownlintGitHub.find((elem) =>
5556
elem.names.includes('no-default-alt-text'),
@@ -105,5 +106,6 @@ export const gitHubDocsMarkdownlint = {
105106
britishEnglishQuotes,
106107
multipleEmphasisPatterns,
107108
noteWarningFormatting,
109+
frontmatterVersionsWhitespace,
108110
],
109111
}

src/content-linter/style/github-docs.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,12 @@ export const githubDocsFrontmatterConfig = {
286286
severity: 'error',
287287
'partial-markdown-files': false,
288288
},
289+
'frontmatter-versions-whitespace': {
290+
// GHD051
291+
severity: 'warning',
292+
'partial-markdown-files': false,
293+
'yml-files': false,
294+
},
289295
}
290296

291297
// Configures rules from the `github/markdownlint-github` repo

0 commit comments

Comments
 (0)