Skip to content

Commit 458a251

Browse files
author
Peter Bengtsson
authored
Unbreak list-first-word-capitalization markdownlint rule (#42642)
1 parent 61464a0 commit 458a251

File tree

3 files changed

+53
-12
lines changed

3 files changed

+53
-12
lines changed

src/content-linter/lib/helpers/utils.js

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,45 @@ export function isStringPunctuated(text) {
3030
// Filters a list of tokens by token type only when they match
3131
// a specific token type order.
3232
// For example, if a list of tokens contains:
33-
// [ { type: 'inline'}, { type: 'list_item_close'}, { type: 'list_item_open'},
34-
// { type: 'paragraph_open'}, { type: 'inline'}, { type: 'paragraph_close'}]
35-
// And if the tokenOrder being looked for is
36-
// `tokenOrder` defined ['list_item_open', 'paragraph_open', 'inline']
33+
//
34+
// [
35+
// { type: 'inline'},
36+
// { type: 'list_item_close'},
37+
// { type: 'list_item_open'},
38+
// { type: 'paragraph_open'},
39+
// { type: 'inline'},
40+
// { type: 'paragraph_close'},
41+
// ]
42+
//
43+
// And if the `tokenOrder` being looked for is:
44+
//
45+
// [
46+
// 'list_item_open',
47+
// 'paragraph_open',
48+
// 'inline'
49+
// ]
50+
//
3751
// Then the return value would be the items that match that seaquence:
3852
// Index 2-4:
39-
// [{ type: 'list_item_open'}, { type: 'paragraph_open'}, { type: 'inline'}]
53+
// [
54+
// { type: 'inline'}, <-- Index 0 - NOT INCLUDED
55+
// { type: 'list_item_close'}, <-- Index 1 - NOT INCLUDED
56+
// { type: 'list_item_open'}, <-- Index 2 - INCLUDED
57+
// { type: 'paragraph_open'}, <-- Index 3 - INCLUDED
58+
// { type: 'inline'}, <-- Index 4 - INCLUDED
59+
// { type: 'paragraph_close'}, <-- Index 5 - NOT INCLUDED
60+
// ]
61+
//
4062
export function filterTokensByOrder(tokens, tokenOrder) {
4163
const matches = []
4264

4365
// Get a list of token indexes that match the
4466
// first token (root) in the tokenOrder array
4567
const tokenRootIndexes = []
68+
const firstTokenOrderType = tokenOrder[0]
4669
tokens.forEach((token, index) => {
47-
if (token.type === tokenOrder[0]) {
48-
return tokenRootIndexes.push(index)
70+
if (token.type === firstTokenOrderType) {
71+
tokenRootIndexes.push(index)
4972
}
5073
})
5174

@@ -54,7 +77,10 @@ export function filterTokensByOrder(tokens, tokenOrder) {
5477
for (const tokenRootIndex of tokenRootIndexes) {
5578
for (let i = 1; i < tokenOrder.length; i++) {
5679
if (tokens[tokenRootIndex + i].type !== tokenOrder[i]) {
57-
return
80+
// This tokenRootIndex was a possible start,
81+
// but doesn't match the tokenOrder perfectly, so break out
82+
// of the inner loop before it reaches the end.
83+
break
5884
}
5985
if (i === tokenOrder.length - 1) {
6086
matches.push(...tokens.slice(tokenRootIndex, tokenRootIndex + i + 1))

src/content-linter/lib/linting-rules/list-first-word-capitalization.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ export const listFirstWordCapitalization = {
77
tags: ['ul', 'ol'],
88
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
99
function: function GHD011(params, onError) {
10+
// We're going to look for a sequence of 3 tokens. If the markdown
11+
// is a really small string, it might not even have that many tokens
12+
// in it. Can bail early.
13+
if (params.tokens.length < 3) return
14+
1015
const inlineListItems = filterTokensByOrder(params.tokens, [
1116
'list_item_open',
1217
'paragraph_open',

src/content-linter/tests/unit/list-first-word-captitalization.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
import { jest } from '@jest/globals'
2-
31
import { runRule } from '../../lib/init-test.js'
42
import { listFirstWordCapitalization } from '../../lib/linting-rules/list-first-word-capitalization.js'
53

6-
jest.setTimeout(30 * 1000)
7-
84
describe(listFirstWordCapitalization.names.join(' - '), () => {
95
test('ensure multi-level lists catch incorrect capitalization errors', async () => {
106
const markdown = [
@@ -59,4 +55,18 @@ describe(listFirstWordCapitalization.names.join(' - '), () => {
5955
const errors = result.markdown
6056
expect(errors.length).toBe(0)
6157
})
58+
59+
test("list items that aren't simple lists", async () => {
60+
const markdown = ['- > Blockquote in a list', '- ### Heading in a list'].join('\n')
61+
const result = await runRule(listFirstWordCapitalization, { markdown })
62+
const errors = result.markdown
63+
expect(errors.length).toBe(0)
64+
})
65+
66+
test('works on markdown that has no lists at all, actually', async () => {
67+
const markdown = '- \n'
68+
const result = await runRule(listFirstWordCapitalization, { markdown })
69+
const errors = result.markdown
70+
expect(errors.length).toBe(0)
71+
})
6272
})

0 commit comments

Comments
 (0)