Skip to content

Commit

Permalink
Markdownlint script output formatting and error updates (#38953)
Browse files Browse the repository at this point in the history
  • Loading branch information
rachmari authored Jul 12, 2023
1 parent 3bc1f7c commit 8370a2b
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 57 deletions.
14 changes: 14 additions & 0 deletions src/content-linter/lib/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { addError, newLineRe } from 'markdownlint-rule-helpers'

// Adds an error object with details conditionally via the onError callback
export function addFixErrorDetail(onError, lineNumber, expected, actual, range, fixInfo) {
addError(onError, lineNumber, `Expected: ${expected}`, ` Actual: ${actual}`, range, fixInfo)
}

export function getCodeFenceTokens(params) {
return params.tokens.filter((t) => t.type === 'fence')
}

export function getCodeFenceLines(token) {
return token.content.split(newLineRe)
}
10 changes: 6 additions & 4 deletions src/content-linter/lib/linting-rules/code-fence-line-length.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { addError } from 'markdownlint-rule-helpers'
import { addError, ellipsify } from 'markdownlint-rule-helpers'

import { getCodeFenceTokens, getCodeFenceLines } from '../markdownlint-helpers.js'
import { getCodeFenceTokens, getCodeFenceLines } from '../helpers.js'

export const codeFenceLineLength = {
names: ['GHD001', 'code-fence-line-length'],
description: 'Code fence lines should not exceed a maximum length',
tags: ['code'],
severity: 'warning',
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
function: function GHD001(params, onError) {
const MAX_LINE_LENGTH = String(params.config.maxLength || 60)
const codeFenceTokens = getCodeFenceTokens(params)
Expand All @@ -22,8 +23,9 @@ export const codeFenceLineLength = {
onError,
lineNumber,
`Code fence line exceeds ${MAX_LINE_LENGTH} characters.`,
undefined, // N/A
undefined, // N/A
ellipsify(line),
[1, line.length],
null, // No fix possible
)
}
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import { addError, forEachInlineChild } from 'markdownlint-rule-helpers'
import { forEachInlineChild } from 'markdownlint-rule-helpers'

import { addFixErrorDetail } from '../helpers.js'

export const imageAltTextEndPunctuation = {
names: ['GHD002', 'image-alt-text-end-punctuation'],
description: 'Images alternate text should end with a punctuation.',
description: 'Alternate text for images should end with a punctuation.',
severity: 'error',
tags: ['accessibility', 'images'],
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
function: function GHD003(params, onError) {
forEachInlineChild(params, 'image', function forToken(token) {
const quoteRegex = /[.?!]['"]$/
const endRegex = /[.?!]$/
const imageAltText = token.content.trim()
const startColumnIndex = token.line.indexOf(imageAltText)
const range = startColumnIndex ? [startColumnIndex + 1, imageAltText.length] : null
if (
(!imageAltText.endsWith('"') && !imageAltText.slice(-1).match(endRegex)) ||
(imageAltText.endsWith('"') && !imageAltText.slice(-2).match(quoteRegex))
) {
addError(
onError,
token.lineNumber,
`On line ${token.lineNumber}, the image alt text '${imageAltText}' must have punctuation at the end of the sentence.`,
undefined,
undefined,
{
lineNumber: token.lineNumber,
editColumn: token.line.indexOf(']') + 1,
deleteCount: 0,
insertText: '.',
},
)
addFixErrorDetail(onError, token.lineNumber, imageAltText + '.', imageAltText, range, {
lineNumber: token.lineNumber,
editColumn: token.line.indexOf(']') + 1,
deleteCount: 0,
insertText: '.',
})
}
})
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const incorrectAltTextLength = {
severity: 'warning',
description: 'Images alternate text should be between 40-150 characters',
tags: ['accessibility', 'images'],
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
function: function GHD004(params, onError) {
forEachInlineChild(params, 'image', async function forToken(token) {
let renderedString = token.content
Expand All @@ -18,7 +19,10 @@ export const incorrectAltTextLength = {
addError(
onError,
token.lineNumber,
`The alt text: ${renderedString}, is ${renderedString.length} characters long`,
`Image alternate text is ${renderedString.length} characters long.`,
renderedString,
null, // No range
null, // No fix possible
)
}
})
Expand Down
13 changes: 10 additions & 3 deletions src/content-linter/lib/linting-rules/image-file-kebab.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { addError, forEachInlineChild } from 'markdownlint-rule-helpers'
import { forEachInlineChild } from 'markdownlint-rule-helpers'

import { addFixErrorDetail } from '../helpers.js'

export const imageFileKebab = {
names: ['GHD004', 'image-file-kebab'],
description: 'Image file names should always be lowercase kebab case',
severity: 'warning',
tags: ['accessibility', 'images'],
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
function: function GHD005(params, onError) {
forEachInlineChild(params, 'image', async function forToken(token) {
const imageFileName = token.attrs[0][1].split('/').pop().split('.')[0]
const nonKebabRegex = /([A-Z]|_)/
const suggestedFileName = imageFileName.toLowerCase().replace(/_/g, '-')
if (imageFileName.match(nonKebabRegex)) {
addError(
addFixErrorDetail(
onError,
token.lineNumber,
`The image file name: ${token.attrs[0][1]}, is not lowercase kebab case.`,
imageFileName,
suggestedFileName,
[token.line.indexOf(imageFileName) + 1, imageFileName.length],
null, // Todo add fix
)
}
})
Expand Down
17 changes: 10 additions & 7 deletions src/content-linter/lib/linting-rules/internal-links-lang.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { addError, filterTokens } from 'markdownlint-rule-helpers'
import { filterTokens } from 'markdownlint-rule-helpers'

import { addFixErrorDetail } from '../helpers.js'
import { languageKeys } from '../../../../lib/languages.js'

export const internalLinksLang = {
names: ['GHD005', 'internal-links-lang'],
description: 'Internal links must not have a hardcoded language code',
severity: 'error',
tags: ['links', 'url'],
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
function: function GHD006(params, onError) {
filterTokens(params, 'inline', (token) => {
let linkHref = ''
Expand All @@ -16,21 +18,22 @@ export const internalLinksLang = {
linkHref = ''
for (const attr of child.attrs) {
if (
languageKeys.includes(attr[1].split('/')[1]) ||
languageKeys.includes(attr[1].split('/')[0])
languageKeys.some(
(lang) => attr[1].startsWith(`/${lang}`) || attr[1].startsWith(lang),
)
) {
internalLinkHasLang = true
linkHref = attr[1]
}
}
} else if (child.type === 'link_close') {
if (internalLinkHasLang) {
addError(
addFixErrorDetail(
onError,
child.lineNumber,
`This internal link: ${linkHref} must not start with a hardcoded language code.`,
undefined,
undefined,
linkHref.replace(/(\/)?[a-z]{2}/, ''),
linkHref,
undefined, // Todo add range
{
lineNumber: child.lineNumber,
editColumn: token.line.indexOf('(') + 2,
Expand Down
24 changes: 10 additions & 14 deletions src/content-linter/lib/linting-rules/internal-links-slash.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { addError, filterTokens } from 'markdownlint-rule-helpers'
import { filterTokens } from 'markdownlint-rule-helpers'

import { addFixErrorDetail } from '../helpers.js'

export const internalLinksSlash = {
names: ['GHD006', 'internal-links-slash'],
description: 'Internal links must start with a /',
severity: 'error',
tags: ['links', 'url'],
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
function: function GHD007(params, onError) {
filterTokens(params, 'inline', (token) => {
let linkHref = ''
Expand All @@ -26,19 +29,12 @@ export const internalLinksSlash = {
}
} else if (child.type === 'link_close') {
if (!internalLinkHasSlash) {
addError(
onError,
child.lineNumber,
`This relative link: ${linkHref} on line ${child.lineNumber} must start with a /`,
undefined,
undefined,
{
lineNumber: child.lineNumber,
editColumn: token.line.indexOf('(') + 2,
deleteCount: 0,
insertText: '/',
},
)
addFixErrorDetail(onError, child.lineNumber, `/${linkHref}`, linkHref, undefined, {
lineNumber: child.lineNumber,
editColumn: token.line.indexOf('(') + 2,
deleteCount: 0,
insertText: '/',
})
internalLinkHasSlash = true
}
}
Expand Down
9 changes: 0 additions & 9 deletions src/content-linter/lib/markdownlint-helpers.js

This file was deleted.

35 changes: 31 additions & 4 deletions src/content-linter/scripts/markdownlint.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ program
)
.option('--fix', 'Fix linting errors.')
.option('--summary-by-rule', 'Summarize the number of errors for each rule.')
.option('--verbose', 'Output full format for all errors.')
.parse(process.argv)

const { files, fix, paths, error, rules, summaryByRule } = program.opts()
const { files, fix, paths, error, rules, summaryByRule, verbose } = program.opts()
const DEFAULT_LINT_DIRS = ['content', 'data']

main()
Expand Down Expand Up @@ -77,7 +78,7 @@ async function main() {
}
}

// Used for a temporary way to allow us to see how many errors currently
// Used for a temparary way to allow us to see how many errors currently
// exist for each rule in the content directory.
if (summaryByRule) {
reportSummaryByRule(result, config)
Expand Down Expand Up @@ -129,12 +130,38 @@ function reportSummaryByRule(result, config) {
function reportResults(results) {
Object.entries(results)
// each result key always has an array value, but it may be empty
.filter(([key, result]) => result.length)
.filter(([, result]) => result.length)
.forEach(([key, result]) => {
console.log(key, result)
console.log(key)
if (!verbose) {
result.forEach((flaw) => {
const simplifiedResult = formatResult(flaw)
console.log(simplifiedResult)
})
} else {
console.log(result)
}
})
}

function getErrorCountByFile(result) {
return Object.values(result).filter((value) => value.length).length
}

function formatResult(object) {
return Object.entries(object).reduce((acc, [key, value]) => {
if (key === 'fixInfo') {
acc.fixable = !!value
delete acc.fixInfo
return acc
}
if (!value) return acc
if (key === 'errorRange') {
acc.columnNumber = value[0]
delete acc.range
return acc
}
acc[key] = value
return acc
}, {})
}

0 comments on commit 8370a2b

Please sign in to comment.