vuepress-plugin-md-power 1.0.0-rc.134 → 1.0.0-rc.136

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.
@@ -134,7 +134,7 @@ function runCode() {
134
134
  }
135
135
 
136
136
  .code-repl-title h4 {
137
- flex: 1;
137
+ flex: 1 2;
138
138
  padding: 0 12px;
139
139
  margin: 0;
140
140
  font-size: 14px;
@@ -175,7 +175,7 @@ function runCode() {
175
175
  }
176
176
 
177
177
  .output-head .title {
178
- flex: 1;
178
+ flex: 1 2;
179
179
  margin-left: 10px;
180
180
  font-size: 14px;
181
181
  font-weight: 500;
@@ -37,7 +37,7 @@
37
37
 
38
38
  .vp-demo-wrapper .demo-info .title::after {
39
39
  display: inline-block;
40
- flex: 1;
40
+ flex: 1 2;
41
41
  height: 0;
42
42
  margin-left: 8px;
43
43
  content: "";
@@ -65,7 +65,7 @@
65
65
 
66
66
  .vp-demo-wrapper .demo-ctrl .extra {
67
67
  display: flex;
68
- flex: 1;
68
+ flex: 1 2;
69
69
  gap: 16px;
70
70
  align-items: center;
71
71
  justify-content: flex-start;
@@ -181,6 +181,10 @@ interface MarkdownPowerPluginOptions {
181
181
  * @default false
182
182
  */
183
183
  annotation?: boolean;
184
+ /**
185
+ * 是否启用 abbr 语法
186
+ * @default false
187
+ */
184
188
  abbr?: boolean;
185
189
  /**
186
190
  * 配置代码块分组
@@ -352,6 +356,8 @@ interface ArtPlayerTokenMeta extends SizeOptions {
352
356
  type?: string;
353
357
  }
354
358
 
359
+ declare function createCodeTabIconGetter(options?: CodeTabsOptions): (filename: string) => string | void;
360
+
355
361
  interface ImgSize {
356
362
  width: number;
357
363
  height: number;
@@ -360,4 +366,4 @@ declare function resolveImageSize(app: App, url: string, remote?: boolean): Prom
360
366
 
361
367
  declare function markdownPowerPlugin(options?: MarkdownPowerPluginOptions): Plugin;
362
368
 
363
- export { type ArtPlayerTokenMeta, type BilibiliTokenMeta, type CanIUseMode, type CanIUseOptions, type CanIUseTokenMeta, type CodeSandboxTokenMeta, type CodeTabsOptions, type CodepenTokenMeta, type DemoContainerRender, type DemoFile, type DemoMeta, type FileTreeIconMode, type FileTreeOptions, type IconsOptions, type JSFiddleTokenMeta, type MarkdownDemoEnv, type MarkdownPowerPluginOptions, type NpmToOptions, type NpmToPackageManager, type PDFEmbedType, type PDFOptions, type PDFTokenMeta, type PlotOptions, type ReplEditorData, type ReplOptions, type ReplitTokenMeta, type SizeOptions, type ThemeOptions, type VideoOptions, type YoutubeTokenMeta, markdownPowerPlugin, resolveImageSize };
369
+ export { type ArtPlayerTokenMeta, type BilibiliTokenMeta, type CanIUseMode, type CanIUseOptions, type CanIUseTokenMeta, type CodeSandboxTokenMeta, type CodeTabsOptions, type CodepenTokenMeta, type DemoContainerRender, type DemoFile, type DemoMeta, type FileTreeIconMode, type FileTreeOptions, type IconsOptions, type JSFiddleTokenMeta, type MarkdownDemoEnv, type MarkdownPowerPluginOptions, type NpmToOptions, type NpmToPackageManager, type PDFEmbedType, type PDFOptions, type PDFTokenMeta, type PlotOptions, type ReplEditorData, type ReplOptions, type ReplitTokenMeta, type SizeOptions, type ThemeOptions, type VideoOptions, type YoutubeTokenMeta, createCodeTabIconGetter, markdownPowerPlugin, resolveImageSize };
package/lib/node/index.js CHANGED
@@ -1,275 +1,3 @@
1
- // src/node/enhance/imageSize.ts
2
- import { Buffer } from "node:buffer";
3
- import http from "node:https";
4
- import { URL } from "node:url";
5
- import { isLinkExternal, isLinkHttp } from "@vuepress/helper";
6
- import imageSize from "image-size";
7
- import { fs, logger, path } from "vuepress/utils";
8
-
9
- // src/node/utils/resolveAttrs.ts
10
- var RE_ATTR_VALUE = /(?:^|\s+)(?<attr>[\w-]+)(?:=\s*(?<quote>['"])(?<value>.+?)\k<quote>)?(?:\s+|$)/;
11
- function resolveAttrs(info) {
12
- info = info.trim();
13
- if (!info)
14
- return { rawAttrs: "", attrs: {} };
15
- const attrs2 = {};
16
- const rawAttrs = info;
17
- let matched;
18
- while (matched = info.match(RE_ATTR_VALUE)) {
19
- const { attr, value } = matched.groups;
20
- attrs2[attr] = value ?? true;
21
- info = info.slice(matched[0].length);
22
- }
23
- Object.keys(attrs2).forEach((key) => {
24
- let value = attrs2[key];
25
- value = typeof value === "string" ? value.trim() : value;
26
- if (value === "true")
27
- value = true;
28
- else if (value === "false")
29
- value = false;
30
- attrs2[key] = value;
31
- if (key.includes("-")) {
32
- const _key = key.replace(/-(\w)/g, (_, c) => c.toUpperCase());
33
- attrs2[_key] = value;
34
- }
35
- });
36
- return { attrs: attrs2, rawAttrs };
37
- }
38
-
39
- // src/node/enhance/imageSize.ts
40
- var REG_IMG = /!\[.*?\]\(.*?\)/g;
41
- var REG_IMG_TAG = /<img(.*?)>/g;
42
- var REG_IMG_TAG_SRC = /src(?:set)?=(['"])(.+?)\1/g;
43
- var BADGE_LIST = [
44
- "https://img.shields.io",
45
- "https://badge.fury.io",
46
- "https://badgen.net",
47
- "https://forthebadge.com",
48
- "https://vercel.com/button"
49
- ];
50
- var cache = /* @__PURE__ */ new Map();
51
- async function imageSizePlugin(app, md, type2 = false) {
52
- if (!app.env.isBuild || !type2)
53
- return;
54
- if (type2 === "all") {
55
- const start = performance.now();
56
- try {
57
- await scanRemoteImageSize(app);
58
- } catch {
59
- }
60
- if (app.env.isDebug) {
61
- logger.info(`[vuepress-plugin-md-power] imageSizePlugin: scan all images time spent: ${performance.now() - start}ms`);
62
- }
63
- }
64
- const imageRule = md.renderer.rules.image;
65
- md.renderer.rules.image = (tokens, idx, options, env, self) => {
66
- if (!env.filePathRelative || !env.filePath)
67
- return imageRule(tokens, idx, options, env, self);
68
- const token = tokens[idx];
69
- const src = token.attrGet("src");
70
- const width = token.attrGet("width");
71
- const height = token.attrGet("height");
72
- const size = resolveSize(src, width, height, env);
73
- if (size) {
74
- token.attrSet("width", `${size.width}`);
75
- token.attrSet("height", `${size.height}`);
76
- }
77
- return imageRule(tokens, idx, options, env, self);
78
- };
79
- const rawHtmlBlockRule = md.renderer.rules.html_block;
80
- const rawHtmlInlineRule = md.renderer.rules.html_inline;
81
- md.renderer.rules.html_block = createHtmlRule(rawHtmlBlockRule);
82
- md.renderer.rules.html_inline = createHtmlRule(rawHtmlInlineRule);
83
- function createHtmlRule(rawHtmlRule) {
84
- return (tokens, idx, options, env, self) => {
85
- const token = tokens[idx];
86
- token.content = token.content.replace(REG_IMG_TAG, (raw, info) => {
87
- const { attrs: attrs2 } = resolveAttrs(info);
88
- const src = attrs2.src || attrs2.srcset;
89
- const size = resolveSize(src, attrs2.width, attrs2.height, env);
90
- if (!size)
91
- return raw;
92
- attrs2.width = size.width;
93
- attrs2.height = size.height;
94
- const imgAttrs = Object.entries(attrs2).map(([key, value]) => typeof value === "boolean" ? key : `${key}="${value}"`).join(" ");
95
- return `<img ${imgAttrs}>`;
96
- });
97
- return rawHtmlRule(tokens, idx, options, env, self);
98
- };
99
- }
100
- function resolveSize(src, width, height, env) {
101
- if (!src || src.startsWith("data:"))
102
- return false;
103
- if (width && height)
104
- return false;
105
- const isExternal = isLinkExternal(src, env.base);
106
- const filepath2 = isExternal ? src : resolveImageUrl(src, env, app);
107
- if (isExternal) {
108
- if (!cache.has(filepath2))
109
- return false;
110
- } else {
111
- if (!cache.has(filepath2)) {
112
- if (!fs.existsSync(filepath2))
113
- return false;
114
- const { width: w, height: h } = imageSize(fs.readFileSync(filepath2));
115
- if (!w || !h)
116
- return false;
117
- cache.set(filepath2, { width: w, height: h });
118
- }
119
- }
120
- const { width: originalWidth, height: originalHeight } = cache.get(filepath2);
121
- const ratio = originalWidth / originalHeight;
122
- if (width && !height) {
123
- const w = Number.parseInt(width, 10);
124
- return { width: w, height: Math.round(w / ratio) };
125
- } else if (height && !width) {
126
- const h = Number.parseInt(height, 10);
127
- return { width: Math.round(h * ratio), height: h };
128
- } else {
129
- return { width: originalWidth, height: originalHeight };
130
- }
131
- }
132
- }
133
- function resolveImageUrl(src, env, app) {
134
- if (src[0] === "/")
135
- return app.dir.public(src.slice(1));
136
- if (env.filePathRelative && src[0] === ".")
137
- return app.dir.source(path.join(path.dirname(env.filePathRelative), src));
138
- if (env.filePath && (src[0] === "." || src[0] === "/"))
139
- return path.resolve(env.filePath, src);
140
- return path.resolve(src);
141
- }
142
- async function scanRemoteImageSize(app) {
143
- if (!app.env.isBuild)
144
- return;
145
- const cwd = app.dir.source();
146
- const files = await fs.readdir(cwd, { recursive: true });
147
- const imgList = [];
148
- for (const file of files) {
149
- const filepath2 = path.join(cwd, file);
150
- if ((await fs.stat(filepath2)).isFile() && !filepath2.includes(".vuepress") && !filepath2.includes("node_modules") && filepath2.endsWith(".md")) {
151
- const content = await fs.readFile(filepath2, "utf-8");
152
- const syntaxMatched = content.match(REG_IMG) ?? [];
153
- for (const img of syntaxMatched) {
154
- const src = img.slice(img.indexOf("](") + 2, -1);
155
- addList(src.split(/\s+/)[0]);
156
- }
157
- const tagMatched = content.match(REG_IMG_TAG) ?? [];
158
- for (const img of tagMatched) {
159
- const src = img.match(REG_IMG_TAG_SRC)?.[2] ?? "";
160
- addList(src);
161
- }
162
- }
163
- }
164
- function addList(src) {
165
- if (src && isLinkHttp(src) && !imgList.includes(src) && !BADGE_LIST.some((badge) => src.startsWith(badge))) {
166
- imgList.push(src);
167
- }
168
- }
169
- await Promise.all(imgList.map(async (src) => {
170
- if (!cache.has(src)) {
171
- const { width, height } = await fetchImageSize(src);
172
- if (width && height)
173
- cache.set(src, { width, height });
174
- }
175
- }));
176
- }
177
- function fetchImageSize(src) {
178
- const link = new URL(src);
179
- return new Promise((resolve) => {
180
- http.get(link, async (stream) => {
181
- const chunks = [];
182
- for await (const chunk of stream) {
183
- chunks.push(chunk);
184
- try {
185
- const { width: width2, height: height2 } = imageSize(Buffer.concat(chunks));
186
- if (width2 && height2) {
187
- return resolve({ width: width2, height: height2 });
188
- }
189
- } catch {
190
- }
191
- }
192
- const { width, height } = imageSize(Buffer.concat(chunks));
193
- resolve({ width, height });
194
- }).on("error", () => resolve({ width: 0, height: 0 }));
195
- });
196
- }
197
- async function resolveImageSize(app, url, remote = false) {
198
- if (cache.has(url))
199
- return cache.get(url);
200
- if (isLinkHttp(url) && remote) {
201
- return await fetchImageSize(url);
202
- }
203
- if (url[0] === "/") {
204
- const filepath2 = app.dir.public(url.slice(1));
205
- if (fs.existsSync(filepath2)) {
206
- const { width, height } = imageSize(fs.readFileSync(filepath2));
207
- return { width, height };
208
- }
209
- }
210
- return { width: 0, height: 0 };
211
- }
212
-
213
- // src/node/plugin.ts
214
- import { addViteOptimizeDepsInclude } from "@vuepress/helper";
215
- import { isPackageExists as isPackageExists3 } from "local-pkg";
216
-
217
- // src/node/container/index.ts
218
- import { isPlainObject as isPlainObject2 } from "@vuepress/helper";
219
-
220
- // src/node/container/createContainer.ts
221
- import container from "markdown-it-container";
222
- function createContainerPlugin(md, type2, options = {}) {
223
- const render = (tokens, index) => {
224
- const token = tokens[index];
225
- const info = token.info.trim().slice(type2.length).trim() || "";
226
- if (token.nesting === 1) {
227
- return options.before?.(info, tokens, index) || `<div class="custom-container ${type2}">`;
228
- } else {
229
- return options.after?.(info, tokens, index) || "</div>";
230
- }
231
- };
232
- md.use(container, type2, { render });
233
- }
234
-
235
- // src/node/container/align.ts
236
- var alignList = ["left", "center", "right", "justify"];
237
- function alignPlugin(md) {
238
- for (const name of alignList) {
239
- createContainerPlugin(md, name, {
240
- before: () => `<div style="text-align:${name}">`
241
- });
242
- }
243
- }
244
-
245
- // src/node/container/card.ts
246
- function cardPlugin(md) {
247
- createContainerPlugin(md, "card", {
248
- before(info) {
249
- const { attrs: attrs2 } = resolveAttrs(info);
250
- const { title, icon } = attrs2;
251
- return `<VPCard${title ? ` title="${title}"` : ""}${icon ? ` icon="${icon}"` : ""}>`;
252
- },
253
- after: () => "</VPCard>"
254
- });
255
- createContainerPlugin(md, "card-grid", {
256
- before: () => "<VPCardGrid>",
257
- after: () => "</VPCardGrid>"
258
- });
259
- createContainerPlugin(md, "card-masonry", {
260
- before: (info) => {
261
- const { attrs: attrs2 } = resolveAttrs(info);
262
- let cols;
263
- if (attrs2.cols) {
264
- cols = attrs2.cols[0] === "{" ? attrs2.cols : Number.parseInt(`${attrs2.cols}`);
265
- }
266
- const gap = Number.parseInt(`${attrs2.gap}`);
267
- return `<VPCardMasonry${cols ? ` :cols="${cols}"` : ""}${gap >= 0 ? ` :gap="${gap}"` : ""}>`;
268
- },
269
- after: () => "</VPCardMasonry>"
270
- });
271
- }
272
-
273
1
  // src/node/container/codeTabs.ts
274
2
  import { tab } from "@mdit/plugin-tab";
275
3
  import { isPlainObject } from "@vuepress/helper";
@@ -1085,113 +813,401 @@ var definitions = {
1085
813
  "TODO.txt": "vscode-icons:file-type-light-todo",
1086
814
  "TODO.md": "vscode-icons:file-type-light-todo"
1087
815
  }
1088
- };
1089
-
1090
- // src/node/fileIcons/findIcon.ts
1091
- function getFileIcon(fileName, type2) {
1092
- const name = getFileIconName(fileName, type2);
1093
- if (!name)
1094
- return type2 !== "folder" ? defaultFile : defaultFolder;
1095
- return name;
816
+ };
817
+
818
+ // src/node/fileIcons/findIcon.ts
819
+ function getFileIcon(fileName, type2) {
820
+ const name = getFileIconName(fileName, type2);
821
+ if (!name)
822
+ return type2 !== "folder" ? defaultFile : defaultFolder;
823
+ return name;
824
+ }
825
+ function getFileIconName(fileName, type2 = "file") {
826
+ if (type2 === "folder") {
827
+ const icon2 = definitions.folders[fileName];
828
+ if (icon2)
829
+ return icon2;
830
+ if (fileName.includes("/"))
831
+ return definitions.folders[fileName.slice(fileName.lastIndexOf("/") + 1)];
832
+ return;
833
+ }
834
+ let icon = definitions.named[fileName] || definitions.files[fileName];
835
+ if (icon)
836
+ return icon;
837
+ icon = getFileIconTypeFromExtension(fileName) || void 0;
838
+ if (icon)
839
+ return icon;
840
+ for (const [partial, partialIcon] of Object.entries(definitions.partials)) {
841
+ if (fileName.includes(partial))
842
+ return partialIcon;
843
+ }
844
+ return icon;
845
+ }
846
+ function getFileIconTypeFromExtension(fileName) {
847
+ const firstDotIndex = fileName.indexOf(".");
848
+ if (firstDotIndex === -1)
849
+ return;
850
+ let extension = fileName.slice(firstDotIndex);
851
+ while (extension !== "") {
852
+ const icon = definitions.extensions[extension];
853
+ if (icon)
854
+ return icon;
855
+ const nextDotIndex = extension.indexOf(".", 1);
856
+ if (nextDotIndex === -1)
857
+ return;
858
+ extension = extension.slice(nextDotIndex);
859
+ }
860
+ }
861
+
862
+ // src/node/utils/cleanMarkdownEnv.ts
863
+ function cleanMarkdownEnv(env) {
864
+ return {
865
+ base: env.base,
866
+ filePath: env.filePath,
867
+ filePathRelative: env.filePathRelative,
868
+ references: env.references,
869
+ abbreviations: env.abbreviations,
870
+ annotations: env.annotations
871
+ };
872
+ }
873
+
874
+ // src/node/utils/stringifyProp.ts
875
+ function stringifyProp(data) {
876
+ return JSON.stringify(data).replace(/'/g, "&#39");
877
+ }
878
+
879
+ // src/node/container/codeTabs.ts
880
+ function createCodeTabIconGetter(options = {}) {
881
+ const noop = () => void 0;
882
+ if (options.icon === false)
883
+ return noop;
884
+ const { named, extensions } = isPlainObject(options.icon) ? options.icon : {};
885
+ return function getIcon(filename) {
886
+ if (named === false && definitions.named[filename])
887
+ return void 0;
888
+ if (extensions === false && getFileIconTypeFromExtension(filename)) {
889
+ return void 0;
890
+ }
891
+ const hasNamed = named && named.length;
892
+ const hasExt = extensions && extensions.length;
893
+ if (hasNamed || hasExt) {
894
+ if (hasNamed && named.includes(filename))
895
+ return definitions.named[filename];
896
+ if (hasExt && extensions.some((ext) => filename.endsWith(ext)))
897
+ return getFileIconTypeFromExtension(filename);
898
+ return void 0;
899
+ }
900
+ return getFileIconName(filename);
901
+ };
902
+ }
903
+ var codeTabs = (md, options = {}) => {
904
+ const getIcon = createCodeTabIconGetter(options);
905
+ tab(md, {
906
+ name: "code-tabs",
907
+ tabsOpenRenderer: ({ active, data }, tokens, index, _, env) => {
908
+ const { meta } = tokens[index];
909
+ const titles = data.map(({ title }) => md.renderInline(title, cleanMarkdownEnv(env)));
910
+ const tabsData = data.map((item, dataIndex) => {
911
+ const { id = titles[dataIndex] } = item;
912
+ return { id };
913
+ });
914
+ const titlesContent = titles.map((title, index2) => {
915
+ const icon = getIcon(title);
916
+ return `<template #title${index2}="{ value, isActive }">${icon ? `<VPIcon name="${icon}"/>` : ""}<span>${title}</span></template>`;
917
+ }).join("");
918
+ return `<CodeTabs id="${index}" :data='${stringifyProp(tabsData)}'${active === -1 ? "" : ` :active="${active}"`}${meta.id ? ` tab-id="${meta.id}"` : ""}>${titlesContent}`;
919
+ },
920
+ tabsCloseRenderer: () => `</CodeTabs>`,
921
+ tabOpenRenderer: ({ index }, tokens, tokenIndex) => {
922
+ let foundFence = false;
923
+ for (let i = tokenIndex; i < tokens.length; i++) {
924
+ const { type: type2 } = tokens[i];
925
+ if (type2 === "code-tabs_tab_close")
926
+ break;
927
+ if ((type2 === "fence" || type2 === "import_code") && !foundFence) {
928
+ foundFence = true;
929
+ continue;
930
+ }
931
+ tokens[i].type = "code_tab_empty";
932
+ tokens[i].hidden = true;
933
+ }
934
+ return `<template #tab${index}="{ value, isActive }">`;
935
+ },
936
+ tabCloseRenderer: () => `</template>`
937
+ });
938
+ };
939
+
940
+ // src/node/enhance/imageSize.ts
941
+ import { Buffer } from "node:buffer";
942
+ import http from "node:https";
943
+ import { URL } from "node:url";
944
+ import { isLinkExternal, isLinkHttp } from "@vuepress/helper";
945
+ import imageSize from "image-size";
946
+ import { fs, logger, path } from "vuepress/utils";
947
+
948
+ // src/node/utils/resolveAttrs.ts
949
+ var RE_ATTR_VALUE = /(?:^|\s+)(?<attr>[\w-]+)(?:=\s*(?<quote>['"])(?<value>.+?)\k<quote>)?(?:\s+|$)/;
950
+ function resolveAttrs(info) {
951
+ info = info.trim();
952
+ if (!info)
953
+ return { rawAttrs: "", attrs: {} };
954
+ const attrs2 = {};
955
+ const rawAttrs = info;
956
+ let matched;
957
+ while (matched = info.match(RE_ATTR_VALUE)) {
958
+ const { attr, value } = matched.groups;
959
+ attrs2[attr] = value ?? true;
960
+ info = info.slice(matched[0].length);
961
+ }
962
+ Object.keys(attrs2).forEach((key) => {
963
+ let value = attrs2[key];
964
+ value = typeof value === "string" ? value.trim() : value;
965
+ if (value === "true")
966
+ value = true;
967
+ else if (value === "false")
968
+ value = false;
969
+ attrs2[key] = value;
970
+ if (key.includes("-")) {
971
+ const _key = key.replace(/-(\w)/g, (_, c) => c.toUpperCase());
972
+ attrs2[_key] = value;
973
+ }
974
+ });
975
+ return { attrs: attrs2, rawAttrs };
976
+ }
977
+
978
+ // src/node/enhance/imageSize.ts
979
+ var REG_IMG = /!\[.*?\]\(.*?\)/g;
980
+ var REG_IMG_TAG = /<img(.*?)>/g;
981
+ var REG_IMG_TAG_SRC = /src(?:set)?=(['"])(.+?)\1/g;
982
+ var BADGE_LIST = [
983
+ "https://img.shields.io",
984
+ "https://badge.fury.io",
985
+ "https://badgen.net",
986
+ "https://forthebadge.com",
987
+ "https://vercel.com/button"
988
+ ];
989
+ var cache = /* @__PURE__ */ new Map();
990
+ async function imageSizePlugin(app, md, type2 = false) {
991
+ if (!app.env.isBuild || !type2)
992
+ return;
993
+ if (type2 === "all") {
994
+ const start = performance.now();
995
+ try {
996
+ await scanRemoteImageSize(app);
997
+ } catch {
998
+ }
999
+ if (app.env.isDebug) {
1000
+ logger.info(`[vuepress-plugin-md-power] imageSizePlugin: scan all images time spent: ${performance.now() - start}ms`);
1001
+ }
1002
+ }
1003
+ const imageRule = md.renderer.rules.image;
1004
+ md.renderer.rules.image = (tokens, idx, options, env, self) => {
1005
+ if (!env.filePathRelative || !env.filePath)
1006
+ return imageRule(tokens, idx, options, env, self);
1007
+ const token = tokens[idx];
1008
+ const src = token.attrGet("src");
1009
+ const width = token.attrGet("width");
1010
+ const height = token.attrGet("height");
1011
+ const size = resolveSize(src, width, height, env);
1012
+ if (size) {
1013
+ token.attrSet("width", `${size.width}`);
1014
+ token.attrSet("height", `${size.height}`);
1015
+ }
1016
+ return imageRule(tokens, idx, options, env, self);
1017
+ };
1018
+ const rawHtmlBlockRule = md.renderer.rules.html_block;
1019
+ const rawHtmlInlineRule = md.renderer.rules.html_inline;
1020
+ md.renderer.rules.html_block = createHtmlRule(rawHtmlBlockRule);
1021
+ md.renderer.rules.html_inline = createHtmlRule(rawHtmlInlineRule);
1022
+ function createHtmlRule(rawHtmlRule) {
1023
+ return (tokens, idx, options, env, self) => {
1024
+ const token = tokens[idx];
1025
+ token.content = token.content.replace(REG_IMG_TAG, (raw, info) => {
1026
+ const { attrs: attrs2 } = resolveAttrs(info);
1027
+ const src = attrs2.src || attrs2.srcset;
1028
+ const size = resolveSize(src, attrs2.width, attrs2.height, env);
1029
+ if (!size)
1030
+ return raw;
1031
+ attrs2.width = size.width;
1032
+ attrs2.height = size.height;
1033
+ const imgAttrs = Object.entries(attrs2).map(([key, value]) => typeof value === "boolean" ? key : `${key}="${value}"`).join(" ");
1034
+ return `<img ${imgAttrs}>`;
1035
+ });
1036
+ return rawHtmlRule(tokens, idx, options, env, self);
1037
+ };
1038
+ }
1039
+ function resolveSize(src, width, height, env) {
1040
+ if (!src || src.startsWith("data:"))
1041
+ return false;
1042
+ if (width && height)
1043
+ return false;
1044
+ const isExternal = isLinkExternal(src, env.base);
1045
+ const filepath2 = isExternal ? src : resolveImageUrl(src, env, app);
1046
+ if (isExternal) {
1047
+ if (!cache.has(filepath2))
1048
+ return false;
1049
+ } else {
1050
+ if (!cache.has(filepath2)) {
1051
+ if (!fs.existsSync(filepath2))
1052
+ return false;
1053
+ const { width: w, height: h } = imageSize(fs.readFileSync(filepath2));
1054
+ if (!w || !h)
1055
+ return false;
1056
+ cache.set(filepath2, { width: w, height: h });
1057
+ }
1058
+ }
1059
+ const { width: originalWidth, height: originalHeight } = cache.get(filepath2);
1060
+ const ratio = originalWidth / originalHeight;
1061
+ if (width && !height) {
1062
+ const w = Number.parseInt(width, 10);
1063
+ return { width: w, height: Math.round(w / ratio) };
1064
+ } else if (height && !width) {
1065
+ const h = Number.parseInt(height, 10);
1066
+ return { width: Math.round(h * ratio), height: h };
1067
+ } else {
1068
+ return { width: originalWidth, height: originalHeight };
1069
+ }
1070
+ }
1096
1071
  }
1097
- function getFileIconName(fileName, type2 = "file") {
1098
- if (type2 === "folder") {
1099
- const icon2 = definitions.folders[fileName];
1100
- if (icon2)
1101
- return icon2;
1102
- if (fileName.includes("/"))
1103
- return definitions.folders[fileName.slice(fileName.lastIndexOf("/") + 1)];
1072
+ function resolveImageUrl(src, env, app) {
1073
+ if (src[0] === "/")
1074
+ return app.dir.public(src.slice(1));
1075
+ if (env.filePathRelative && src[0] === ".")
1076
+ return app.dir.source(path.join(path.dirname(env.filePathRelative), src));
1077
+ if (env.filePath && (src[0] === "." || src[0] === "/"))
1078
+ return path.resolve(env.filePath, src);
1079
+ return path.resolve(src);
1080
+ }
1081
+ async function scanRemoteImageSize(app) {
1082
+ if (!app.env.isBuild)
1104
1083
  return;
1084
+ const cwd = app.dir.source();
1085
+ const files = await fs.readdir(cwd, { recursive: true });
1086
+ const imgList = [];
1087
+ for (const file of files) {
1088
+ const filepath2 = path.join(cwd, file);
1089
+ if ((await fs.stat(filepath2)).isFile() && !filepath2.includes(".vuepress") && !filepath2.includes("node_modules") && filepath2.endsWith(".md")) {
1090
+ const content = await fs.readFile(filepath2, "utf-8");
1091
+ const syntaxMatched = content.match(REG_IMG) ?? [];
1092
+ for (const img of syntaxMatched) {
1093
+ const src = img.slice(img.indexOf("](") + 2, -1);
1094
+ addList(src.split(/\s+/)[0]);
1095
+ }
1096
+ const tagMatched = content.match(REG_IMG_TAG) ?? [];
1097
+ for (const img of tagMatched) {
1098
+ const src = img.match(REG_IMG_TAG_SRC)?.[2] ?? "";
1099
+ addList(src);
1100
+ }
1101
+ }
1105
1102
  }
1106
- let icon = definitions.named[fileName] || definitions.files[fileName];
1107
- if (icon)
1108
- return icon;
1109
- icon = getFileIconTypeFromExtension(fileName) || void 0;
1110
- if (icon)
1111
- return icon;
1112
- for (const [partial, partialIcon] of Object.entries(definitions.partials)) {
1113
- if (fileName.includes(partial))
1114
- return partialIcon;
1103
+ function addList(src) {
1104
+ if (src && isLinkHttp(src) && !imgList.includes(src) && !BADGE_LIST.some((badge) => src.startsWith(badge))) {
1105
+ imgList.push(src);
1106
+ }
1115
1107
  }
1116
- return icon;
1108
+ await Promise.all(imgList.map(async (src) => {
1109
+ if (!cache.has(src)) {
1110
+ const { width, height } = await fetchImageSize(src);
1111
+ if (width && height)
1112
+ cache.set(src, { width, height });
1113
+ }
1114
+ }));
1117
1115
  }
1118
- function getFileIconTypeFromExtension(fileName) {
1119
- const firstDotIndex = fileName.indexOf(".");
1120
- if (firstDotIndex === -1)
1121
- return;
1122
- let extension = fileName.slice(firstDotIndex);
1123
- while (extension !== "") {
1124
- const icon = definitions.extensions[extension];
1125
- if (icon)
1126
- return icon;
1127
- const nextDotIndex = extension.indexOf(".", 1);
1128
- if (nextDotIndex === -1)
1129
- return;
1130
- extension = extension.slice(nextDotIndex);
1116
+ function fetchImageSize(src) {
1117
+ const link = new URL(src);
1118
+ return new Promise((resolve) => {
1119
+ http.get(link, async (stream) => {
1120
+ const chunks = [];
1121
+ for await (const chunk of stream) {
1122
+ chunks.push(chunk);
1123
+ try {
1124
+ const { width: width2, height: height2 } = imageSize(Buffer.concat(chunks));
1125
+ if (width2 && height2) {
1126
+ return resolve({ width: width2, height: height2 });
1127
+ }
1128
+ } catch {
1129
+ }
1130
+ }
1131
+ const { width, height } = imageSize(Buffer.concat(chunks));
1132
+ resolve({ width, height });
1133
+ }).on("error", () => resolve({ width: 0, height: 0 }));
1134
+ });
1135
+ }
1136
+ async function resolveImageSize(app, url, remote = false) {
1137
+ if (cache.has(url))
1138
+ return cache.get(url);
1139
+ if (isLinkHttp(url) && remote) {
1140
+ return await fetchImageSize(url);
1141
+ }
1142
+ if (url[0] === "/") {
1143
+ const filepath2 = app.dir.public(url.slice(1));
1144
+ if (fs.existsSync(filepath2)) {
1145
+ const { width, height } = imageSize(fs.readFileSync(filepath2));
1146
+ return { width, height };
1147
+ }
1131
1148
  }
1149
+ return { width: 0, height: 0 };
1132
1150
  }
1133
1151
 
1134
- // src/node/utils/stringifyProp.ts
1135
- function stringifyProp(data) {
1136
- return JSON.stringify(data).replace(/'/g, "&#39");
1137
- }
1152
+ // src/node/plugin.ts
1153
+ import { addViteOptimizeDepsInclude } from "@vuepress/helper";
1154
+ import { isPackageExists as isPackageExists3 } from "local-pkg";
1138
1155
 
1139
- // src/node/container/codeTabs.ts
1140
- var codeTabs = (md, options = {}) => {
1141
- const getIcon = (filename) => {
1142
- if (options.icon === false)
1143
- return void 0;
1144
- const { named, extensions } = isPlainObject(options.icon) ? options.icon : {};
1145
- if (named === false && definitions.named[filename])
1146
- return void 0;
1147
- if (extensions === false && getFileIconTypeFromExtension(filename)) {
1148
- return void 0;
1149
- }
1150
- const hasNamed = named && named.length;
1151
- const hasExt = extensions && extensions.length;
1152
- if (hasNamed || hasExt) {
1153
- if (hasNamed && named.includes(filename))
1154
- return definitions.named[filename];
1155
- if (hasExt && extensions.some((ext) => filename.endsWith(ext)))
1156
- return getFileIconTypeFromExtension(filename);
1157
- return void 0;
1156
+ // src/node/container/index.ts
1157
+ import { isPlainObject as isPlainObject2 } from "@vuepress/helper";
1158
+
1159
+ // src/node/container/createContainer.ts
1160
+ import container from "markdown-it-container";
1161
+ function createContainerPlugin(md, type2, options = {}) {
1162
+ const render = (tokens, index) => {
1163
+ const token = tokens[index];
1164
+ const info = token.info.trim().slice(type2.length).trim() || "";
1165
+ if (token.nesting === 1) {
1166
+ return options.before?.(info, tokens, index) || `<div class="custom-container ${type2}">`;
1167
+ } else {
1168
+ return options.after?.(info, tokens, index) || "</div>";
1158
1169
  }
1159
- return getFileIconName(filename);
1160
1170
  };
1161
- tab(md, {
1162
- name: "code-tabs",
1163
- tabsOpenRenderer: ({ active, data }, tokens, index, _, env) => {
1164
- const { meta } = tokens[index];
1165
- const titles = data.map(({ title }) => md.renderInline(title, env));
1166
- const tabsData = data.map((item, dataIndex) => {
1167
- const { id = titles[dataIndex] } = item;
1168
- return { id };
1169
- });
1170
- const titlesContent = titles.map((title, index2) => {
1171
- const icon = getIcon(title);
1172
- return `<template #title${index2}="{ value, isActive }">${icon ? `<VPIcon name="${icon}"/>` : ""}<span>${title}</span></template>`;
1173
- }).join("");
1174
- return `<CodeTabs id="${index}" :data='${stringifyProp(tabsData)}'${active === -1 ? "" : ` :active="${active}"`}${meta.id ? ` tab-id="${meta.id}"` : ""}>${titlesContent}`;
1171
+ md.use(container, type2, { render });
1172
+ }
1173
+
1174
+ // src/node/container/align.ts
1175
+ var alignList = ["left", "center", "right", "justify"];
1176
+ function alignPlugin(md) {
1177
+ for (const name of alignList) {
1178
+ createContainerPlugin(md, name, {
1179
+ before: () => `<div style="text-align:${name}">`
1180
+ });
1181
+ }
1182
+ }
1183
+
1184
+ // src/node/container/card.ts
1185
+ function cardPlugin(md) {
1186
+ createContainerPlugin(md, "card", {
1187
+ before(info) {
1188
+ const { attrs: attrs2 } = resolveAttrs(info);
1189
+ const { title, icon } = attrs2;
1190
+ return `<VPCard${title ? ` title="${title}"` : ""}${icon ? ` icon="${icon}"` : ""}>`;
1175
1191
  },
1176
- tabsCloseRenderer: () => `</CodeTabs>`,
1177
- tabOpenRenderer: ({ index }, tokens, tokenIndex) => {
1178
- let foundFence = false;
1179
- for (let i = tokenIndex; i < tokens.length; i++) {
1180
- const { type: type2 } = tokens[i];
1181
- if (type2 === "code-tabs_tab_close")
1182
- break;
1183
- if ((type2 === "fence" || type2 === "import_code") && !foundFence) {
1184
- foundFence = true;
1185
- continue;
1186
- }
1187
- tokens[i].type = "code_tab_empty";
1188
- tokens[i].hidden = true;
1192
+ after: () => "</VPCard>"
1193
+ });
1194
+ createContainerPlugin(md, "card-grid", {
1195
+ before: () => "<VPCardGrid>",
1196
+ after: () => "</VPCardGrid>"
1197
+ });
1198
+ createContainerPlugin(md, "card-masonry", {
1199
+ before: (info) => {
1200
+ const { attrs: attrs2 } = resolveAttrs(info);
1201
+ let cols;
1202
+ if (attrs2.cols) {
1203
+ cols = attrs2.cols[0] === "{" ? attrs2.cols : Number.parseInt(`${attrs2.cols}`);
1189
1204
  }
1190
- return `<template #tab${index}="{ value, isActive }">`;
1205
+ const gap = Number.parseInt(`${attrs2.gap}`);
1206
+ return `<VPCardMasonry${cols ? ` :cols="${cols}"` : ""}${gap >= 0 ? ` :gap="${gap}"` : ""}>`;
1191
1207
  },
1192
- tabCloseRenderer: () => `</template>`
1208
+ after: () => "</VPCardMasonry>"
1193
1209
  });
1194
- };
1210
+ }
1195
1211
 
1196
1212
  // src/node/container/demoWrapper.ts
1197
1213
  function demoWrapperPlugin(md) {
@@ -1760,7 +1776,7 @@ function parseArgs(line) {
1760
1776
  isNextValue = !isBool;
1761
1777
  }
1762
1778
  if (!isKey && !isNextValue) {
1763
- cmd += `${value}`;
1779
+ cmd += ` ${value}`;
1764
1780
  } else {
1765
1781
  newLine += `${value}${i !== npmArgs.length - 1 ? v : ""}`;
1766
1782
  if (!isKey && isNextValue) {
@@ -1791,7 +1807,7 @@ var tabs = (md) => {
1791
1807
  name: "tabs",
1792
1808
  tabsOpenRenderer: ({ active, data }, tokens, index, _, env) => {
1793
1809
  const { meta } = tokens[index];
1794
- const titles = data.map(({ title }) => md.renderInline(title, env));
1810
+ const titles = data.map(({ title }) => md.renderInline(title, cleanMarkdownEnv(env)));
1795
1811
  const tabsData = data.map((item, dataIndex) => {
1796
1812
  const { id = titles[dataIndex] } = item;
1797
1813
  return { id };
@@ -3116,7 +3132,7 @@ var abbrPlugin = (md) => {
3116
3132
  md.core.ruler.after("linkify", "abbr_replace", abbrReplace);
3117
3133
  md.renderer.rules.abbreviation = (tokens, idx, _, env) => {
3118
3134
  const { content, info } = tokens[idx];
3119
- return `<Abbreviation>${content}${info ? `<template #tooltip>${md.renderInline(info, env)}</template>` : ""}</Abbreviation>`;
3135
+ return `<Abbreviation>${content}${info ? `<template #tooltip>${md.renderInline(info, cleanMarkdownEnv(env))}</template>` : ""}</Abbreviation>`;
3120
3136
  };
3121
3137
  };
3122
3138
 
@@ -3363,7 +3379,7 @@ async function prepareConfigFile(app, options) {
3363
3379
  enhances.add(`app.component('CodePenViewer', CodePen)`);
3364
3380
  }
3365
3381
  if (options.jsfiddle) {
3366
- imports.add(`import JSFiddle from '${CLIENT_FOLDER}components/JSFiddle.vue'`);
3382
+ imports.add(`import JSFiddle from '${CLIENT_FOLDER}components/JsFiddle.vue'`);
3367
3383
  enhances.add(`app.component('JSFiddleViewer', JSFiddle)`);
3368
3384
  }
3369
3385
  if (options.replit) {
@@ -3480,6 +3496,7 @@ function markdownPowerPlugin(options = {}) {
3480
3496
  };
3481
3497
  }
3482
3498
  export {
3499
+ createCodeTabIconGetter,
3483
3500
  markdownPowerPlugin,
3484
3501
  resolveImageSize
3485
3502
  };
@@ -180,6 +180,10 @@ interface MarkdownPowerPluginOptions {
180
180
  * @default false
181
181
  */
182
182
  annotation?: boolean;
183
+ /**
184
+ * 是否启用 abbr 语法
185
+ * @default false
186
+ */
183
187
  abbr?: boolean;
184
188
  /**
185
189
  * 配置代码块分组
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vuepress-plugin-md-power",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.134",
4
+ "version": "1.0.0-rc.136",
5
5
  "description": "The Plugin for VuePress 2 - markdown power",
6
6
  "author": "pengzhanbo <volodymyr@foxmail.com>",
7
7
  "license": "MIT",
@@ -31,16 +31,16 @@
31
31
  "lib"
32
32
  ],
33
33
  "peerDependencies": {
34
- "artplayer": "^5.2.0",
34
+ "artplayer": "^5.2.2",
35
35
  "dashjs": "^5.0.0",
36
- "esbuild": ">=0.24.2",
37
- "hls.js": "^1.5.18",
38
- "less": "^4.2.1",
39
- "markdown-it": "^14.0.0",
40
- "mpegts.js": "^1.8.0",
41
- "sass": "^1.83.0",
42
- "sass-embedded": "^1.83.0",
43
- "stylus": ">=0.64.0",
36
+ "esbuild": "^0.25.1",
37
+ "hls.js": "^1.5.20",
38
+ "less": "^4.2.2",
39
+ "markdown-it": "^14.1.0",
40
+ "mpegts.js": "^1.7.3",
41
+ "sass": "^1.85.1",
42
+ "sass-embedded": "^1.85.1",
43
+ "stylus": "^0.64.0",
44
44
  "vuepress": "2.0.0-rc.20"
45
45
  },
46
46
  "peerDependenciesMeta": {
@@ -62,23 +62,23 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@mdit/plugin-attrs": "^0.16.7",
65
- "@mdit/plugin-footnote": "^0.16.0",
65
+ "@mdit/plugin-footnote": "^0.16.8",
66
66
  "@mdit/plugin-mark": "^0.16.0",
67
67
  "@mdit/plugin-sub": "^0.16.0",
68
68
  "@mdit/plugin-sup": "^0.16.0",
69
69
  "@mdit/plugin-tab": "^0.16.0",
70
70
  "@mdit/plugin-tasklist": "^0.16.0",
71
- "@vuepress/helper": "2.0.0-rc.79",
72
- "@vueuse/core": "^12.7.0",
71
+ "@vuepress/helper": "2.0.0-rc.82",
72
+ "@vueuse/core": "^13.0.0",
73
73
  "chokidar": "3.6.0",
74
- "image-size": "^2.0.0",
75
- "local-pkg": "^1.1.0",
74
+ "image-size": "^2.0.1",
75
+ "local-pkg": "^1.1.1",
76
76
  "lru-cache": "^11.0.2",
77
77
  "markdown-it-container": "^4.0.0",
78
- "nanoid": "^5.1.2",
79
- "shiki": "^3.1.0",
80
- "tm-grammars": "^1.22.19",
81
- "tm-themes": "^1.9.14",
78
+ "nanoid": "^5.1.3",
79
+ "shiki": "^3.2.1",
80
+ "tm-grammars": "^1.23.3",
81
+ "tm-themes": "^1.10.1",
82
82
  "vue": "^3.5.13"
83
83
  },
84
84
  "devDependencies": {
@@ -86,7 +86,7 @@
86
86
  "artplayer": "^5.2.2",
87
87
  "dashjs": "^5.0.0",
88
88
  "hls.js": "^1.5.20",
89
- "mpegts.js": "^1.8.0"
89
+ "mpegts.js": "1.7.3"
90
90
  },
91
91
  "publishConfig": {
92
92
  "access": "public"