diff --git a/configs/markdownlint.json b/configs/markdownlint.json index c09bb1d..0968feb 100644 --- a/configs/markdownlint.json +++ b/configs/markdownlint.json @@ -26,6 +26,7 @@ "no-inline-html": false, "no-angle-brackets": false, "no-curly-braces": false, + "no-newline-in-links": false, "link-image-style": { "shortcut": false } diff --git a/markdownlint-rules/emd004.js b/markdownlint-rules/emd004.js new file mode 100644 index 0000000..16919f2 --- /dev/null +++ b/markdownlint-rules/emd004.js @@ -0,0 +1,30 @@ +const { addError, filterTokens } = require('markdownlint/helpers'); + +module.exports = { + names: ['EMD004', 'no-newline-in-links'], + description: 'Newlines inside link text', + tags: ['newline', 'links'], + parser: 'markdownit', + function: function EMD004(params, onError) { + filterTokens(params, 'inline', (token) => { + const { children } = token; + let { lineNumber } = token; + let inLink = false; + for (const child of children) { + const { type } = child; + if (type === 'link_open') { + inLink = true; + } else if (type === 'link_close') { + inLink = false; + } else if (type === 'softbreak') { + if (inLink) { + addError(onError, lineNumber); + break; + } else { + lineNumber++; + } + } + } + }); + }, +}; diff --git a/markdownlint-rules/index.js b/markdownlint-rules/index.js index 79cb282..71ef263 100644 --- a/markdownlint-rules/index.js +++ b/markdownlint-rules/index.js @@ -1,4 +1,5 @@ const EMD002 = require('./emd002.js'); const EMD003 = require('./emd003.js'); +const EMD004 = require('./emd004.js'); -module.exports = [EMD002, EMD003]; +module.exports = [EMD002, EMD003, EMD004]; diff --git a/tests/__snapshots__/electron-markdownlint.spec.ts.snap b/tests/__snapshots__/electron-markdownlint.spec.ts.snap index 4ebec0b..08c8988 100644 --- a/tests/__snapshots__/electron-markdownlint.spec.ts.snap +++ b/tests/__snapshots__/electron-markdownlint.spec.ts.snap @@ -1,5 +1,13 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`electron-markdownlint > should not allow newlines in link text if EMD004 enabled 1`] = ` +"newline-in-link-text.md:1 EMD004/no-newline-in-links Newlines inside link text +newline-in-link-text.md:4 EMD004/no-newline-in-links Newlines inside link text +newline-in-link-text.md:7 EMD004/no-newline-in-links Newlines inside link text +newline-in-link-text.md:10 EMD004/no-newline-in-links Newlines inside link text +" +`; + exports[`electron-markdownlint > should not allow opening angle brackets if EMD002 enabled 1`] = ` "angle-brackets.md:3:13 EMD002/no-angle-brackets No unescaped opening angle brackets in text (does not play nice with MDX) [Unescaped opening angle bracket] angle-brackets.md:9:16 EMD002/no-angle-brackets No unescaped opening angle brackets in text (does not play nice with MDX) [Unescaped opening angle bracket] diff --git a/tests/electron-markdownlint.spec.ts b/tests/electron-markdownlint.spec.ts index 706d562..3176f19 100644 --- a/tests/electron-markdownlint.spec.ts +++ b/tests/electron-markdownlint.spec.ts @@ -117,4 +117,46 @@ describe('electron-markdownlint', () => { expect(stdout).toBe(''); expect(status).toEqual(0); }); + + it('should not allow newlines in link text if EMD004 enabled', () => { + const { status, stderr, stdout } = cp.spawnSync( + process.execPath, + [ + path.resolve(__dirname, '../dist/bin/markdownlint-cli-wrapper.js'), + '--enable', + 'EMD004', + '--', + path.resolve(FIXTURES_DIR, 'newline-in-link-text.md'), + ], + { stdio: 'pipe', encoding: 'utf-8' }, + ); + + let fixturesRoot = `${FIXTURES_DIR}${path.sep}`; + + if (os.platform() === 'win32') { + fixturesRoot = fixturesRoot.replace(/\\/g, '\\\\'); + } + + expect(stderr.replace(new RegExp(fixturesRoot, 'g'), '')).toMatchSnapshot(); + expect(stdout).toBe(''); + expect(status).toEqual(1); + }); + + it('should allow newlines in link text if EMD004 not enabled', () => { + const { status, stderr, stdout } = cp.spawnSync( + process.execPath, + [ + path.resolve(__dirname, '../dist/bin/markdownlint-cli-wrapper.js'), + '--disable', + 'EMD004', + '--', + path.resolve(FIXTURES_DIR, 'newline-in-link-text.md'), + ], + { stdio: 'pipe', encoding: 'utf-8' }, + ); + + expect(stderr).toBe(''); + expect(stdout).toBe(''); + expect(status).toEqual(0); + }); }); diff --git a/tests/fixtures/angle-brackets.md b/tests/fixtures/angle-brackets.md index fe52b54..820d1a5 100644 --- a/tests/fixtures/angle-brackets.md +++ b/tests/fixtures/angle-brackets.md @@ -88,9 +88,9 @@ protocol.registerSchemesAsPrivileged([ ]) ``` -A standard scheme adheres to what RFC 3986 calls [generic URI -syntax](https://tools.ietf.org/html/rfc3986#section-3). For example `http` and -`https` are standard schemes, while `file` is not. +A standard scheme adheres to what RFC 3986 calls +[generic URI syntax](https://tools.ietf.org/html/rfc3986#section-3). +For example `http` and `https` are standard schemes, while `file` is not. Registering a scheme as standard allows relative and absolute resources to be resolved correctly when served. Otherwise the scheme will behave like the diff --git a/tests/fixtures/newline-in-link-text.md b/tests/fixtures/newline-in-link-text.md new file mode 100644 index 0000000..55e2716 --- /dev/null +++ b/tests/fixtures/newline-in-link-text.md @@ -0,0 +1,11 @@ +This has [a link +with a newline](https://google.com) + +This has [a link + with a newline](https://google.com) + +This has [ +a link with a newline](https://google.com) + +This has [a link with a newline +](https://google.com)