From 0f186126dceed4eec52c8f1c844d305a6335cf4c Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Thu, 30 Jun 2022 13:42:13 +0800 Subject: [PATCH] refactor: externalize component plugin (#3) --- package.json | 1 + pnpm-lock.yaml | 9 + src/markdown.ts | 2 +- src/plugins/component.ts | 199 ---------------------- test/__snapshots__/transform.test.ts.snap | 2 +- 5 files changed, 12 insertions(+), 201 deletions(-) delete mode 100644 src/plugins/component.ts diff --git a/package.json b/package.json index 06c16d3..3cf9f48 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ }, "dependencies": { "@antfu/utils": "^0.5.2", + "@mdit-vue/plugin-component": "^0.1.1", "@rollup/pluginutils": "^4.2.1", "@types/markdown-it": "^12.2.3", "gray-matter": "^4.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b0cc51..1e91eda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ importers: '@antfu/eslint-config': ^0.25.2 '@antfu/ni': ^0.16.3 '@antfu/utils': ^0.5.2 + '@mdit-vue/plugin-component': ^0.1.1 '@rollup/pluginutils': ^4.2.1 '@types/markdown-it': ^12.2.3 '@types/node': ^18.0.0 @@ -22,6 +23,7 @@ importers: vitest: ^0.16.0 dependencies: '@antfu/utils': 0.5.2 + '@mdit-vue/plugin-component': 0.1.1 '@rollup/pluginutils': 4.2.1 '@types/markdown-it': 12.2.3 gray-matter: 4.0.3 @@ -258,6 +260,13 @@ packages: type-detect: 4.0.8 dev: true + /@mdit-vue/plugin-component/0.1.1: + resolution: {integrity: sha512-DC4OEZO737FbvwZxVToO2d9jprbQEs4lor7bylEBrPDYDds8pjRBUIx2BNc54X0effIopsZhq+b4gxkOHJOPNQ==} + dependencies: + '@types/markdown-it': 12.2.3 + markdown-it: 13.0.1 + dev: false + /@nodelib/fs.scandir/2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} diff --git a/src/markdown.ts b/src/markdown.ts index 9b11016..104558b 100644 --- a/src/markdown.ts +++ b/src/markdown.ts @@ -1,8 +1,8 @@ import MarkdownIt from 'markdown-it' import matter from 'gray-matter' import { toArray, uniq } from '@antfu/utils' +import { componentPlugin } from '@mdit-vue/plugin-component' import type { ResolvedOptions } from './types' -import { componentPlugin } from './plugins/component' const scriptSetupRE = /<\s*script([^>]*)\bsetup\b([^>]*)>([\s\S]*)<\/script>/mg const defineExposeRE = /defineExpose\s*\(/mg diff --git a/src/plugins/component.ts b/src/plugins/component.ts deleted file mode 100644 index 4d70827..0000000 --- a/src/plugins/component.ts +++ /dev/null @@ -1,199 +0,0 @@ -// ported from https://github.com/vuejs/vitepress/blob/d0fdda69045bb280dbf39e035f1f8474247196a6/src/node/markdown/plugins/component.ts - -import type MarkdownIt from 'markdown-it' -import blockNames from 'markdown-it/lib/common/html_blocks' - -/** - * Vue reserved tags - * - * @see https://vuejs.org/api/built-in-components.html - */ -const vueReservedTags = [ - 'template', - 'component', - 'transition', - 'transition-group', - 'keep-alive', - 'slot', - 'teleport', -] - -/** - * According to markdown spec, all non-block html tags are treated as "inline" - * tags (wrapped with

), including those "unknown" tags. - * - * Therefore, markdown-it processes "inline" tags and "unknown" tags in the - * same way, and does not care if a tag is "inline" or "unknown". - * - * As we want to take those "unknown" tags as custom components, we should - * treat them as "block" tags. - * - * So we have to distinguish between "inline" and "unknown" tags ourselves. - * - * The inline tags list comes from MDN. - * - * @see https://spec.commonmark.org/0.29/#raw-html - * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements - */ -const inlineTags = [ - 'a', - 'abbr', - 'acronym', - 'audio', - 'b', - 'bdi', - 'bdo', - 'big', - 'br', - 'button', - 'canvas', - 'cite', - 'code', - 'data', - 'datalist', - 'del', - 'dfn', - 'em', - 'embed', - 'i', - // iframe is treated as HTML blocks in markdown spec - // 'iframe', - 'img', - 'input', - 'ins', - 'kbd', - 'label', - 'map', - 'mark', - 'meter', - 'noscript', - 'object', - 'output', - 'picture', - 'progress', - 'q', - 'ruby', - 's', - 'samp', - 'script', - 'select', - 'slot', - 'small', - 'span', - 'strong', - 'sub', - 'sup', - 'svg', - 'template', - 'textarea', - 'time', - 'u', - 'tt', - 'var', - 'video', - 'wbr', -] - -// replacing the default htmlBlock rule to allow using custom components at -// root level -// -// an array of opening and corresponding closing sequences for html tags, -// last argument defines whether it can terminate a paragraph or not -const HTML_SEQUENCES: [RegExp, RegExp, boolean][] = [ - [/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true], - [/^/, true], - [/^<\?/, /\?>/, true], - [/^/, true], - [/^/, true], - - // MODIFIED HERE: treat vue reserved tags as block tags - [ - new RegExp(`^|$))`, 'i'), - /^$/, - true, - ], - - // MODIFIED HERE: treat unknown tags as block tags (custom components), - // excluding known inline tags - [ - new RegExp( - `^]`, - ), - /^$/, - true, - ], - - [ - new RegExp(`^|$))`, 'i'), - /^$/, - true, - ], - - [ - // eslint-disable-next-line no-control-regex - /^(?:<[A-Za-z][A-Za-z0-9\-]*(?:\s+[a-zA-Z_:@][a-zA-Z0-9:._-]*(?:\s*=\s*(?:[^"'=<>`\x00-\x20]+|'[^']*'|"[^"]*"))?)*\s*\/?>|<\/[A-Za-z][A-Za-z0-9\-]*\s*>)/, - /^$/, - true, - ], -] - -export const componentPlugin = (md: MarkdownIt) => { - md.block.ruler.at('html_block', (state, startLine, endLine, silent): boolean => { - let i, nextLine, lineText - let pos = state.bMarks[startLine] + state.tShift[startLine] - let max = state.eMarks[startLine] - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) - return false - - if (!state.md.options.html) - return false - - if (state.src.charCodeAt(pos) !== 0x3C /* < */) - return false - - lineText = state.src.slice(pos, max) - - for (i = 0; i < HTML_SEQUENCES.length; i++) { - if (HTML_SEQUENCES[i][0].test(lineText)) - break - } - - if (i === HTML_SEQUENCES.length) - return false - - if (silent) { - // true if this sequence can be a terminator, false otherwise - return HTML_SEQUENCES[i][2] - } - - nextLine = startLine + 1 - - // if we are here - we detected HTML block. let's roll down till block end - if (!HTML_SEQUENCES[i][1].test(lineText)) { - for (; nextLine < endLine; nextLine++) { - if (state.sCount[nextLine] < state.blkIndent) - break - - pos = state.bMarks[nextLine] + state.tShift[nextLine] - max = state.eMarks[nextLine] - lineText = state.src.slice(pos, max) - - if (HTML_SEQUENCES[i][1].test(lineText)) { - if (lineText.length !== 0) - nextLine++ - break - } - } - } - - state.line = nextLine - - const token = state.push('html_block', '', 0) - token.map = [startLine, nextLine] - token.content = state.getLines(startLine, nextLine, state.blkIndent, true) - - return true - }) -} diff --git a/test/__snapshots__/transform.test.ts.snap b/test/__snapshots__/transform.test.ts.snap index 8fa15b6..bb3592e 100644 --- a/test/__snapshots__/transform.test.ts.snap +++ b/test/__snapshots__/transform.test.ts.snap @@ -2,7 +2,7 @@ exports[`transform > Vue directives 1`] = ` "