11#!/usr/bin/env node
2+ // @ts -check
23
34import fs from "node:fs"
45import path from "node:path"
5- import glob from "glob"
6+ import glob from "fast- glob"
67import { parse } from "graphql"
7- import chalk from "chalk"
88import { fileURLToPath } from "node:url"
99
1010const __filename = fileURLToPath ( import . meta. url )
1111const __dirname = path . dirname ( __filename )
1212const projectRoot = path . resolve ( __dirname , "../" )
1313
14+ /** @type {string } Glob pattern for MDX files to validate */
1415const MDX_GLOB = "./src/pages/learn/**/*.mdx"
16+ /** @type {RegExp } Regex to match code blocks in markdown */
1517const CODE_BLOCK_REGEX = / ^ ( ` { 3 , } ) ( \w + ) \s * \n ( [ \s \S ] * ?) \r ? \n \1$ / gm
18+ /** @type {string } Comment to ignore code snippets */
1619const IGNORE_COMMENT = "snippet-ignore"
1720
21+ /** @type {number } */
1822let totalFiles = 0
23+ /** @type {number } */
1924let totalSnippets = 0
25+ /** @type {number } */
2026let totalErrors = 0
2127
22- // TODO: Add JS linting after JS code snippet modernization
23- // async function lintJavaScript(code, filePath) {
24- // const eslint = new ESLint({
25- // useEslintrc: true,
26- // baseConfig: {
27- // parserOptions: {
28- // ecmaVersion: "latest",
29- // sourceType: "module",
30- // },
31- // },
32- // })
33-
34- // let preparedCode = code.trim()
35-
36- // if (preparedCode.startsWith("function")) {
37- // preparedCode = "/* eslint-disable no-unused-vars */\n" + preparedCode
38- // }
39-
40- // const results = await eslint.lintText(preparedCode, { filePath })
41- // return results.flatMap(result => result.messages)
42- // }
28+ /**
29+ * @typedef {{ message: string } } ParseError
30+ */
4331
32+ /**
33+ * @param {string } code
34+ * @returns {ParseError[] }
35+ */
4436function validateGraphQL ( code ) {
4537 try {
4638 parse ( code )
4739 return [ ]
4840 } catch ( error ) {
49- return [ { message : error . message } ]
41+ return [ { message : error instanceof Error ? error . message : String ( error ) } ]
5042 }
5143}
5244
45+ /**
46+ * @typedef {{
47+ * lang: string,
48+ * code: string,
49+ * lineNumber: number,
50+ * filePath: string
51+ * }} Snippet
52+
53+ /**
54+ * Extracts code snippets from MDX content
55+ * @param {string } content - The MDX file content
56+ * @param {string } filePath - The path to the file being processed
57+ * @returns {Snippet[] } Array of extracted code snippets
58+ */
5359function extractSnippets ( content , filePath ) {
5460 const snippets = [ ]
5561 let match
@@ -69,43 +75,48 @@ function extractSnippets(content, filePath) {
6975 return snippets
7076}
7177
78+ /**
79+ * @typedef {{
80+ * type: string,
81+ * file: string,
82+ * line: number,
83+ * message: string
84+ * }} ValidationError
85+ */
86+
87+ /**
88+ * @param {Snippet } snippet - The code snippet to validate
89+ * @returns {Promise<ValidationError[]> } Array of validation errors
90+ */
7291async function validateSnippet ( snippet ) {
7392 const { lang, code, lineNumber, filePath } = snippet
7493
7594 if ( ! code . trim ( ) ) return [ ]
7695
77- // TODO: Add section after JS code snippet modernization
78- // if (["js", "javascript", "ts", "typescript"].includes(lang)) {
79- // const messages = await lintJavaScript(code, filePath)
80- // return messages.map(msg => ({
81- // type: "JS/TS",
82- // file: filePath,
83- // line: lineNumber + (msg.line || 1),
84- // message: msg.message,
85- // }))
86- // }
87-
8896 if ( lang === "graphql" ) {
8997 const messages = validateGraphQL ( code )
9098 return messages . map ( msg => ( {
9199 type : "GraphQL" ,
92100 file : filePath ,
93- line : lineNumber + ( msg . line || 1 ) ,
101+ line : lineNumber ,
94102 message : msg . message ,
95103 } ) )
96104 }
97105
98106 return [ ]
99107}
100108
109+ /**
110+ * @returns {Promise<void> }
111+ */
101112async function main ( ) {
102- console . log ( `Validating code snippets in: ${ projectRoot } / ${ MDX_GLOB } ` )
113+ console . log ( `Validating code snippets in: ${ MDX_GLOB } ` )
103114
104115 const files = glob . sync ( MDX_GLOB , { cwd : projectRoot } )
105116 totalFiles = files . length
106117
107118 if ( totalFiles === 0 ) {
108- console . log ( chalk . green ( "No MDX files found to validate." ) )
119+ console . log ( "No MDX files found to validate." )
109120 return
110121 }
111122
@@ -127,25 +138,21 @@ async function main() {
127138 if ( totalErrors > 0 ) {
128139 errors . forEach ( err => {
129140 const errorMessage = `${ err . type } Error in ${ err . file } at line ${ err . line } : ${ err . message } `
130- console . error ( chalk . red ( errorMessage ) )
141+ console . error ( errorMessage )
131142
132143 if ( process . env . GITHUB_ACTIONS ) {
133144 console . log ( `::error file=${ err . file } ,line=${ err . line } ::${ err . message } ` )
134145 }
135146 } )
136147
137- console . error (
138- chalk . red ( "\nCode snippet validation failed. Check error logs." ) ,
139- )
148+ console . error ( "\nCode snippet validation failed. Check error logs." )
140149 console . error ( `Files checked: ${ totalFiles } ` )
141150 console . error ( `Snippets checked: ${ totalSnippets } ` )
142151 console . error ( `Errors found: ${ totalErrors } ` )
143152 process . exit ( 1 )
144153 } else {
145154 console . log (
146- chalk . green (
147- "\nCode snippet validation passed. All code snippets are valid." ,
148- ) ,
155+ "\n✅ Code snippet validation passed. All code snippets are valid." ,
149156 )
150157 console . log ( `Files checked: ${ totalFiles } ` )
151158 console . log ( `Snippets checked: ${ totalSnippets } ` )
0 commit comments