diff --git a/package.json b/package.json index 4ccbf626..18a7d6fa 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ }, "devDependencies": { "@arethetypeswrong/cli": "^0.18.3", + "@eslint-markdown/parser": "^0.0.0-canary.3", "@eslint/js": "^10.0.1", "@eslint/json": "^2.0.1", "@types/mdast": "^4.0.4", diff --git a/src/language/markdown-language.js b/src/language/markdown-language.js index 12cc528b..96ccff78 100644 --- a/src/language/markdown-language.js +++ b/src/language/markdown-language.js @@ -26,10 +26,9 @@ import { math } from "micromark-extension-math"; * @import { Language, File, ParseResult, OkParseResult } from "@eslint/core"; * @import { Root } from "mdast"; * @import { Options } from "mdast-util-from-markdown"; - * @import { MarkdownLanguageOptions, MarkdownLanguageContext } from "../types.js"; + * @import { MarkdownLanguageOptions, MarkdownLanguageContext, MarkdownParserMode } from "../types.js"; * @typedef {Options['extensions']} Extensions * @typedef {Options['mdastExtensions']} MdastExtensions - * @typedef {"commonmark"|"gfm"} ParserMode */ //----------------------------------------------------------------------------- @@ -55,7 +54,7 @@ const jsonFrontmatterConfig = { /** * Create parser options based on `mode` and `languageOptions`. - * @param {ParserMode} mode The markdown parser mode. + * @param {MarkdownParserMode} mode The markdown parser mode. * @param {MarkdownLanguageOptions} languageOptions Language options. * @returns {{extensions: Extensions, mdastExtensions: MdastExtensions}} Parser options for micromark and mdast. */ @@ -145,18 +144,29 @@ export class MarkdownLanguage { defaultLanguageOptions = { frontmatter: false, math: false, + parser: { + meta: { + name: "mdast-util-from-markdown", + }, + parse(text, { mode, ...languageOptions }) { + return fromMarkdown( + text, + createParserOptions(mode, languageOptions), + ); + }, + }, }; /** * The Markdown parser mode. - * @type {ParserMode} + * @type {MarkdownParserMode} */ #mode = "commonmark"; /** * Creates a new instance. - * @param {Object} options The options to use for this instance. - * @param {ParserMode} [options.mode] The Markdown parser mode to use. + * @param {Object} [options] The options to use for this instance. + * @param {MarkdownParserMode} [options.mode] The Markdown parser mode to use. */ constructor({ mode } = {}) { if (mode) { @@ -166,7 +176,7 @@ export class MarkdownLanguage { /** * Validates the language options. - * @param {MarkdownLanguageOptions} languageOptions The language options to validate. + * @param {MarkdownLanguageOptions} [languageOptions] The language options to validate. * @returns {void} * @throws {Error} When the language options are invalid. */ @@ -197,18 +207,40 @@ export class MarkdownLanguage { `Invalid language option value \`${mathOption}\` for math. Expected a boolean.`, ); } + + // `parser` option validation + const parserOption = languageOptions?.parser; + + if (parserOption !== undefined) { + if (typeof parserOption !== "object" || parserOption === null) { + throw new Error( + `Invalid language option value \`${String(parserOption)}\` for parser. Expected a non-null object.`, + ); + } + + if (typeof parserOption.parse !== "function") { + throw new Error( + "Invalid language option `parser`. Expected an object with a `parse` method.", + ); + } + } } /** * Parses the given file into an AST. * @param {File} file The virtual file to parse. - * @param {MarkdownLanguageContext} context The options to use for parsing. + * @param {MarkdownLanguageContext} [context] The options to use for parsing. * @returns {ParseResult} The result of parsing. */ parse(file, context) { // Note: BOM already removed const text = /** @type {string} */ (file.body); + const { + parser = this.defaultLanguageOptions.parser, + ...restLanguageOptions + } = context?.languageOptions ?? {}; + /* * Check for parsing errors first. If there's a parsing error, nothing * else can happen. However, a parsing error does not throw an error @@ -216,11 +248,10 @@ export class MarkdownLanguage { * problem that ESLint identified just like any other. */ try { - const options = createParserOptions( - this.#mode, - context?.languageOptions, - ); - const root = fromMarkdown(text, options); + const root = parser.parse(text, { + ...restLanguageOptions, + mode: this.#mode, + }); return { ok: true, diff --git a/src/types.ts b/src/types.ts index fcd77a5d..12b561d2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -42,6 +42,7 @@ import type { InlineMath, Math } from "mdast-util-math"; import type { LanguageContext, LanguageOptions, + ObjectMetaProperties, RuleVisitor, } from "@eslint/core"; import type { @@ -133,6 +134,51 @@ declare module "mdast" { // Exports: Language and Source Code //------------------------------------------------------------------------------ +/** + * @deprecated Use `MarkdownParserMode` instead. + */ +export type ParserMode = MarkdownParserMode; + +/** + * The mode of the Markdown parser to use. + * @default "commonmark" + */ +export type MarkdownParserMode = "commonmark" | "gfm"; + +/** + * A parser that converts Markdown source text into an + * [mdast](https://github.com/syntax-tree/mdast#readme) syntax tree. + */ +export type MarkdownParser = ObjectMetaProperties & { + /** + * Parses Markdown source text into an + * [mdast](https://github.com/syntax-tree/mdast#readme) syntax tree. + * @param text The Markdown source text to parse. + * @param options The parser-specific options. + * @returns The root of the mdast syntax tree. + */ + parse( + text: string, + options: MarkdownLanguageOptions & { + /** + * The mode of the Markdown parser to use. + * @default "commonmark" + */ + mode: MarkdownParserMode; + + /* + * Don't use `Omit` here. + * Because `MarkdownLanguageOptions` has a string index signature, + * `Omit` would lose its named option properties. + */ + /** + * Parsers cannot override the `parser` used by the language. + */ + parser?: never; + }, + ): Root; +}; + /** * Language options provided for Markdown files. */ @@ -148,6 +194,12 @@ export interface MarkdownLanguageOptions extends LanguageOptions { * @default false */ math?: boolean; + + /** + * An object with a `parse()` method and optional metadata properties. + * If not configured, the default ESLint Markdown parser (`mdast-util-from-markdown`) will be used. + */ + parser?: MarkdownParser; } /** diff --git a/tests/fixtures/parser.js b/tests/fixtures/parser.js new file mode 100644 index 00000000..ebbc7234 --- /dev/null +++ b/tests/fixtures/parser.js @@ -0,0 +1,21 @@ +import markdown from "../../src/index.js"; + +export default [ + { + files: ["**/*.md"], + plugins: { markdown }, + language: "markdown/commonmark", + languageOptions: { + parser: { + parse() { + throw new Error( + "The configured Markdown parser was called.", + ); + }, + }, + }, + rules: { + "markdown/heading-increment": "error", + }, + }, +]; diff --git a/tests/language/markdown-language.test.js b/tests/language/markdown-language.test.js index b9c5c4cf..ca9ea148 100644 --- a/tests/language/markdown-language.test.js +++ b/tests/language/markdown-language.test.js @@ -139,6 +139,22 @@ describe("MarkdownLanguage", () => { language.validateLanguageOptions({ math: false }); }); }); + + it("should throw the expected error when `parser` is a symbol", () => { + const language = new MarkdownLanguage(); + + assert.throws( + () => { + language.validateLanguageOptions({ + parser: Symbol("parser"), + }); + }, + { + message: + "Invalid language option value `Symbol(parser)` for parser. Expected a non-null object.", + }, + ); + }); }); describe("parse()", () => { @@ -402,6 +418,26 @@ describe("MarkdownLanguage", () => { assert.strictEqual(result.ast.children[1].children[0].type, "text"); assert.strictEqual(result.ast.children[2].type, "math"); }); + + it("should not allow language options to override commonmark mode", () => { + const language = new MarkdownLanguage({ + mode: "commonmark", + }); + const result = language.parse( + { + body: "| Column 1 | Column 2 |\n| -------- | -------- |\n| Cell 1 | Cell 2 |", + path: "test.md", + }, + { + languageOptions: { + mode: "gfm", + }, + }, + ); + + assert.strictEqual(result.ok, true); + assert.strictEqual(result.ast.children[0].type, "paragraph"); + }); }); describe("createSourceCode()", () => { diff --git a/tests/plugin.test.js b/tests/plugin.test.js index ac88c6d4..86a5c600 100644 --- a/tests/plugin.test.js +++ b/tests/plugin.test.js @@ -8,6 +8,7 @@ //----------------------------------------------------------------------------- import assert from "node:assert"; +import { spawnSync } from "node:child_process"; import path from "node:path"; import fs from "node:fs"; import { fileURLToPath, pathToFileURL } from "node:url"; @@ -1228,6 +1229,25 @@ describe("FlatESLint", () => { .map(([name]) => `markdown/${name}`); assert.deepStrictEqual(actualRuleIds, expectedRuleIds); }); + + it("should serialize the default Markdown parser", async () => { + const markdownESLint = new ESLint({ + overrideConfigFile: true, + overrideConfig: { + files: ["**/*.md"], + plugins: { markdown: plugin }, + language: "markdown/commonmark", + }, + }); + const config = + await markdownESLint.calculateConfigForFile("test.md"); + const serializedConfig = JSON.parse(JSON.stringify(config)); + + assert.strictEqual( + serializedConfig.languageOptions.parser, + "mdast-util-from-markdown", + ); + }); }); describe("plugin", () => { @@ -2440,4 +2460,53 @@ describe("FlatESLint", () => { } }); }); + + describe("CLI", () => { + it("should override the Markdown parser with `--parser @eslint-markdown/parser`", () => { + const eslintPath = fileURLToPath( + new URL( + "bin/eslint.js", + import.meta.resolve("eslint/package.json"), + ), + ); + const args = [ + eslintPath, + "--config=parser.js", + "--stdin", + "--stdin-filename=test.md", + "--format=json", + ]; + const options = { + cwd: path.resolve(__dirname, "fixtures"), + input: "# Hello\n\n### Skipped level\n", + encoding: "utf8", + }; + + const withoutParser = spawnSync(process.execPath, args, options); + + assert.strictEqual(withoutParser.status, 1); + const [configuredResult] = JSON.parse(withoutParser.stdout); + + assert.strictEqual(configuredResult.messages.length, 1); + assert.strictEqual( + configuredResult.messages[0].message, + "Parsing error: The configured Markdown parser was called.", + ); + + const withParser = spawnSync( + process.execPath, + [...args, "--parser", "@eslint-markdown/parser"], + options, + ); + + assert.strictEqual(withParser.status, 1); + const [overriddenResult] = JSON.parse(withParser.stdout); + + assert.strictEqual(overriddenResult.messages.length, 1); + assert.strictEqual( + overriddenResult.messages[0].ruleId, + "markdown/heading-increment", + ); + }); + }); }); diff --git a/tests/rules/_utils/rule-tester.js b/tests/rules/_utils/rule-tester.js new file mode 100644 index 00000000..ae832a38 --- /dev/null +++ b/tests/rules/_utils/rule-tester.js @@ -0,0 +1,101 @@ +/** + * @fileoverview A rule tester helper for running code with both `mdast-util-from-markdown` and `@eslint-markdown/parser`. + * @author lumir(lumirlumir) + */ + +// -------------------------------------------------------------------------------- +// Imports +// -------------------------------------------------------------------------------- + +import parser from "@eslint-markdown/parser"; +import { RuleTester } from "eslint"; +import markdown from "../../../src/index.js"; + +//------------------------------------------------------------------------------ +// Typedefs +//------------------------------------------------------------------------------ + +/** + * @import { MarkdownRuleDefinition } from "../../../src/types.ts"; + */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Creates a new `RuleTester` instance with the `mdast-util-from-markdown` JS parser and CommonMark syntax. + */ +const ruleTesterJSCommonMark = new RuleTester({ + plugins: { + markdown, + }, + language: "markdown/commonmark", +}); + +/** + * Creates a new `RuleTester` instance with the `mdast-util-from-markdown` JS parser and GFM syntax. + */ +const ruleTesterJSGFM = new RuleTester({ + plugins: { + markdown, + }, + language: "markdown/gfm", +}); + +/** + * Creates a new `RuleTester` instance with the `@eslint-markdown/parser` Rust parser and CommonMark syntax. + */ +const ruleTesterRustCommonMark = new RuleTester({ + plugins: { + markdown, + }, + language: "markdown/commonmark", + languageOptions: { + parser, + }, +}); + +/** + * Creates a new `RuleTester` instance with the `@eslint-markdown/parser` Rust parser and GFM syntax. + */ +const ruleTesterRustGFM = new RuleTester({ + plugins: { + markdown, + }, + language: "markdown/gfm", + languageOptions: { + parser, + }, +}); + +//------------------------------------------------------------------------------ +// Exports +//------------------------------------------------------------------------------ + +/** + * Markdown rule tester. + * @param {string} ruleName Rule name. + * @param {MarkdownRuleDefinition} rule Rule definition. + * @param {Parameters[2]} tests Test cases. + * @returns {void} + */ +export default function ruleTester(ruleName, rule, tests) { + describe("JS: mdast-util-from-markdown", () => { + describe("CommonMark", () => { + ruleTesterJSCommonMark.run(ruleName, rule, tests); + }); + describe("GFM", () => { + ruleTesterJSGFM.run(ruleName, rule, tests); + }); + }); + + describe("Rust: @eslint-markdown/parser", () => { + describe("CommonMark", () => { + ruleTesterRustCommonMark.run(ruleName, rule, tests); + }); + describe("GFM", () => { + ruleTesterRustGFM.run(ruleName, rule, tests); + }); + }); +} diff --git a/tests/rules/fenced-code-language.test.js b/tests/rules/fenced-code-language.test.js index 8540a6ca..0632c5e9 100644 --- a/tests/rules/fenced-code-language.test.js +++ b/tests/rules/fenced-code-language.test.js @@ -7,22 +7,14 @@ // Imports //------------------------------------------------------------------------------ +import ruleTester from "./_utils/rule-tester.js"; import rule from "../../src/rules/fenced-code-language.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("fenced-code-language", rule, { +ruleTester("fenced-code-language", rule, { valid: [ // backtick code block `\`\`\`js diff --git a/tests/rules/fenced-code-meta.test.js b/tests/rules/fenced-code-meta.test.js index ab017483..1dade856 100644 --- a/tests/rules/fenced-code-meta.test.js +++ b/tests/rules/fenced-code-meta.test.js @@ -7,23 +7,15 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/fenced-code-meta.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; import dedent from "dedent"; +import ruleTester from "./_utils/rule-tester.js"; +import rule from "../../src/rules/fenced-code-meta.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("fenced-code-meta", rule, { +ruleTester("fenced-code-meta", rule, { valid: [ dedent`\`\`\` console.log("Hello, world!"); diff --git a/tests/rules/heading-increment.test.js b/tests/rules/heading-increment.test.js index adc3b1de..640b2590 100644 --- a/tests/rules/heading-increment.test.js +++ b/tests/rules/heading-increment.test.js @@ -7,23 +7,15 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/heading-increment.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; import dedent from "dedent"; +import ruleTester from "./_utils/rule-tester.js"; +import rule from "../../src/rules/heading-increment.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("heading-increment", rule, { +ruleTester("heading-increment", rule, { valid: [ "# Heading 1", "## Heading 2", diff --git a/tests/rules/no-bare-urls.test.js b/tests/rules/no-bare-urls.test.js index df9d171b..aa219cae 100644 --- a/tests/rules/no-bare-urls.test.js +++ b/tests/rules/no-bare-urls.test.js @@ -23,6 +23,8 @@ const ruleTester = new RuleTester({ language: "markdown/gfm", }); +// TODO + ruleTester.run("no-bare-urls", rule, { valid: [ "", diff --git a/tests/rules/no-duplicate-definitions.test.js b/tests/rules/no-duplicate-definitions.test.js index 47fd4d66..1dcc9b44 100644 --- a/tests/rules/no-duplicate-definitions.test.js +++ b/tests/rules/no-duplicate-definitions.test.js @@ -7,22 +7,14 @@ // Imports //------------------------------------------------------------------------------ +import ruleTester from "./_utils/rule-tester.js"; import rule from "../../src/rules/no-duplicate-definitions.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/gfm", -}); - -ruleTester.run("no-duplicate-definitions", rule, { +ruleTester("no-duplicate-definitions", rule, { valid: [ ` [mercury]: https://example.com/mercury/ @@ -33,20 +25,29 @@ ruleTester.run("no-duplicate-definitions", rule, { [venus]: https://example.com/venus/ `, - ` + { + code: ` [^mercury]: Hello, Mercury! `, + language: "markdown/gfm", + }, - ` + { + code: ` [^mercury]: Hello, Mercury! [^venus]: Hello, Venus! `, + language: "markdown/gfm", + }, - ` + { + code: ` [alpha]: bravo [^alpha]: bravo `, + language: "markdown/gfm", + }, ` [//]: # (This is a comment 1) @@ -70,6 +71,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["mercury"], @@ -136,6 +138,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^MERCURY]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["MERCURY"], @@ -147,6 +150,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^MERCURY]: Hello, Venus! `, + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["mercury"], @@ -158,6 +162,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", options: [ { checkFootnoteDefinitions: false, @@ -169,6 +174,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", options: [ { checkFootnoteDefinitions: true, @@ -191,6 +197,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^Grüsse]: Grüsse [^Grüsse]: Grüsse `, + language: "markdown/gfm", options: [{ allowFootnoteDefinitions: ["GRÜẞE"] }], }, ] @@ -348,6 +355,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", errors: [ { messageId: "duplicateFootnoteDefinition", @@ -372,6 +380,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Earth! [^mercury]: Hello, Mars! `, + language: "markdown/gfm", errors: [ { messageId: "duplicateFootnoteDefinition", @@ -420,6 +429,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^Mercury]: Hello, Venus! `, // case insensitive + language: "markdown/gfm", errors: [ { messageId: "duplicateFootnoteDefinition", @@ -442,6 +452,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", options: [ { allowDefinitions: ["mercury"], @@ -471,6 +482,7 @@ ruleTester.run("no-duplicate-definitions", rule, { [^mercury]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", options: [ { checkFootnoteDefinitions: true, diff --git a/tests/rules/no-duplicate-headings.test.js b/tests/rules/no-duplicate-headings.test.js index 39b1b5cd..9172a1a0 100644 --- a/tests/rules/no-duplicate-headings.test.js +++ b/tests/rules/no-duplicate-headings.test.js @@ -7,23 +7,17 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-duplicate-headings.js"; -import markdown from "../../src/index.js"; -import { Linter, RuleTester } from "eslint"; +import { Linter } from "eslint"; import dedent from "dedent"; +import ruleTester from "./_utils/rule-tester.js"; +import markdown from "../../src/index.js"; +import rule from "../../src/rules/no-duplicate-headings.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("no-duplicate-headings", rule, { +ruleTester("no-duplicate-headings", rule, { valid: [ // Using emphasis in headings should not be considered a duplicate by default. dedent` diff --git a/tests/rules/no-empty-definitions.test.js b/tests/rules/no-empty-definitions.test.js index f98797f3..7528207a 100644 --- a/tests/rules/no-empty-definitions.test.js +++ b/tests/rules/no-empty-definitions.test.js @@ -7,23 +7,15 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-empty-definitions.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; import dedent from "dedent"; +import ruleTester from "./_utils/rule-tester.js"; +import rule from "../../src/rules/no-empty-definitions.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/gfm", -}); - -ruleTester.run("no-empty-definitions", rule, { +ruleTester("no-empty-definitions", rule, { valid: [ "[foo]: bar", "[foo]: #bar", @@ -31,24 +23,57 @@ ruleTester.run("no-empty-definitions", rule, { "[foo]: ", "[//]: # (This is a comment 1)", "[//]: <> (This is a comment 2)", - "[^note]: This is a footnote.", - "[^note]: ![]()", - "[^note]: [text](url)", - "[^note]:\n Content", - "[^note]:\n > blockquote", - "[^note]: ", + { + code: "[^note]: This is a footnote.", + language: "markdown/gfm", + }, + { + code: "[^note]: ![]()", + language: "markdown/gfm", + }, + { + code: "[^note]: [text](url)", + language: "markdown/gfm", + }, + { + code: "[^note]:\n Content", + language: "markdown/gfm", + }, + { + code: "[^note]:\n > blockquote", + language: "markdown/gfm", + }, + { + code: "[^note]: ", + language: "markdown/gfm", + }, "\\[^note]:", "[\\^note]:", "[^note\\]:", "[^note]\\:", - "[^foo]: ", - "[^foo]: content ", - "[^foo]: content", - "[^foo]: content ", - dedent` - [^foo]: content - `, + { + code: "[^foo]: ", + language: "markdown/gfm", + }, + { + code: "[^foo]: content ", + language: "markdown/gfm", + }, + { + code: "[^foo]: content", + language: "markdown/gfm", + }, + { + code: "[^foo]: content ", + language: "markdown/gfm", + }, + { + code: dedent` + [^foo]: content + `, + language: "markdown/gfm", + }, { code: "[foo]: #", options: [{ allowDefinitions: ["foo"] }], @@ -63,10 +88,12 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^note]:", + language: "markdown/gfm", options: [{ checkFootnoteDefinitions: false }], }, { code: "[^note]:", + language: "markdown/gfm", options: [ { checkFootnoteDefinitions: true, @@ -124,18 +151,22 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^NOTE]:", + language: "markdown/gfm", options: [{ allowFootnoteDefinitions: ["NOTE"] }], }, { code: "[^note]:", + language: "markdown/gfm", options: [{ allowFootnoteDefinitions: ["NOTE"] }], }, { code: "[^NOTE]:", + language: "markdown/gfm", options: [{ allowFootnoteDefinitions: ["note"] }], }, { code: "[^note]:", + language: "markdown/gfm", options: [{ allowFootnoteDefinitions: [" note "] }], }, // This test case is skipped when running on Bun @@ -151,6 +182,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^Grüsse]:", + language: "markdown/gfm", options: [{ allowFootnoteDefinitions: ["GRÜẞE"] }], }, ] @@ -235,6 +267,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^note]:", + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -248,6 +281,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^Note]:", + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -261,6 +295,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^note]: ", + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -274,6 +309,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^note]:\n", + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -287,6 +323,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^a]:\n[^b]:", + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -308,6 +345,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[foo]: #\n[^note]:", + language: "markdown/gfm", errors: [ { messageId: "emptyDefinition", @@ -329,6 +367,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[foo]: #\n[^note]:", + language: "markdown/gfm", options: [{ checkFootnoteDefinitions: false }], errors: [ { @@ -343,6 +382,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^foo]: ", + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -358,6 +398,7 @@ ruleTester.run("no-empty-definitions", rule, { code: dedent` [^foo]: `, + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -373,6 +414,7 @@ ruleTester.run("no-empty-definitions", rule, { code: dedent` [^foo]: `, + language: "markdown/gfm", errors: [ { messageId: "emptyFootnoteDefinition", @@ -418,6 +460,7 @@ ruleTester.run("no-empty-definitions", rule, { }, { code: "[^foo]:", + language: "markdown/gfm", options: [ { allowDefinitions: ["foo"], diff --git a/tests/rules/no-empty-images.test.js b/tests/rules/no-empty-images.test.js index 16baa13c..abba9856 100644 --- a/tests/rules/no-empty-images.test.js +++ b/tests/rules/no-empty-images.test.js @@ -7,22 +7,14 @@ // Imports //------------------------------------------------------------------------------ +import ruleTester from "./_utils/rule-tester.js"; import rule from "../../src/rules/no-empty-images.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("no-empty-images", rule, { +ruleTester("no-empty-images", rule, { valid: ["![foo](bar)", "![foo](#bar)", "![foo](http://bar.com/image.png)"], invalid: [ { diff --git a/tests/rules/no-empty-links.test.js b/tests/rules/no-empty-links.test.js index 75e950da..de0ab0b3 100644 --- a/tests/rules/no-empty-links.test.js +++ b/tests/rules/no-empty-links.test.js @@ -7,22 +7,14 @@ // Imports //------------------------------------------------------------------------------ +import ruleTester from "./_utils/rule-tester.js"; import rule from "../../src/rules/no-empty-links.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("no-empty-links", rule, { +ruleTester("no-empty-links", rule, { valid: ["[foo](bar)", "[foo](#bar)", "[foo](http://bar.com)"], invalid: [ { diff --git a/tests/rules/no-html.test.js b/tests/rules/no-html.test.js index 9dcaa189..ecd1cf18 100644 --- a/tests/rules/no-html.test.js +++ b/tests/rules/no-html.test.js @@ -7,23 +7,15 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-html.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; import dedent from "dedent"; +import ruleTester from "./_utils/rule-tester.js"; +import rule from "../../src/rules/no-html.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("no-html", rule, { +ruleTester("no-html", rule, { valid: [ "Hello world!", " 1 < 5", diff --git a/tests/rules/no-invalid-label-refs.test.js b/tests/rules/no-invalid-label-refs.test.js index 231f1490..5cf67a46 100644 --- a/tests/rules/no-invalid-label-refs.test.js +++ b/tests/rules/no-invalid-label-refs.test.js @@ -7,22 +7,14 @@ // Imports //------------------------------------------------------------------------------ +import ruleTester from "./_utils/rule-tester.js"; import rule from "../../src/rules/no-invalid-label-refs.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("no-invalid-label-refs", rule, { +ruleTester("no-invalid-label-refs", rule, { valid: [ "[*foo*]", "[foo]\n\n[foo]: http://bar.com", diff --git a/tests/rules/no-missing-atx-heading-space.test.js b/tests/rules/no-missing-atx-heading-space.test.js index c7bfc273..6a6ed093 100644 --- a/tests/rules/no-missing-atx-heading-space.test.js +++ b/tests/rules/no-missing-atx-heading-space.test.js @@ -7,21 +7,9 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-missing-atx-heading-space.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; import dedent from "dedent"; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/gfm", -}); +import ruleTester from "./_utils/rule-tester.js"; +import rule from "../../src/rules/no-missing-atx-heading-space.js"; //------------------------------------------------------------------------------ // Valid Test Cases @@ -1170,7 +1158,7 @@ const invalidTests = [ }, ]; -ruleTester.run("no-missing-atx-heading-space", rule, { +ruleTester("no-missing-atx-heading-space", rule, { valid: validHeadings, invalid: invalidTests, }); diff --git a/tests/rules/no-missing-label-refs.test.js b/tests/rules/no-missing-label-refs.test.js index 07ce97e2..61c275bc 100644 --- a/tests/rules/no-missing-label-refs.test.js +++ b/tests/rules/no-missing-label-refs.test.js @@ -7,23 +7,17 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-missing-label-refs.js"; -import markdown from "../../src/index.js"; -import { Linter, RuleTester } from "eslint"; import dedent from "dedent"; +import { Linter } from "eslint"; +import ruleTester from "./_utils/rule-tester.js"; +import markdown from "../../src/index.js"; +import rule from "../../src/rules/no-missing-label-refs.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/commonmark", -}); - -ruleTester.run("no-missing-label-refs", rule, { +ruleTester("no-missing-label-refs", rule, { valid: [ "[*foo*]", "[foo]\n\n[foo]: http://bar.com", diff --git a/tests/rules/no-missing-link-fragments.test.js b/tests/rules/no-missing-link-fragments.test.js index 573a814a..19e6a0a6 100644 --- a/tests/rules/no-missing-link-fragments.test.js +++ b/tests/rules/no-missing-link-fragments.test.js @@ -7,23 +7,17 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-missing-link-fragments.js"; -import markdown from "../../src/index.js"; -import { Linter, RuleTester } from "eslint"; import dedent from "dedent"; +import { Linter } from "eslint"; +import ruleTester from "./_utils/rule-tester.js"; +import markdown from "../../src/index.js"; +import rule from "../../src/rules/no-missing-link-fragments.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/gfm", -}); - -ruleTester.run("no-missing-link-fragments", rule, { +ruleTester("no-missing-link-fragments", rule, { valid: [ // Basic heading match with `Link` node dedent` diff --git a/tests/rules/no-multiple-h1.test.js b/tests/rules/no-multiple-h1.test.js index e86cdfd7..e45dcdf2 100644 --- a/tests/rules/no-multiple-h1.test.js +++ b/tests/rules/no-multiple-h1.test.js @@ -16,6 +16,8 @@ import dedent from "dedent"; // Tests //------------------------------------------------------------------------------ +// TODO + const ruleTester = new RuleTester({ plugins: { markdown, diff --git a/tests/rules/no-reference-like-urls.test.js b/tests/rules/no-reference-like-urls.test.js index 3f6b6a33..2a9b8e29 100644 --- a/tests/rules/no-reference-like-urls.test.js +++ b/tests/rules/no-reference-like-urls.test.js @@ -16,6 +16,8 @@ import dedent from "dedent"; // Tests //------------------------------------------------------------------------------ +// TODO + const ruleTester = new RuleTester({ plugins: { markdown, diff --git a/tests/rules/no-reversed-media-syntax.test.js b/tests/rules/no-reversed-media-syntax.test.js index ffbb14df..294a0de6 100644 --- a/tests/rules/no-reversed-media-syntax.test.js +++ b/tests/rules/no-reversed-media-syntax.test.js @@ -16,6 +16,8 @@ import dedent from "dedent"; // Tests //------------------------------------------------------------------------------ +// TODO + const ruleTester = new RuleTester({ plugins: { markdown, diff --git a/tests/rules/no-space-in-emphasis.test.js b/tests/rules/no-space-in-emphasis.test.js index 72ef6730..f86167de 100644 --- a/tests/rules/no-space-in-emphasis.test.js +++ b/tests/rules/no-space-in-emphasis.test.js @@ -7,23 +7,15 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/no-space-in-emphasis.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; import dedent from "dedent"; +import ruleTester from "./_utils/rule-tester.js"; +import rule from "../../src/rules/no-space-in-emphasis.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/gfm", -}); - -ruleTester.run("no-space-in-emphasis", rule, { +ruleTester("no-space-in-emphasis", rule, { valid: [ "Line with *Normal emphasis*", "Line with **Normal strong**", @@ -31,9 +23,18 @@ ruleTester.run("no-space-in-emphasis", rule, { "Line with _Normal emphasis_", "Line with __Normal strong__", "Line with ___Normal strong and emphasis___", - "Line with ~Normal strikethrough~", - "Line with ~~Normal strikethrough~~", - "Line with ~~*strikethrough and emphasis*~~", + { + code: "Line with ~Normal strikethrough~", + language: "markdown/gfm", + }, + { + code: "Line with ~~Normal strikethrough~~", + language: "markdown/gfm", + }, + { + code: "Line with ~~*strikethrough and emphasis*~~", + language: "markdown/gfm", + }, "But not with escaped\\* asterisks\\* \\_and \\_underscores.", "But not with escaped\\~ tildes\\~ either.", "* Emphasis* with left space is recognized as a list", @@ -75,8 +76,14 @@ ruleTester.run("no-space-in-emphasis", rule, { "Mixed `code*span` scenarios are *also* okay.", "Mixed `code*span` scenarios are _also_ okay.", "Mixed `code_span` scenarios are *also* okay.", - "Text ~ strikethrough ~ with spaces", - "Text ~~ strikethrough ~~ with spaces", + { + code: "Text ~ strikethrough ~ with spaces", + language: "markdown/gfm", + }, + { + code: "Text ~~ strikethrough ~~ with spaces", + language: "markdown/gfm", + }, "[Link](under_score) followed by _underscore_", "[Link](un_der_score) followed by _underscore_", "[Link](un_der_sco_re) followed by _underscore_", @@ -108,8 +115,14 @@ ruleTester.run("no-space-in-emphasis", rule, { "text _underscore_*star* text _underscore_**star** text", "text _underscore_**star** text _underscore_*star* text", "text _underscore_**star** text _underscore_**star** text", - "text ~~strike~~*star* text ~strike~**star** text", - "text ~~strike~~_underscore_ text ~strike~__underscore__ text", + { + code: "text ~~strike~~*star* text ~strike~**star** text", + language: "markdown/gfm", + }, + { + code: "text ~~strike~~_underscore_ text ~strike~__underscore__ text", + language: "markdown/gfm", + }, dedent` > * List with *emphasis* in blockquote > @@ -182,35 +195,47 @@ ruleTester.run("no-space-in-emphasis", rule, { **text** *** `, - dedent` - | Table | Table | - | ----- | ----- | - | star | x * y | - | under | x _ y | - `, - dedent` - | Table | Table | - | ----- | ----- | - | star | x * y | - | star | x * y | - | under | x _ y | - | under | x _ y | - `, - dedent` - | Table | Table | - | ----- | ------------------------- | - | star | text *text* text | - | under | text _text_ text | - | strike| text ~text~ text | - `, - dedent` - | Table | Table | - | ----- | ----- | - | x * y | x * y | - | x** y | x** y | - | x _ y | x _ y | - | x__ y | x__ y | - `, + { + code: dedent` + | Table | Table | + | ----- | ----- | + | star | x * y | + | under | x _ y | + `, + language: "markdown/gfm", + }, + { + code: dedent` + | Table | Table | + | ----- | ----- | + | star | x * y | + | star | x * y | + | under | x _ y | + | under | x _ y | + `, + language: "markdown/gfm", + }, + { + code: dedent` + | Table | Table | + | ----- | ------------------------- | + | star | text *text* text | + | under | text _text_ text | + | strike| text ~text~ text | + `, + language: "markdown/gfm", + }, + { + code: dedent` + | Table | Table | + | ----- | ----- | + | x * y | x * y | + | x** y | x** y | + | x _ y | x _ y | + | x__ y | x__ y | + `, + language: "markdown/gfm", + }, dedent` \`\`\`yaml /* autogenerated */ # YAML... @@ -1248,6 +1273,7 @@ ruleTester.run("no-space-in-emphasis", rule, { | under | text _text_ text | | under | text _text_ text | `, + language: "markdown/gfm", errors: [ { messageId: "spaceInEmphasis", @@ -1282,6 +1308,7 @@ ruleTester.run("no-space-in-emphasis", rule, { { code: "Broken ~ strikethrough ~ with spaces", output: "Broken ~strikethrough~ with spaces", + language: "markdown/gfm", options: [{ checkStrikethrough: true }], errors: [ { @@ -1303,6 +1330,7 @@ ruleTester.run("no-space-in-emphasis", rule, { { code: "Broken ~~ strikethrough ~~ with spaces", output: "Broken ~~strikethrough~~ with spaces", + language: "markdown/gfm", options: [{ checkStrikethrough: true }], errors: [ { @@ -1324,6 +1352,7 @@ ruleTester.run("no-space-in-emphasis", rule, { { code: "Mixed ~~ok strikethrough~~ and ~~ broken strikethrough ~~", output: "Mixed ~~ok strikethrough~~ and ~~broken strikethrough~~", + language: "markdown/gfm", options: [{ checkStrikethrough: true }], errors: [ { @@ -1345,6 +1374,7 @@ ruleTester.run("no-space-in-emphasis", rule, { { code: "Mixed ~ strikethrough ~ and * emphasis * with spaces", output: "Mixed ~strikethrough~ and *emphasis* with spaces", + language: "markdown/gfm", options: [{ checkStrikethrough: true }], errors: [ { @@ -1380,6 +1410,7 @@ ruleTester.run("no-space-in-emphasis", rule, { { code: "Mixed ~ok strikethrough~ and ~ broken strikethrough ~", output: "Mixed ~ok strikethrough~ and ~broken strikethrough~", + language: "markdown/gfm", options: [{ checkStrikethrough: true }], errors: [ { @@ -1401,6 +1432,7 @@ ruleTester.run("no-space-in-emphasis", rule, { { code: "# Broken ~ strikethrough ~ with spaces", output: "# Broken ~strikethrough~ with spaces", + language: "markdown/gfm", options: [{ checkStrikethrough: true }], errors: [ { @@ -1432,6 +1464,7 @@ ruleTester.run("no-space-in-emphasis", rule, { | strike | text ~text~ text | | strike | text ~text~ text | `, + language: "markdown/gfm", options: [{ checkStrikethrough: true }], errors: [ { diff --git a/tests/rules/no-unused-definitions.test.js b/tests/rules/no-unused-definitions.test.js index 45669b12..cfe53195 100644 --- a/tests/rules/no-unused-definitions.test.js +++ b/tests/rules/no-unused-definitions.test.js @@ -7,22 +7,14 @@ // Imports //------------------------------------------------------------------------------ +import ruleTester from "./_utils/rule-tester.js"; import rule from "../../src/rules/no-unused-definitions.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/gfm", -}); - -ruleTester.run("no-unused-definitions", rule, { +ruleTester("no-unused-definitions", rule, { valid: [ "", " ", @@ -68,34 +60,46 @@ ruleTester.run("no-unused-definitions", rule, { [venus]: https://example.com/venus.jpg `, // Image - shortcut - ` + { + code: ` Mercury[^mercury] [^mercury]: Hello, Mercury! `, - ` + language: "markdown/gfm", + }, + { + code: ` Mercury[^mercury] [^mercury]: Hello, Mercury! [^Mercury]: Hello, Venus! `, // case insensitive - ` + language: "markdown/gfm", + }, + { + code: ` Mercury[^mercury] [^mercury]: https://example.com/mercury/ [ ^mercury ]: https://example.com/venus/ `, // with extra spaces + language: "markdown/gfm", + }, ` [//]: # (This is a comment 1) [//]: <> (This is a comment 2) `, - ` + { + code: ` [Alpha][alpha] and [Alpha][^alpha] [alpha]: bravo [^alpha]: bravo `, + language: "markdown/gfm", + }, { code: ` [mercury]: https://example.com/mercury/ @@ -113,6 +117,7 @@ Mercury[^mercury] [^mercury]: Hello, Mercury! [^mercury]: Hello, Venus! `, + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["mercury"], @@ -169,6 +174,7 @@ Mercury[^mercury] }, { code: "[^MERCURY]: Hello, Mercury!", + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["MERCURY"], @@ -177,6 +183,7 @@ Mercury[^mercury] }, { code: "[^mercury]: Hello, Mercury!", + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["MERCURY"], @@ -185,6 +192,7 @@ Mercury[^mercury] }, { code: "[^MERCURY]: Hello, Mercury!", + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["mercury"], @@ -193,6 +201,7 @@ Mercury[^mercury] }, { code: "[^mercury]: Hello, Mercury!", + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: [" mercury "], @@ -201,6 +210,7 @@ Mercury[^mercury] }, { code: "[^mercury]: Hello, Mercury!", + language: "markdown/gfm", options: [ { checkFootnoteDefinitions: false, @@ -209,6 +219,7 @@ Mercury[^mercury] }, { code: "[^mercury]: Hello, Mercury!", + language: "markdown/gfm", options: [ { checkFootnoteDefinitions: true, @@ -229,6 +240,7 @@ Mercury[^mercury] }, { code: "[^Grüsse]: Grüsse", + language: "markdown/gfm", options: [ { allowFootnoteDefinitions: ["GRÜẞE"], @@ -349,6 +361,7 @@ Mercury[^mercury] code: ` [^mercury]: Hello, Mercury! `, + language: "markdown/gfm", errors: [ { messageId: "unusedFootnoteDefinition", @@ -365,6 +378,7 @@ Mercury[^mercury] code: ` [^Mercury]: Hello, Mercury! `, + language: "markdown/gfm", errors: [ { messageId: "unusedFootnoteDefinition", @@ -383,6 +397,7 @@ Mercury[^mercury] [^mercury]: Hello, Venus! [^mercury]: Hello, Earth! `, + language: "markdown/gfm", errors: [ { messageId: "unusedFootnoteDefinition", @@ -415,6 +430,7 @@ Mercury[^mercury] code: ` [^mercury]: Hello, Mercury! `, + language: "markdown/gfm", options: [ { allowDefinitions: ["mercury"], @@ -438,6 +454,7 @@ Mercury[^mercury] code: ` [^mercury]: Hello, Mercury! `, + language: "markdown/gfm", options: [ { checkFootnoteDefinitions: true, diff --git a/tests/rules/require-alt-text.test.js b/tests/rules/require-alt-text.test.js index 22890ff8..b7c990ac 100644 --- a/tests/rules/require-alt-text.test.js +++ b/tests/rules/require-alt-text.test.js @@ -16,6 +16,8 @@ import dedent from "dedent"; // Tests //------------------------------------------------------------------------------ +// TODO + const ruleTester = new RuleTester({ plugins: { markdown, diff --git a/tests/rules/table-column-count.test.js b/tests/rules/table-column-count.test.js index c6fd71c2..51369ee2 100644 --- a/tests/rules/table-column-count.test.js +++ b/tests/rules/table-column-count.test.js @@ -7,53 +7,64 @@ // Imports //------------------------------------------------------------------------------ -import rule from "../../src/rules/table-column-count.js"; -import markdown from "../../src/index.js"; -import { RuleTester } from "eslint"; import dedent from "dedent"; +import ruleTester from "./_utils/rule-tester.js"; +import rule from "../../src/rules/table-column-count.js"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const ruleTester = new RuleTester({ - plugins: { - markdown, - }, - language: "markdown/gfm", -}); - -ruleTester.run("table-column-count", rule, { +ruleTester("table-column-count", rule, { valid: [ - dedent` + { + code: dedent` | Header | Header | | ------ | ------ | | Cell | Cell | | Cell | Cell | `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | Header | Header | Header | | ------ | ------ | ------ | | Cell | Cell | | Cell | | `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | A | B | |---|---| | | | | C | | `, - `Just some text. | not a table |`, - dedent` + language: "markdown/gfm", + }, + { + code: `Just some text. | not a table |`, + language: "markdown/gfm", + }, + { + code: dedent` | Header | Header | | ------ | ------ | ----- | | Cell | Cell | `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | Header | Header | | ------ | ------ | `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` Some text before. | H1 | H2 | @@ -62,48 +73,67 @@ ruleTester.run("table-column-count", rule, { Some text after. `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | Valid | Table | | ----- | ----- | | Row | Here | `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | abc | defghi | :-: | -----------: bar | baz `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | f|oo | | ------ | | b \`|\` az | | b **|** im | `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | abc | def | | --- | --- | | bar | baz | > bar `, - dedent` + language: "markdown/gfm", + }, + { + code: dedent` | abc | def | | --- | --- | `, + language: "markdown/gfm", + }, { code: dedent` | Header | Header | | ------ | ------ | - | Cell | Cell | - | Cell | Cell | + | Cell | Cell | + | Cell | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], }, { code: dedent` | Header | Header | | ------ | ------ | - | Cell | | - | Cell | Cell | + | Cell | | + | Cell | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], }, ], @@ -115,6 +145,7 @@ ruleTester.run("table-column-count", rule, { | ----- | ----- | | R1C1 | R1C2 | R2C3 | `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -132,6 +163,7 @@ ruleTester.run("table-column-count", rule, { | ----- | ----- | | R1C1 | R1C2 | R2C3 | R3C4 | `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -149,6 +181,7 @@ ruleTester.run("table-column-count", rule, { | - | | 1 | 2 | `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -171,6 +204,7 @@ ruleTester.run("table-column-count", rule, { Some concluding text. `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -189,6 +223,7 @@ ruleTester.run("table-column-count", rule, { bar | baz bar | baz | bad `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -207,6 +242,7 @@ ruleTester.run("table-column-count", rule, { | bar | baz | Extra | > This is a blockquote after `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -225,6 +261,7 @@ ruleTester.run("table-column-count", rule, { | bar | baz | Extra1 | | bar | baz | Extra2 | `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -252,6 +289,7 @@ ruleTester.run("table-column-count", rule, { | bar | baz | | bar | baz | Extra2 | `, + language: "markdown/gfm", errors: [ { messageId: "extraCells", @@ -277,6 +315,7 @@ ruleTester.run("table-column-count", rule, { | ------ | ------ | ------ | | Cell | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], errors: [ { @@ -296,6 +335,7 @@ ruleTester.run("table-column-count", rule, { | Cell | | Cell | | Cell | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], errors: [ { @@ -316,6 +356,7 @@ ruleTester.run("table-column-count", rule, { | Cell | Cell | | Cell | Cell | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], errors: [ { @@ -344,6 +385,7 @@ ruleTester.run("table-column-count", rule, { | Cell | | Cell | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], errors: [ { @@ -372,6 +414,7 @@ ruleTester.run("table-column-count", rule, { | Cell | | Cell | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], errors: [ { @@ -400,6 +443,7 @@ ruleTester.run("table-column-count", rule, { | Cell | Cell | Cell | Cell | | Cell | `, + language: "markdown/gfm", options: [{ checkMissingCells: true }], errors: [ {