Skip to content

fix: code block filename regexp group names missed #567

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/smart-pillows-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-mdx": patch
---

fix: code block filename regexp group names missed
14 changes: 10 additions & 4 deletions packages/eslint-plugin-mdx/src/processors/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const DEFAULT_LANGUAGE_MAPPER: Record<string, string> = {
export const DEFAULT_LANGUAGE_MAPPER = {
ecmascript: 'js',
javascript: 'js',
javascriptreact: 'jsx',
Expand All @@ -9,7 +9,7 @@ export const DEFAULT_LANGUAGE_MAPPER: Record<string, string> = {
markdownreact: 'mdx',
mdown: 'md',
mkdn: 'md',
}
} as const

export function getShortLang(
filename: string,
Expand All @@ -19,7 +19,13 @@ export function getShortLang(
if (languageMapper === false) {
return language
}
languageMapper = { ...DEFAULT_LANGUAGE_MAPPER, ...languageMapper }
languageMapper = languageMapper
? { ...DEFAULT_LANGUAGE_MAPPER, ...languageMapper }
: DEFAULT_LANGUAGE_MAPPER
const mapped = languageMapper[language]
if (mapped) {
return mapped
}
const lang = language.toLowerCase()
return languageMapper[language] || languageMapper[lang] || lang
return languageMapper[lang] || lang
}
53 changes: 25 additions & 28 deletions packages/eslint-plugin-mdx/src/processors/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

import type { AST, Linter, Rule } from 'eslint'
import type { Node, Parent, Nodes, Root, RootContentMap } from 'mdast'
import type { MdxFlowExpression, MdxTextExpression } from 'mdast-util-mdx'

import { fromMarkdown } from '../from-markdown.js'
import { meta } from '../meta.js'

import type { Block, RangeMap } from './types.js'
import type { CodeBlock, RangeMap } from './types.js'

const UNSATISFIABLE_RULES = new Set([
'eol-last', // The Markdown parser strips trailing newlines in code fences
Expand All @@ -18,7 +19,7 @@ const SUPPORTS_AUTOFIX = true

const BOM = '\uFEFF'

const blocksCache: Map<string, Block[]> = new Map()
const blocksCache: Map<string, CodeBlock[]> = new Map()

/**
* Performs a depth-first traversal of the Markdown AST.
Expand Down Expand Up @@ -64,10 +65,10 @@ const COMMENTS = [
const eslintCommentRegex = /^(?:eslint\b|global\s)/u

/**
* Extracts `eslint-*` or `global` comments from HTML comments if present.
* @param value The text content of an HTML AST node.
* Extracts `eslint-*` or `global` comments from HTML/MDX comments if present.
* @param value The text content of an HTML/MDX AST node.
* @returns The comment's text without the opening and closing tags or
* an empty string if the text is not an ESLint HTML comment.
* an empty string if the text is not an ESLint HTML/MDX comment.
*/
function getComment(value: string, isMdx = false) {
const [commentStart, commentEnd] = COMMENTS[+isMdx]
Expand Down Expand Up @@ -241,14 +242,15 @@ function getBlockRangeMap(text: string, node: Node, comments: string[]) {
return rangeMap
}

const codeBlockFileNameRegex = /filename=(["']).*?\1/u
// 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
const codeBlockFileNameRegex = /filename=(?<quote>["'])(?<filename>.*?)\1/u

/**
* Parses the file name from a block meta, if available.
* @param block A code block.
* @returns The filename, if parsed from block meta.
*/
function fileNameFromMeta(block: Block) {
function fileNameFromMeta(block: CodeBlock) {
// istanbul ignore next
return codeBlockFileNameRegex
.exec(block.meta)
Expand All @@ -270,19 +272,28 @@ function preprocess(sourceText: string, filename: string) {
// FIXME: how to read `extensions` and `markdownExtensions` parser options?
filename.endsWith('.mdx'),
)
const blocks: Block[] = []
const blocks: CodeBlock[] = []

blocksCache.set(filename, blocks)

/**
* During the depth-first traversal, keep track of any sequences of HTML
* During the depth-first traversal, keep track of any sequences of HTML/MDX
* comment nodes containing `eslint-*` or `global` comments. If a code
* block immediately follows such a sequence, insert the comments at the
* top of the code block. Any non-ESLint comment or other node type breaks
* and empties the sequence.
*/
let allComments: string[] = []

function mdxExpression(node: MdxFlowExpression | MdxTextExpression) {
const comment = getComment(node.value, true)
if (comment) {
allComments.push(comment)
} else {
allComments = []
}
}

traverse(ast, {
'*'() {
allComments = []
Expand Down Expand Up @@ -330,25 +341,11 @@ function preprocess(sourceText: string, filename: string) {
allComments = []
}
},
mdxFlowExpression(node) {
const comment = getComment(node.value, true)
if (comment) {
allComments.push(comment)
} else {
allComments = []
}
},
mdxTextExpression(node) {
const comment = getComment(node.value, true)
if (comment) {
allComments.push(comment)
} else {
allComments = []
}
},

mdxFlowExpression: mdxExpression,
mdxTextExpression: mdxExpression,
})

// istanbul ignore next
return blocks.map((block, index) => {
const [language] = block.lang.trim().split(' ')
return {
Expand All @@ -364,7 +361,7 @@ function preprocess(sourceText: string, filename: string) {
* @param fix A fix to adjust.
* @returns The fix with adjusted ranges.
*/
function adjustFix(block: Block, fix: Rule.Fix): Rule.Fix {
function adjustFix(block: CodeBlock, fix: Rule.Fix): Rule.Fix {
return {
range: fix.range.map(range => {
// Advance through the block's range map to find the last
Expand All @@ -388,7 +385,7 @@ function adjustFix(block: Block, fix: Rule.Fix): Rule.Fix {
* @param block A code block.
* @returns A function that adjusts messages in a code block.
*/
function adjustBlock(block: Block) {
function adjustBlock(block: CodeBlock) {
const leadingCommentLines = block.comments.reduce(
(count, comment) => count + comment.split('\n').length,
0,
Expand Down
4 changes: 1 addition & 3 deletions packages/eslint-plugin-mdx/src/processors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ export interface RangeMap {
md: number
}

export interface BlockBase {
export interface CodeBlock extends Code {
baseIndentText: string
comments: string[]
rangeMap: RangeMap[]
}

export interface Block extends Code, BlockBase {}
14 changes: 7 additions & 7 deletions test/__snapshots__/fixtures.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ exports[`fixtures lint code blocks should work as expected: code-blocks/455.mdx
"endLine": 2,
"fix": {
"range": [
6,
9,
25,
28,
],
"text": "let",
},
Expand All @@ -194,8 +194,8 @@ exports[`fixtures lint code blocks should work as expected: code-blocks/455.mdx
"endLine": 4,
"fix": {
"range": [
13,
30,
32,
49,
],
"text": "const b = [1, 2, 3]",
},
Expand All @@ -212,8 +212,8 @@ exports[`fixtures lint code blocks should work as expected: code-blocks/455.mdx
"endLine": 6,
"fix": {
"range": [
43,
57,
62,
76,
],
"text": ".at(-1)",
},
Expand All @@ -228,7 +228,7 @@ exports[`fixtures lint code blocks should work as expected: code-blocks/455.mdx
`;

exports[`fixtures lint code blocks should work as expected: code-blocks/455.mdx 2`] = `
"\`\`\`js
"\`\`\`js filename="test.js"
let a

const b = [1, 2, 3]
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/code-blocks/455.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
```js
```js filename="test.js"
var a

let b = [1, 2, 3]
Expand Down
Loading