1
- import { promises as fs } from 'fs' ;
1
+ import { promises as fs } from 'fs' ;
2
2
import glob from 'glob' ;
3
3
import util from 'util' ;
4
4
5
5
const globPromise = util . promisify ( glob ) ;
6
6
7
- const extractJsSnippets = ( markdownContent ) => {
7
+ const extractJsSnippets = markdownContent => {
8
8
const pattern = / ` ` ` (?: j s | j a v a s c r i p t ) \n ( [ \s \S ] * ?) ` ` ` / g;
9
9
const matches = [ ] ;
10
10
let match ;
@@ -15,7 +15,7 @@ const extractJsSnippets = (markdownContent) => {
15
15
start : match . index ,
16
16
end : match . index + match [ 0 ] . length ,
17
17
startLine : startLine ,
18
- language : match [ 0 ] . startsWith ( '```javascript' ) ? 'javascript' : 'js'
18
+ language : match [ 0 ] . startsWith ( '```javascript' ) ? 'javascript' : 'js' ,
19
19
} ) ;
20
20
}
21
21
return matches ;
@@ -29,17 +29,22 @@ const checkSemicolonsAndEllipsis = (code, startLine) => {
29
29
let openSquareBrackets = 0 ;
30
30
let inMultiLineComment = false ;
31
31
32
- const isJSDocOrComment = ( line ) => {
33
- return line . trim ( ) . startsWith ( '*' ) ||
32
+ const isJSDocOrComment = line => {
33
+ return (
34
+ line . trim ( ) . startsWith ( '*' ) ||
34
35
line . trim ( ) . startsWith ( '/**' ) ||
35
36
line . trim ( ) . startsWith ( '*/' ) ||
36
- line . trim ( ) . startsWith ( '//' ) ;
37
+ line . trim ( ) . startsWith ( '//' )
38
+ ) ;
37
39
} ;
38
40
39
41
const isStatementEnd = ( line , nextLine ) => {
40
42
const strippedLine = line . replace ( / \/ \/ .* $ / , '' ) . trim ( ) ;
41
- const strippedNextLine = nextLine ? nextLine . replace ( / \/ \/ .* $ / , '' ) . trim ( ) : '' ;
42
- return strippedLine &&
43
+ const strippedNextLine = nextLine
44
+ ? nextLine . replace ( / \/ \/ .* $ / , '' ) . trim ( )
45
+ : '' ;
46
+ return (
47
+ strippedLine &&
43
48
! strippedLine . endsWith ( '{' ) &&
44
49
! strippedLine . endsWith ( '}' ) &&
45
50
! strippedLine . endsWith ( ':' ) &&
@@ -61,19 +66,25 @@ const checkSemicolonsAndEllipsis = (code, startLine) => {
61
66
! strippedNextLine . trim ( ) . startsWith ( '.finally' ) &&
62
67
openBrackets === 0 &&
63
68
openParens === 0 &&
64
- openSquareBrackets === 0 ;
69
+ openSquareBrackets === 0
70
+ ) ;
65
71
} ;
66
72
67
73
const shouldHaveSemicolon = ( line , nextLine ) => {
68
74
const strippedLine = line . replace ( / \/ \/ .* $ / , '' ) . trim ( ) ;
69
- return ( strippedLine . startsWith ( 'const ' ) ||
70
- strippedLine . startsWith ( 'let ' ) ||
71
- strippedLine . startsWith ( 'var ' ) ||
72
- strippedLine . includes ( '=' ) ||
73
- / \b a w a i t \b / . test ( strippedLine ) ||
74
- ( / ^ [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ( \. [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ) * $ / . test ( strippedLine ) && ! strippedLine . endsWith ( '.' ) ) ||
75
- ( / ^ [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * \[ [ 0 - 9 ] + \] $ / . test ( strippedLine ) ) ) &&
76
- isStatementEnd ( line , nextLine ) ;
75
+ return (
76
+ ( strippedLine . startsWith ( 'const ' ) ||
77
+ strippedLine . startsWith ( 'let ' ) ||
78
+ strippedLine . startsWith ( 'var ' ) ||
79
+ strippedLine . includes ( '=' ) ||
80
+ / \b a w a i t \b / . test ( strippedLine ) ||
81
+ ( / ^ [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ( \. [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ) * $ / . test (
82
+ strippedLine ,
83
+ ) &&
84
+ ! strippedLine . endsWith ( '.' ) ) ||
85
+ / ^ [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * \[ [ 0 - 9 ] + \] $ / . test ( strippedLine ) ) &&
86
+ isStatementEnd ( line , nextLine )
87
+ ) ;
77
88
} ;
78
89
79
90
for ( let i = 0 ; i < lines . length ; i ++ ) {
@@ -87,9 +98,12 @@ const checkSemicolonsAndEllipsis = (code, startLine) => {
87
98
}
88
99
if ( inMultiLineComment || isJSDocOrComment ( line ) ) continue ;
89
100
90
- openBrackets += ( line . match ( / \{ / g) || [ ] ) . length - ( line . match ( / \} / g) || [ ] ) . length ;
91
- openParens += ( line . match ( / \( / g) || [ ] ) . length - ( line . match ( / \) / g) || [ ] ) . length ;
92
- openSquareBrackets += ( line . match ( / \[ / g) || [ ] ) . length - ( line . match ( / \] / g) || [ ] ) . length ;
101
+ openBrackets +=
102
+ ( line . match ( / \{ / g) || [ ] ) . length - ( line . match ( / \} / g) || [ ] ) . length ;
103
+ openParens +=
104
+ ( line . match ( / \( / g) || [ ] ) . length - ( line . match ( / \) / g) || [ ] ) . length ;
105
+ openSquareBrackets +=
106
+ ( line . match ( / \[ / g) || [ ] ) . length - ( line . match ( / \] / g) || [ ] ) . length ;
93
107
94
108
const codeWithoutComment = line . replace ( / \/ \/ .* $ / , '' ) . trim ( ) ;
95
109
@@ -99,22 +113,24 @@ const checkSemicolonsAndEllipsis = (code, startLine) => {
99
113
line : startLine + i ,
100
114
original : line . trim ( ) ,
101
115
fixed : '// ...' ,
102
- type : 'ellipsis'
116
+ type : 'ellipsis' ,
103
117
} ) ;
104
- } else if ( shouldHaveSemicolon ( line , nextLine ) && ! codeWithoutComment . endsWith ( ';' ) ) {
118
+ } else if (
119
+ shouldHaveSemicolon ( line , nextLine ) &&
120
+ ! codeWithoutComment . endsWith ( ';' )
121
+ ) {
105
122
issues . push ( {
106
123
line : startLine + i ,
107
124
original : line . trim ( ) ,
108
125
fixed : `${ codeWithoutComment } ;${ line . includes ( '//' ) ? ' ' + line . split ( '//' ) [ 1 ] : '' } ` ,
109
- type : 'semicolon'
126
+ type : 'semicolon' ,
110
127
} ) ;
111
128
}
112
129
}
113
130
114
131
return issues ;
115
132
} ;
116
133
117
-
118
134
const lintMarkdownFile = async ( filePath , fix = false ) => {
119
135
try {
120
136
const content = await fs . readFile ( filePath , 'utf8' ) ;
@@ -124,7 +140,10 @@ const lintMarkdownFile = async (filePath, fix = false) => {
124
140
125
141
for ( let i = jsSnippets . length - 1 ; i >= 0 ; i -- ) {
126
142
const snippet = jsSnippets [ i ] ;
127
- const issues = checkSemicolonsAndEllipsis ( snippet . content , snippet . startLine ) ;
143
+ const issues = checkSemicolonsAndEllipsis (
144
+ snippet . content ,
145
+ snippet . startLine ,
146
+ ) ;
128
147
allIssues . push ( ...issues . map ( issue => ( { ...issue , snippet : i + 1 } ) ) ) ;
129
148
130
149
if ( fix ) {
@@ -134,8 +153,11 @@ const lintMarkdownFile = async (filePath, fix = false) => {
134
153
fixedLines [ lineIndex ] = issue . fixed ;
135
154
} ) ;
136
155
const fixedSnippet = fixedLines . join ( '\n' ) ;
137
- fixedContent = fixedContent . slice ( 0 , snippet . start ) +
138
- '```js\n' + fixedSnippet + '```' +
156
+ fixedContent =
157
+ fixedContent . slice ( 0 , snippet . start ) +
158
+ '```js\n' +
159
+ fixedSnippet +
160
+ '```' +
139
161
fixedContent . slice ( snippet . end ) ;
140
162
}
141
163
}
@@ -156,7 +178,7 @@ const lintMarkdownFile = async (filePath, fix = false) => {
156
178
filePath,
157
179
issues : allIssues ,
158
180
fixedContent : fix ? fixedContent : null ,
159
- javascriptCount : javascriptCount
181
+ javascriptCount : javascriptCount ,
160
182
} ;
161
183
} catch ( error ) {
162
184
console . error ( `Error processing file ${ filePath } : ${ error . message } ` ) ;
@@ -176,7 +198,10 @@ const processFiles = async (globPattern, fix = false) => {
176
198
let hasErrors = false ;
177
199
178
200
for ( const file of files ) {
179
- const { issues, error, javascriptCount } = await lintMarkdownFile ( file , fix ) ;
201
+ const { issues, error, javascriptCount } = await lintMarkdownFile (
202
+ file ,
203
+ fix ,
204
+ ) ;
180
205
if ( error ) {
181
206
console . error ( `\nError in file ${ file } :` ) ;
182
207
console . error ( error ) ;
@@ -187,13 +212,17 @@ const processFiles = async (globPattern, fix = false) => {
187
212
issues . forEach ( issue => {
188
213
console . error ( `\nSnippet ${ issue . snippet } , Line ${ issue . line } :` ) ;
189
214
console . error ( `Original: ${ issue . original } ` ) ;
190
- console . error ( `${ fix ? 'Fixed: ' : 'Suggested:' } ${ issue . fixed } ` ) ;
215
+ console . error (
216
+ `${ fix ? 'Fixed: ' : 'Suggested:' } ${ issue . fixed } ` ,
217
+ ) ;
191
218
} ) ;
192
219
totalIssues += issues . length ;
193
220
hasErrors = true ;
194
221
}
195
222
if ( javascriptCount > 0 ) {
196
- console . error ( `\nFound ${ javascriptCount } instance(s) of \`\`\`javascript in ${ file } ` ) ;
223
+ console . error (
224
+ `\nFound ${ javascriptCount } instance(s) of \`\`\`javascript in ${ file } ` ,
225
+ ) ;
197
226
totalJavascriptInstances += javascriptCount ;
198
227
hasErrors = true ;
199
228
}
@@ -202,14 +231,18 @@ const processFiles = async (globPattern, fix = false) => {
202
231
203
232
if ( totalIssues > 0 || totalJavascriptInstances > 0 ) {
204
233
console . error ( `\nTotal errors found: ${ totalIssues } ` ) ;
205
- console . error ( `Total \`\`\`javascript instances found: ${ totalJavascriptInstances } ` ) ;
234
+ console . error (
235
+ `Total \`\`\`javascript instances found: ${ totalJavascriptInstances } ` ,
236
+ ) ;
206
237
if ( fix ) {
207
- console . log ( "All matching files have been updated with the necessary changes." ) ;
238
+ console . log (
239
+ 'All matching files have been updated with the necessary changes.' ,
240
+ ) ;
208
241
} else {
209
- console . error ( " Run `yarn format` to automatically fix these errors" ) ;
242
+ console . error ( ' Run `yarn format` to automatically fix these errors' ) ;
210
243
}
211
244
} else {
212
- console . log ( " No errors found in any of the matching files." ) ;
245
+ console . log ( ' No errors found in any of the matching files.' ) ;
213
246
}
214
247
215
248
if ( hasErrors && ! fix ) {
@@ -223,7 +256,7 @@ const processFiles = async (globPattern, fix = false) => {
223
256
224
257
const main = async ( ) => {
225
258
if ( process . argv . length < 3 || process . argv . length > 4 ) {
226
- console . error ( " Usage: node linter.js <glob_pattern> [--fix]" ) ;
259
+ console . error ( ' Usage: node linter.js <glob_pattern> [--fix]' ) ;
227
260
process . exit ( 1 ) ;
228
261
}
229
262
0 commit comments