@@ -35,10 +35,35 @@ import { visit } from 'unist-util-visit'
3535import { h } from 'hastscript'
3636import { fromMarkdown } from 'mdast-util-from-markdown'
3737import { toHast } from 'mdast-util-to-hast'
38+ import type { Root } from 'mdast'
3839import { header } from './code-header'
3940import findPage from '@/frame/lib/find-page'
4041
41- const languages = yaml . load ( fs . readFileSync ( './data/code-languages.yml' , 'utf8' ) )
42+ interface LanguageConfig {
43+ comment : 'number' | 'slash' | 'xml' | 'percent' | 'hyphen'
44+ [ key : string ] : any
45+ }
46+
47+ interface ElementNode {
48+ type : 'element'
49+ tagName : string
50+ properties : {
51+ className ?: string [ ]
52+ [ key : string ] : any
53+ }
54+ children : any [ ]
55+ data ?: {
56+ meta ?: {
57+ annotate ?: boolean
58+ [ key : string ] : any
59+ }
60+ }
61+ }
62+
63+ const languages = yaml . load ( fs . readFileSync ( './data/code-languages.yml' , 'utf8' ) ) as Record <
64+ string ,
65+ LanguageConfig
66+ >
4267
4368const commentRegexes = {
4469 // Also known has hash or sharp; but the unicode name is "number sign".
@@ -64,18 +89,25 @@ const commentRegexes = {
6489 hyphen : / ^ \s * - - \s * / ,
6590}
6691
67- const matcher = ( node ) =>
68- node . type === 'element' && node . tagName === 'pre' && getPreMeta ( node ) . annotate
92+ // Using 'any' for node because unist-util-visit requires broad type compatibility
93+ const matcher = ( node : any ) : node is ElementNode =>
94+ node . type === 'element' && node . tagName === 'pre' && Boolean ( getPreMeta ( node ) . annotate )
6995
70- export default function annotate ( context ) {
71- return ( tree ) => {
72- visit ( tree , matcher , ( node , index , parent ) => {
73- parent . children [ index ] = createAnnotatedNode ( node , context )
96+ // Using 'any' for context because unified plugins receive different context types depending on processor configuration
97+ export default function annotate ( context : any ) {
98+ // Using 'any' for tree because unified's AST types are complex and vary between processors
99+ return ( tree : any ) => {
100+ // Using 'any' for parent because unist-util-visit's callback typing doesn't provide specific parent types
101+ visit ( tree , matcher , ( node : ElementNode , index : number | undefined , parent : any ) => {
102+ if ( index !== undefined && parent ) {
103+ parent . children [ index ] = createAnnotatedNode ( node , context )
104+ }
74105 } )
75106 }
76107}
77108
78- function createAnnotatedNode ( node , context ) {
109+ // Using 'any' for context to match the plugin signature, and return type because hastscript returns complex hast types
110+ function createAnnotatedNode ( node : ElementNode , context : any ) : any {
79111 const lang = node . children [ 0 ] . properties . className [ 0 ] . replace ( 'language-' , '' )
80112 const code = node . children [ 0 ] . children [ 0 ] . value
81113
@@ -102,7 +134,7 @@ function createAnnotatedNode(node, context) {
102134 return template ( { lang, code, rows, context } )
103135}
104136
105- function validate ( lang , code ) {
137+ function validate ( lang : string , code : string ) : void {
106138 if ( ! lang ) {
107139 throw new Error ( 'No language specific for annotate info string.' )
108140 }
@@ -128,33 +160,34 @@ function validate(lang, code) {
128160 }
129161}
130162
131- function getRegexp ( lang ) {
163+ function getRegexp ( lang : string ) : RegExp {
132164 return commentRegexes [ languages [ lang ] . comment ]
133165}
134166
135- function hasChar ( line ) {
167+ function hasChar ( line : string ) : boolean {
136168 return Boolean ( line . trim ( ) )
137169}
138170
139- function chunkBy ( arr , predicate ) {
140- const groups = [ [ ] ]
171+ function chunkBy ( arr : string [ ] , predicate : ( item : string ) => boolean ) : string [ ] [ ] {
172+ const groups : string [ ] [ ] = [ [ ] ]
141173 let on = predicate ( arr [ 0 ] )
142174 for ( const item of arr ) {
143175 if ( ( ! on && predicate ( item ) ) || ( on && ! predicate ( item ) ) ) {
144176 on = ! on
145177 groups . push ( [ ] )
146178 }
147- last ( groups ) . push ( item )
179+ last ( groups ) ! . push ( item )
148180 }
149181 return groups
150182}
151183
152- function matchComment ( lang ) {
184+ function matchComment ( lang : string ) : ( line : string ) => boolean {
153185 const regex = getRegexp ( lang )
154186 return ( line ) => regex . test ( line )
155187}
156188
157- function getSubnav ( ) {
189+ // Using 'any' return type because hastscript's h() function returns complex hast element types
190+ function getSubnav ( ) : any {
158191 const besideBtn = h (
159192 'button' ,
160193 {
@@ -179,7 +212,18 @@ function getSubnav() {
179212 return h ( 'div' , { className : 'annotate-toggle' } , [ besideBtn , inlineBtn ] )
180213}
181214
182- function template ( { lang, code, rows, context } ) {
215+ // Using 'any' for context and return type due to hastscript's complex type definitions
216+ function template ( {
217+ lang,
218+ code,
219+ rows,
220+ context,
221+ } : {
222+ lang : string
223+ code : string
224+ rows : string [ ] [ ] [ ]
225+ context : any
226+ } ) : any {
183227 return h (
184228 'div' ,
185229 { class : 'annotate beside' } ,
@@ -210,20 +254,20 @@ function template({ lang, code, rows, context }) {
210254 )
211255}
212256
213- function mdToHast ( text , context ) {
214- const mdast = fromMarkdown ( text )
257+ // Using 'any' for context and return type to maintain compatibility with mdast-util-to-hast complex types
258+ function mdToHast ( text : string , context : any ) : any {
259+ const mdast : Root = fromMarkdown ( text )
215260
216- // Process AUTOTITLE links if context is available
217- if ( context ) {
218- processAutotitleInMdast ( mdast , context )
219- }
261+ // Process AUTOTITLE links
262+ processAutotitleInMdast ( mdast , context )
220263
221264 return toHast ( mdast )
222265}
223266
224267// Helper method to process AUTOTITLE links in MDAST
225268// This can be reused for other MDAST processing that needs AUTOTITLE support
226- function processAutotitleInMdast ( mdast , context ) {
269+ // Using 'any' for context because it may or may not have pages/redirects properties depending on usage
270+ function processAutotitleInMdast ( mdast : Root , context : any ) : void {
227271 visit ( mdast , 'link' , ( node ) => {
228272 if ( node . url && node . url . startsWith ( '/' ) ) {
229273 for ( const child of node . children ) {
@@ -236,7 +280,10 @@ function processAutotitleInMdast(mdast, context) {
236280 child . value = page . rawTitle || 'AUTOTITLE'
237281 } catch ( error ) {
238282 // Keep AUTOTITLE if we can't get the title
239- console . warn ( `Could not resolve AUTOTITLE for ${ node . url } :` , error . message )
283+ console . warn (
284+ `Could not resolve AUTOTITLE for ${ node . url } :` ,
285+ error instanceof Error ? error . message : String ( error ) ,
286+ )
240287 }
241288 }
242289 }
@@ -245,12 +292,12 @@ function processAutotitleInMdast(mdast, context) {
245292 } )
246293}
247294
248- function removeComment ( lang ) {
295+ function removeComment ( lang : string ) : ( line : string ) => string {
249296 const regex = getRegexp ( lang )
250297 return ( line ) => line . replace ( regex , '' )
251298}
252299
253- function getPreMeta ( node ) {
300+ function getPreMeta ( node : ElementNode ) : { annotate ?: boolean ; [ key : string ] : any } {
254301 // Here's why this monstrosity works:
255302 // https://github.com/syntax-tree/mdast-util-to-hast/blob/c87cd606731c88a27dbce4bfeaab913a9589bf83/lib/handlers/code.js#L40-L42
256303 return node . children [ 0 ] ?. data ?. meta || { }
0 commit comments