From f9f953ef909a94126b16dd4a546f4df18e0268dd Mon Sep 17 00:00:00 2001 From: Ahad Birang Date: Thu, 9 Jun 2022 10:50:28 +0200 Subject: [PATCH] fix(markdown): detect inline component followed non whitespace characters (#1227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(markdown): detect inline component followed by `.` and `,` * test: add test * chore: code style * fix: detect inline component regardless of ending character * test: update tests * docs(mdc): add example for inline ending chars * test: fix Co-authored-by: Yaël Guilloux --- docs/content/3.guide/1.writing/3.mdc.md | 8 +++ .../micromark-extension/constants.ts | 68 ++++++++++--------- .../micromark-extension/tokenize-inline.ts | 7 +- test/features/parser-markdown.ts | 27 ++++++++ 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/docs/content/3.guide/1.writing/3.mdc.md b/docs/content/3.guide/1.writing/3.mdc.md index 320c7c2ac..28cc72b4e 100755 --- a/docs/content/3.guide/1.writing/3.mdc.md +++ b/docs/content/3.guide/1.writing/3.mdc.md @@ -164,6 +164,14 @@ They can be used with the `:` identifier. ``` :: +If you want to use an inline component followed by specific characters like `-`, `_` or `:`, you can use a dummy props specifier after it. + +```md +:hello{}-world +``` + +In this example, `:hello{}` will search for the `` component, and `-world` will be plain text. + ## Props There are two ways to pass props to components using MDC. diff --git a/src/runtime/markdown-parser/remark-mdc/micromark-extension/constants.ts b/src/runtime/markdown-parser/remark-mdc/micromark-extension/constants.ts index fe8d8e898..07a4371a6 100644 --- a/src/runtime/markdown-parser/remark-mdc/micromark-extension/constants.ts +++ b/src/runtime/markdown-parser/remark-mdc/micromark-extension/constants.ts @@ -11,6 +11,10 @@ export const Codes = { * null */ EOF: null, + /** + * ' ' + */ + space: 32, /** * '"' */ @@ -24,13 +28,25 @@ export const Codes = { */ apostrophe: 39, /** - * '`' + * '(' */ - backTick: 96, + openingParentheses: 40, /** - * '\' + * ')' */ - backSlash: 92, + closingParentheses: 41, + /** + * ',' + **/ + comma: 44, + /** + * '-' + */ + dash: 45, + /** + * '.' + */ + dot: 46, /** * ':' */ @@ -48,51 +64,39 @@ export const Codes = { */ greaterThan: 62, /** - * '-' - */ - dash: 45, - /** - * '.' - */ - dot: 46, - /** - * ' ' + * 'X' */ - space: 32, + uppercaseX: 88, /** * '[' */ openingSquareBracket: 91, /** - * ']' - */ - closingSquareBracket: 93, - /** - * '{' + * '\' */ - openingCurlyBracket: 123, + backSlash: 92, /** - * '}' + * ']' */ - closingCurlyBracket: 125, + closingSquareBracket: 93, /** - * '(' + * '_' */ - openingParentheses: 40, + underscore: 95, /** - * ')' + * '`' */ - closingParentheses: 41, + backTick: 96, /** - * '_' + * 'x' */ - underscore: 95, + lowercaseX: 120, /** - * 'X' + * '{' */ - uppercaseX: 88, + openingCurlyBracket: 123, /** - * 'x' + * '}' */ - lowercaseX: 120 + closingCurlyBracket: 125 } diff --git a/src/runtime/markdown-parser/remark-mdc/micromark-extension/tokenize-inline.ts b/src/runtime/markdown-parser/remark-mdc/micromark-extension/tokenize-inline.ts index 216c95bb0..d82975da0 100644 --- a/src/runtime/markdown-parser/remark-mdc/micromark-extension/tokenize-inline.ts +++ b/src/runtime/markdown-parser/remark-mdc/micromark-extension/tokenize-inline.ts @@ -78,9 +78,10 @@ function tokenize (this: TokenizeContext, effects: Effects, ok: State, nok: Stat } function exit (code: Code): void | State { - if (!markdownLineEndingOrSpace(code) && code !== null && ![Codes.closingSquareBracket].includes(code)) { - return nok(code) - } + // Allow everything else to exit the componentText state + // if (!markdownLineEndingOrSpace(code) && ![Codes.EOF, Codes.closingSquareBracket, Codes.dot, Codes.comma].includes(code)) { + // return nok(code) + // } effects.exit('componentText') return ok(code) } diff --git a/test/features/parser-markdown.ts b/test/features/parser-markdown.ts index d73a54234..bf9aaec1e 100644 --- a/test/features/parser-markdown.ts +++ b/test/features/parser-markdown.ts @@ -1,5 +1,6 @@ import { describe, test, expect, assert } from 'vitest' import { $fetch } from '@nuxt/test-utils' +import { visit } from 'unist-util-visit' export const testMarkdownParser = () => { describe('parser:markdown', () => { @@ -69,6 +70,32 @@ export const testMarkdownParser = () => { expect(parsed.body.children.length).toEqual(0) }) + test('inline component followed by non-space characters', async () => { + const parsed = await $fetch('/api/parse', { + method: 'POST', + body: { + id: 'content:index.md', + content: [ + ':hello', // valid + ':hello,', // valid + ':hello-world', // valid but with different name + ':hello{}-world', // valid + ':hello:', // invalid + ':rocket:' // emoji + ].join('\n') + } + }) + + let compComponentCount = 0 + visit(parsed.body, node => (node as any).tag === 'hello', () => { + compComponentCount += 1 + }) + expect(compComponentCount).toEqual(3) + + // Check conflict between inline compoenent and emoji + expect(parsed.body.children[0].children.pop().value).toContain('🚀') + }) + test('h1 tags', async () => { const parsed = await $fetch('/api/parse', { method: 'POST',