1+ import { visit } from "unist-util-visit" ;
2+ import type { Root , Element , Text , ElementContent } from "hast" ;
3+
4+ /**
5+ * Rehype plugin that wraps timecode patterns in <code> tags.
6+ * Looks for list items that start with `[<timecode link>]` pattern
7+ * and wraps them in <code> tags for styling.
8+ *
9+ * Input: <li>[<a href="#intro">00:00:00</a>] Intro</li>
10+ * Output: <li><code>[<a href="#intro">00:00:00</a>]</code> Intro</li>
11+ */
12+ export default function rehypeWrapTimecodes ( ) {
13+ return ( tree : Root ) => {
14+ visit ( tree , "element" , ( node ) => {
15+ // Only process <li> elements
16+ if ( node . tagName !== "li" ) {
17+ return ;
18+ }
19+
20+ // Check if first child is a text node starting with "["
21+ if ( node . children . length < 3 ) {
22+ return ;
23+ }
24+
25+ const firstChild = node . children [ 0 ] ;
26+ if ( firstChild . type !== "text" || ! firstChild . value . startsWith ( "[" ) ) {
27+ return ;
28+ }
29+
30+ // Check if second child is a link with a timecode pattern
31+ const secondChild = node . children [ 1 ] ;
32+ if ( secondChild . type !== "element" || secondChild . tagName !== "a" ) {
33+ return ;
34+ }
35+
36+ // Check if the link text matches timecode pattern (HH:MM:SS or MM:SS)
37+ const linkText =
38+ secondChild . children [ 0 ] ?. type === "text"
39+ ? secondChild . children [ 0 ] . value
40+ : "" ;
41+ const timecodePattern = / ^ \d { 1 , 2 } : \d { 2 } ( : \d { 2 } ) ? $ / ;
42+ if ( ! timecodePattern . test ( linkText ) ) {
43+ return ;
44+ }
45+
46+ // Check if third child is a text node starting with "]"
47+ const thirdChild = node . children [ 2 ] ;
48+ if ( thirdChild . type !== "text" || ! thirdChild . value . startsWith ( "]" ) ) {
49+ return ;
50+ }
51+
52+ // Extract and modify the bracket text nodes
53+ const openingBracket : Text = {
54+ type : "text" ,
55+ value : "[" ,
56+ } ;
57+
58+ const closingBracket : Text = {
59+ type : "text" ,
60+ value : "]" ,
61+ } ;
62+
63+ // Update the original text nodes to remove the brackets
64+ const firstTextNode = firstChild as Text ;
65+ firstTextNode . value = firstTextNode . value . slice ( 1 ) ; // Remove leading "["
66+
67+ const thirdTextNode = thirdChild as Text ;
68+ thirdTextNode . value = thirdTextNode . value . slice ( 1 ) ; // Remove leading "]"
69+
70+ // Create the <code> wrapper with the timecode content
71+ const codeElement : Element = {
72+ type : "element" ,
73+ tagName : "code" ,
74+ properties : { } ,
75+ children : [ openingBracket , secondChild , closingBracket ] ,
76+ } ;
77+
78+ // Rebuild the children array
79+ const remainingContent = node . children . slice ( 2 ) ;
80+ node . children = [ codeElement , ...remainingContent ] ;
81+ } ) ;
82+ } ;
83+ }
0 commit comments