1+ // @ts -ignore - no types available for markdownlint-rule-helpers
12import { addError } from 'markdownlint-rule-helpers'
23import { getFrontmatter } from '@/content-linter/lib/helpers/utils'
34
5+ import type { RuleParams , RuleErrorCallback } from '@/content-linter/types'
6+
7+ interface PropertyLimits {
8+ max : number
9+ recommended : number
10+ required ?: boolean
11+ }
12+
13+ interface ContentRules {
14+ title : PropertyLimits
15+ shortTitle : PropertyLimits
16+ intro : PropertyLimits
17+ requiredProperties : string [ ]
18+ }
19+
20+ type ContentType = 'category' | 'mapTopic' | 'article' | null
21+
422// Strip liquid tags from text for character counting purposes
5- function stripLiquidTags ( text ) {
6- if ( typeof text !== 'string' ) return text
23+ function stripLiquidTags ( text : unknown ) : string {
24+ if ( typeof text !== 'string' ) return text as string
725 // Remove both {% %} and {{ }} liquid tags
826 return text . replace ( / \{ % .* ?% \} / g, '' ) . replace ( / \{ \{ .* ?\} \} / g, '' )
927}
@@ -13,15 +31,15 @@ export const frontmatterValidation = {
1331 description :
1432 'Frontmatter properties must meet character limits and required property requirements' ,
1533 tags : [ 'frontmatter' , 'character-limits' , 'required-properties' ] ,
16- function : ( params , onError ) => {
17- const fm = getFrontmatter ( params . lines )
34+ function : ( params : RuleParams , onError : RuleErrorCallback ) => {
35+ const fm = getFrontmatter ( params . lines as string [ ] )
1836 if ( ! fm ) return
1937
2038 // Detect content type based on frontmatter properties and file path
2139 const contentType = detectContentType ( fm , params . name )
2240
2341 // Define character limits and requirements for different content types
24- const contentRules = {
42+ const contentRules : Record < string , ContentRules > = {
2543 category : {
2644 title : { max : 70 , recommended : 67 } ,
2745 shortTitle : { max : 30 , recommended : 27 } ,
@@ -42,7 +60,7 @@ export const frontmatterValidation = {
4260 } ,
4361 }
4462
45- const rules = contentRules [ contentType ]
63+ const rules = contentType ? contentRules [ contentType ] : null
4664 if ( ! rules ) return
4765
4866 // Check required properties
@@ -61,14 +79,21 @@ export const frontmatterValidation = {
6179
6280 // Check title length
6381 if ( fm . title ) {
64- validatePropertyLength ( onError , params . lines , 'title' , fm . title , rules . title , 'Title' )
82+ validatePropertyLength (
83+ onError ,
84+ params . lines as string [ ] ,
85+ 'title' ,
86+ fm . title ,
87+ rules . title ,
88+ 'Title' ,
89+ )
6590 }
6691
6792 // Check shortTitle length
6893 if ( fm . shortTitle ) {
6994 validatePropertyLength (
7095 onError ,
71- params . lines ,
96+ params . lines as string [ ] ,
7297 'shortTitle' ,
7398 fm . shortTitle ,
7499 rules . shortTitle ,
@@ -78,17 +103,24 @@ export const frontmatterValidation = {
78103
79104 // Check intro length if it exists
80105 if ( fm . intro && rules . intro ) {
81- validatePropertyLength ( onError , params . lines , 'intro' , fm . intro , rules . intro , 'Intro' )
106+ validatePropertyLength (
107+ onError ,
108+ params . lines as string [ ] ,
109+ 'intro' ,
110+ fm . intro ,
111+ rules . intro ,
112+ 'Intro' ,
113+ )
82114 }
83115
84116 // Cross-property validation: if title is longer than shortTitle limit, shortTitle must exist
85117 const strippedTitle = stripLiquidTags ( fm . title )
86- if ( fm . title && strippedTitle . length > rules . shortTitle . max && ! fm . shortTitle ) {
87- const titleLine = findPropertyLine ( params . lines , 'title' )
118+ if ( fm . title && ( strippedTitle as string ) . length > rules . shortTitle . max && ! fm . shortTitle ) {
119+ const titleLine = findPropertyLine ( params . lines as string [ ] , 'title' )
88120 addError (
89121 onError ,
90122 titleLine ,
91- `Title is ${ strippedTitle . length } characters, which exceeds the shortTitle limit of ${ rules . shortTitle . max } characters. A shortTitle must be provided.` ,
123+ `Title is ${ ( strippedTitle as string ) . length } characters, which exceeds the shortTitle limit of ${ rules . shortTitle . max } characters. A shortTitle must be provided.` ,
92124 fm . title ,
93125 null ,
94126 null ,
@@ -98,10 +130,10 @@ export const frontmatterValidation = {
98130 // Special validation for articles: should have at least one topic
99131 if ( contentType === 'article' && fm . topics ) {
100132 if ( ! Array . isArray ( fm . topics ) ) {
101- const topicsLine = findPropertyLine ( params . lines , 'topics' )
133+ const topicsLine = findPropertyLine ( params . lines as string [ ] , 'topics' )
102134 addError ( onError , topicsLine , 'Topics must be an array' , String ( fm . topics ) , null , null )
103135 } else if ( fm . topics . length === 0 ) {
104- const topicsLine = findPropertyLine ( params . lines , 'topics' )
136+ const topicsLine = findPropertyLine ( params . lines as string [ ] , 'topics' )
105137 addError (
106138 onError ,
107139 topicsLine ,
@@ -115,9 +147,16 @@ export const frontmatterValidation = {
115147 } ,
116148}
117149
118- function validatePropertyLength ( onError , lines , propertyName , propertyValue , limits , displayName ) {
150+ function validatePropertyLength (
151+ onError : RuleErrorCallback ,
152+ lines : string [ ] ,
153+ propertyName : string ,
154+ propertyValue : string ,
155+ limits : PropertyLimits ,
156+ displayName : string ,
157+ ) : void {
119158 const strippedValue = stripLiquidTags ( propertyValue )
120- const propertyLength = strippedValue . length
159+ const propertyLength = ( strippedValue as string ) . length
121160 const propertyLine = findPropertyLine ( lines , propertyName )
122161
123162 // Only report the most severe error - maximum takes precedence over recommended
@@ -142,7 +181,8 @@ function validatePropertyLength(onError, lines, propertyName, propertyValue, lim
142181 }
143182}
144183
145- function detectContentType ( frontmatter , filePath ) {
184+ // frontmatter object structure varies based on YAML content, using any for flexibility
185+ function detectContentType ( frontmatter : any , filePath : string ) : ContentType {
146186 // Only apply validation to markdown files
147187 if ( ! filePath || ! filePath . endsWith ( '.md' ) ) {
148188 return null
@@ -168,7 +208,7 @@ function detectContentType(frontmatter, filePath) {
168208 return 'article'
169209}
170210
171- function findPropertyLine ( lines , property ) {
211+ function findPropertyLine ( lines : string [ ] , property : string ) : number {
172212 const line = lines . find ( ( line ) => line . trim ( ) . startsWith ( `${ property } :` ) )
173213 return line ? lines . indexOf ( line ) + 1 : 1
174214}
0 commit comments