1- import markdown from "markdown-it" ;
1+ import MarkdownIt from "markdown-it" ;
22// @types /markdown-it is busted, this type isn't exported with ESM.
33import type md from "markdown-it" with { "resolution-mode" : "require" } ;
44
@@ -12,6 +12,7 @@ import type { DefaultTheme, DefaultThemeRenderContext, Renderer } from "../index
1212import { Slugger } from "./default/Slugger.js" ;
1313import { anchorIcon } from "./default/partials/anchor-icon.js" ;
1414import { type Reflection , ReflectionKind , type CommentDisplayPart } from "../../models/index.js" ;
15+ import type { TranslatedString , TranslationProxy } from "../../internationalization/index.js" ;
1516
1617let defaultSlugger : Slugger | undefined ;
1718function getDefaultSlugger ( logger : Logger ) {
@@ -39,7 +40,7 @@ export class MarkedPlugin extends ContextAwareRendererComponent {
3940 @Option ( "markdownLinkExternal" )
4041 accessor markdownLinkExternal ! : boolean ;
4142
42- private parser ?: markdown ;
43+ private parser ?: MarkdownIt ;
4344
4445 /**
4546 * This needing to be here really feels hacky... probably some nicer way to do this.
@@ -225,7 +226,7 @@ export class MarkedPlugin extends ContextAwareRendererComponent {
225226 * @returns The options object for the markdown parser.
226227 */
227228 private setupParser ( ) {
228- this . parser = markdown ( {
229+ this . parser = MarkdownIt ( {
229230 ...this . markdownItOptions ,
230231 highlight : ( code , lang ) => {
231232 code = this . getHighlighted ( code , lang || "ts" ) ;
@@ -239,6 +240,8 @@ export class MarkedPlugin extends ContextAwareRendererComponent {
239240 } ,
240241 } ) ;
241242
243+ githubAlertMarkdownPlugin ( this . parser , this . application . i18n ) ;
244+
242245 const loader = this . application . options . getValue ( "markdownItLoader" ) ;
243246 loader ( this . parser ) ;
244247
@@ -285,6 +288,13 @@ export class MarkedPlugin extends ContextAwareRendererComponent {
285288 }
286289 return self . renderToken ( tokens , idx , options ) ;
287290 } ;
291+
292+ this . parser . renderer . rules [ "alert_open" ] = ( tokens , idx ) => {
293+ const icon = this . renderContext . icons [ tokens [ idx ] . attrGet ( "icon" ) as AlertIconName ] ;
294+ const iconHtml = renderElement ( icon ( ) ) ;
295+
296+ return `<div class="${ tokens [ idx ] . attrGet ( "class" ) } "><div class="tsd-alert-title">${ iconHtml } <span>${ tokens [ idx ] . attrGet ( "alert" ) } </span></div>` ;
297+ } ;
288298 }
289299
290300 /**
@@ -303,3 +313,63 @@ function getTokenTextContent(token: md.Token): string {
303313 }
304314 return token . content ;
305315}
316+
317+ const kindNames = [ "note" , "tip" , "important" , "warning" , "caution" ] ;
318+ const iconNames = [ "alertNote" , "alertTip" , "alertImportant" , "alertWarning" , "alertCaution" ] as const ;
319+ type AlertIconName = ( typeof iconNames ) [ number ] ;
320+ const kindTranslations : Array < ( i18n : TranslationProxy ) => TranslatedString > = [
321+ ( i18n ) => i18n . alert_note ( ) ,
322+ ( i18n ) => i18n . alert_tip ( ) ,
323+ ( i18n ) => i18n . alert_important ( ) ,
324+ ( i18n ) => i18n . alert_warning ( ) ,
325+ ( i18n ) => i18n . alert_caution ( ) ,
326+ ] ;
327+
328+ function githubAlertMarkdownPlugin ( md : MarkdownIt , i18n : TranslationProxy ) {
329+ md . core . ruler . after ( "block" , "typedoc-github-alert-plugin" , ( state ) => {
330+ let bqStarts : number [ ] = [ ] ;
331+
332+ for ( let i = 0 ; i < state . tokens . length ; ++ i ) {
333+ const token = state . tokens [ i ] ;
334+ if ( token . type === "blockquote_open" ) {
335+ bqStarts . push ( i ) ;
336+ } else if ( token . type === "blockquote_close" ) {
337+ if ( bqStarts . length === 1 ) {
338+ checkForAlert ( state . tokens , bqStarts [ 0 ] , i , i18n ) ;
339+ }
340+ bqStarts . pop ( ) ;
341+ }
342+ }
343+ } ) ;
344+ }
345+
346+ function checkForAlert ( tokens : md . Token [ ] , start : number , end : number , i18n : TranslationProxy ) {
347+ let alertKind = - 1 ;
348+
349+ // Search for the first "inline" token. That will be the blockquote text.
350+ for ( let i = start ; i < end ; ++ i ) {
351+ if ( tokens [ i ] . type === "inline" ) {
352+ // Check for `[!NOTE]`
353+ const kindString = tokens [ i ] . content . match ( / ^ \[ ! ( \w + ) \] / ) ;
354+ const kindIndex = kindNames . indexOf ( kindString ?. [ 1 ] . toLowerCase ( ) || "" ) ;
355+ if ( kindIndex !== - 1 ) {
356+ tokens [ i ] . content = tokens [ i ] . content . substring ( kindString ! [ 0 ] . length ) ;
357+ alertKind = kindIndex ;
358+ }
359+ break ;
360+ }
361+ }
362+
363+ // If we found an alert, then replace the blockquote_open and blockquote_close tokens with
364+ // alert_open and alert_close tokens that can be rendered specially.
365+ if ( alertKind === - 1 ) return ;
366+
367+ tokens [ start ] . type = "alert_open" ;
368+ tokens [ start ] . tag = "div" ;
369+ tokens [ start ] . attrPush ( [ "class" , `tsd-alert tsd-alert-${ kindNames [ alertKind ] } ` ] ) ;
370+ tokens [ start ] . attrPush ( [ "alert" , kindTranslations [ alertKind ] ( i18n ) ] ) ;
371+ tokens [ start ] . attrPush ( [ "icon" , iconNames [ alertKind ] ] ) ;
372+
373+ tokens [ end ] . type = "alert_close" ;
374+ tokens [ end ] . tag = "div" ;
375+ }
0 commit comments