diff --git a/src/language/markdown-source-code.js b/src/language/markdown-source-code.js index 2c45f96b..ddb8f84c 100644 --- a/src/language/markdown-source-code.js +++ b/src/language/markdown-source-code.js @@ -13,6 +13,7 @@ import { ConfigCommentParser, Directive, } from "@eslint/plugin-kit"; +import { lineEndingPattern } from "../util.js"; //----------------------------------------------------------------------------- // Types @@ -152,7 +153,7 @@ export class MarkdownSourceCode extends TextSourceCodeBase { * @param {Root} options.ast The root AST node. */ constructor({ text, ast }) { - super({ ast, text }); + super({ ast, text, lineEndingPattern }); this.ast = ast; // need to traverse the source code to get the inline config nodes diff --git a/src/util.js b/src/util.js index 36cf9954..b69e80eb 100644 --- a/src/util.js +++ b/src/util.js @@ -7,6 +7,12 @@ // Regex Patterns //----------------------------------------------------------------------------- +/** + * Line ending pattern to match all line endings (CRLF, CR, LF). (CommonMark spec) + * @see https://spec.commonmark.org/0.31.2/#line-ending + */ +export const lineEndingPattern = /\r\n|[\r\n]/u; + /** * CommonMark does not allow any white space between the brackets in a reference link. * If that pattern is detected, then it's treated as text and not as a link. This pattern @@ -24,16 +30,16 @@ export const htmlCommentPattern = //gu; //----------------------------------------------------------------------------- /** - * Checks if a frontmatter block contains a title matching the given pattern - * @param {string} value The frontmatter content - * @param {RegExp|null} pattern The pattern to match against - * @returns {boolean} Whether a title was found + * Checks if a frontmatter block contains a title matching the given pattern. + * @param {string} value The frontmatter content. + * @param {RegExp|null} pattern The pattern to match against. + * @returns {boolean} Whether a title was found. */ export function frontmatterHasTitle(value, pattern) { if (!pattern) { return false; } - const lines = value.split("\n"); + const lines = value.split(lineEndingPattern); for (const line of lines) { if (pattern.test(line)) { return true; diff --git a/tests/language/markdown-source-code.test.js b/tests/language/markdown-source-code.test.js index b4011af7..daac0ca2 100644 --- a/tests/language/markdown-source-code.test.js +++ b/tests/language/markdown-source-code.test.js @@ -66,6 +66,67 @@ describe("MarkdownSourceCode", () => { sourceCode = new MarkdownSourceCode({ text: markdownText, ast }); }); + describe("constructor", () => { + it("should create a MarkdownSourceCode instance", () => { + assert.strictEqual( + sourceCode.constructor.name, + "MarkdownSourceCode", + ); + assert.strictEqual(sourceCode.ast, ast); + assert.strictEqual(sourceCode.text, markdownText); + }); + }); + + describe("lines", () => { + it("should parse CRLF line endings", () => { + const text = "lumir\r\nlumir"; + const sourceCodeWithCRLF = new MarkdownSourceCode({ + text, + ast: fromMarkdown(text), + }); + + assert.deepStrictEqual(sourceCodeWithCRLF.lines, [ + "lumir", + "lumir", + ]); + }); + + it("should parse CR line endings", () => { + const text = "lumir\rlumir"; + const sourceCodeWithCR = new MarkdownSourceCode({ + text, + ast: fromMarkdown(text), + }); + + assert.deepStrictEqual(sourceCodeWithCR.lines, ["lumir", "lumir"]); + }); + + it("should parse LF line endings", () => { + const text = "lumir\nlumir"; + const sourceCodeWithLF = new MarkdownSourceCode({ + text, + ast: fromMarkdown(text), + }); + + assert.deepStrictEqual(sourceCodeWithLF.lines, ["lumir", "lumir"]); + }); + + it("should parse CRLF CR LF line endings", () => { + const text = "lumir\r\nlumir\rlumir\nlumir"; + const sourceCodeWithCRLFCRLF = new MarkdownSourceCode({ + text, + ast: fromMarkdown(text), + }); + + assert.deepStrictEqual(sourceCodeWithCRLFCRLF.lines, [ + "lumir", + "lumir", + "lumir", + "lumir", + ]); + }); + }); + describe("getText()", () => { it("should return the text of the Markdown source code", () => { assert.strictEqual(sourceCode.getText(), markdownText); diff --git a/tests/util.test.js b/tests/util.test.js index f5c2de57..ccaaf5f8 100644 --- a/tests/util.test.js +++ b/tests/util.test.js @@ -36,7 +36,25 @@ describe("util", () => { ); }); - it("should return true if the pattern matches any line in multiline frontmatter", () => { + it("should return true if the pattern matches any line in multiline frontmatter (CRLF)", () => { + const frontmatter = [ + "description: Test", + "title: My Document", + "author: lumirlumir", + ].join("\r\n"); + assert.strictEqual(frontmatterHasTitle(frontmatter, pattern), true); + }); + + it("should return true if the pattern matches any line in multiline frontmatter (CR)", () => { + const frontmatter = [ + "description: Test", + "title: My Document", + "author: lumirlumir", + ].join("\r"); + assert.strictEqual(frontmatterHasTitle(frontmatter, pattern), true); + }); + + it("should return true if the pattern matches any line in multiline frontmatter (LF)", () => { const frontmatter = [ "description: Test", "title: My Document",