Skip to content

Commit

Permalink
Add rule for Markdown table Liquid syntax (#52092)
Browse files Browse the repository at this point in the history
  • Loading branch information
rachmari authored Aug 26, 2024
1 parent 1e6fe77 commit 148ac48
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 1 deletion.
3 changes: 2 additions & 1 deletion data/reusables/contributing/content-linter-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@
| GHD035 | rai-reusable-usage | RAI articles and reusables can only reference reusable content in the data/reusables/rai directory | error | feature, rai |
| GHD036 | image-no-gif | Image must not be a gif, styleguide reference: contributing/style-guide-and-content-model/style-guide.md#images | error | images |
| GHD038 | expired-content | Expired content must be remediated. | error | expired |
| GHD039 | expiring-soon | Content that expires soon should be proactively addressed. | warning | expired |
| GHD039 | expiring-soon | Content that expires soon should be proactively addressed. | warning | expired |
| [GHD040](https://github.com/github/docs/blob/main/src/content-linter/README.md) | table-liquid-versioning | Tables must use the correct liquid versioning format | error | tables |
2 changes: 2 additions & 0 deletions src/content-linter/lib/linting-rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { liquidIfTags, liquidIfVersionTags, liquidIfVersionVersions } from './li
import { raiReusableUsage } from './rai-reusable-usage.js'
import { imageNoGif } from './image-no-gif.js'
import { expiredContent, expiringSoon } from './expired-content.js'
import { tableLiquidVersioning } from './table-liquid-versioning.js'

const noDefaultAltText = markdownlintGitHub.find((elem) =>
elem.names.includes('no-default-alt-text'),
Expand Down Expand Up @@ -73,5 +74,6 @@ export const gitHubDocsMarkdownlint = {
imageNoGif,
expiredContent,
expiringSoon,
tableLiquidVersioning,
],
}
83 changes: 83 additions & 0 deletions src/content-linter/lib/linting-rules/table-liquid-versioning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { addError, filterTokens } from 'markdownlint-rule-helpers'

// Detects a Markdown table delimiter row
const delimiterRegexPure = /(\s)*(:)?(-+)(:)?(\s)*(\|)/
// Detects a Markdown table delimiter row with a Liquid tag
const delimiterRegex = /(\s)*(:)?(-+)(:)?(\s)*(\|).*({%.*(ifversion|else|endif).*%})/
// Detects a Liquid versioning tag
const liquidRegex = /^{%-?\s*(ifversion|else|endif).*-?%}/
// Detects a Markdown table row with a Liquid versioning tag
const liquidAfterRowRegex = /(\|{1}).*(\|{1}).*{%\s*(ifversion|else|endif).*%}$/

export const tableLiquidVersioning = {
names: ['GHD040', 'table-liquid-versioning'],
description: 'Tables must use the correct liquid versioning format',
severity: 'error',
tags: ['tables'],
information: new URL('https://github.com/github/docs/blob/main/src/content-linter/README.md'),
function: function GHD040(params, onError) {
const lines = params.lines
let inTable = false
for (let i = 0; i < lines.length; i++) {
const line = lines[i]

if (inTable && (!line || isPreviousLineIndented(lines[i], lines[i - 1]))) {
inTable = false
continue
}

if (delimiterRegexPure.test(line)) {
// A table with rows is at least 3 lines
if (lines[i - 1] && lines[i + 1]) {
inTable = true
if (liquidAfterRowRegex.test(lines[i - 1])) {
addError(
onError,
i,
'Liquid conditionals that version rows of data should be placed on their own line in the format `| {% ifversion enterprise %} |`.',
lines[i - 1],
null,
)
}
if (delimiterRegex.test(line)) {
addError(
onError,
i + 1,
'Liquid conditionals that version rows of data should be placed on their own line in the format `| {% ifversion enterprise %} |`.',
line,
null,
)
}
continue
}
}
if (inTable) {
if (liquidRegex.test(line)) {
addError(
onError,
i + 1,
'Liquid conditionals that version rows of data should be placed on their own line in the format `| {% ifversion enterprise %} |`. If the conditional is on its own line but is not related to the table, ensure there is one new line beween a Liquid version tag and the table.',
line,
null,
)
}
if (liquidAfterRowRegex.test(line)) {
addError(
onError,
i + 1,
'Liquid conditionals that version rows of data should be placed on their own line in the format `| {% ifversion enterprise %} |`.',
line,
null,
)
}
}
}
},
}

function isPreviousLineIndented(line, previousLine) {
if (!line || !previousLine) return false
const numWhitespaceLine = line.length - line.trimLeft().length
const numWhitespacePrevLine = previousLine.length - previousLine.trimLeft().length
return numWhitespaceLine < numWhitespacePrevLine
}
5 changes: 5 additions & 0 deletions src/content-linter/style/github-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ const githubDocsConfig = {
severity: 'warning',
'partial-markdown-files': true,
},
'table-liquid-versioning': {
// GH040
severity: 'error',
'partial-markdown-files': true,
},
}

export const githubDocsFrontmatterConfig = {
Expand Down
59 changes: 59 additions & 0 deletions src/content-linter/tests/fixtures/tables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: Examples of tables in Markdown
descriptions: Examples of tables in Markdown
versions:
fpt: '*'
ghes: '*'
ghec: '*'
---

## Good

| Package manager | Languages | Recommended formats | All supported formats |
| --- | --- | --- | ---|
| {% ifversion volvo %} |
| Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock` |
| {% endif %} |

| Package manager | Languages | Recommended formats | All supported formats |
| --- | --- | --- | ---|
| {%- ifversion volvo %} |
| Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock` |
| {%- endif %} |

{% ifversion volvo %}

1. This is a list with a table
| Package manager | Languages | Recommended formats | All supported formats |
| --- | --- | --- | ---|
| {%- ifversion volvo %} |
| Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock` |
| {%- endif %} |
{% endif %}

## Bad

| Package manager | Languages | Recommended formats | All supported formats |
| --- | --- | --- | ---|
{%- ifversion volvo %}
| Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock` |
{%- endif %}

| Package manager | Languages | Recommended formats | All supported formats |
| --- | --- | --- | ---|{% ifversion volvo %}
| Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock` |{% endif %}

{% ifversion volvo %}

| Package manager | Languages | Recommended formats | All supported formats |
| --- | --- | --- | ---|
| Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock` |
{% endif %}

Package manager | Languages | Recommended formats | All supported formats {% ifversion fpt %}
:- | :- | :- | :-{% endif %}{% ifversion volvo %}
Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock` {% endif %}

| Package manager | Languages | Recommended formats | All supported formats | {% ifversion fpt %}
| :- | :- | :- | :-|{% endif %}{% ifversion volvo %}
|Cargo | Rust | `Cargo.lock` | `Cargo.toml`, `Cargo.lock`| {% endif %}
17 changes: 17 additions & 0 deletions src/content-linter/tests/unit/table-liquid-versioning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { describe, expect, test } from 'vitest'

import { runRule } from '../../lib/init-test.js'
import { tableLiquidVersioning } from '../../lib/linting-rules/table-liquid-versioning.js'

const FIXTURE_FILEPATH = 'src/content-linter/tests/fixtures/tables.md'

describe(tableLiquidVersioning.names.join(' - '), () => {
test('non-early access file with early access references fails', async () => {
const result = await runRule(tableLiquidVersioning, { files: [FIXTURE_FILEPATH] })
const errors = result[FIXTURE_FILEPATH]
expect(errors.length).toBe(11)
const lineNumbers = errors.map((error) => error.lineNumber)
const expectedErrorLines = [38, 40, 43, 44, 51, 53, 54, 55, 57, 58, 59]
expect(JSON.stringify(lineNumbers)).toEqual(JSON.stringify(expectedErrorLines))
})
})

0 comments on commit 148ac48

Please sign in to comment.