Skip to content

Commit f697540

Browse files
authored
feat: support eslint config comments (#332)
* feat: support `applyInlineConfig` * wip: add more test cases
1 parent 77b2d6d commit f697540

File tree

2 files changed

+180
-4
lines changed

2 files changed

+180
-4
lines changed

src/language/markdown-source-code.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { findOffsets } from "../util.js";
3232
/** @typedef {import("@eslint/core").SourceRange} SourceRange */
3333
/** @typedef {import("@eslint/core").FileProblem} FileProblem */
3434
/** @typedef {import("@eslint/core").DirectiveType} DirectiveType */
35+
/** @typedef {import("@eslint/core").RulesConfig} RulesConfig */
3536
/** @typedef {import("../types.ts").IMarkdownSourceCode} IMarkdownSourceCode */
3637

3738
//-----------------------------------------------------------------------------
@@ -40,7 +41,7 @@ import { findOffsets } from "../util.js";
4041

4142
const commentParser = new ConfigCommentParser();
4243
const configCommentStart =
43-
/<!--\s*(eslint(?:-enable|-disable(?:(?:-next)?-line)?))(?:\s|-->)/u;
44+
/<!--\s*(?:eslint(?:-enable|-disable(?:(?:-next)?-line)?)?)(?:\s|-->)/u;
4445
const htmlComment = /<!--(.*?)-->/gsu;
4546

4647
/**
@@ -265,6 +266,50 @@ export class MarkdownSourceCode extends TextSourceCodeBase {
265266
return { problems, directives };
266267
}
267268

269+
/**
270+
* Returns inline rule configurations along with any problems
271+
* encountered while parsing the configurations.
272+
* @returns {{problems:Array<FileProblem>,configs:Array<{config:{rules:RulesConfig},loc:SourceLocation}>}} Information
273+
* that ESLint needs to further process the rule configurations.
274+
*/
275+
applyInlineConfig() {
276+
const problems = [];
277+
const configs = [];
278+
279+
this.getInlineConfigNodes().forEach(comment => {
280+
const { label, value } = commentParser.parseDirective(
281+
comment.value,
282+
);
283+
284+
if (label === "eslint") {
285+
const parseResult = commentParser.parseJSONLikeConfig(value);
286+
287+
if (parseResult.ok) {
288+
configs.push({
289+
config: {
290+
rules: parseResult.config,
291+
},
292+
loc: comment.position,
293+
});
294+
} else {
295+
problems.push({
296+
ruleId: null,
297+
message:
298+
/** @type {{ok: false, error: { message: string }}} */ (
299+
parseResult
300+
).error.message,
301+
loc: comment.position,
302+
});
303+
}
304+
}
305+
});
306+
307+
return {
308+
configs,
309+
problems,
310+
};
311+
}
312+
268313
/**
269314
* Traverse the source code and return the steps that were taken.
270315
* @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.

tests/language/markdown-source-code.test.js

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,21 @@ eslint-enable no-console -- ok to use console here
3737
3838
<!--
3939
eslint-disable-line no-console
40-
-->`;
40+
-->
41+
42+
<!-- eslint markdown/no-html: "error" -->
43+
44+
<div> This is a div </div>
45+
46+
<!-- eslint-disable markdown/no-html -- ok here -->
47+
48+
<!-- eslint markdown/no-html: warn, markdown/no-empty-links: error -->
49+
50+
<!-- invalid rule config comments -->
51+
52+
<!-- eslint markdown/no-html: [error -->
53+
54+
<!-- eslint markdown/no-html: ["error", { allowed: ["b"] ] -->`;
4155

4256
const ast = fromMarkdown(markdownText);
4357

@@ -93,7 +107,7 @@ describe("MarkdownSourceCode", () => {
93107
describe("getInlineConfigNodes()", () => {
94108
it("should return the inline config nodes", () => {
95109
const nodes = sourceCode.getInlineConfigNodes();
96-
assert.strictEqual(nodes.length, 5);
110+
assert.strictEqual(nodes.length, 10);
97111

98112
/* eslint-disable no-restricted-properties -- Needed to avoid extra asserts. */
99113

@@ -137,6 +151,46 @@ describe("MarkdownSourceCode", () => {
137151
},
138152
});
139153

154+
assert.deepEqual(nodes[5], {
155+
value: 'eslint markdown/no-html: "error"',
156+
position: {
157+
start: { line: 25, column: 1, offset: 429 },
158+
end: { line: 25, column: 42, offset: 470 },
159+
},
160+
});
161+
162+
assert.deepEqual(nodes[6], {
163+
value: "eslint-disable markdown/no-html -- ok here",
164+
position: {
165+
start: { line: 29, column: 1, offset: 500 },
166+
end: { line: 29, column: 52, offset: 551 },
167+
},
168+
});
169+
170+
assert.deepEqual(nodes[7], {
171+
value: "eslint markdown/no-html: warn, markdown/no-empty-links: error",
172+
position: {
173+
start: { line: 31, column: 1, offset: 553 },
174+
end: { line: 31, column: 71, offset: 623 },
175+
},
176+
});
177+
178+
assert.deepEqual(nodes[8], {
179+
value: "eslint markdown/no-html: [error",
180+
position: {
181+
start: { line: 35, column: 1, offset: 664 },
182+
end: { line: 35, column: 41, offset: 704 },
183+
},
184+
});
185+
186+
assert.deepEqual(nodes[9], {
187+
value: 'eslint markdown/no-html: ["error", { allowed: ["b"] ]',
188+
position: {
189+
start: { line: 37, column: 1, offset: 706 },
190+
end: { line: 37, column: 63, offset: 768 },
191+
},
192+
});
193+
140194
/* eslint-enable no-restricted-properties -- Needed to avoid extra asserts. */
141195
});
142196
});
@@ -157,7 +211,7 @@ describe("MarkdownSourceCode", () => {
157211
end: { line: 23, column: 5, offset: 427 },
158212
});
159213

160-
assert.strictEqual(directives.length, 4);
214+
assert.strictEqual(directives.length, 5);
161215

162216
assert.strictEqual(directives[0].type, "disable-next-line");
163217
assert.strictEqual(directives[0].value, "no-console");
@@ -177,6 +231,45 @@ describe("MarkdownSourceCode", () => {
177231
assert.strictEqual(directives[3].type, "disable");
178232
assert.strictEqual(directives[3].value, "semi");
179233
assert.strictEqual(directives[3].justification, "");
234+
235+
assert.strictEqual(directives[4].type, "disable");
236+
assert.strictEqual(directives[4].value, "markdown/no-html");
237+
assert.strictEqual(directives[4].justification, "ok here");
238+
});
239+
});
240+
241+
describe("applyInlineConfig()", () => {
242+
it("should return rule configs and problems", () => {
243+
const allComments = sourceCode.getInlineConfigNodes();
244+
const { configs, problems } = sourceCode.applyInlineConfig();
245+
246+
assert.deepStrictEqual(configs, [
247+
{
248+
config: {
249+
rules: {
250+
"markdown/no-html": "error",
251+
},
252+
},
253+
loc: allComments[5].position,
254+
},
255+
{
256+
config: {
257+
rules: {
258+
"markdown/no-html": "warn",
259+
"markdown/no-empty-links": "error",
260+
},
261+
},
262+
loc: allComments[7].position,
263+
},
264+
]);
265+
266+
assert.strictEqual(problems.length, 2);
267+
assert.strictEqual(problems[0].ruleId, null);
268+
assert.match(problems[0].message, /Failed to parse/u);
269+
assert.strictEqual(problems[0].loc, allComments[8].position);
270+
assert.strictEqual(problems[1].ruleId, null);
271+
assert.match(problems[1].message, /Failed to parse/u);
272+
assert.strictEqual(problems[1].loc, allComments[9].position);
180273
});
181274
});
182275

@@ -243,6 +336,44 @@ describe("MarkdownSourceCode", () => {
243336
],
244337
[1, "html", "<!--\n eslint-disable-line no-console\n -->"],
245338
[2, "html", "<!--\n eslint-disable-line no-console\n -->"],
339+
[1, "html", '<!-- eslint markdown/no-html: "error" -->'],
340+
[2, "html", '<!-- eslint markdown/no-html: "error" -->'],
341+
[1, "html", "<div> This is a div </div>"],
342+
[2, "html", "<div> This is a div </div>"],
343+
[
344+
1,
345+
"html",
346+
"<!-- eslint-disable markdown/no-html -- ok here -->",
347+
],
348+
[
349+
2,
350+
"html",
351+
"<!-- eslint-disable markdown/no-html -- ok here -->",
352+
],
353+
[
354+
1,
355+
"html",
356+
"<!-- eslint markdown/no-html: warn, markdown/no-empty-links: error -->",
357+
],
358+
[
359+
2,
360+
"html",
361+
"<!-- eslint markdown/no-html: warn, markdown/no-empty-links: error -->",
362+
],
363+
[1, "html", "<!-- invalid rule config comments -->"],
364+
[2, "html", "<!-- invalid rule config comments -->"],
365+
[1, "html", "<!-- eslint markdown/no-html: [error -->"],
366+
[2, "html", "<!-- eslint markdown/no-html: [error -->"],
367+
[
368+
1,
369+
"html",
370+
'<!-- eslint markdown/no-html: ["error", { allowed: ["b"] ] -->',
371+
],
372+
[
373+
2,
374+
"html",
375+
'<!-- eslint markdown/no-html: ["error", { allowed: ["b"] ] -->',
376+
],
246377
[2, "root", void 0],
247378
]);
248379
});

0 commit comments

Comments
 (0)