1+ const fs = require ( 'fs' ) ;
2+ const path = require ( 'path' ) ;
3+
4+ // --- CONFIGURATION ---
5+ // The directories to scan for snippets
6+ const DOCS_DIRS = [ './docs' , './unversioned' ] ;
7+ // ---------------------
8+
9+ const snippetRegistry = new Map ( ) ;
10+ let isIndexed = false ;
11+
12+ // Helper: Recursively find all .md/.mdx files
13+ const getAllFiles = ( dirPath , arrayOfFiles = [ ] ) => {
14+ if ( ! fs . existsSync ( dirPath ) ) return arrayOfFiles ;
15+
16+ const files = fs . readdirSync ( dirPath ) ;
17+ files . forEach ( ( file ) => {
18+ const fullPath = path . join ( dirPath , file ) ;
19+ if ( fs . statSync ( fullPath ) . isDirectory ( ) ) {
20+ getAllFiles ( fullPath , arrayOfFiles ) ;
21+ } else if ( file . endsWith ( '.md' ) || file . endsWith ( '.mdx' ) ) {
22+ arrayOfFiles . push ( fullPath ) ;
23+ }
24+ } ) ;
25+ return arrayOfFiles ;
26+ } ;
27+
28+ // Helper: Extract Doc ID from Frontmatter
29+ const getDocId = ( content , filename ) => {
30+ const idMatch = content . match ( / ^ - - - \s + [ \s \S ] * ?\n i d : \s * ( .* ?) \s * [ \n \r ] / m) ;
31+ if ( idMatch && idMatch [ 1 ] ) {
32+ return idMatch [ 1 ] . replace ( / [ ' " ] / g, '' ) . trim ( ) ;
33+ }
34+ return filename ;
35+ } ;
36+
37+ // --- CORE LOGIC ---
38+ const buildIndex = ( ) => {
39+ if ( isIndexed ) return ;
40+ console . log ( '[ExtractPreprocessor] ⚡ Indexing snippets via Regex...' ) ;
41+
42+ const allFiles = [ ] ;
43+ DOCS_DIRS . forEach ( dir => getAllFiles ( path . resolve ( process . cwd ( ) , dir ) , allFiles ) ) ;
44+
45+ let count = 0 ;
46+
47+ // Regex to find: <div data-extract="ID"> CONTENT </div>
48+ // We use [\s\S]*? to match content across multiple lines (lazy match)
49+ const extractRegex = / < d i v \s + d a t a - e x t r a c t = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] [ ^ > ] * > ( [ \s \S ] * ?) < \/ d i v > / g;
50+
51+ allFiles . forEach ( filePath => {
52+ try {
53+ const content = fs . readFileSync ( filePath , 'utf8' ) ;
54+ const filename = path . basename ( filePath , path . extname ( filePath ) ) ;
55+ const docId = getDocId ( content , filename ) ;
56+
57+ let match ;
58+ // Loop through all matches in the file
59+ while ( ( match = extractRegex . exec ( content ) ) !== null ) {
60+ let [ fullTag , extractId , snippetContent ] = match ;
61+
62+ // Clean up the content (optional: trim leading/trailing newlines)
63+ snippetContent = snippetContent . replace ( / ^ \n + | \n + $ / g, '' ) ;
64+
65+ // Generate Key: "docId:snippetId"
66+ // If the ID already has a colon, assume user provided full ID
67+ const key = extractId . includes ( ':' ) ? extractId : `${ docId } :${ extractId } ` ;
68+
69+ snippetRegistry . set ( key , snippetContent ) ;
70+ console . log ( `[ExtractPreprocessor] ⚡ Indexed snippet: ${ key } ` ) ;
71+ count ++ ;
72+ }
73+ } catch ( e ) {
74+ console . warn ( `[ExtractPreprocessor] Failed to read ${ filePath } ` ) ;
75+ }
76+ } ) ;
77+
78+ isIndexed = true ;
79+ console . log ( `[ExtractPreprocessor] ⚡ Indexed ${ count } snippets.` ) ;
80+ } ;
81+
82+ // This function is called by Docusaurus for EVERY markdown file
83+ const preprocessor = ( { filePath, fileContent } ) => {
84+ // 1. Ensure Index exists (runs once)
85+ buildIndex ( ) ;
86+
87+ // 2. Regex to find: <div data-extract-copy="ID" />
88+ // Matches <div data-extract-copy="xyz"></div> OR <div data-extract-copy="xyz" />
89+ const copyRegex = / < d i v \s + d a t a - e x t r a c t - c o p y = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] \s * \/ ? > \s * (?: < \/ d i v > ) ? / g;
90+
91+ // 3. Replace with content
92+ return fileContent . replace ( copyRegex , ( match , requestedId ) => {
93+ if ( snippetRegistry . has ( requestedId ) ) {
94+ // Return the stored snippet content
95+ return snippetRegistry . get ( requestedId ) ;
96+ } else {
97+ console . error ( `[ExtractPreprocessor] ❌ Snippet not found: "${ requestedId } " in ${ path . basename ( filePath ) } ` ) ;
98+ // Return an error message in the UI so you see it
99+ return `> **Error: Snippet "${ requestedId } " not found.**` ;
100+ }
101+ } ) ;
102+ } ;
103+
104+ module . exports = preprocessor ;
0 commit comments