Migrate 7 content-linter files to TypeScript (#58001)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
|
||||
import { addError } from 'markdownlint-rule-helpers'
|
||||
|
||||
import {
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
isInAllGhes,
|
||||
} from '@/ghes-releases/scripts/version-utils'
|
||||
import { deprecated, oldestSupported } from '@/versions/lib/enterprise-server-releases'
|
||||
import type { RuleParams, RuleErrorCallback } from '@/content-linter/types'
|
||||
|
||||
export const liquidIfversionVersions = {
|
||||
names: ['GHD022', 'liquid-ifversion-versions'],
|
||||
@@ -24,7 +26,7 @@ export const liquidIfversionVersions = {
|
||||
'Liquid `ifversion`, `elsif`, and `else` tags should be valid and not contain unsupported versions.',
|
||||
tags: ['liquid', 'versioning'],
|
||||
asynchronous: true,
|
||||
function: async (params, onError) => {
|
||||
function: async (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
// The versions frontmatter object or all versions if the file
|
||||
// being processed is a data file.
|
||||
const fm = getFrontmatter(params.lines)
|
||||
@@ -34,7 +36,7 @@ export const liquidIfversionVersions = {
|
||||
? { ghec: '*', ghes: '*', fpt: '*' }
|
||||
: fm
|
||||
? fm.versions
|
||||
: getFrontmatter(params.frontMatterLines).versions
|
||||
: getFrontmatter(params.frontMatterLines)?.versions
|
||||
// This will only contain valid (non-deprecated) and future versions
|
||||
const fileVersions = getApplicableVersions(fileVersionsFm, '', {
|
||||
doNotThrow: true,
|
||||
@@ -45,7 +47,8 @@ export const liquidIfversionVersions = {
|
||||
// Array of arrays - each array entry is an array of items that
|
||||
// make up a full if/elsif/else/endif statement.
|
||||
// [ [ifversion, elsif, else, endif], [nested ifversion, elsif, else, endif] ]
|
||||
const condStmtStack = []
|
||||
// Using any[] because these are complex dynamic objects with properties added at runtime
|
||||
const condStmtStack: any[] = []
|
||||
|
||||
// Tokens are in the order they are read in file, so we need to iterate
|
||||
// through and group full if/elsif/else/endif statements together.
|
||||
@@ -76,10 +79,12 @@ export const liquidIfversionVersions = {
|
||||
// The versions of an else tag are the set of file versions that are
|
||||
// not supported by the previous ifversion or elsif tags.
|
||||
const siblingVersions = condTagItems
|
||||
.filter((item) => item.name === 'ifversion' || item.name === 'elsif')
|
||||
.map((item) => item.versions)
|
||||
// Using any because condTagItems contains dynamic objects from initTagObject
|
||||
.filter((item: any) => item.name === 'ifversion' || item.name === 'elsif')
|
||||
.map((item: any) => item.versions)
|
||||
.flat()
|
||||
condTagItem.versions = difference(fileVersions, siblingVersions)
|
||||
// Using any because versions property is added dynamically to condTagItem
|
||||
;(condTagItem as any).versions = difference(fileVersions, siblingVersions)
|
||||
condTagItems.push(condTagItem)
|
||||
condStmtStack.push(condTagItems)
|
||||
} else if (token.name === 'endif') {
|
||||
@@ -87,14 +92,15 @@ export const liquidIfversionVersions = {
|
||||
const condTagItems = condStmtStack.pop()
|
||||
const condTagItem = await initTagObject(token, defaultProps)
|
||||
condTagItems.push(condTagItem)
|
||||
decorateCondTagItems(condTagItems, params.lines)
|
||||
decorateCondTagItems(condTagItems)
|
||||
setLiquidErrors(condTagItems, onError, params.lines)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
function setLiquidErrors(condTagItems, onError, lines) {
|
||||
// Using any[] because condTagItems contains dynamic objects with properties added at runtime
|
||||
function setLiquidErrors(condTagItems: any[], onError: RuleErrorCallback, lines: string[]) {
|
||||
for (let i = 0; i < condTagItems.length; i++) {
|
||||
const item = condTagItems[i]
|
||||
const tagNameNoCond = item.name === 'endif' || item.name === 'else'
|
||||
@@ -175,8 +181,9 @@ function setLiquidErrors(condTagItems, onError, lines) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getApplicableVersionFromLiquidTag(conditionStr, filename) {
|
||||
const newConditionObject = {}
|
||||
async function getApplicableVersionFromLiquidTag(conditionStr: string) {
|
||||
// Using Record<string, any> because version object keys are dynamic (fpt, ghec, ghes, feature, etc.)
|
||||
const newConditionObject: Record<string, any> = {}
|
||||
const condition = conditionStr.replace('not ', '')
|
||||
const liquidTagVersions = condition.split(' or ').map((item) => item.trim())
|
||||
for (const ver of liquidTagVersions) {
|
||||
@@ -227,7 +234,8 @@ async function getApplicableVersionFromLiquidTag(conditionStr, filename) {
|
||||
return newConditionObject
|
||||
}
|
||||
|
||||
async function initTagObject(token, props) {
|
||||
// Using any for token and props because they come from markdownlint library without full type definitions
|
||||
async function initTagObject(token: any, props: any) {
|
||||
const condTagItem = {
|
||||
name: token.name,
|
||||
cond: token.content.replace(`${token.name} `, '').trim(),
|
||||
@@ -245,15 +253,17 @@ async function initTagObject(token, props) {
|
||||
parent: props.parent,
|
||||
}
|
||||
if (token.name === 'ifversion' || token.name === 'elsif') {
|
||||
condTagItem.versionsObj = await getApplicableVersionFromLiquidTag(
|
||||
condTagItem.cond,
|
||||
props.filename,
|
||||
)
|
||||
condTagItem.featureVersionsObj = condTagItem.versionsObj.feature
|
||||
? getFeatureVersionsObject(condTagItem.versionsObj.feature)
|
||||
// Using any because these properties (versionsObj, featureVersionsObj, versionsObjAll, versions)
|
||||
// are added dynamically to condTagItem and not part of its initial type definition
|
||||
;(condTagItem as any).versionsObj = await getApplicableVersionFromLiquidTag(condTagItem.cond)
|
||||
;(condTagItem as any).featureVersionsObj = (condTagItem as any).versionsObj.feature
|
||||
? getFeatureVersionsObject((condTagItem as any).versionsObj.feature)
|
||||
: undefined
|
||||
condTagItem.versionsObjAll = { ...condTagItem.versionsObj, ...condTagItem.featureVersionsObj }
|
||||
condTagItem.versions = getApplicableVersions(condTagItem.versionsObj, '', {
|
||||
;(condTagItem as any).versionsObjAll = {
|
||||
...(condTagItem as any).versionsObj,
|
||||
...(condTagItem as any).featureVersionsObj,
|
||||
}
|
||||
;(condTagItem as any).versions = getApplicableVersions((condTagItem as any).versionsObj, '', {
|
||||
doNotThrow: true,
|
||||
includeNextVersion: true,
|
||||
})
|
||||
@@ -270,7 +280,8 @@ async function initTagObject(token, props) {
|
||||
Then create flaws per stack item.
|
||||
newCond
|
||||
*/
|
||||
function decorateCondTagItems(condTagItems, lines) {
|
||||
// Using any[] because condTagItems contains dynamic objects with action property added at runtime
|
||||
function decorateCondTagItems(condTagItems: any[]) {
|
||||
for (const item of condTagItems) {
|
||||
item.action = {
|
||||
type: 'none',
|
||||
@@ -287,7 +298,8 @@ function decorateCondTagItems(condTagItems, lines) {
|
||||
return
|
||||
}
|
||||
|
||||
function updateConditionals(condTagItems) {
|
||||
// Using any[] because condTagItems contains dynamic objects with various properties added at runtime
|
||||
function updateConditionals(condTagItems: any[]) {
|
||||
// iterate through the ifversion, elsif, and else
|
||||
// tags but NOT the endif tag. endif tags have
|
||||
// no versions associated with them and are handled
|
||||
@@ -470,7 +482,8 @@ function updateConditionals(condTagItems) {
|
||||
}
|
||||
}
|
||||
|
||||
function processConditionals(item, condTagItems, indexOfAllItem) {
|
||||
// Using any for item and any[] for condTagItems because they contain dynamic objects with action property
|
||||
function processConditionals(item: any, condTagItems: any[], indexOfAllItem: number) {
|
||||
item.action.type = 'all'
|
||||
// if any tag in a statement is 'all', the
|
||||
// remaining tags are obsolete.
|
||||
@@ -6,8 +6,20 @@ import { frontmatterVersionsWhitespace } from '@/content-linter/lib/linting-rule
|
||||
// Configure the test fixture to not split frontmatter and content
|
||||
const fmOptions = { markdownlintOptions: { frontMatter: null } }
|
||||
|
||||
interface ValidTestCase {
|
||||
name: string
|
||||
content: string
|
||||
}
|
||||
|
||||
interface InvalidTestCase {
|
||||
name: string
|
||||
content: string
|
||||
expectedErrors: number
|
||||
expectedMessage?: string
|
||||
}
|
||||
|
||||
// Valid cases - should pass
|
||||
const validCases = [
|
||||
const validCases: ValidTestCase[] = [
|
||||
{
|
||||
name: 'valid-simple-versions',
|
||||
content: `---
|
||||
@@ -52,7 +64,7 @@ This is a test.
|
||||
]
|
||||
|
||||
// Invalid cases - should fail
|
||||
const invalidCases = [
|
||||
const invalidCases: InvalidTestCase[] = [
|
||||
{
|
||||
name: 'trailing-whitespace',
|
||||
content: `---
|
||||
@@ -184,7 +196,7 @@ versions:
|
||||
})
|
||||
expect(result.content.length).toBe(1)
|
||||
expect(result.content[0].fixInfo).toBeDefined()
|
||||
expect(result.content[0].fixInfo.insertText).toBe('*')
|
||||
expect(result.content[0].fixInfo?.insertText).toBe('*')
|
||||
})
|
||||
|
||||
test('should provide fix information for leading whitespace', async () => {
|
||||
@@ -202,7 +214,7 @@ versions:
|
||||
})
|
||||
expect(result.content.length).toBe(1)
|
||||
expect(result.content[0].fixInfo).toBeDefined()
|
||||
expect(result.content[0].fixInfo.insertText).toBe('*')
|
||||
expect(result.content[0].fixInfo?.insertText).toBe('*')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -46,8 +46,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(2)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Add a blank line before {% note %}')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Add a blank line before {% note %}')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -63,8 +63,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(4)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Add a blank line after {% endnote %}')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Add a blank line after {% endnote %}')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -78,8 +78,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(2)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Add a blank line before > [!WARNING]')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Add a blank line before > [!WARNING]')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -94,8 +94,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(4)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Add a blank line after > [!DANGER]')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Add a blank line after > [!DANGER]')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -117,8 +117,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(8)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Do not include more than 2 bullet points')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Do not include more than 2 bullet points')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -137,8 +137,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(7)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Do not include more than 2 bullet points')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Do not include more than 2 bullet points')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -156,12 +156,12 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(4)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('should start with **Note:**')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('should start with **Note:**')
|
||||
}
|
||||
expect(errors[0].fixInfo).toBeDefined()
|
||||
if (errors[0].fixInfo) {
|
||||
expect(errors[0].fixInfo.insertText).toBe('**Note:** ')
|
||||
expect(errors[0].fixInfo?.insertText).toBe('**Note:** ')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -177,8 +177,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(3)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('should be inside a callout block')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('should be inside a callout block')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -194,8 +194,8 @@ describe(noteWarningFormatting.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(3)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Warning prefix should be inside a callout block')
|
||||
if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Warning prefix should be inside a callout block')
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user