AbdulElahGwaith's picture
Upload folder using huggingface_hub
88df9e4 verified
import { addError } from 'markdownlint-rule-helpers'
import { getFrontmatter } from '../helpers/utils'
import { liquid } from '@/content-render/index'
import { isLiquidError } from '@/languages/lib/render-with-fallback'
import type { RuleParams, RuleErrorCallback } from '@/content-linter/types'
interface ErrorMessageInfo {
errorDescription: string
lineNumber: number
columnNumber: number
}
/*
Attempts to parse all liquid in the frontmatter of a file
to verify the syntax is correct.
*/
export const frontmatterLiquidSyntax = {
names: ['GHD017', 'frontmatter-liquid-syntax'],
description: 'Frontmatter properties must use valid Liquid',
tags: ['liquid', 'frontmatter'],
function: (params: RuleParams, onError: RuleErrorCallback) => {
const fm = getFrontmatter(params.lines)
if (!fm) return
// Currently this list is hardcoded, but in the future we plan to
// use a custom key in the frontmatter to determine which keys
// contain Liquid.
const keysWithLiquid = ['title', 'shortTitle', 'intro', 'product', 'permissions'].filter(
(key) => Boolean(fm[key]),
)
for (const key of keysWithLiquid) {
const value = fm[key]
if (typeof value !== 'string') continue
try {
liquid.parse(value)
} catch (error) {
// If the error source is not a Liquid error but rather a
// ReferenceError or bad type we should allow that error to be thrown
if (!isLiquidError(error)) throw error
const { errorDescription, columnNumber } = getErrorMessageInfo((error as Error).message)
const lineNumber = params.lines.findIndex((line) => line.trim().startsWith(`${key}:`)) + 1
// Add the key length plus 3 to the column number to account colon and
// for the space after the key and column number starting at 1.
// If there is no space after the colon, a YAMLException will be thrown.
const startRange = columnNumber + key.length + 3
// If the range is greater than the length of the line, we need to adjust the range to the end of the line
const endRange =
startRange + value.length - 1 > params.lines[lineNumber - 1].length
? params.lines[lineNumber - 1].length - startRange + 1
: value.length
const range: [number, number] = [startRange, endRange]
addError(
onError,
lineNumber,
`Liquid syntax error: ${errorDescription}`,
value,
range,
null, // No fix possible
)
}
}
},
}
/*
Attempts to parse all liquid in the Markdown content of a file
to verify the syntax is correct.
*/
export const liquidSyntax = {
names: ['GHD018', 'liquid-syntax'],
description: 'Markdown content must use valid Liquid',
tags: ['liquid'],
function: function GHD018(params: RuleParams, onError: RuleErrorCallback) {
try {
liquid.parse(params.lines.join('\n'))
} catch (error) {
// If the error source is not a Liquid error but rather a
// ReferenceError or bad type we should allow that error to be thrown
if (!isLiquidError(error)) throw error
const { errorDescription, lineNumber, columnNumber } = getErrorMessageInfo(
(error as Error).message,
)
const line = params.lines[lineNumber - 1]
// We don't have enough information to know the length of the full
// liquid tag without doing some regex testing and making assumptions
// about if the end tag is correctly formed, so we just give a
// range from the start of the tag to the end of the line.
const range: [number, number] = [columnNumber, line.slice(columnNumber - 1).length]
addError(
onError,
lineNumber,
`Liquid syntax error: ${errorDescription}`,
line,
range,
null, // No fix possible
)
}
},
}
function getErrorMessageInfo(message: string): ErrorMessageInfo {
const [errorDescription, lineString, columnString] = message.split(',')
// There has to be a line number so we'll default to line 1 if the message
// doesn't contain a line number.
if (!columnString || !lineString)
throw new Error('Liquid error message does not contain line or column number')
const lineNumber = parseInt(lineString.trim().replace('line:', ''), 10)
const columnNumber = parseInt(columnString.trim().replace('col:', ''), 10)
return { errorDescription, lineNumber, columnNumber }
}