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 @@
MIT License
Copyright (c) 2022 Anthony Fu <https://github.com/antfu>
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.
+55
View File
@@ -0,0 +1,55 @@
# markdown-it-async
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
Enhance [`markdown-it`](https://github.com/markdown-it/markdown-it) to support **async** highlight function.
## Usage
```ts
import MarkdownItAsync from 'markdown-it-async'
const md = MarkdownItAsync({
async highlight(code, lang) {
const { codeToHtml } = await import('shiki')
return await codeToHtml(code, { lang, theme: 'vitesse-dark' })
}
})
const html = await md.renderAsync(markdown)
```
## How it works?
This package is a thin wrapper around `markdown-it` to support async highlight function. It uses [the approch suggested in `markdown-it`'s docs](https://github.com/markdown-it/markdown-it/blob/master/docs/development.md#i-need-async-rule-how-to-do-it), by putting placeholders in sync mode and then replace them with async results.
The goal is to make it easy to use and absorb the "hack" into the library itself.
## Sponsors
<p align="center">
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
</a>
</p>
## License
[MIT](./LICENSE) License © 2024-PRESENT [Anthony Fu](https://github.com/antfu)
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/markdown-it-async?style=flat&colorA=080f12&colorB=1fa669
[npm-version-href]: https://npmjs.com/package/markdown-it-async
[npm-downloads-src]: https://img.shields.io/npm/dm/markdown-it-async?style=flat&colorA=080f12&colorB=1fa669
[npm-downloads-href]: https://npmjs.com/package/markdown-it-async
[bundle-src]: https://img.shields.io/bundlephobia/minzip/markdown-it-async?style=flat&colorA=080f12&colorB=1fa669&label=minzip
[bundle-href]: https://bundlephobia.com/result?p=markdown-it-async
[license-src]: https://img.shields.io/github/license/antfu/markdown-it-async.svg?style=flat&colorA=080f12&colorB=1fa669
[license-href]: https://github.com/antfu/markdown-it-async/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
[jsdocs-href]: https://www.jsdocs.io/package/markdown-it-async
+88
View File
@@ -0,0 +1,88 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const MarkdownIt = require('markdown-it');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const MarkdownIt__default = /*#__PURE__*/_interopDefaultCompat(MarkdownIt);
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const placeholder = (id) => `<pre>::markdown-it-async::${id}::</pre>`;
const placeholderRe = /<pre>::markdown-it-async::(\w+)::<\/pre>/g;
function randStr() {
return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
}
class MarkdownItAsync extends MarkdownIt__default {
constructor(...args) {
const map = /* @__PURE__ */ new Map();
const options = args.length === 2 ? args[1] : args[0];
if (options && "highlight" in options)
options.highlight = wrapHightlight(options.highlight, map);
super(...args);
__publicField(this, "map");
this.map = map;
}
async renderAsync(src, env) {
const result = this.render(src, env);
return replaceAsync(result, placeholderRe, async (match, id) => {
if (!this.map.has(id))
throw new Error(`Unknown highlight placeholder id: ${id}`);
const [promise, _str, lang, _attrs] = this.map.get(id);
const result2 = await promise || "";
this.map.delete(id);
if (result2.startsWith("<pre"))
return result2;
else
return `<pre><code class="language-${lang}">${result2}</code></pre>`;
});
}
}
function createMarkdownItAsync(...args) {
return new MarkdownItAsync(...args);
}
function replaceAsync(string, searchValue, replacer) {
try {
if (typeof replacer === "function") {
const values = [];
String.prototype.replace.call(string, searchValue, (...args) => {
values.push(replacer(...args));
return "";
});
return Promise.all(values).then((resolvedValues) => {
return String.prototype.replace.call(string, searchValue, () => {
return resolvedValues.shift() || "";
});
});
} else {
return Promise.resolve(
String.prototype.replace.call(string, searchValue, replacer)
);
}
} catch (error) {
return Promise.reject(error);
}
}
function wrapHightlight(highlight, map) {
if (!highlight)
return void 0;
return (str, lang, attrs) => {
const promise = highlight(str, lang, attrs);
if (typeof promise === "string")
return promise;
const id = randStr();
map.set(id, [promise, str, lang, attrs]);
return placeholder(id);
};
}
exports.MarkdownItAsync = MarkdownItAsync;
exports.createMarkdownItAsync = createMarkdownItAsync;
exports.default = createMarkdownItAsync;
exports.replaceAsync = replaceAsync;
+26
View File
@@ -0,0 +1,26 @@
import MarkdownIt, { Options, PresetName } from 'markdown-it';
export { PluginSimple, PluginWithOptions, PluginWithParams } from 'markdown-it';
interface MarkdownItAsyncOptions extends Omit<Options, 'highlight'> {
/**
* Highlighter function for fenced code blocks.
* Highlighter `function (str, lang, attrs)` should return escaped HTML. It can
* also return empty string if the source was not changed and should be escaped
* externally. If result starts with <pre... internal wrapper is skipped.
* @default null
*/
highlight?: ((str: string, lang: string, attrs: string) => string | Promise<string>) | null | undefined;
}
type MarkdownItASyncPlaceholderMap = Map<string, [promise: Promise<string>, str: string, lang: string, attrs: string]>;
declare class MarkdownItAsync extends MarkdownIt {
map: MarkdownItASyncPlaceholderMap;
constructor(presetName: PresetName, options?: MarkdownItAsyncOptions);
constructor(options?: MarkdownItAsyncOptions);
renderAsync(src: string, env?: any): Promise<string>;
}
declare function createMarkdownItAsync(presetName: PresetName, options?: MarkdownItAsyncOptions): MarkdownItAsync;
declare function createMarkdownItAsync(options?: MarkdownItAsyncOptions): MarkdownItAsync;
declare function replaceAsync(string: string, searchValue: RegExp, replacer: (...args: string[]) => Promise<string>): Promise<string>;
export { type MarkdownItASyncPlaceholderMap, MarkdownItAsync, type MarkdownItAsyncOptions, type MarkdownItAsyncOptions as Options, createMarkdownItAsync, createMarkdownItAsync as default, replaceAsync };
+26
View File
@@ -0,0 +1,26 @@
import MarkdownIt, { Options, PresetName } from 'markdown-it';
export { PluginSimple, PluginWithOptions, PluginWithParams } from 'markdown-it';
interface MarkdownItAsyncOptions extends Omit<Options, 'highlight'> {
/**
* Highlighter function for fenced code blocks.
* Highlighter `function (str, lang, attrs)` should return escaped HTML. It can
* also return empty string if the source was not changed and should be escaped
* externally. If result starts with <pre... internal wrapper is skipped.
* @default null
*/
highlight?: ((str: string, lang: string, attrs: string) => string | Promise<string>) | null | undefined;
}
type MarkdownItASyncPlaceholderMap = Map<string, [promise: Promise<string>, str: string, lang: string, attrs: string]>;
declare class MarkdownItAsync extends MarkdownIt {
map: MarkdownItASyncPlaceholderMap;
constructor(presetName: PresetName, options?: MarkdownItAsyncOptions);
constructor(options?: MarkdownItAsyncOptions);
renderAsync(src: string, env?: any): Promise<string>;
}
declare function createMarkdownItAsync(presetName: PresetName, options?: MarkdownItAsyncOptions): MarkdownItAsync;
declare function createMarkdownItAsync(options?: MarkdownItAsyncOptions): MarkdownItAsync;
declare function replaceAsync(string: string, searchValue: RegExp, replacer: (...args: string[]) => Promise<string>): Promise<string>;
export { type MarkdownItASyncPlaceholderMap, MarkdownItAsync, type MarkdownItAsyncOptions, type MarkdownItAsyncOptions as Options, createMarkdownItAsync, createMarkdownItAsync as default, replaceAsync };
+26
View File
@@ -0,0 +1,26 @@
import MarkdownIt, { Options, PresetName } from 'markdown-it';
export { PluginSimple, PluginWithOptions, PluginWithParams } from 'markdown-it';
interface MarkdownItAsyncOptions extends Omit<Options, 'highlight'> {
/**
* Highlighter function for fenced code blocks.
* Highlighter `function (str, lang, attrs)` should return escaped HTML. It can
* also return empty string if the source was not changed and should be escaped
* externally. If result starts with <pre... internal wrapper is skipped.
* @default null
*/
highlight?: ((str: string, lang: string, attrs: string) => string | Promise<string>) | null | undefined;
}
type MarkdownItASyncPlaceholderMap = Map<string, [promise: Promise<string>, str: string, lang: string, attrs: string]>;
declare class MarkdownItAsync extends MarkdownIt {
map: MarkdownItASyncPlaceholderMap;
constructor(presetName: PresetName, options?: MarkdownItAsyncOptions);
constructor(options?: MarkdownItAsyncOptions);
renderAsync(src: string, env?: any): Promise<string>;
}
declare function createMarkdownItAsync(presetName: PresetName, options?: MarkdownItAsyncOptions): MarkdownItAsync;
declare function createMarkdownItAsync(options?: MarkdownItAsyncOptions): MarkdownItAsync;
declare function replaceAsync(string: string, searchValue: RegExp, replacer: (...args: string[]) => Promise<string>): Promise<string>;
export { type MarkdownItASyncPlaceholderMap, MarkdownItAsync, type MarkdownItAsyncOptions, type MarkdownItAsyncOptions as Options, createMarkdownItAsync, createMarkdownItAsync as default, replaceAsync };
+77
View File
@@ -0,0 +1,77 @@
import MarkdownIt from 'markdown-it';
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const placeholder = (id) => `<pre>::markdown-it-async::${id}::</pre>`;
const placeholderRe = /<pre>::markdown-it-async::(\w+)::<\/pre>/g;
function randStr() {
return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
}
class MarkdownItAsync extends MarkdownIt {
constructor(...args) {
const map = /* @__PURE__ */ new Map();
const options = args.length === 2 ? args[1] : args[0];
if (options && "highlight" in options)
options.highlight = wrapHightlight(options.highlight, map);
super(...args);
__publicField(this, "map");
this.map = map;
}
async renderAsync(src, env) {
const result = this.render(src, env);
return replaceAsync(result, placeholderRe, async (match, id) => {
if (!this.map.has(id))
throw new Error(`Unknown highlight placeholder id: ${id}`);
const [promise, _str, lang, _attrs] = this.map.get(id);
const result2 = await promise || "";
this.map.delete(id);
if (result2.startsWith("<pre"))
return result2;
else
return `<pre><code class="language-${lang}">${result2}</code></pre>`;
});
}
}
function createMarkdownItAsync(...args) {
return new MarkdownItAsync(...args);
}
function replaceAsync(string, searchValue, replacer) {
try {
if (typeof replacer === "function") {
const values = [];
String.prototype.replace.call(string, searchValue, (...args) => {
values.push(replacer(...args));
return "";
});
return Promise.all(values).then((resolvedValues) => {
return String.prototype.replace.call(string, searchValue, () => {
return resolvedValues.shift() || "";
});
});
} else {
return Promise.resolve(
String.prototype.replace.call(string, searchValue, replacer)
);
}
} catch (error) {
return Promise.reject(error);
}
}
function wrapHightlight(highlight, map) {
if (!highlight)
return void 0;
return (str, lang, attrs) => {
const promise = highlight(str, lang, attrs);
if (typeof promise === "string")
return promise;
const id = randStr();
map.set(id, [promise, str, lang, attrs]);
return placeholder(id);
};
}
export { MarkdownItAsync, createMarkdownItAsync, createMarkdownItAsync as default, replaceAsync };
+74
View File
@@ -0,0 +1,74 @@
{
"name": "markdown-it-async",
"type": "module",
"version": "0.1.3",
"description": "_description_",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/antfu/markdown-it-async#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/antfu/markdown-it-async.git"
},
"bugs": "https://github.com/antfu/markdown-it-async/issues",
"keywords": [],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./dist/index.d.ts"
]
}
},
"files": [
"dist"
],
"dependencies": {
"@types/markdown-it": "^14.1.2",
"markdown-it": "^14.1.0"
},
"devDependencies": {
"@antfu/eslint-config": "^3.11.2",
"@antfu/ni": "^0.23.1",
"@antfu/utils": "^0.7.10",
"@types/node": "^22.10.1",
"bumpp": "^9.8.1",
"eslint": "^9.15.0",
"esno": "^4.8.0",
"lint-staged": "^15.2.10",
"pnpm": "^9.14.3",
"shiki": "^1.24.0",
"simple-git-hooks": "^2.11.1",
"typescript": "^5.7.2",
"unbuild": "^2.0.0",
"vite": "^6.0.1",
"vitest": "^2.1.6"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
},
"scripts": {
"build": "unbuild",
"dev": "unbuild --stub",
"lint": "eslint .",
"release": "bumpp && pnpm publish",
"start": "esno src/index.ts",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}