4
4
5
5
import type { AST , Linter , Rule } from 'eslint'
6
6
import type { Node , Parent , Nodes , Root , RootContentMap } from 'mdast'
7
+ import type { MdxFlowExpression , MdxTextExpression } from 'mdast-util-mdx'
7
8
8
9
import { fromMarkdown } from '../from-markdown.js'
9
10
import { meta } from '../meta.js'
10
11
11
- import type { Block , RangeMap } from './types.js'
12
+ import type { CodeBlock , RangeMap } from './types.js'
12
13
13
14
const UNSATISFIABLE_RULES = new Set ( [
14
15
'eol-last' , // The Markdown parser strips trailing newlines in code fences
@@ -18,7 +19,7 @@ const SUPPORTS_AUTOFIX = true
18
19
19
20
const BOM = '\uFEFF'
20
21
21
- const blocksCache : Map < string , Block [ ] > = new Map ( )
22
+ const blocksCache : Map < string , CodeBlock [ ] > = new Map ( )
22
23
23
24
/**
24
25
* Performs a depth-first traversal of the Markdown AST.
@@ -64,10 +65,10 @@ const COMMENTS = [
64
65
const eslintCommentRegex = / ^ (?: e s l i n t \b | g l o b a l \s ) / u
65
66
66
67
/**
67
- * Extracts `eslint-*` or `global` comments from HTML comments if present.
68
- * @param value The text content of an HTML AST node.
68
+ * Extracts `eslint-*` or `global` comments from HTML/MDX comments if present.
69
+ * @param value The text content of an HTML/MDX AST node.
69
70
* @returns The comment's text without the opening and closing tags or
70
- * an empty string if the text is not an ESLint HTML comment.
71
+ * an empty string if the text is not an ESLint HTML/MDX comment.
71
72
*/
72
73
function getComment ( value : string , isMdx = false ) {
73
74
const [ commentStart , commentEnd ] = COMMENTS [ + isMdx ]
@@ -241,14 +242,15 @@ function getBlockRangeMap(text: string, node: Node, comments: string[]) {
241
242
return rangeMap
242
243
}
243
244
244
- const codeBlockFileNameRegex = / f i l e n a m e = ( [ " ' ] ) .* ?\1/ u
245
+ // eslint-disable-next-line sonarjs/unused-named-groups -- https://community.sonarsource.com/t/names-of-regular-expressions-named-groups-should-be-used-for-self-reference/138306
246
+ const codeBlockFileNameRegex = / f i l e n a m e = (?< quote > [ " ' ] ) (?< filename > .* ?) \1/ u
245
247
246
248
/**
247
249
* Parses the file name from a block meta, if available.
248
250
* @param block A code block.
249
251
* @returns The filename, if parsed from block meta.
250
252
*/
251
- function fileNameFromMeta ( block : Block ) {
253
+ function fileNameFromMeta ( block : CodeBlock ) {
252
254
// istanbul ignore next
253
255
return codeBlockFileNameRegex
254
256
. exec ( block . meta )
@@ -270,19 +272,28 @@ function preprocess(sourceText: string, filename: string) {
270
272
// FIXME: how to read `extensions` and `markdownExtensions` parser options?
271
273
filename . endsWith ( '.mdx' ) ,
272
274
)
273
- const blocks : Block [ ] = [ ]
275
+ const blocks : CodeBlock [ ] = [ ]
274
276
275
277
blocksCache . set ( filename , blocks )
276
278
277
279
/**
278
- * During the depth-first traversal, keep track of any sequences of HTML
280
+ * During the depth-first traversal, keep track of any sequences of HTML/MDX
279
281
* comment nodes containing `eslint-*` or `global` comments. If a code
280
282
* block immediately follows such a sequence, insert the comments at the
281
283
* top of the code block. Any non-ESLint comment or other node type breaks
282
284
* and empties the sequence.
283
285
*/
284
286
let allComments : string [ ] = [ ]
285
287
288
+ function mdxExpression ( node : MdxFlowExpression | MdxTextExpression ) {
289
+ const comment = getComment ( node . value , true )
290
+ if ( comment ) {
291
+ allComments . push ( comment )
292
+ } else {
293
+ allComments = [ ]
294
+ }
295
+ }
296
+
286
297
traverse ( ast , {
287
298
'*' ( ) {
288
299
allComments = [ ]
@@ -330,25 +341,11 @@ function preprocess(sourceText: string, filename: string) {
330
341
allComments = [ ]
331
342
}
332
343
} ,
333
- mdxFlowExpression ( node ) {
334
- const comment = getComment ( node . value , true )
335
- if ( comment ) {
336
- allComments . push ( comment )
337
- } else {
338
- allComments = [ ]
339
- }
340
- } ,
341
- mdxTextExpression ( node ) {
342
- const comment = getComment ( node . value , true )
343
- if ( comment ) {
344
- allComments . push ( comment )
345
- } else {
346
- allComments = [ ]
347
- }
348
- } ,
344
+
345
+ mdxFlowExpression : mdxExpression ,
346
+ mdxTextExpression : mdxExpression ,
349
347
} )
350
348
351
- // istanbul ignore next
352
349
return blocks . map ( ( block , index ) => {
353
350
const [ language ] = block . lang . trim ( ) . split ( ' ' )
354
351
return {
@@ -364,7 +361,7 @@ function preprocess(sourceText: string, filename: string) {
364
361
* @param fix A fix to adjust.
365
362
* @returns The fix with adjusted ranges.
366
363
*/
367
- function adjustFix ( block : Block , fix : Rule . Fix ) : Rule . Fix {
364
+ function adjustFix ( block : CodeBlock , fix : Rule . Fix ) : Rule . Fix {
368
365
return {
369
366
range : fix . range . map ( range => {
370
367
// Advance through the block's range map to find the last
@@ -388,7 +385,7 @@ function adjustFix(block: Block, fix: Rule.Fix): Rule.Fix {
388
385
* @param block A code block.
389
386
* @returns A function that adjusts messages in a code block.
390
387
*/
391
- function adjustBlock ( block : Block ) {
388
+ function adjustBlock ( block : CodeBlock ) {
392
389
const leadingCommentLines = block . comments . reduce (
393
390
( count , comment ) => count + comment . split ( '\n' ) . length ,
394
391
0 ,
0 commit comments