1
1
#!/usr/bin/env node
2
+ // @ts -check
2
3
3
4
import fs from "node:fs"
4
5
import path from "node:path"
5
- import glob from "glob"
6
+ import glob from "fast- glob"
6
7
import { parse } from "graphql"
7
- import chalk from "chalk"
8
8
import { fileURLToPath } from "node:url"
9
9
10
10
const __filename = fileURLToPath ( import . meta. url )
11
11
const __dirname = path . dirname ( __filename )
12
12
const projectRoot = path . resolve ( __dirname , "../" )
13
13
14
+ /** @type {string } Glob pattern for MDX files to validate */
14
15
const MDX_GLOB = "./src/pages/learn/**/*.mdx"
16
+ /** @type {RegExp } Regex to match code blocks in markdown */
15
17
const CODE_BLOCK_REGEX = / ^ ( ` { 3 , } ) ( \w + ) \s * \n ( [ \s \S ] * ?) \r ? \n \1$ / gm
18
+ /** @type {string } Comment to ignore code snippets */
16
19
const IGNORE_COMMENT = "snippet-ignore"
17
20
21
+ /** @type {number } */
18
22
let totalFiles = 0
23
+ /** @type {number } */
19
24
let totalSnippets = 0
25
+ /** @type {number } */
20
26
let totalErrors = 0
21
27
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
+ */
43
31
32
+ /**
33
+ * @param {string } code
34
+ * @returns {ParseError[] }
35
+ */
44
36
function validateGraphQL ( code ) {
45
37
try {
46
38
parse ( code )
47
39
return [ ]
48
40
} catch ( error ) {
49
- return [ { message : error . message } ]
41
+ return [ { message : error instanceof Error ? error . message : String ( error ) } ]
50
42
}
51
43
}
52
44
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
+ */
53
59
function extractSnippets ( content , filePath ) {
54
60
const snippets = [ ]
55
61
let match
@@ -69,43 +75,48 @@ function extractSnippets(content, filePath) {
69
75
return snippets
70
76
}
71
77
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
+ */
72
91
async function validateSnippet ( snippet ) {
73
92
const { lang, code, lineNumber, filePath } = snippet
74
93
75
94
if ( ! code . trim ( ) ) return [ ]
76
95
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
-
88
96
if ( lang === "graphql" ) {
89
97
const messages = validateGraphQL ( code )
90
98
return messages . map ( msg => ( {
91
99
type : "GraphQL" ,
92
100
file : filePath ,
93
- line : lineNumber + ( msg . line || 1 ) ,
101
+ line : lineNumber ,
94
102
message : msg . message ,
95
103
} ) )
96
104
}
97
105
98
106
return [ ]
99
107
}
100
108
109
+ /**
110
+ * @returns {Promise<void> }
111
+ */
101
112
async function main ( ) {
102
- console . log ( `Validating code snippets in: ${ projectRoot } / ${ MDX_GLOB } ` )
113
+ console . log ( `Validating code snippets in: ${ MDX_GLOB } ` )
103
114
104
115
const files = glob . sync ( MDX_GLOB , { cwd : projectRoot } )
105
116
totalFiles = files . length
106
117
107
118
if ( totalFiles === 0 ) {
108
- console . log ( chalk . green ( "No MDX files found to validate." ) )
119
+ console . log ( "No MDX files found to validate." )
109
120
return
110
121
}
111
122
@@ -127,25 +138,21 @@ async function main() {
127
138
if ( totalErrors > 0 ) {
128
139
errors . forEach ( err => {
129
140
const errorMessage = `${ err . type } Error in ${ err . file } at line ${ err . line } : ${ err . message } `
130
- console . error ( chalk . red ( errorMessage ) )
141
+ console . error ( errorMessage )
131
142
132
143
if ( process . env . GITHUB_ACTIONS ) {
133
144
console . log ( `::error file=${ err . file } ,line=${ err . line } ::${ err . message } ` )
134
145
}
135
146
} )
136
147
137
- console . error (
138
- chalk . red ( "\nCode snippet validation failed. Check error logs." ) ,
139
- )
148
+ console . error ( "\nCode snippet validation failed. Check error logs." )
140
149
console . error ( `Files checked: ${ totalFiles } ` )
141
150
console . error ( `Snippets checked: ${ totalSnippets } ` )
142
151
console . error ( `Errors found: ${ totalErrors } ` )
143
152
process . exit ( 1 )
144
153
} else {
145
154
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." ,
149
156
)
150
157
console . log ( `Files checked: ${ totalFiles } ` )
151
158
console . log ( `Snippets checked: ${ totalSnippets } ` )
0 commit comments