feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2022-present, mdit-vue Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+75
View File
@@ -0,0 +1,75 @@
# @mdit-vue/plugin-component
[![npm](https://badgen.net/npm/v/@mdit-vue/plugin-component)](https://www.npmjs.com/package/@mdit-vue/plugin-component)
[![license](https://badgen.net/github/license/mdit-vue/mdit-vue)](https://github.com/mdit-vue/mdit-vue/blob/main/LICENSE)
A [markdown-it](https://github.com/markdown-it/markdown-it) plugin to allow Vue components in markdown.
- Treats [vue built-in components](https://vuejs.org/api/built-in-components.html) and unknown HTML tags as vue components (markdown-it would treat them as inline tags by default).
- Allows vue [`@` directive](https://vuejs.org/api/built-in-directives.html#v-on) on native HTML tags.
## Install
```sh
npm i @mdit-vue/plugin-component
```
## Usage
This plugin will only take effects when the `html` option of markdown-it is enabled:
```ts
import { componentPlugin } from '@mdit-vue/plugin-component';
import MarkdownIt from 'markdown-it';
const md = MarkdownIt({ html: true }).use(componentPlugin, {
// options
});
const rendered = md.render(
`\
<!-- @ shorthand is supported -->
<Foo @click="onClick" />
<!-- multi-line syntax won't be wrapped with <p> -->
<Foo
class="foo"
:bar="bar"
/>
`,
);
```
## Options
### blockTags
- Type: `string[]`
- Default: `[]`
- Details:
Extra tags to be treated as block tags.
By default, all standard [HTML inline elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements) will be treated as inline tags (excluding [Vue built-in special elements](https://vuejs.org/api/built-in-special-elements.html)). All unknown elements will be assumed as Vue components, and will be treated as block tags (with slight differences).
In some cases (should be rare though) you might want to force some tags to behave like block tags, then you can use this option to specify the tag names.
Notice that this option is case-sensitive, and has higher priority than the [inlineTags](#inlinetags) option.
### inlineTags
- Type: `string[]`
- Default: `[]`
- Details:
Extra tags to be treated as inline tags.
By default, only standard [HTML inline elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements) will be treated as inline tags (excluding [Vue built-in special elements](https://vuejs.org/api/built-in-special-elements.html)). All unknown elements will be assumed as Vue components, and will be treated as block tags (with slight differences).
Treating Vue components like block tags would work as expected in most cases. However, in some cases you might want to force some tags to behave like inline tags, then you can use this option to specify the tag names.
Notice that this option is case-sensitive, and has lower priority than the [blockTags](#blocktags) option.
+316
View File
@@ -0,0 +1,316 @@
'use strict';
const attr_name = "[a-zA-Z_:@][a-zA-Z0-9:._-]*";
const unquoted = "[^\"'=<>`\\x00-\\x20]+";
const single_quoted = "'[^']*'";
const double_quoted = '"[^"]*"';
const attr_value = "(?:" + unquoted + "|" + single_quoted + "|" + double_quoted + ")";
const attribute = "(?:\\s+" + attr_name + "(?:\\s*=\\s*" + attr_value + ")?)";
const open_tag = "<[A-Za-z][A-Za-z0-9\\-]*" + attribute + "*\\s*\\/?>";
const close_tag = "<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>";
const comment = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->";
const processing = "<[?][\\s\\S]*?[?]>";
const declaration = "<![A-Z]+\\s+[^>]*>";
const cdata = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>";
const HTML_TAG_RE = new RegExp(
"^(?:" + open_tag + "|" + close_tag + "|" + comment + "|" + processing + "|" + declaration + "|" + cdata + ")"
);
const HTML_OPEN_CLOSE_TAG_RE = new RegExp(
"^(?:" + open_tag + "|" + close_tag + ")"
);
const HTML_SELF_CLOSING_TAG_RE = new RegExp(
"^<[A-Za-z][A-Za-z0-9\\-]*" + attribute + "*\\s*\\/>"
);
const HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE = new RegExp(
"^<([A-Za-z][A-Za-z0-9\\-]*)" + attribute + "*\\s*>.*<\\/\\1\\s*>"
);
const TAGS_BLOCK = [
"address",
"article",
"aside",
"base",
"basefont",
"blockquote",
"body",
"caption",
"center",
"col",
"colgroup",
"dd",
"details",
"dialog",
"dir",
"div",
"dl",
"dt",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hr",
"html",
"iframe",
"legend",
"li",
"link",
"main",
"menu",
"menuitem",
"nav",
"noframes",
"ol",
"optgroup",
"option",
"p",
"param",
"search",
"section",
"summary",
"table",
"tbody",
"td",
"tfoot",
"th",
"thead",
"title",
"tr",
"track",
"ul"
];
const TAGS_INLINE = [
"a",
"abbr",
"acronym",
"audio",
"b",
"bdi",
"bdo",
"big",
"br",
"button",
"canvas",
"cite",
"code",
"data",
"datalist",
"del",
"dfn",
"em",
"embed",
"i",
"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"
];
const TAGS_VUE_RESERVED = [
"template",
"component",
"transition",
"transition-group",
"keep-alive",
"slot",
"teleport"
];
const createHtmlSequences = ({
blockTags,
inlineTags
}) => {
const forceBlockTags = [...blockTags, ...TAGS_BLOCK];
const forceInlineTags = [
...inlineTags,
...TAGS_INLINE.filter((item) => !TAGS_VUE_RESERVED.includes(item))
];
const HTML_SEQUENCES = [
[/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true],
[/^<!--/, /-->/, true],
[/^<\?/, /\?>/, true],
[/^<![A-Z]/, />/, true],
[/^<!\[CDATA\[/, /\]\]>/, true],
// MODIFIED: Support extra block tags from user options
[
new RegExp("^</?(" + forceBlockTags.join("|") + ")(?=(\\s|/?>|$))", "i"),
/^$/,
true
],
// ADDED: Matching component tags (all unknown tags) (i = 6)
[
new RegExp(
"^</?(?!(" + forceInlineTags.join("|") + ")(?![\\w-]))[A-Za-z][A-Za-z0-9\\-]*(?=(\\s|/?>|$))"
),
/^$/,
true
],
// this line is to treat a line that only have a single self-closed html tag
// as html_block, even if it's a self-closed inline tag
// MODIFIED: Tweak the original HTML_OPEN_CLOSE_TAG_RE
[new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + "\\s*$"), /^$/, false]
];
return HTML_SEQUENCES;
};
const createHtmlBlockRule = (options) => {
const HTML_SEQUENCES = createHtmlSequences(options);
return (state, startLine, endLine, silent) => {
let i;
let nextLine;
let lineText;
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
if (state.sCount[startLine] - state.blkIndent >= 4) {
return false;
}
if (!state.md.options.html) {
return false;
}
if (state.src.charCodeAt(pos) !== 60) {
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) {
return HTML_SEQUENCES[i][2];
}
if (i === 6) {
const match = (
// if the component tag is self-closing
lineText.match(HTML_SELF_CLOSING_TAG_RE) ?? // or has open and close tag in the same line
lineText.match(HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE)
);
if (match) {
state.line = startLine + 1;
let token2 = state.push("html_inline", "", 0);
token2.content = match[0];
token2.map = [startLine, state.line];
token2 = state.push("inline", "", 0);
token2.content = lineText.slice(match[0].length);
token2.map = [startLine, state.line];
token2.children = [];
return true;
}
}
nextLine = startLine + 1;
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;
};
};
const isLetter = (ch) => {
const lc = ch | 32;
return lc >= 97 && lc <= 122;
};
const htmlInlineRule = (state, silent) => {
const { pos } = state;
if (!state.md.options.html) {
return false;
}
const max = state.posMax;
if (state.src.charCodeAt(pos) !== 60 || pos + 2 >= max) {
return false;
}
const ch = state.src.charCodeAt(pos + 1);
if (ch !== 33 && ch !== 63 && ch !== 47 && !isLetter(ch)) {
return false;
}
const match = state.src.slice(pos).match(HTML_TAG_RE);
if (!match) {
return false;
}
if (!silent) {
const token = state.push("html_inline", "", 0);
token.content = state.src.slice(pos, pos + match[0].length);
}
state.pos += match[0].length;
return true;
};
const componentPlugin = (md, { blockTags = [], inlineTags = [] } = {}) => {
const htmlBlockRule = createHtmlBlockRule({
blockTags,
inlineTags
});
md.block.ruler.at("html_block", htmlBlockRule, {
alt: ["paragraph", "reference", "blockquote"]
});
md.inline.ruler.at("html_inline", htmlInlineRule);
};
exports.HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE = HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE;
exports.HTML_OPEN_CLOSE_TAG_RE = HTML_OPEN_CLOSE_TAG_RE;
exports.HTML_SELF_CLOSING_TAG_RE = HTML_SELF_CLOSING_TAG_RE;
exports.HTML_TAG_RE = HTML_TAG_RE;
exports.TAGS_BLOCK = TAGS_BLOCK;
exports.TAGS_INLINE = TAGS_INLINE;
exports.TAGS_VUE_RESERVED = TAGS_VUE_RESERVED;
exports.componentPlugin = componentPlugin;
exports.createHtmlBlockRule = createHtmlBlockRule;
exports.htmlInlineRule = htmlInlineRule;
+71
View File
@@ -0,0 +1,71 @@
import { PluginWithOptions } from 'markdown-it';
import { RuleBlock } from 'markdown-it/lib/parser_block.mjs';
import { RuleInline } from 'markdown-it/lib/parser_inline.mjs';
/**
* Options of @mdit-vue/plugin-component
*/
interface ComponentPluginOptions {
/**
* Extra tags to be treated as block tags.
*
* @default []
*/
blockTags?: string[];
/**
* Extra tags to be treated as inline tags.
*
* @default []
*/
inlineTags?: string[];
}
/**
* Allows better use of Vue components in Markdown
*/
declare const componentPlugin: PluginWithOptions<ComponentPluginOptions>;
declare const createHtmlBlockRule: (options: Required<ComponentPluginOptions>) => RuleBlock;
declare const htmlInlineRule: RuleInline;
declare const HTML_TAG_RE: RegExp;
declare const HTML_OPEN_CLOSE_TAG_RE: RegExp;
declare const HTML_SELF_CLOSING_TAG_RE: RegExp;
declare const HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE: RegExp;
/**
* List of block tags
*
* @see https://spec.commonmark.org/0.30/#html-blocks
* @see https://github.com/markdown-it/markdown-it/blob/master/lib/common/html_blocks.mjs
*/
declare const TAGS_BLOCK: string[];
/**
* According to markdown spec, all non-block html tags are treated as "inline"
* tags (wrapped with <p></p>), 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.30/#raw-html
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
*/
declare const TAGS_INLINE: string[];
/**
* Tags of Vue built-in components
*
* @see https://vuejs.org/api/built-in-components.html
* @see https://vuejs.org/api/built-in-special-elements.html
*/
declare const TAGS_VUE_RESERVED: string[];
export { HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE, HTML_OPEN_CLOSE_TAG_RE, HTML_SELF_CLOSING_TAG_RE, HTML_TAG_RE, TAGS_BLOCK, TAGS_INLINE, TAGS_VUE_RESERVED, componentPlugin, createHtmlBlockRule, htmlInlineRule };
export type { ComponentPluginOptions };
+71
View File
@@ -0,0 +1,71 @@
import { PluginWithOptions } from 'markdown-it';
import { RuleBlock } from 'markdown-it/lib/parser_block.mjs';
import { RuleInline } from 'markdown-it/lib/parser_inline.mjs';
/**
* Options of @mdit-vue/plugin-component
*/
interface ComponentPluginOptions {
/**
* Extra tags to be treated as block tags.
*
* @default []
*/
blockTags?: string[];
/**
* Extra tags to be treated as inline tags.
*
* @default []
*/
inlineTags?: string[];
}
/**
* Allows better use of Vue components in Markdown
*/
declare const componentPlugin: PluginWithOptions<ComponentPluginOptions>;
declare const createHtmlBlockRule: (options: Required<ComponentPluginOptions>) => RuleBlock;
declare const htmlInlineRule: RuleInline;
declare const HTML_TAG_RE: RegExp;
declare const HTML_OPEN_CLOSE_TAG_RE: RegExp;
declare const HTML_SELF_CLOSING_TAG_RE: RegExp;
declare const HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE: RegExp;
/**
* List of block tags
*
* @see https://spec.commonmark.org/0.30/#html-blocks
* @see https://github.com/markdown-it/markdown-it/blob/master/lib/common/html_blocks.mjs
*/
declare const TAGS_BLOCK: string[];
/**
* According to markdown spec, all non-block html tags are treated as "inline"
* tags (wrapped with <p></p>), 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.30/#raw-html
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
*/
declare const TAGS_INLINE: string[];
/**
* Tags of Vue built-in components
*
* @see https://vuejs.org/api/built-in-components.html
* @see https://vuejs.org/api/built-in-special-elements.html
*/
declare const TAGS_VUE_RESERVED: string[];
export { HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE, HTML_OPEN_CLOSE_TAG_RE, HTML_SELF_CLOSING_TAG_RE, HTML_TAG_RE, TAGS_BLOCK, TAGS_INLINE, TAGS_VUE_RESERVED, componentPlugin, createHtmlBlockRule, htmlInlineRule };
export type { ComponentPluginOptions };
+71
View File
@@ -0,0 +1,71 @@
import { PluginWithOptions } from 'markdown-it';
import { RuleBlock } from 'markdown-it/lib/parser_block.mjs';
import { RuleInline } from 'markdown-it/lib/parser_inline.mjs';
/**
* Options of @mdit-vue/plugin-component
*/
interface ComponentPluginOptions {
/**
* Extra tags to be treated as block tags.
*
* @default []
*/
blockTags?: string[];
/**
* Extra tags to be treated as inline tags.
*
* @default []
*/
inlineTags?: string[];
}
/**
* Allows better use of Vue components in Markdown
*/
declare const componentPlugin: PluginWithOptions<ComponentPluginOptions>;
declare const createHtmlBlockRule: (options: Required<ComponentPluginOptions>) => RuleBlock;
declare const htmlInlineRule: RuleInline;
declare const HTML_TAG_RE: RegExp;
declare const HTML_OPEN_CLOSE_TAG_RE: RegExp;
declare const HTML_SELF_CLOSING_TAG_RE: RegExp;
declare const HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE: RegExp;
/**
* List of block tags
*
* @see https://spec.commonmark.org/0.30/#html-blocks
* @see https://github.com/markdown-it/markdown-it/blob/master/lib/common/html_blocks.mjs
*/
declare const TAGS_BLOCK: string[];
/**
* According to markdown spec, all non-block html tags are treated as "inline"
* tags (wrapped with <p></p>), 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.30/#raw-html
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
*/
declare const TAGS_INLINE: string[];
/**
* Tags of Vue built-in components
*
* @see https://vuejs.org/api/built-in-components.html
* @see https://vuejs.org/api/built-in-special-elements.html
*/
declare const TAGS_VUE_RESERVED: string[];
export { HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE, HTML_OPEN_CLOSE_TAG_RE, HTML_SELF_CLOSING_TAG_RE, HTML_TAG_RE, TAGS_BLOCK, TAGS_INLINE, TAGS_VUE_RESERVED, componentPlugin, createHtmlBlockRule, htmlInlineRule };
export type { ComponentPluginOptions };
+305
View File
@@ -0,0 +1,305 @@
const attr_name = "[a-zA-Z_:@][a-zA-Z0-9:._-]*";
const unquoted = "[^\"'=<>`\\x00-\\x20]+";
const single_quoted = "'[^']*'";
const double_quoted = '"[^"]*"';
const attr_value = "(?:" + unquoted + "|" + single_quoted + "|" + double_quoted + ")";
const attribute = "(?:\\s+" + attr_name + "(?:\\s*=\\s*" + attr_value + ")?)";
const open_tag = "<[A-Za-z][A-Za-z0-9\\-]*" + attribute + "*\\s*\\/?>";
const close_tag = "<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>";
const comment = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->";
const processing = "<[?][\\s\\S]*?[?]>";
const declaration = "<![A-Z]+\\s+[^>]*>";
const cdata = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>";
const HTML_TAG_RE = new RegExp(
"^(?:" + open_tag + "|" + close_tag + "|" + comment + "|" + processing + "|" + declaration + "|" + cdata + ")"
);
const HTML_OPEN_CLOSE_TAG_RE = new RegExp(
"^(?:" + open_tag + "|" + close_tag + ")"
);
const HTML_SELF_CLOSING_TAG_RE = new RegExp(
"^<[A-Za-z][A-Za-z0-9\\-]*" + attribute + "*\\s*\\/>"
);
const HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE = new RegExp(
"^<([A-Za-z][A-Za-z0-9\\-]*)" + attribute + "*\\s*>.*<\\/\\1\\s*>"
);
const TAGS_BLOCK = [
"address",
"article",
"aside",
"base",
"basefont",
"blockquote",
"body",
"caption",
"center",
"col",
"colgroup",
"dd",
"details",
"dialog",
"dir",
"div",
"dl",
"dt",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hr",
"html",
"iframe",
"legend",
"li",
"link",
"main",
"menu",
"menuitem",
"nav",
"noframes",
"ol",
"optgroup",
"option",
"p",
"param",
"search",
"section",
"summary",
"table",
"tbody",
"td",
"tfoot",
"th",
"thead",
"title",
"tr",
"track",
"ul"
];
const TAGS_INLINE = [
"a",
"abbr",
"acronym",
"audio",
"b",
"bdi",
"bdo",
"big",
"br",
"button",
"canvas",
"cite",
"code",
"data",
"datalist",
"del",
"dfn",
"em",
"embed",
"i",
"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"
];
const TAGS_VUE_RESERVED = [
"template",
"component",
"transition",
"transition-group",
"keep-alive",
"slot",
"teleport"
];
const createHtmlSequences = ({
blockTags,
inlineTags
}) => {
const forceBlockTags = [...blockTags, ...TAGS_BLOCK];
const forceInlineTags = [
...inlineTags,
...TAGS_INLINE.filter((item) => !TAGS_VUE_RESERVED.includes(item))
];
const HTML_SEQUENCES = [
[/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true],
[/^<!--/, /-->/, true],
[/^<\?/, /\?>/, true],
[/^<![A-Z]/, />/, true],
[/^<!\[CDATA\[/, /\]\]>/, true],
// MODIFIED: Support extra block tags from user options
[
new RegExp("^</?(" + forceBlockTags.join("|") + ")(?=(\\s|/?>|$))", "i"),
/^$/,
true
],
// ADDED: Matching component tags (all unknown tags) (i = 6)
[
new RegExp(
"^</?(?!(" + forceInlineTags.join("|") + ")(?![\\w-]))[A-Za-z][A-Za-z0-9\\-]*(?=(\\s|/?>|$))"
),
/^$/,
true
],
// this line is to treat a line that only have a single self-closed html tag
// as html_block, even if it's a self-closed inline tag
// MODIFIED: Tweak the original HTML_OPEN_CLOSE_TAG_RE
[new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + "\\s*$"), /^$/, false]
];
return HTML_SEQUENCES;
};
const createHtmlBlockRule = (options) => {
const HTML_SEQUENCES = createHtmlSequences(options);
return (state, startLine, endLine, silent) => {
let i;
let nextLine;
let lineText;
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
if (state.sCount[startLine] - state.blkIndent >= 4) {
return false;
}
if (!state.md.options.html) {
return false;
}
if (state.src.charCodeAt(pos) !== 60) {
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) {
return HTML_SEQUENCES[i][2];
}
if (i === 6) {
const match = (
// if the component tag is self-closing
lineText.match(HTML_SELF_CLOSING_TAG_RE) ?? // or has open and close tag in the same line
lineText.match(HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE)
);
if (match) {
state.line = startLine + 1;
let token2 = state.push("html_inline", "", 0);
token2.content = match[0];
token2.map = [startLine, state.line];
token2 = state.push("inline", "", 0);
token2.content = lineText.slice(match[0].length);
token2.map = [startLine, state.line];
token2.children = [];
return true;
}
}
nextLine = startLine + 1;
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;
};
};
const isLetter = (ch) => {
const lc = ch | 32;
return lc >= 97 && lc <= 122;
};
const htmlInlineRule = (state, silent) => {
const { pos } = state;
if (!state.md.options.html) {
return false;
}
const max = state.posMax;
if (state.src.charCodeAt(pos) !== 60 || pos + 2 >= max) {
return false;
}
const ch = state.src.charCodeAt(pos + 1);
if (ch !== 33 && ch !== 63 && ch !== 47 && !isLetter(ch)) {
return false;
}
const match = state.src.slice(pos).match(HTML_TAG_RE);
if (!match) {
return false;
}
if (!silent) {
const token = state.push("html_inline", "", 0);
token.content = state.src.slice(pos, pos + match[0].length);
}
state.pos += match[0].length;
return true;
};
const componentPlugin = (md, { blockTags = [], inlineTags = [] } = {}) => {
const htmlBlockRule = createHtmlBlockRule({
blockTags,
inlineTags
});
md.block.ruler.at("html_block", htmlBlockRule, {
alt: ["paragraph", "reference", "blockquote"]
});
md.inline.ruler.at("html_inline", htmlInlineRule);
};
export { HTML_OPEN_AND_CLOSE_TAG_IN_THE_SAME_LINE_RE, HTML_OPEN_CLOSE_TAG_RE, HTML_SELF_CLOSING_TAG_RE, HTML_TAG_RE, TAGS_BLOCK, TAGS_INLINE, TAGS_VUE_RESERVED, componentPlugin, createHtmlBlockRule, htmlInlineRule };
+55
View File
@@ -0,0 +1,55 @@
{
"name": "@mdit-vue/plugin-component",
"version": "2.1.4",
"description": "A markdown-it plugin to allow vue components in markdown",
"keywords": [
"markdown-it",
"markdown-it-plugin",
"vue",
"component"
],
"homepage": "https://github.com/mdit-vue",
"bugs": {
"url": "https://github.com/mdit-vue/mdit-vue/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mdit-vue/mdit-vue.git"
},
"license": "MIT",
"author": "meteorlxy <meteor.lxy@foxmail.com>",
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "unbuild",
"test": "vitest"
},
"dependencies": {
"@types/markdown-it": "^14.1.2",
"markdown-it": "^14.1.0"
},
"devDependencies": {
"@vue/shared": "^3.5.13"
},
"publishConfig": {
"access": "public"
},
"gitHead": "01a6daa37fff48ea3992becfd4ba8c0f97a605ad"
}
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2022-present, mdit-vue Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+73
View File
@@ -0,0 +1,73 @@
# @mdit-vue/plugin-frontmatter
[![npm](https://badgen.net/npm/v/@mdit-vue/plugin-frontmatter)](https://www.npmjs.com/package/@mdit-vue/plugin-frontmatter)
[![license](https://badgen.net/github/license/mdit-vue/mdit-vue)](https://github.com/mdit-vue/mdit-vue/blob/main/LICENSE)
A [markdown-it](https://github.com/markdown-it/markdown-it) plugin to get markdown frontmatter with [gray-matter](https://github.com/jonschlinkert/gray-matter).
- Extracts frontmatter into markdown-it `env.frontmatter`.
- Allows providing default frontmatter via markdown-it `env.frontmatter`.
- Extracts markdown content without frontmatter into markdown-it `env.content`.
- Supports extracting rendered excerpt into markdown-it `env.excerpt`.
## Install
```sh
npm i @mdit-vue/plugin-frontmatter
```
## Usage
```ts
import { frontmatterPlugin } from '@mdit-vue/plugin-frontmatter';
import type { MarkdownItEnv } from '@mdit-vue/types';
import MarkdownIt from 'markdown-it';
const md = MarkdownIt({ html: true }).use(frontmatterPlugin, {
// options
grayMatterOptions: {
excerpt: true,
excerpt_separator: '<!-- more -->',
},
});
const env: MarkdownItEnv = {};
const rendered = md.render(
`\
---
title: foobar
---
Everything above **more** comment will be extracted as excerpt.
<!-- more -->
Things below **more** comment will not be included in the excerpt.
`,
env,
);
console.log(env.frontmatter);
console.log(env.content);
console.log(env.excerpt);
```
## Options
### grayMatterOptions
- Type: `GrayMatterOptions`
- Details:
Options of [gray-matter](https://github.com/jonschlinkert/gray-matter).
### renderExcerpt
- Type: `boolean`
- Default: `true`
Render the [excerpt](https://github.com/jonschlinkert/gray-matter#optionsexcerpt) with markdown-it or not.
This option will only take effects when `grayMatterOptions.excerpt` is enabled (disabled by default) or `grayMatterOptions.excerpt_separator` is set (undefined by default).
+32
View File
@@ -0,0 +1,32 @@
'use strict';
const grayMatter = require('gray-matter');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const grayMatter__default = /*#__PURE__*/_interopDefaultCompat(grayMatter);
const frontmatterPlugin = (md, { grayMatterOptions, renderExcerpt = true } = {}) => {
const parse = md.parse.bind(md);
md.parse = (src, env = {}) => {
const { data, content, excerpt = "" } = grayMatter__default(src, grayMatterOptions);
env.content = content;
env.frontmatter = {
// allow providing default value
...env.frontmatter,
...data
};
env.excerpt = renderExcerpt && excerpt ? (
// render the excerpt with original markdown-it render method.
// here we spread `env` to avoid mutating the original object.
// using deep clone might be a safer choice.
md.render(excerpt, { ...env })
) : (
// use the raw excerpt directly
excerpt
);
return parse(content, env);
};
};
exports.frontmatterPlugin = frontmatterPlugin;
@@ -0,0 +1,50 @@
import { PluginWithOptions } from 'markdown-it';
import matter from 'gray-matter';
type GrayMatterOptions = matter.GrayMatterOption<string, GrayMatterOptions>;
/**
* Options of @mdit-vue/plugin-frontmatter
*/
interface FrontmatterPluginOptions {
/**
* Options of gray-matter
*
* @see https://github.com/jonschlinkert/gray-matter#options
*/
grayMatterOptions?: GrayMatterOptions;
/**
* Render the excerpt or not
*
* @default true
*/
renderExcerpt?: boolean;
}
declare module '@mdit-vue/types' {
interface MarkdownItEnv {
/**
* The raw Markdown content without frontmatter
*/
content?: string;
/**
* The excerpt that extracted by `@mdit-vue/plugin-frontmatter`
*
* - Would be the rendered HTML when `renderExcerpt` is enabled
* - Would be the raw Markdown when `renderExcerpt` is disabled
*/
excerpt?: string;
/**
* The frontmatter that extracted by `@mdit-vue/plugin-frontmatter`
*/
frontmatter?: Record<string, unknown>;
}
}
/**
* Get markdown frontmatter and excerpt
*
* Extract them into env
*/
declare const frontmatterPlugin: PluginWithOptions<FrontmatterPluginOptions>;
export { frontmatterPlugin };
export type { FrontmatterPluginOptions };
@@ -0,0 +1,50 @@
import { PluginWithOptions } from 'markdown-it';
import matter from 'gray-matter';
type GrayMatterOptions = matter.GrayMatterOption<string, GrayMatterOptions>;
/**
* Options of @mdit-vue/plugin-frontmatter
*/
interface FrontmatterPluginOptions {
/**
* Options of gray-matter
*
* @see https://github.com/jonschlinkert/gray-matter#options
*/
grayMatterOptions?: GrayMatterOptions;
/**
* Render the excerpt or not
*
* @default true
*/
renderExcerpt?: boolean;
}
declare module '@mdit-vue/types' {
interface MarkdownItEnv {
/**
* The raw Markdown content without frontmatter
*/
content?: string;
/**
* The excerpt that extracted by `@mdit-vue/plugin-frontmatter`
*
* - Would be the rendered HTML when `renderExcerpt` is enabled
* - Would be the raw Markdown when `renderExcerpt` is disabled
*/
excerpt?: string;
/**
* The frontmatter that extracted by `@mdit-vue/plugin-frontmatter`
*/
frontmatter?: Record<string, unknown>;
}
}
/**
* Get markdown frontmatter and excerpt
*
* Extract them into env
*/
declare const frontmatterPlugin: PluginWithOptions<FrontmatterPluginOptions>;
export { frontmatterPlugin };
export type { FrontmatterPluginOptions };
@@ -0,0 +1,50 @@
import { PluginWithOptions } from 'markdown-it';
import matter from 'gray-matter';
type GrayMatterOptions = matter.GrayMatterOption<string, GrayMatterOptions>;
/**
* Options of @mdit-vue/plugin-frontmatter
*/
interface FrontmatterPluginOptions {
/**
* Options of gray-matter
*
* @see https://github.com/jonschlinkert/gray-matter#options
*/
grayMatterOptions?: GrayMatterOptions;
/**
* Render the excerpt or not
*
* @default true
*/
renderExcerpt?: boolean;
}
declare module '@mdit-vue/types' {
interface MarkdownItEnv {
/**
* The raw Markdown content without frontmatter
*/
content?: string;
/**
* The excerpt that extracted by `@mdit-vue/plugin-frontmatter`
*
* - Would be the rendered HTML when `renderExcerpt` is enabled
* - Would be the raw Markdown when `renderExcerpt` is disabled
*/
excerpt?: string;
/**
* The frontmatter that extracted by `@mdit-vue/plugin-frontmatter`
*/
frontmatter?: Record<string, unknown>;
}
}
/**
* Get markdown frontmatter and excerpt
*
* Extract them into env
*/
declare const frontmatterPlugin: PluginWithOptions<FrontmatterPluginOptions>;
export { frontmatterPlugin };
export type { FrontmatterPluginOptions };
+26
View File
@@ -0,0 +1,26 @@
import grayMatter from 'gray-matter';
const frontmatterPlugin = (md, { grayMatterOptions, renderExcerpt = true } = {}) => {
const parse = md.parse.bind(md);
md.parse = (src, env = {}) => {
const { data, content, excerpt = "" } = grayMatter(src, grayMatterOptions);
env.content = content;
env.frontmatter = {
// allow providing default value
...env.frontmatter,
...data
};
env.excerpt = renderExcerpt && excerpt ? (
// render the excerpt with original markdown-it render method.
// here we spread `env` to avoid mutating the original object.
// using deep clone might be a safer choice.
md.render(excerpt, { ...env })
) : (
// use the raw excerpt directly
excerpt
);
return parse(content, env);
};
};
export { frontmatterPlugin };
+55
View File
@@ -0,0 +1,55 @@
{
"name": "@mdit-vue/plugin-frontmatter",
"version": "2.1.4",
"description": "A markdown-it plugin to get markdown frontmatter",
"keywords": [
"markdown-it",
"markdown-it-plugin",
"vue",
"frontmatter",
"excerpt"
],
"homepage": "https://github.com/mdit-vue",
"bugs": {
"url": "https://github.com/mdit-vue/mdit-vue/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mdit-vue/mdit-vue.git"
},
"license": "MIT",
"author": "meteorlxy <meteor.lxy@foxmail.com>",
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "unbuild",
"test": "vitest"
},
"dependencies": {
"@mdit-vue/types": "2.1.4",
"@types/markdown-it": "^14.1.2",
"gray-matter": "^4.0.3",
"markdown-it": "^14.1.0"
},
"publishConfig": {
"access": "public"
},
"gitHead": "01a6daa37fff48ea3992becfd4ba8c0f97a605ad"
}
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2022-present, mdit-vue Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+32
View File
@@ -0,0 +1,32 @@
# @mdit-vue/types
[![npm](https://badgen.net/npm/v/@mdit-vue/types)](https://www.npmjs.com/package/@mdit-vue/types)
Shared types definition of mdit-vue.
## Install
```sh
npm i @mdit-vue/types
```
## Usage
Add more properties to `env` via [TypeScript module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation):
```ts
declare module '@mdit-vue/types' {
interface MarkdownItEnv {
foo?: string[];
}
}
```
```ts
import type { MarkdownItEnv } from '@mdit-vue/types';
import MarkdownIt from 'markdown-it';
const md = MarkdownIt({ html: true });
const env: MarkdownItEnv = {};
const rendered = md.render('raw markdown text', env);
```
+2
View File
@@ -0,0 +1,2 @@
'use strict';
+32
View File
@@ -0,0 +1,32 @@
interface MarkdownItEnv {
}
interface MarkdownItHeader {
/**
* The level of the header
*
* `1` to `6` for `<h1>` to `<h6>`
*/
level: number;
/**
* The title of the header
*/
title: string;
/**
* The slug of the header
*
* Typically the `id` attr of the header anchor
*/
slug: string;
/**
* Link of the header
*
* Typically using `#${slug}` as the anchor hash
*/
link: string;
/**
* The children of the header
*/
children: MarkdownItHeader[];
}
export type { MarkdownItEnv, MarkdownItHeader };
+32
View File
@@ -0,0 +1,32 @@
interface MarkdownItEnv {
}
interface MarkdownItHeader {
/**
* The level of the header
*
* `1` to `6` for `<h1>` to `<h6>`
*/
level: number;
/**
* The title of the header
*/
title: string;
/**
* The slug of the header
*
* Typically the `id` attr of the header anchor
*/
slug: string;
/**
* Link of the header
*
* Typically using `#${slug}` as the anchor hash
*/
link: string;
/**
* The children of the header
*/
children: MarkdownItHeader[];
}
export type { MarkdownItEnv, MarkdownItHeader };
+32
View File
@@ -0,0 +1,32 @@
interface MarkdownItEnv {
}
interface MarkdownItHeader {
/**
* The level of the header
*
* `1` to `6` for `<h1>` to `<h6>`
*/
level: number;
/**
* The title of the header
*/
title: string;
/**
* The slug of the header
*
* Typically the `id` attr of the header anchor
*/
slug: string;
/**
* Link of the header
*
* Typically using `#${slug}` as the anchor hash
*/
link: string;
/**
* The children of the header
*/
children: MarkdownItHeader[];
}
export type { MarkdownItEnv, MarkdownItHeader };
+1
View File
@@ -0,0 +1 @@
+46
View File
@@ -0,0 +1,46 @@
{
"name": "@mdit-vue/types",
"version": "2.1.4",
"description": "Shared types definition of mdit-vue",
"keywords": [
"markdown-it",
"vue",
"types"
],
"homepage": "https://github.com/mdit-vue",
"bugs": {
"url": "https://github.com/mdit-vue/mdit-vue/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mdit-vue/mdit-vue.git"
},
"license": "MIT",
"author": "meteorlxy <meteor.lxy@foxmail.com>",
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "unbuild"
},
"publishConfig": {
"access": "public"
},
"gitHead": "01a6daa37fff48ea3992becfd4ba8c0f97a605ad"
}