vuepress-plugin-md-power 1.0.0-rc.152 → 1.0.0-rc.154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/components/CodeEditor.vue +1 -1
- package/lib/client/components/Plot.vue +47 -49
- package/lib/client/composables/audio.d.ts +2 -1
- package/lib/client/composables/codeRepl.d.ts +1 -2
- package/lib/client/composables/codeRepl.js +26 -3
- package/lib/client/composables/demo.d.ts +0 -1
- package/lib/client/composables/rustRepl.d.ts +0 -1
- package/lib/client/composables/size.d.ts +0 -1
- package/lib/client/options.d.ts +2 -1
- package/lib/client/utils/http.d.ts +0 -1
- package/lib/client/utils/link.d.ts +0 -1
- package/lib/client/utils/sleep.d.ts +0 -1
- package/lib/node/index.d.ts +15 -39
- package/lib/node/index.js +249 -55
- package/lib/shared/index.d.ts +15 -36
- package/package.json +36 -20
package/lib/node/index.js
CHANGED
|
@@ -6,10 +6,10 @@ import http from "node:https";
|
|
|
6
6
|
import { URL, URLSearchParams } from "node:url";
|
|
7
7
|
import { camelCase, isBoolean, isEmptyObject, isNull, isNumber, isPlainObject as isPlainObject$1, isString, isUndefined, kebabCase, notNullish, omit, toArray, uniqueBy, withTimeout } from "@pengzhanbo/utils";
|
|
8
8
|
import imageSize from "image-size";
|
|
9
|
-
import { colors, fs, getDirname, logger, path } from "vuepress/utils";
|
|
9
|
+
import { colors, fs, getDirname, logger, ora, path } from "vuepress/utils";
|
|
10
10
|
import path$1 from "node:path";
|
|
11
11
|
import { globSync } from "tinyglobby";
|
|
12
|
-
import { isLinkHttp as isLinkHttp$1, removeEndingSlash, removeLeadingSlash } from "vuepress/shared";
|
|
12
|
+
import { isLinkHttp as isLinkHttp$1, isLinkWithProtocol, removeEndingSlash, removeLeadingSlash } from "vuepress/shared";
|
|
13
13
|
import fs$1, { promises } from "node:fs";
|
|
14
14
|
import process from "node:process";
|
|
15
15
|
import container from "markdown-it-container";
|
|
@@ -908,7 +908,7 @@ const codeTabs = (md, options = {}) => {
|
|
|
908
908
|
const getIcon = createCodeTabIconGetter(options);
|
|
909
909
|
tab(md, {
|
|
910
910
|
name: "code-tabs",
|
|
911
|
-
|
|
911
|
+
openRender: ({ active, data }, tokens, index, _, env) => {
|
|
912
912
|
const { meta } = tokens[index];
|
|
913
913
|
const titles = data.map(({ title }) => md.renderInline(title, cleanMarkdownEnv(env)));
|
|
914
914
|
const tabsData = data.map((item, dataIndex) => {
|
|
@@ -921,8 +921,8 @@ const codeTabs = (md, options = {}) => {
|
|
|
921
921
|
}).join("");
|
|
922
922
|
return `<CodeTabs id="${index}" :data='${stringifyProp(tabsData)}'${active === -1 ? "" : ` :active="${active}"`}${meta.id ? ` tab-id="${meta.id}"` : ""}>${titlesContent}`;
|
|
923
923
|
},
|
|
924
|
-
|
|
925
|
-
|
|
924
|
+
closeRender: () => `</CodeTabs>`,
|
|
925
|
+
tabOpenRender: ({ index }, tokens, tokenIndex) => {
|
|
926
926
|
let foundFence = false;
|
|
927
927
|
for (let i = tokenIndex; i < tokens.length; i++) {
|
|
928
928
|
const { type } = tokens[i];
|
|
@@ -936,7 +936,7 @@ const codeTabs = (md, options = {}) => {
|
|
|
936
936
|
}
|
|
937
937
|
return `<template #tab${index}="{ value, isActive }">`;
|
|
938
938
|
},
|
|
939
|
-
|
|
939
|
+
tabCloseRender: () => `</template>`
|
|
940
940
|
});
|
|
941
941
|
};
|
|
942
942
|
|
|
@@ -985,7 +985,7 @@ const BADGE_LIST = [
|
|
|
985
985
|
"https://forthebadge.com",
|
|
986
986
|
"https://vercel.com/button"
|
|
987
987
|
];
|
|
988
|
-
const cache$1 = new Map();
|
|
988
|
+
const cache$1 = /* @__PURE__ */ new Map();
|
|
989
989
|
async function imageSizePlugin(app, md, type = false) {
|
|
990
990
|
if (!app.env.isBuild || !type) return;
|
|
991
991
|
if (type === "all") {
|
|
@@ -1113,18 +1113,25 @@ function fetchImageSize(src) {
|
|
|
1113
1113
|
for await (const chunk of stream) {
|
|
1114
1114
|
chunks.push(chunk);
|
|
1115
1115
|
try {
|
|
1116
|
-
const { width
|
|
1117
|
-
if (width
|
|
1118
|
-
width
|
|
1119
|
-
height
|
|
1116
|
+
const { width, height } = imageSize(Buffer.concat(chunks));
|
|
1117
|
+
if (width && height) return resolve({
|
|
1118
|
+
width,
|
|
1119
|
+
height
|
|
1120
1120
|
});
|
|
1121
1121
|
} catch {}
|
|
1122
1122
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1123
|
+
try {
|
|
1124
|
+
const { width, height } = imageSize(Buffer.concat(chunks));
|
|
1125
|
+
resolve({
|
|
1126
|
+
width,
|
|
1127
|
+
height
|
|
1128
|
+
});
|
|
1129
|
+
} catch {
|
|
1130
|
+
resolve({
|
|
1131
|
+
width: 0,
|
|
1132
|
+
height: 0
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1128
1135
|
}).on("error", () => resolve({
|
|
1129
1136
|
width: 0,
|
|
1130
1137
|
height: 0
|
|
@@ -1249,6 +1256,15 @@ function stringifyAttrs(attrs$1, withUndefined = false) {
|
|
|
1249
1256
|
|
|
1250
1257
|
//#endregion
|
|
1251
1258
|
//#region src/node/container/createContainer.ts
|
|
1259
|
+
/**
|
|
1260
|
+
* 创建 markdown-it 的自定义容器插件。
|
|
1261
|
+
*
|
|
1262
|
+
* @param md markdown-it 实例
|
|
1263
|
+
* @param type 容器类型(如 'tip', 'warning' 等)
|
|
1264
|
+
* @param options 可选的 before/after 渲染钩子
|
|
1265
|
+
* @param options.before 渲染容器起始标签时的回调函数
|
|
1266
|
+
* @param options.after 渲染容器结束标签时的回调函数
|
|
1267
|
+
*/
|
|
1252
1268
|
function createContainerPlugin(md, type, { before, after } = {}) {
|
|
1253
1269
|
const render = (tokens, index, options, env) => {
|
|
1254
1270
|
const token = tokens[index];
|
|
@@ -1278,6 +1294,14 @@ function createContainerPlugin(md, type, { before, after } = {}) {
|
|
|
1278
1294
|
function createContainerSyntaxPlugin(md, type, render) {
|
|
1279
1295
|
const maker = ":";
|
|
1280
1296
|
const markerMinLen = 3;
|
|
1297
|
+
/**
|
|
1298
|
+
* 自定义容器的 block 规则定义。
|
|
1299
|
+
* @param state 当前 block 状态
|
|
1300
|
+
* @param startLine 起始行
|
|
1301
|
+
* @param endLine 结束行
|
|
1302
|
+
* @param silent 是否为静默模式
|
|
1303
|
+
* @returns 是否匹配到自定义容器
|
|
1304
|
+
*/
|
|
1281
1305
|
function defineContainer(state, startLine, endLine, silent) {
|
|
1282
1306
|
const start = state.bMarks[startLine] + state.tShift[startLine];
|
|
1283
1307
|
const max = state.eMarks[startLine];
|
|
@@ -1340,6 +1364,11 @@ const UNSUPPORTED_FILE_TYPES = [
|
|
|
1340
1364
|
"xls",
|
|
1341
1365
|
"xlsx"
|
|
1342
1366
|
];
|
|
1367
|
+
/**
|
|
1368
|
+
* 将文件路径数组解析为文件树节点结构
|
|
1369
|
+
* @param files 文件路径数组
|
|
1370
|
+
* @returns 文件树节点数组
|
|
1371
|
+
*/
|
|
1343
1372
|
function parseFileNodes(files) {
|
|
1344
1373
|
const nodes = [];
|
|
1345
1374
|
for (const file of files) {
|
|
@@ -1363,12 +1392,24 @@ function parseFileNodes(files) {
|
|
|
1363
1392
|
}
|
|
1364
1393
|
return nodes;
|
|
1365
1394
|
}
|
|
1395
|
+
/**
|
|
1396
|
+
* 注册 code-tree 容器和嵌入语法的 markdown 插件
|
|
1397
|
+
* @param md markdown-it 实例
|
|
1398
|
+
* @param app vuepress app 实例
|
|
1399
|
+
* @param options code-tree 配置项
|
|
1400
|
+
*/
|
|
1366
1401
|
function codeTreePlugin(md, app, options = {}) {
|
|
1402
|
+
/**
|
|
1403
|
+
* 获取文件或文件夹的图标
|
|
1404
|
+
*/
|
|
1367
1405
|
const getIcon = (filename, type, mode) => {
|
|
1368
1406
|
mode ||= options.icon || "colored";
|
|
1369
1407
|
if (mode === "simple") return type === "folder" ? defaultFolder : defaultFile;
|
|
1370
1408
|
return getFileIcon(filename, type);
|
|
1371
1409
|
};
|
|
1410
|
+
/**
|
|
1411
|
+
* 渲染文件树节点为组件字符串
|
|
1412
|
+
*/
|
|
1372
1413
|
function renderFileTree(nodes, mode) {
|
|
1373
1414
|
return nodes.map((node) => {
|
|
1374
1415
|
const props = {
|
|
@@ -1457,6 +1498,10 @@ function codeTreePlugin(md, app, options = {}) {
|
|
|
1457
1498
|
}
|
|
1458
1499
|
});
|
|
1459
1500
|
}
|
|
1501
|
+
/**
|
|
1502
|
+
* 扩展页面依赖,将 codeTreeFiles 添加到页面依赖中
|
|
1503
|
+
* @param page vuepress 页面对象
|
|
1504
|
+
*/
|
|
1460
1505
|
function extendsPageWithCodeTree(page) {
|
|
1461
1506
|
const markdownEnv = page.markdownEnv;
|
|
1462
1507
|
const codeTreeFiles = markdownEnv.codeTreeFiles ?? [];
|
|
@@ -1465,13 +1510,13 @@ function extendsPageWithCodeTree(page) {
|
|
|
1465
1510
|
|
|
1466
1511
|
//#endregion
|
|
1467
1512
|
//#region src/node/container/align.ts
|
|
1468
|
-
const alignList = [
|
|
1469
|
-
"left",
|
|
1470
|
-
"center",
|
|
1471
|
-
"right",
|
|
1472
|
-
"justify"
|
|
1473
|
-
];
|
|
1474
1513
|
function alignPlugin(md) {
|
|
1514
|
+
const alignList = [
|
|
1515
|
+
"left",
|
|
1516
|
+
"center",
|
|
1517
|
+
"right",
|
|
1518
|
+
"justify"
|
|
1519
|
+
];
|
|
1475
1520
|
for (const name of alignList) createContainerPlugin(md, name, { before: () => `<div style="text-align:${name}">` });
|
|
1476
1521
|
createContainerPlugin(md, "flex", { before: (info) => {
|
|
1477
1522
|
const { attrs: attrs$1 } = resolveAttrs(info);
|
|
@@ -1723,6 +1768,11 @@ function fieldPlugin(md) {
|
|
|
1723
1768
|
|
|
1724
1769
|
//#endregion
|
|
1725
1770
|
//#region src/node/container/fileTree.ts
|
|
1771
|
+
/**
|
|
1772
|
+
* 解析原始文件树内容为节点树结构
|
|
1773
|
+
* @param content 文件树的原始文本内容
|
|
1774
|
+
* @returns 文件树节点数组
|
|
1775
|
+
*/
|
|
1726
1776
|
function parseFileTreeRawContent(content) {
|
|
1727
1777
|
const root = {
|
|
1728
1778
|
info: "",
|
|
@@ -1749,6 +1799,11 @@ function parseFileTreeRawContent(content) {
|
|
|
1749
1799
|
return root.children;
|
|
1750
1800
|
}
|
|
1751
1801
|
const RE_FOCUS = /^\*\*(.*)\*\*(?:$|\s+)/;
|
|
1802
|
+
/**
|
|
1803
|
+
* 解析单个节点的 info 字符串,提取文件名、注释、类型等属性
|
|
1804
|
+
* @param info 节点描述字符串
|
|
1805
|
+
* @returns 文件树节点属性
|
|
1806
|
+
*/
|
|
1752
1807
|
function parseFileTreeNodeInfo(info) {
|
|
1753
1808
|
let filename = "";
|
|
1754
1809
|
let comment = "";
|
|
@@ -1788,12 +1843,23 @@ function parseFileTreeNodeInfo(info) {
|
|
|
1788
1843
|
diff
|
|
1789
1844
|
};
|
|
1790
1845
|
}
|
|
1846
|
+
/**
|
|
1847
|
+
* 文件树 markdown 插件主函数
|
|
1848
|
+
* @param md markdown 实例
|
|
1849
|
+
* @param options 文件树渲染选项
|
|
1850
|
+
*/
|
|
1791
1851
|
function fileTreePlugin(md, options = {}) {
|
|
1852
|
+
/**
|
|
1853
|
+
* 获取文件或文件夹的图标
|
|
1854
|
+
*/
|
|
1792
1855
|
const getIcon = (filename, type, mode) => {
|
|
1793
1856
|
mode ||= options.icon || "colored";
|
|
1794
1857
|
if (mode === "simple") return type === "folder" ? defaultFolder : defaultFile;
|
|
1795
1858
|
return getFileIcon(filename, type);
|
|
1796
1859
|
};
|
|
1860
|
+
/**
|
|
1861
|
+
* 递归渲染文件树节点
|
|
1862
|
+
*/
|
|
1797
1863
|
const renderFileTree = (nodes, meta) => nodes.map((node) => {
|
|
1798
1864
|
const { info, level, children } = node;
|
|
1799
1865
|
const { filename, comment, focus, expanded, type, diff } = parseFileTreeNodeInfo(info);
|
|
@@ -1828,7 +1894,7 @@ ${renderedIcon}${renderedComment}${children.length > 0 ? renderFileTree(children
|
|
|
1828
1894
|
|
|
1829
1895
|
//#endregion
|
|
1830
1896
|
//#region src/node/container/langRepl.ts
|
|
1831
|
-
async function langReplPlugin(app, md, { theme, go = false, kotlin = false, rust = false }) {
|
|
1897
|
+
async function langReplPlugin(app, md, { theme, go = false, kotlin = false, rust = false, python = false }) {
|
|
1832
1898
|
const container$1 = (lang) => createContainerPlugin(md, `${lang}-repl`, {
|
|
1833
1899
|
before(info) {
|
|
1834
1900
|
const { attrs: attrs$1 } = resolveAttrs(info);
|
|
@@ -1843,6 +1909,7 @@ async function langReplPlugin(app, md, { theme, go = false, kotlin = false, rust
|
|
|
1843
1909
|
if (kotlin) container$1("kotlin");
|
|
1844
1910
|
if (go) container$1("go");
|
|
1845
1911
|
if (rust) container$1("rust");
|
|
1912
|
+
if (python) container$1("python");
|
|
1846
1913
|
theme ??= {
|
|
1847
1914
|
light: "github-light",
|
|
1848
1915
|
dark: "github-dark"
|
|
@@ -1861,6 +1928,7 @@ async function langReplPlugin(app, md, { theme, go = false, kotlin = false, rust
|
|
|
1861
1928
|
if (kotlin) data.grammars.kotlin = await readGrammar("kotlin");
|
|
1862
1929
|
if (go) data.grammars.go = await readGrammar("go");
|
|
1863
1930
|
if (rust) data.grammars.rust = await readGrammar("rust");
|
|
1931
|
+
if (python) data.grammars.python = await readGrammar("python");
|
|
1864
1932
|
} catch {
|
|
1865
1933
|
/* istanbul ignore next -- @preserve */
|
|
1866
1934
|
logger.error("[vuepress-plugin-md-power]", `Failed to load packages: ${colors.green("tm-themes")}, ${colors.green("tm-grammars")}, Please install them manually.`);
|
|
@@ -1875,6 +1943,59 @@ async function read(file) {
|
|
|
1875
1943
|
return void 0;
|
|
1876
1944
|
}
|
|
1877
1945
|
|
|
1946
|
+
//#endregion
|
|
1947
|
+
//#region src/node/utils/logger.ts
|
|
1948
|
+
/**
|
|
1949
|
+
* Logger utils
|
|
1950
|
+
*/
|
|
1951
|
+
var Logger = class {
|
|
1952
|
+
constructor(name = "") {
|
|
1953
|
+
this.name = name;
|
|
1954
|
+
}
|
|
1955
|
+
init(subname, text) {
|
|
1956
|
+
return ora({
|
|
1957
|
+
prefixText: colors.blue(`${this.name}${subname ? `:${subname}` : ""}: `),
|
|
1958
|
+
text
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Create a loading spinner with text
|
|
1963
|
+
*/
|
|
1964
|
+
load(subname, msg) {
|
|
1965
|
+
const instance = this.init(subname, msg);
|
|
1966
|
+
return {
|
|
1967
|
+
succeed: (text) => instance.succeed(text),
|
|
1968
|
+
fail: (text) => instance.succeed(text)
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
info(subname, text = "", ...args) {
|
|
1972
|
+
this.init(subname, colors.blue(text)).info();
|
|
1973
|
+
if (args.length) console.info(...args);
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Log success msg
|
|
1977
|
+
*/
|
|
1978
|
+
succeed(subname, text = "", ...args) {
|
|
1979
|
+
this.init(subname, colors.green(text)).succeed();
|
|
1980
|
+
if (args.length) console.log(...args);
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Log warning msg
|
|
1984
|
+
*/
|
|
1985
|
+
warn(subname, text = "", ...args) {
|
|
1986
|
+
this.init(subname, colors.yellow(text)).warn();
|
|
1987
|
+
if (args.length) console.warn(...args);
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* Log error msg
|
|
1991
|
+
*/
|
|
1992
|
+
error(subname, text = "", ...args) {
|
|
1993
|
+
this.init(subname, colors.red(text)).fail();
|
|
1994
|
+
if (args.length) console.error(...args);
|
|
1995
|
+
}
|
|
1996
|
+
};
|
|
1997
|
+
const logger$1 = new Logger("vuepress-plugin-md-power");
|
|
1998
|
+
|
|
1878
1999
|
//#endregion
|
|
1879
2000
|
//#region src/node/container/npmToPreset.ts
|
|
1880
2001
|
const ALLOW_LIST = [
|
|
@@ -2124,6 +2245,9 @@ const MANAGERS_CONFIG = {
|
|
|
2124
2245
|
|
|
2125
2246
|
//#endregion
|
|
2126
2247
|
//#region src/node/container/npmTo.ts
|
|
2248
|
+
/**
|
|
2249
|
+
* 注册 npm-to 容器插件,将 npm 代码块自动转换为多包管理器命令分组
|
|
2250
|
+
*/
|
|
2127
2251
|
function npmToPlugins(md, options = {}) {
|
|
2128
2252
|
const opt = isArray(options) ? { tabs: options } : options;
|
|
2129
2253
|
const defaultTabs = opt.tabs?.length ? opt.tabs : DEFAULT_TABS;
|
|
@@ -2140,12 +2264,19 @@ function npmToPlugins(md, options = {}) {
|
|
|
2140
2264
|
const lines = content.split(/(\n|\s*&&\s*)/);
|
|
2141
2265
|
return md.render(resolveNpmTo(lines, token.info.trim(), idx, tabs$1), cleanMarkdownEnv(env));
|
|
2142
2266
|
}
|
|
2143
|
-
|
|
2267
|
+
logger$1.warn("npm-to", `Invalid npm-to container in ${colors.gray(env.filePathRelative || env.filePath)}`);
|
|
2144
2268
|
return "";
|
|
2145
2269
|
},
|
|
2146
2270
|
after: () => ""
|
|
2147
2271
|
});
|
|
2148
2272
|
}
|
|
2273
|
+
/**
|
|
2274
|
+
* 将 npm 命令转换为各包管理器命令分组
|
|
2275
|
+
* @param lines 命令行数组
|
|
2276
|
+
* @param info 代码块类型
|
|
2277
|
+
* @param idx token 索引
|
|
2278
|
+
* @param tabs 需要支持的包管理器
|
|
2279
|
+
*/
|
|
2149
2280
|
function resolveNpmTo(lines, info, idx, tabs$1) {
|
|
2150
2281
|
tabs$1 = validateTabs(tabs$1);
|
|
2151
2282
|
const res = [];
|
|
@@ -2172,16 +2303,25 @@ function resolveNpmTo(lines, info, idx, tabs$1) {
|
|
|
2172
2303
|
}
|
|
2173
2304
|
return `:::code-tabs#npm-to-${tabs$1.join("-")}\n${res.join("\n")}\n:::`;
|
|
2174
2305
|
}
|
|
2306
|
+
/**
|
|
2307
|
+
* 根据命令行内容查找对应的包管理器配置
|
|
2308
|
+
*/
|
|
2175
2309
|
function findConfig(line) {
|
|
2176
2310
|
for (const { pattern,...config } of Object.values(MANAGERS_CONFIG)) if (pattern.test(line)) return config;
|
|
2177
2311
|
return void 0;
|
|
2178
2312
|
}
|
|
2313
|
+
/**
|
|
2314
|
+
* 校验 tabs 合法性,返回允许的包管理器列表
|
|
2315
|
+
*/
|
|
2179
2316
|
function validateTabs(tabs$1) {
|
|
2180
2317
|
tabs$1 = tabs$1.filter((tab$1) => ALLOW_LIST.includes(tab$1));
|
|
2181
2318
|
if (tabs$1.length === 0) return DEFAULT_TABS;
|
|
2182
2319
|
return tabs$1;
|
|
2183
2320
|
}
|
|
2184
2321
|
const LINE_REG = /(.*)(npm|npx)\s+(.*)/;
|
|
2322
|
+
/**
|
|
2323
|
+
* 解析一行 npm/npx 命令,拆分出环境变量、命令、参数等
|
|
2324
|
+
*/
|
|
2185
2325
|
function parseLine(line) {
|
|
2186
2326
|
const match = line.match(LINE_REG);
|
|
2187
2327
|
if (!match) return false;
|
|
@@ -2212,6 +2352,9 @@ function parseLine(line) {
|
|
|
2212
2352
|
...parseArgs(rest.slice(idx + 1))
|
|
2213
2353
|
};
|
|
2214
2354
|
}
|
|
2355
|
+
/**
|
|
2356
|
+
* 解析 npm 命令参数,区分命令、参数、脚本参数
|
|
2357
|
+
*/
|
|
2215
2358
|
function parseArgs(line) {
|
|
2216
2359
|
line = line?.trim();
|
|
2217
2360
|
const [npmArgs, scriptArgs] = line.split(/\s+--\s+/);
|
|
@@ -2289,7 +2432,7 @@ function stepsPlugin(md) {
|
|
|
2289
2432
|
const tabs = (md) => {
|
|
2290
2433
|
tab(md, {
|
|
2291
2434
|
name: "tabs",
|
|
2292
|
-
|
|
2435
|
+
openRender: ({ active, data }, tokens, index, _, env) => {
|
|
2293
2436
|
const { meta } = tokens[index];
|
|
2294
2437
|
const titles = data.map(({ title }) => md.renderInline(title, cleanMarkdownEnv(env)));
|
|
2295
2438
|
const tabsData = data.map((item, dataIndex) => {
|
|
@@ -2299,9 +2442,9 @@ const tabs = (md) => {
|
|
|
2299
2442
|
return `<Tabs id="${index}" :data='${stringifyProp(tabsData)}'${active === -1 ? "" : ` :active="${active}"`}${meta.id ? ` tab-id="${meta.id}"` : ""}>
|
|
2300
2443
|
${titles.map((title, titleIndex) => `<template #title${titleIndex}="{ value, isActive }">${title}</template>`).join("")}`;
|
|
2301
2444
|
},
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2445
|
+
closeRender: () => `</Tabs>`,
|
|
2446
|
+
tabOpenRender: ({ index }) => `<template #tab${index}="{ value, isActive }">`,
|
|
2447
|
+
tabCloseRender: () => `</template>`
|
|
2305
2448
|
});
|
|
2306
2449
|
};
|
|
2307
2450
|
|
|
@@ -2417,7 +2560,7 @@ function markdownEmbed(app, md, env, { url, title, desc, codeSetting = "", expan
|
|
|
2417
2560
|
const filepath$1 = findFile(app, env, url);
|
|
2418
2561
|
const code = readFileSync(filepath$1);
|
|
2419
2562
|
if (code === false) {
|
|
2420
|
-
|
|
2563
|
+
logger$1.warn("demo-markdown", `Cannot read markdown file: ${colors.gray(filepath$1)}\n at: ${colors.gray(env.filePathRelative || "")}`);
|
|
2421
2564
|
return "";
|
|
2422
2565
|
}
|
|
2423
2566
|
const demo = {
|
|
@@ -2692,7 +2835,7 @@ async function compileCode(code, output) {
|
|
|
2692
2835
|
if (code.css) res.css = await compileStyle(code.css.trim(), code.cssType);
|
|
2693
2836
|
if (code.html) res.html = code.html.trim();
|
|
2694
2837
|
} catch (e) {
|
|
2695
|
-
|
|
2838
|
+
logger$1.error("demo-normal", "demo parse error: \n", e);
|
|
2696
2839
|
}
|
|
2697
2840
|
writeFileSync(output, `import { ref } from "vue"\nexport default ref(${JSON.stringify(res, null, 2)})`);
|
|
2698
2841
|
checkDemoRender();
|
|
@@ -2701,7 +2844,7 @@ function normalEmbed(app, md, env, { url, title, desc, codeSetting = "", expande
|
|
|
2701
2844
|
const filepath$1 = findFile(app, env, url);
|
|
2702
2845
|
const code = readFileSync(filepath$1);
|
|
2703
2846
|
if (code === false) {
|
|
2704
|
-
|
|
2847
|
+
logger$1.warn("demo-normal", `Cannot read demo file: ${colors.gray(filepath$1)}\n at: ${colors.gray(env.filePathRelative || "")}`);
|
|
2705
2848
|
return "";
|
|
2706
2849
|
}
|
|
2707
2850
|
const source = parseEmbedCode(code);
|
|
@@ -2830,7 +2973,7 @@ function vueEmbed(app, md, env, { url, title, desc, codeSetting = "", expanded =
|
|
|
2830
2973
|
const filepath$1 = findFile(app, env, url);
|
|
2831
2974
|
const code = readFileSync(filepath$1);
|
|
2832
2975
|
if (code === false) {
|
|
2833
|
-
|
|
2976
|
+
logger$1.warn("demo-vue", `Cannot read vue demo file: ${colors.gray(filepath$1)}\n at: ${colors.gray(env.filePathRelative || "")}`);
|
|
2834
2977
|
return "";
|
|
2835
2978
|
}
|
|
2836
2979
|
const basename = path$1.basename(filepath$1).replace(/-|\./g, "_");
|
|
@@ -2947,6 +3090,15 @@ function transformStyle(code) {
|
|
|
2947
3090
|
|
|
2948
3091
|
//#endregion
|
|
2949
3092
|
//#region src/node/demo/demo.ts
|
|
3093
|
+
const embedMap = {
|
|
3094
|
+
vue: vueEmbed,
|
|
3095
|
+
normal: normalEmbed,
|
|
3096
|
+
markdown: markdownEmbed
|
|
3097
|
+
};
|
|
3098
|
+
/**
|
|
3099
|
+
* 嵌入语法
|
|
3100
|
+
* @[demo type info](url)
|
|
3101
|
+
*/
|
|
2950
3102
|
function demoEmbed(app, md) {
|
|
2951
3103
|
createEmbedRuleBlock(md, {
|
|
2952
3104
|
type: "demo",
|
|
@@ -2959,12 +3111,10 @@ function demoEmbed(app, md) {
|
|
|
2959
3111
|
content: (meta, content, env) => {
|
|
2960
3112
|
const { url, type } = meta;
|
|
2961
3113
|
if (!url) {
|
|
2962
|
-
|
|
3114
|
+
logger$1.warn("demo-vue", `Invalid filepath: ${colors.gray(url)}`);
|
|
2963
3115
|
return content;
|
|
2964
3116
|
}
|
|
2965
|
-
if (type
|
|
2966
|
-
if (type === "normal") return normalEmbed(app, md, env, meta);
|
|
2967
|
-
if (type === "markdown") return markdownEmbed(app, md, env, meta);
|
|
3117
|
+
if (embedMap[type]) return embedMap[type](app, md, env, meta);
|
|
2968
3118
|
return content;
|
|
2969
3119
|
}
|
|
2970
3120
|
});
|
|
@@ -3074,7 +3224,7 @@ const audioReader = (state, silent) => {
|
|
|
3074
3224
|
};
|
|
3075
3225
|
const audioReaderPlugin = (md) => {
|
|
3076
3226
|
md.renderer.rules.audio_reader = (tokens, idx) => {
|
|
3077
|
-
const meta = tokens[idx].meta
|
|
3227
|
+
const meta = tokens[idx].meta;
|
|
3078
3228
|
if (meta.startTime) meta.startTime = Number(meta.startTime);
|
|
3079
3229
|
if (meta.endTime) meta.endTime = Number(meta.endTime);
|
|
3080
3230
|
if (meta.volume) meta.volume = Number(meta.volume);
|
|
@@ -3355,10 +3505,10 @@ function checkSupportType(type) {
|
|
|
3355
3505
|
break;
|
|
3356
3506
|
}
|
|
3357
3507
|
/* istanbul ignore if -- @preserve */
|
|
3358
|
-
if (name)
|
|
3508
|
+
if (name) logger$1.warn("artPlayer", `${colors.cyan(name)} is not installed, please install it via npm or yarn or pnpm`);
|
|
3359
3509
|
} else
|
|
3360
3510
|
/* istanbul ignore next -- @preserve */
|
|
3361
|
-
|
|
3511
|
+
logger$1.warn("artPlayer", `unsupported video type: ${colors.cyan(type)}`);
|
|
3362
3512
|
}
|
|
3363
3513
|
|
|
3364
3514
|
//#endregion
|
|
@@ -3520,6 +3670,46 @@ function parseSource(source) {
|
|
|
3520
3670
|
}
|
|
3521
3671
|
}
|
|
3522
3672
|
|
|
3673
|
+
//#endregion
|
|
3674
|
+
//#region src/node/enhance/links.ts
|
|
3675
|
+
function linksPlugin(md) {
|
|
3676
|
+
const externalAttrs = {
|
|
3677
|
+
target: "_blank",
|
|
3678
|
+
rel: "noopener noreferrer"
|
|
3679
|
+
};
|
|
3680
|
+
let hasOpenInternalLink = false;
|
|
3681
|
+
const internalTag = "VPLink";
|
|
3682
|
+
function handleLinkOpen(tokens, idx) {
|
|
3683
|
+
hasOpenInternalLink = false;
|
|
3684
|
+
const token = tokens[idx];
|
|
3685
|
+
const hrefIndex = token.attrIndex("href");
|
|
3686
|
+
/* istanbul ignore if -- @preserve */
|
|
3687
|
+
if (hrefIndex < 0) return;
|
|
3688
|
+
const hrefAttr = token.attrs[hrefIndex];
|
|
3689
|
+
const hrefLink = hrefAttr[1];
|
|
3690
|
+
if (isLinkWithProtocol(hrefLink)) {
|
|
3691
|
+
Object.entries(externalAttrs).forEach(([key, val]) => {
|
|
3692
|
+
token.attrSet(key, val);
|
|
3693
|
+
});
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
if (hrefLink[0] === "#") return;
|
|
3697
|
+
hasOpenInternalLink = true;
|
|
3698
|
+
token.tag = internalTag;
|
|
3699
|
+
}
|
|
3700
|
+
md.renderer.rules.link_open = (tokens, idx, opts, env, self) => {
|
|
3701
|
+
handleLinkOpen(tokens, idx);
|
|
3702
|
+
return self.renderToken(tokens, idx, opts);
|
|
3703
|
+
};
|
|
3704
|
+
md.renderer.rules.link_close = (tokens, idx, opts, _env, self) => {
|
|
3705
|
+
if (hasOpenInternalLink) {
|
|
3706
|
+
hasOpenInternalLink = false;
|
|
3707
|
+
tokens[idx].tag = internalTag;
|
|
3708
|
+
}
|
|
3709
|
+
return self.renderToken(tokens, idx, opts);
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
|
|
3523
3713
|
//#endregion
|
|
3524
3714
|
//#region src/node/icon/createIconRule.ts
|
|
3525
3715
|
function createIconRule([l1, l2, r1, r2], deprecated) {
|
|
@@ -3636,7 +3826,7 @@ const iconPlugin = (md, options = {}) => {
|
|
|
3636
3826
|
const [name, opt = ""] = content.split(" ");
|
|
3637
3827
|
const [size, color] = opt.trim().split("/");
|
|
3638
3828
|
icon = `${name}${size ? ` =${size}` : ""}${color ? ` /${color}` : ""}`;
|
|
3639
|
-
|
|
3829
|
+
logger$1.warn("icon", `The icon syntax of \`${colors.yellow(`:[${content}]:`)}\` is deprecated, please use \`${colors.green(`::${icon}::`)}\` instead. (${colors.gray(env.filePathRelative || env.filePath)})`);
|
|
3640
3830
|
}
|
|
3641
3831
|
return iconRender(icon, options);
|
|
3642
3832
|
};
|
|
@@ -3689,7 +3879,7 @@ function normalizeAsset(asset, provide) {
|
|
|
3689
3879
|
link,
|
|
3690
3880
|
provide
|
|
3691
3881
|
};
|
|
3692
|
-
|
|
3882
|
+
logger$1.error("icon", `Can not recognize icon link: "${asset}"`);
|
|
3693
3883
|
return null;
|
|
3694
3884
|
}
|
|
3695
3885
|
function normalizeLink(link) {
|
|
@@ -3881,18 +4071,18 @@ const plotDef = (state, silent) => {
|
|
|
3881
4071
|
const content = state.src.slice(start + 2, state.pos);
|
|
3882
4072
|
state.posMax = state.pos;
|
|
3883
4073
|
state.pos = start + 2;
|
|
3884
|
-
const
|
|
3885
|
-
|
|
3886
|
-
|
|
4074
|
+
const openToken = state.push("plot_inline_open", "Plot", 1);
|
|
4075
|
+
openToken.markup = "!!";
|
|
4076
|
+
openToken.content = content;
|
|
4077
|
+
const contentToken = state.push("text", "", 0);
|
|
4078
|
+
contentToken.content = content;
|
|
4079
|
+
const closeToken = state.push("plot_inline_close", "Plot", -1);
|
|
4080
|
+
closeToken.markup = "!!";
|
|
3887
4081
|
state.pos = state.posMax + 2;
|
|
3888
4082
|
state.posMax = max;
|
|
3889
4083
|
return true;
|
|
3890
4084
|
};
|
|
3891
4085
|
const plotPlugin = (md) => {
|
|
3892
|
-
md.renderer.rules.plot_inline = (tokens, idx) => {
|
|
3893
|
-
const token = tokens[idx];
|
|
3894
|
-
return `<Plot>${token.content}</Plot>`;
|
|
3895
|
-
};
|
|
3896
4086
|
md.inline.ruler.before("emphasis", "plot", plotDef);
|
|
3897
4087
|
};
|
|
3898
4088
|
|
|
@@ -3928,8 +4118,8 @@ const { url: filepath } = import.meta;
|
|
|
3928
4118
|
const __dirname = getDirname(filepath);
|
|
3929
4119
|
const CLIENT_FOLDER = ensureEndingSlash(path.resolve(__dirname, "../client"));
|
|
3930
4120
|
async function prepareConfigFile(app, options) {
|
|
3931
|
-
const imports = new Set();
|
|
3932
|
-
const enhances = new Set();
|
|
4121
|
+
const imports = /* @__PURE__ */ new Set();
|
|
4122
|
+
const enhances = /* @__PURE__ */ new Set();
|
|
3933
4123
|
imports.add(`import Tabs from '${CLIENT_FOLDER}components/Tabs.vue'`);
|
|
3934
4124
|
enhances.add(`app.component('Tabs', Tabs)`);
|
|
3935
4125
|
imports.add(`import CodeTabs from '${CLIENT_FOLDER}components/CodeTabs.vue'`);
|
|
@@ -4065,11 +4255,14 @@ function markdownPowerPlugin(options = {}) {
|
|
|
4065
4255
|
clientConfigFile: (app) => prepareConfigFile(app, options),
|
|
4066
4256
|
define: provideData(options),
|
|
4067
4257
|
extendsBundlerOptions(bundlerOptions, app) {
|
|
4068
|
-
if (options.repl)
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4258
|
+
if (options.repl) {
|
|
4259
|
+
addViteOptimizeDepsInclude(bundlerOptions, app, [
|
|
4260
|
+
"shiki/core",
|
|
4261
|
+
"shiki/wasm",
|
|
4262
|
+
"shiki/engine/oniguruma"
|
|
4263
|
+
]);
|
|
4264
|
+
if (options.repl.python) addViteOptimizeDepsInclude(bundlerOptions, app, ["pyodide"]);
|
|
4265
|
+
}
|
|
4073
4266
|
if (options.artPlayer) addViteOptimizeDepsInclude(bundlerOptions, app, [
|
|
4074
4267
|
"artplayer",
|
|
4075
4268
|
"dashjs",
|
|
@@ -4078,6 +4271,7 @@ function markdownPowerPlugin(options = {}) {
|
|
|
4078
4271
|
]);
|
|
4079
4272
|
},
|
|
4080
4273
|
extendsMarkdown: async (md, app) => {
|
|
4274
|
+
linksPlugin(md);
|
|
4081
4275
|
docsTitlePlugin(md);
|
|
4082
4276
|
embedSyntaxPlugin(md, options);
|
|
4083
4277
|
inlineSyntaxPlugin(md, options);
|