@slidev/cli 52.14.1 → 52.15.0

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.
@@ -1,4 +1,4 @@
1
- import { a as resolveImportPath, c as toAtFS, i as resolveEntry, n as getRoots, o as resolveImportUrl, r as isInstalledGlobally, s as resolveSourceFiles, t as createResolver } from "./resolver-B4CxEjJF.mjs";
1
+ import { a as resolveImportPath, c as toAtFS, i as resolveEntry, n as getRoots, o as resolveImportUrl, r as isInstalledGlobally, s as resolveSourceFiles, t as createResolver } from "./resolver-XedCEhxI.mjs";
2
2
  import { builtinModules } from "node:module";
3
3
  import path, { basename, dirname, join, relative, resolve } from "node:path";
4
4
  import { loadConfigFromFile, mergeConfig } from "vite";
@@ -6,7 +6,7 @@ import MarkdownExit from "markdown-exit";
6
6
  import fs, { existsSync } from "node:fs";
7
7
  import fs$1, { readFile } from "node:fs/promises";
8
8
  import { fileURLToPath, pathToFileURL } from "node:url";
9
- import { isString, isTruthy, notNullish, objectEntries, objectMap, range, slash, uniq } from "@antfu/utils";
9
+ import { ensureSuffix, isString, isTruthy, notNullish, objectEntries, objectMap, range, slash, uniq } from "@antfu/utils";
10
10
  import { bold, gray, red, white, yellow } from "ansis";
11
11
  import { createResolve } from "mlly";
12
12
  import { findDepPkgJsonPath } from "vitefu";
@@ -21,7 +21,8 @@ import fg from "fast-glob";
21
21
  import { createDebug } from "obug";
22
22
  import pm from "picomatch";
23
23
  import { satisfies } from "semver";
24
- import { createHead, extractUnheadInputFromHtml, transformHtmlTemplate } from "unhead/server";
24
+ import { parseHtmlForUnheadExtraction } from "unhead/parser";
25
+ import { createHead, transformHtmlTemplate } from "unhead/server";
25
26
  import { createBundledHighlighter, createSingletonShorthands } from "shiki/core";
26
27
  import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
27
28
  import { bundledLanguages, bundledThemes } from "shiki";
@@ -30,18 +31,21 @@ import MagicString from "magic-string-stack";
30
31
  import Markdown from "unplugin-vue-markdown/vite";
31
32
  import MarkdownItComark from "@comark/markdown-it";
32
33
  import MarkdownItFootnote from "markdown-it-footnote";
33
- import { fromAsyncCodeToHtml } from "@shikijs/markdown-it/async";
34
- import { SourceMapConsumer } from "source-map-js";
34
+ import MarkdownItGitHubAlerts from "markdown-it-github-alerts";
35
+ import { defineCodeblockTransformer } from "@slidev/types";
35
36
  import lz from "lz-string";
36
37
  import { toKeyedTokens } from "shiki-magic-move/core";
37
38
  import { encode } from "plantuml-encoder";
39
+ import { SourceMapConsumer } from "source-map-js";
40
+ import { fromAsyncCodeToHtml } from "@shikijs/markdown-it/async";
38
41
  import ServerRef from "vite-plugin-vue-server-ref";
39
42
  import UnoCSS from "unocss/vite";
40
43
  import { mergeConfigs, presetIcons } from "unocss";
41
44
  import Vue from "@vitejs/plugin-vue";
42
45
  import VueJsx from "@vitejs/plugin-vue-jsx";
43
46
 
44
- //#region node/syntax/markdown-it/markdown-it-link.ts
47
+ //#region node/syntax/link.ts
48
+ const RE_DIGITS_ONLY = /^\d+$/;
45
49
  function MarkdownItLink(md) {
46
50
  const defaultRender = md.renderer.rules.link_open ?? ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));
47
51
  md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
@@ -49,7 +53,7 @@ function MarkdownItLink(md) {
49
53
  const hrefIndex = token.attrIndex("href");
50
54
  const attr = token.attrs?.[hrefIndex];
51
55
  const href = attr?.[1] ?? "";
52
- if ("./#".includes(href[0]) || /^\d+$/.test(href)) {
56
+ if ("./#".includes(href[0]) || RE_DIGITS_ONLY.test(href)) {
53
57
  token.tag = "Link";
54
58
  attr[0] = "to";
55
59
  for (let i = idx + 1; i < tokens.length; i++) if (tokens[i].type === "link_close") {
@@ -63,17 +67,20 @@ function MarkdownItLink(md) {
63
67
 
64
68
  //#endregion
65
69
  //#region node/utils.ts
70
+ const RE_WHITESPACE_ONLY = /^\s*$/;
71
+ const RE_QUOTED_STRING = /^(['"])(.*)\1$/;
72
+ const RE_WHITESPACE = /\s+/g;
66
73
  let jiti;
67
74
  function loadModule(absolutePath) {
68
75
  jiti ??= createJiti(fileURLToPath(import.meta.url), { moduleCache: false });
69
76
  return jiti.import(absolutePath);
70
77
  }
71
78
  function stringifyMarkdownTokens(tokens) {
72
- return tokens.map((token) => token.children?.filter((t) => ["text", "code_inline"].includes(t.type) && !t.content.match(/^\s*$/)).map((t) => t.content.trim()).join(" ")).filter(Boolean).join(" ");
79
+ return tokens.map((token) => token.children?.filter((t) => ["text", "code_inline"].includes(t.type) && !t.content.match(RE_WHITESPACE_ONLY)).map((t) => t.content.trim()).join(" ")).filter(Boolean).join(" ");
73
80
  }
74
81
  function generateFontParams(options) {
75
82
  const weights = options.weights.flatMap((i) => options.italic ? [`0,${i}`, `1,${i}`] : [`${i}`]).sort().join(";");
76
- return options.webfonts.map((i) => `family=${i.replace(/^(['"])(.*)\1$/, "$1").replace(/\s+/g, "+")}:${options.italic ? "ital," : ""}wght@${weights}`).join("&");
83
+ return options.webfonts.map((i) => `family=${i.replace(RE_QUOTED_STRING, "$1").replace(RE_WHITESPACE, "+")}:${options.italic ? "ital," : ""}wght@${weights}`).join("&");
77
84
  }
78
85
  function generateGoogleFontsUrl(options) {
79
86
  return `https://fonts.googleapis.com/css2?${generateFontParams(options)}&display=swap`;
@@ -124,12 +131,11 @@ function getBodyJson(req) {
124
131
  });
125
132
  });
126
133
  }
127
- function makeAbsoluteImportGlob(userRoot, globs, options = {}) {
128
- const relativeGlobs = globs.map((glob) => `./${slash(relative(userRoot, glob))}`);
134
+ function makeAbsoluteImportGlob(self, globs, options = {}) {
135
+ const relativeGlobs = globs.map((glob) => `${slash(relative(self, glob))}`);
129
136
  const opts = {
130
137
  eager: true,
131
138
  exhaustive: true,
132
- base: "/",
133
139
  ...options
134
140
  };
135
141
  return `import.meta.glob(${JSON.stringify(relativeGlobs)}, ${JSON.stringify(opts)})`;
@@ -155,6 +161,7 @@ async function setupVitePlugins(options) {
155
161
 
156
162
  //#endregion
157
163
  //#region node/vite/compilerFlagsVue.ts
164
+ const RE_VUE_FILE = /\.vue(?:$|\?)/;
158
165
  /**
159
166
  * Replace compiler flags like `__DEV__` in Vue SFC
160
167
  */
@@ -163,8 +170,8 @@ function createVueCompilerFlagsPlugin(options) {
163
170
  return {
164
171
  name: "slidev:flags",
165
172
  enforce: "pre",
166
- transform: { handler(code, id) {
167
- if (!id.match(/\.vue($|\?)/) && !id.includes("?vue&")) return;
173
+ transform: { handler(code, id$2) {
174
+ if (!RE_VUE_FILE.test(id$2) && !id$2.includes("?vue&")) return;
168
175
  const original = code;
169
176
  define.forEach(([from, to]) => {
170
177
  code = code.replaceAll(from, to);
@@ -176,6 +183,11 @@ function createVueCompilerFlagsPlugin(options) {
176
183
 
177
184
  //#endregion
178
185
  //#region node/vite/components.ts
186
+ const RE_VUE_EXT$1 = /\.vue$/;
187
+ const RE_VUE_QUERY_VUE$1 = /\.vue\?vue/;
188
+ const RE_VUE_QUERY_V$1 = /\.vue\?v=/;
189
+ const RE_MD_EXT$2 = /\.md$/;
190
+ const RE_MD_QUERY_VUE$1 = /\.md\?vue/;
179
191
  function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
180
192
  return Components({
181
193
  extensions: [
@@ -189,11 +201,11 @@ function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
189
201
  dirs: [join(clientRoot, "builtin"), ...roots.map((i) => join(i, "components"))],
190
202
  globsExclude: [],
191
203
  include: [
192
- /\.vue$/,
193
- /\.vue\?vue/,
194
- /\.vue\?v=/,
195
- /\.md$/,
196
- /\.md\?vue/
204
+ RE_VUE_EXT$1,
205
+ RE_VUE_QUERY_VUE$1,
206
+ RE_VUE_QUERY_V$1,
207
+ RE_MD_EXT$2,
208
+ RE_MD_QUERY_VUE$1
197
209
  ],
198
210
  exclude: [],
199
211
  resolvers: [IconsResolver({
@@ -216,14 +228,16 @@ const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $pa
216
228
 
217
229
  //#endregion
218
230
  //#region node/vite/contextInjection.ts
231
+ const RE_EXPORT_DEFAULT_OBJECT = /export\s+default\s+\{/;
232
+ const RE_INJECT_OPTION = /.*inject\s*:\s*([[{])/;
219
233
  /**
220
234
  * Inject `$slidev` into the script block of a Vue component
221
235
  */
222
236
  function createContextInjectionPlugin() {
223
237
  return {
224
238
  name: "slidev:context-injection",
225
- transform: { async handler(code, id) {
226
- if (!id.endsWith(".vue") || id.includes("/@slidev/client/") || id.includes("/packages/client/")) return;
239
+ transform: { async handler(code, id$2) {
240
+ if (!id$2.endsWith(".vue") || id$2.includes("/@slidev/client/") || id$2.includes("/packages/client/")) return;
227
241
  if (code.includes(templateInjectionMarker) || code.includes("useSlideContext()")) return code;
228
242
  const imports = [
229
243
  templateImportContextUtils,
@@ -237,7 +251,7 @@ function createContextInjectionPlugin() {
237
251
  const setupTagEnd = (setupScriptMatch.index || 0) + setupTag.length;
238
252
  return `${code.slice(0, setupTagEnd)}\n${imports.join("\n")}\n${code.slice(setupTagEnd)}`;
239
253
  } else if (!setupScriptMatch && matchScripts.length === 1) {
240
- const matchExport = code.match(/export\s+default\s+\{/);
254
+ const matchExport = code.match(RE_EXPORT_DEFAULT_OBJECT);
241
255
  if (matchExport) {
242
256
  const exportIndex = (matchExport.index || 0) + matchExport[0].length;
243
257
  let component = code.slice(exportIndex);
@@ -248,7 +262,7 @@ import { injectionSlidevContext } from "@slidev/client/constants.ts"
248
262
  ${code.slice(scriptIndex)}`;
249
263
  let injectIndex = exportIndex + 70;
250
264
  let injectObject = "$slidev: { from: injectionSlidevContext },";
251
- const matchInject = component.match(/.*inject\s*:\s*([[{])/);
265
+ const matchInject = component.match(RE_INJECT_OPTION);
252
266
  if (matchInject) {
253
267
  injectIndex += (matchInject.index || 0) + matchInject[0].length;
254
268
  if (matchInject[1] === "[") {
@@ -269,6 +283,10 @@ ${code.slice(scriptIndex)}`;
269
283
 
270
284
  //#endregion
271
285
  //#region node/vite/extendConfig.ts
286
+ const RE_SLIDEV_CLIENT = /^@slidev\/client$/;
287
+ const RE_SLIDEV_CLIENT_SUBPATH = /^@slidev\/client\/(.*)/;
288
+ const RE_SLIDEV_VIRTUAL = /^#slidev\/(.*)/;
289
+ const RE_MONACO_EDITOR = /\/monaco-editor(?:-core)?\//;
272
290
  const INCLUDE_GLOBAL = [
273
291
  "@typescript/ata",
274
292
  "file-saver",
@@ -335,15 +353,15 @@ function createConfigPlugin(options) {
335
353
  resolve: {
336
354
  alias: [
337
355
  {
338
- find: /^@slidev\/client$/,
356
+ find: RE_SLIDEV_CLIENT,
339
357
  replacement: `${toAtFS(options.clientRoot)}/index.ts`
340
358
  },
341
359
  {
342
- find: /^@slidev\/client\/(.*)/,
360
+ find: RE_SLIDEV_CLIENT_SUBPATH,
343
361
  replacement: `${toAtFS(options.clientRoot)}/$1`
344
362
  },
345
363
  {
346
- find: /^#slidev\/(.*)/,
364
+ find: RE_SLIDEV_VIRTUAL,
347
365
  replacement: "/@slidev/$1"
348
366
  },
349
367
  {
@@ -380,21 +398,21 @@ function createConfigPlugin(options) {
380
398
  const DEFAULT = "assets/[name]-[hash].js";
381
399
  if (chunkInfo.name.includes("/")) return DEFAULT;
382
400
  if (chunkInfo.moduleIds.filter((i) => isSlidevClient(i)).length > chunkInfo.moduleIds.length * .6) return "assets/slidev/[name]-[hash].js";
383
- if (chunkInfo.moduleIds.filter((i) => i.match(/\/monaco-editor(-core)?\//)).length > chunkInfo.moduleIds.length * .6) return "assets/monaco/[name]-[hash].js";
401
+ if (chunkInfo.moduleIds.filter((i) => i.match(RE_MONACO_EDITOR)).length > chunkInfo.moduleIds.length * .6) return "assets/monaco/[name]-[hash].js";
384
402
  return DEFAULT;
385
403
  },
386
- manualChunks(id) {
387
- if (id.startsWith("/@slidev-monaco-types/") || id.includes("/@slidev/monaco-types") || id.endsWith("?monaco-types&raw")) return "monaco/bundled-types";
388
- if (id.includes("/shiki/") || id.includes("/@shikijs/")) return `modules/shiki`;
389
- if (id.startsWith("~icons/")) return "modules/unplugin-icons";
390
- const matchedAsyncModule = ASYNC_MODULES.find((i) => id.includes(`/node_modules/${i}`));
404
+ manualChunks(id$2) {
405
+ if (id$2.startsWith("/@slidev-monaco-types/") || id$2.includes("/@slidev/monaco-types") || id$2.endsWith("?monaco-types&raw")) return "monaco/bundled-types";
406
+ if (id$2.includes("/shiki/") || id$2.includes("/@shikijs/")) return `modules/shiki`;
407
+ if (id$2.startsWith("~icons/")) return "modules/unplugin-icons";
408
+ const matchedAsyncModule = ASYNC_MODULES.find((i) => id$2.includes(`/node_modules/${i}`));
391
409
  if (matchedAsyncModule) return `modules/${matchedAsyncModule.replace("@", "").replace("/", "-")}`;
392
410
  }
393
411
  } } },
394
412
  cacheDir: isInstalledGlobally.value ? join(options.cliRoot, "node_modules/.vite") : void 0
395
413
  };
396
- function isSlidevClient(id) {
397
- return id.includes("/@slidev/") || id.includes("/slidev/packages/client/") || id.includes("/@vueuse/");
414
+ function isSlidevClient(id$2) {
415
+ return id$2.includes("/@slidev/") || id$2.includes("/slidev/packages/client/") || id$2.includes("/@vueuse/");
398
416
  }
399
417
  return mergeConfig(injection, config);
400
418
  },
@@ -426,8 +444,8 @@ function createHmrPatchPlugin() {
426
444
  name: "slidev:hmr-patch",
427
445
  transform: {
428
446
  filter: { id: { include: regexSlideSourceId } },
429
- handler(code, id) {
430
- if (!id.match(regexSlideSourceId)) return;
447
+ handler(code, id$2) {
448
+ if (!id$2.match(regexSlideSourceId)) return;
431
449
  return code.replace("if (_rerender_only)", "if (false)");
432
450
  }
433
451
  }
@@ -458,13 +476,14 @@ async function createInspectPlugin(options, pluginOptions) {
458
476
 
459
477
  //#endregion
460
478
  //#region node/vite/layoutWrapper.ts
479
+ const RE_SCRIPT_SETUP_TAG = /^<script setup.*>/m;
461
480
  function createLayoutWrapperPlugin({ data, utils }) {
462
481
  return {
463
482
  name: "slidev:layout-wrapper",
464
483
  transform: {
465
484
  filter: { id: { include: regexSlideSourceId } },
466
- async handler(code, id) {
467
- const match = id.match(regexSlideSourceId);
485
+ async handler(code, id$2) {
486
+ const match = id$2.match(regexSlideSourceId);
468
487
  if (!match) return;
469
488
  const [, no, type] = match;
470
489
  if (type !== "md") return;
@@ -476,7 +495,7 @@ function createLayoutWrapperPlugin({ data, utils }) {
476
495
  console.error();
477
496
  layoutName = "default";
478
497
  }
479
- const setupTag = code.match(/^<script setup.*>/m);
498
+ const setupTag = code.match(RE_SCRIPT_SETUP_TAG);
480
499
  if (!setupTag) throw new Error(`[Slidev] Internal error: <script setup> block not found in slide ${index + 1}.`);
481
500
  const templatePart = code.slice(0, setupTag.index);
482
501
  const scriptPart = code.slice(setupTag.index);
@@ -503,7 +522,7 @@ function createLayoutWrapperPlugin({ data, utils }) {
503
522
 
504
523
  //#endregion
505
524
  //#region package.json
506
- var version = "52.14.1";
525
+ var version = "52.15.0";
507
526
 
508
527
  //#endregion
509
528
  //#region node/integrations/addons.ts
@@ -545,6 +564,8 @@ async function getThemeMeta(name, root) {
545
564
 
546
565
  //#endregion
547
566
  //#region node/setups/indexHtml.ts
567
+ const RE_TRAILING_SLASH = /\/$/;
568
+ const RE_BODY_CONTENT = /<body>([\s\S]*?)<\/body>/i;
548
569
  function escapeHtml(str) {
549
570
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
550
571
  }
@@ -555,7 +576,7 @@ function collectPreloadImages(data, base) {
555
576
  if (data.config.preloadImages === false) return [];
556
577
  const seen = /* @__PURE__ */ new Set();
557
578
  const links = [];
558
- const basePrefix = base ? base.replace(/\/$/, "") : "";
579
+ const basePrefix = base ? base.replace(RE_TRAILING_SLASH, "") : "";
559
580
  for (const slide of data.slides) {
560
581
  const images = slide.images || slide.source?.images;
561
582
  if (!images?.length) continue;
@@ -585,10 +606,11 @@ async function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data,
585
606
  console.error(yellow("This file may be generated by Slidev, please remove it from your project."));
586
607
  continue;
587
608
  }
588
- inputs.push(extractUnheadInputFromHtml(html).input);
589
- body += `\n${(html.match(/<body>([\s\S]*?)<\/body>/i)?.[1] || "").trim()}`;
609
+ inputs.push(parseHtmlForUnheadExtraction(html).input);
610
+ body += `\n${(html.match(RE_BODY_CONTENT)?.[1] || "").trim()}`;
590
611
  }
591
612
  if (data.features.tweet) body += "\n<script async src=\"https://platform.twitter.com/widgets.js\"><\/script>";
613
+ if (data.features.bluesky) body += "\n<script async src=\"https://embed.bsky.app/static/embed.js\" charset=\"utf-8\"><\/script>";
592
614
  const webFontsLink = [];
593
615
  if (data.config.fonts.webfonts.length) {
594
616
  const { provider } = data.config.fonts;
@@ -799,6 +821,7 @@ async function setupShiki(roots) {
799
821
 
800
822
  //#endregion
801
823
  //#region node/options.ts
824
+ const RE_FILE_EXTENSION = /\.\w+$/;
802
825
  const debug = createDebug("slidev:options");
803
826
  async function resolveOptions(entryOptions, mode) {
804
827
  const entry = await resolveEntry(entryOptions.entry);
@@ -817,6 +840,7 @@ async function resolveOptions(entryOptions, mode) {
817
840
  rootsInfo.userRoot
818
841
  ]);
819
842
  if (entryOptions.download) config.download ||= entryOptions.download;
843
+ if (entryOptions.routerMode) config.routerMode = entryOptions.routerMode;
820
844
  debug({
821
845
  ...rootsInfo,
822
846
  ...entryOptions,
@@ -875,7 +899,7 @@ async function createDataUtils(resolved) {
875
899
  suppressErrors: true
876
900
  })));
877
901
  for (const layoutPath of layoutPaths.flat(1)) {
878
- const layoutName = path.basename(layoutPath).replace(/\.\w+$/, "");
902
+ const layoutName = path.basename(layoutPath).replace(RE_FILE_EXTENSION, "");
879
903
  layouts[layoutName] = layoutPath;
880
904
  }
881
905
  return layouts;
@@ -906,52 +930,10 @@ function getDefine(options) {
906
930
  }
907
931
 
908
932
  //#endregion
909
- //#region node/syntax/transform/utils.ts
933
+ //#region node/syntax/utils.ts
910
934
  function normalizeRangeStr(rangeStr = "") {
911
935
  return !rangeStr.trim() ? [] : rangeStr.trim().split(/\|/g).map((i) => i.trim());
912
936
  }
913
- function getCodeBlocks(md) {
914
- const codeblocks = Array.from(md.matchAll(/^```[\s\S]*?^```/gm)).map((m) => {
915
- const start = m.index;
916
- const end = m.index + m[0].length;
917
- return [
918
- start,
919
- end,
920
- md.slice(0, start).match(/\n/g)?.length || 0,
921
- md.slice(0, end).match(/\n/g)?.length || 0
922
- ];
923
- });
924
- return {
925
- codeblocks,
926
- isInsideCodeblocks(idx) {
927
- return codeblocks.some(([s, e]) => s <= idx && idx <= e);
928
- },
929
- isLineInsideCodeblocks(line) {
930
- return codeblocks.some(([, , s, e]) => s <= line && line <= e);
931
- }
932
- };
933
- }
934
- function getCommentBlocks(md) {
935
- const commentBlocks = Array.from(md.matchAll(/<!--[\s\S]*?-->/g)).map((m) => {
936
- const start = m.index;
937
- const end = m.index + m[0].length;
938
- return [
939
- start,
940
- end,
941
- md.slice(0, start).match(/\n/g)?.length || 0,
942
- md.slice(0, end).match(/\n/g)?.length || 0
943
- ];
944
- });
945
- return {
946
- commentBlocks,
947
- isInsideCommentBlocks(idx) {
948
- return commentBlocks.some(([s, e]) => s <= idx && idx <= e);
949
- },
950
- isLineInsideCommentBlocks(line) {
951
- return commentBlocks.some(([, , s, e]) => s <= line && line <= e);
952
- }
953
- };
954
- }
955
937
  /**
956
938
  * Escape `{{` in code block to prevent Vue interpret it, #99, #1316
957
939
  */
@@ -960,7 +942,8 @@ function escapeVueInCode(md) {
960
942
  }
961
943
 
962
944
  //#endregion
963
- //#region node/syntax/markdown-it/markdown-it-katex.ts
945
+ //#region node/syntax/katex.ts
946
+ const RE_KATEX_BLOCK_INFO = /^\{([\w*,|-]+)\}\s*(\{[^}]*\})?/;
964
947
  function isValidDelim(state, pos) {
965
948
  const max = state.posMax;
966
949
  let can_open = true;
@@ -1015,7 +998,7 @@ function math_inline(state, silent) {
1015
998
  state.pos = match + 1;
1016
999
  return true;
1017
1000
  }
1018
- function math_block(state, start, end, silent) {
1001
+ const math_block = function(state, start, end, silent) {
1019
1002
  let firstLine;
1020
1003
  let lastLine;
1021
1004
  let next;
@@ -1026,11 +1009,13 @@ function math_block(state, start, end, silent) {
1026
1009
  if (pos + 2 > max) return false;
1027
1010
  if (state.src.slice(pos, pos + 2) !== "$$") return false;
1028
1011
  pos += 2;
1029
- firstLine = state.src.slice(pos, max);
1012
+ firstLine = state.src.slice(pos, max).trim();
1030
1013
  if (silent) return true;
1031
- if (firstLine.trim().slice(-2) === "$$") {
1032
- firstLine = firstLine.trim().slice(0, -2);
1014
+ let singleLine = false;
1015
+ if (firstLine.slice(-2) === "$$") {
1016
+ firstLine = firstLine.slice(0, -2).trim();
1033
1017
  found = true;
1018
+ singleLine = true;
1034
1019
  }
1035
1020
  for (next = start; !found;) {
1036
1021
  next++;
@@ -1047,11 +1032,15 @@ function math_block(state, start, end, silent) {
1047
1032
  state.line = next + 1;
1048
1033
  const token = state.push("math_block", "math", 0);
1049
1034
  token.block = true;
1050
- token.content = (firstLine && firstLine.trim() ? `${firstLine}\n` : "") + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
1035
+ if (singleLine) token.content = firstLine;
1036
+ else {
1037
+ token.info = firstLine;
1038
+ token.content = state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
1039
+ }
1051
1040
  token.map = [start, state.line];
1052
1041
  token.markup = "$$";
1053
1042
  return true;
1054
- }
1043
+ };
1055
1044
  function MarkdownItKatex(md, options) {
1056
1045
  const katexInline = function(latex) {
1057
1046
  options.displayMode = false;
@@ -1075,7 +1064,10 @@ function MarkdownItKatex(md, options) {
1075
1064
  }
1076
1065
  };
1077
1066
  const blockRenderer = function(tokens, idx) {
1078
- return `${katexBlock(tokens[idx].content)}\n`;
1067
+ const token = tokens[idx];
1068
+ const [, rangeStr, options$1] = RE_KATEX_BLOCK_INFO.exec(token.info) || [];
1069
+ const ranges = normalizeRangeStr(rangeStr);
1070
+ return `<KaTexBlockWrapper ${options$1 ? `v-bind="${options$1}"` : ""} :ranges='${JSON.stringify(ranges)}'>${katexBlock(tokens[idx].content)}</KaTexBlockWrapper>\n`;
1079
1071
  };
1080
1072
  md.inline.ruler.after("escape", "math_inline", math_inline);
1081
1073
  md.block.ruler.after("blockquote", "math_block", math_block, { alt: [
@@ -1088,6 +1080,23 @@ function MarkdownItKatex(md, options) {
1088
1080
  md.renderer.rules.math_block = blockRenderer;
1089
1081
  }
1090
1082
 
1083
+ //#endregion
1084
+ //#region node/virtual/conditional-styles.ts
1085
+ const id$1 = "/@slidev/conditional-styles";
1086
+ const templateConditionalStyles = {
1087
+ id: id$1,
1088
+ async getContent({ data, roots }) {
1089
+ const imports = [];
1090
+ for (const root of roots) imports.push(makeAbsoluteImportGlob(id$1, [
1091
+ join(root, "styles/index.{ts,js,css}"),
1092
+ join(root, "styles.{ts,js,css}"),
1093
+ join(root, "style.{ts,js,css}")
1094
+ ]));
1095
+ if (data.features.katex) imports.push(`import "${await resolveImportUrl("katex/dist/katex.min.css")}"`);
1096
+ return imports.join("\n");
1097
+ }
1098
+ };
1099
+
1091
1100
  //#endregion
1092
1101
  //#region node/virtual/configs.ts
1093
1102
  const templateConfigs = {
@@ -1138,15 +1147,16 @@ console.warn('/@slidev/titles.md is deprecated, import from #slidev/title-render
1138
1147
 
1139
1148
  //#endregion
1140
1149
  //#region node/virtual/global-layers.ts
1150
+ const id = `/@slidev/global-layers`;
1141
1151
  const templateGlobalLayers = {
1142
- id: `/@slidev/global-layers`,
1143
- getContent({ userRoot, roots }) {
1152
+ id,
1153
+ getContent({ roots }) {
1144
1154
  function* getComponent(name, names) {
1145
1155
  yield `const ${name}Components = [\n`;
1146
1156
  for (const root of roots) {
1147
1157
  const globs = names.map((name$1) => join(root, `${name$1}.{ts,js,vue}`));
1148
1158
  yield " Object.values(";
1149
- yield makeAbsoluteImportGlob(userRoot, globs, { import: "default" });
1159
+ yield makeAbsoluteImportGlob(id, globs, { import: "default" });
1150
1160
  yield ")[0],\n";
1151
1161
  }
1152
1162
  yield `].filter(Boolean)\n`;
@@ -1262,11 +1272,12 @@ export default {
1262
1272
  //#endregion
1263
1273
  //#region node/virtual/setups.ts
1264
1274
  function createSetupTemplate(name) {
1275
+ const id$2 = `/@slidev/setups/${name}`;
1265
1276
  return {
1266
- id: `/@slidev/setups/${name}`,
1267
- getContent({ userRoot, roots }) {
1277
+ id: id$2,
1278
+ getContent({ roots }) {
1268
1279
  return `export default [${roots.map((root) => {
1269
- return `Object.values(${makeAbsoluteImportGlob(userRoot, [join(root, `setup/${name}.{ts,js,mts,mjs}`)], { import: "default" })})[0]`;
1280
+ return `Object.values(${makeAbsoluteImportGlob(id$2, [join(root, `setup/${name}.{ts,js,mts,mjs}`)], { import: "default" })})[0]`;
1270
1281
  }).join(", ")}].filter(Boolean)`;
1271
1282
  }
1272
1283
  };
@@ -1323,34 +1334,6 @@ const templateSlides = {
1323
1334
  }
1324
1335
  };
1325
1336
 
1326
- //#endregion
1327
- //#region node/virtual/styles.ts
1328
- const templateStyle = {
1329
- id: "/@slidev/styles",
1330
- async getContent({ data, clientRoot, userRoot, roots }) {
1331
- function resolveUrlOfClient(name) {
1332
- return toAtFS(join(clientRoot, name));
1333
- }
1334
- const imports = [
1335
- "styles/vars.css",
1336
- "styles/index.css",
1337
- "styles/code.css",
1338
- "styles/katex.css",
1339
- "styles/transitions.css"
1340
- ].map((path$1) => makeAbsoluteImportGlob(userRoot, [join(clientRoot, path$1)]));
1341
- for (const root of roots) imports.push(makeAbsoluteImportGlob(userRoot, [
1342
- join(root, "styles/index.{ts,js,css}"),
1343
- join(root, "styles.{ts,js,css}"),
1344
- join(root, "style.{ts,js,css}")
1345
- ]));
1346
- if (data.features.katex) imports.push(`import "${await resolveImportUrl("katex/dist/katex.min.css")}"`);
1347
- if (data.config.highlighter === "shiki") imports.push(`import "${await resolveImportUrl("@shikijs/vitepress-twoslash/style.css")}"`, `import "${resolveUrlOfClient("styles/shiki-twoslash.css")}"`, `import "${await resolveImportUrl("shiki-magic-move/style.css")}"`);
1348
- imports.unshift(`import "${await resolveImportUrl("@unocss/reset/tailwind.css")}"`, "import \"uno:preflights.css\"", "import \"uno:typography.css\"", "import \"uno:shortcuts.css\"");
1349
- imports.push("import \"uno.css\"");
1350
- return imports.join("\n");
1351
- }
1352
- };
1353
-
1354
1337
  //#endregion
1355
1338
  //#region node/virtual/titles.ts
1356
1339
  const templateTitleRendererMd = {
@@ -1374,7 +1357,7 @@ const templates = [
1374
1357
  templateMonacoTypes,
1375
1358
  templateMonacoRunDeps,
1376
1359
  templateConfigs,
1377
- templateStyle,
1360
+ templateConditionalStyles,
1378
1361
  templateGlobalLayers,
1379
1362
  templateNavControls,
1380
1363
  templateSlides,
@@ -1388,6 +1371,7 @@ const templates = [
1388
1371
 
1389
1372
  //#endregion
1390
1373
  //#region node/vite/loaders.ts
1374
+ const RE_WORD_CHARS_ONLY = /^[\w-]+$/;
1391
1375
  function createSlidesLoader(options, serverOptions) {
1392
1376
  const { data, mode, utils, withoutNotes } = options;
1393
1377
  const notesMd = MarkdownExit({ html: true });
@@ -1528,32 +1512,32 @@ function createSlidesLoader(options, serverOptions) {
1528
1512
  const moduleEntries = [
1529
1513
  ...ctx.modules.filter((i) => i.id === templateMonacoRunDeps.id || i.id === templateMonacoTypes.id),
1530
1514
  ...vueModules,
1531
- ...Array.from(moduleIds).map((id) => ctx.server.moduleGraph.getModuleById(id))
1515
+ ...Array.from(moduleIds).map((id$2) => ctx.server.moduleGraph.getModuleById(id$2))
1532
1516
  ].filter(notNullish).filter((i) => !i.id?.startsWith("/@id/@vite-icons"));
1533
1517
  updateServerWatcher();
1534
1518
  return moduleEntries;
1535
1519
  },
1536
1520
  resolveId: {
1537
1521
  order: "pre",
1538
- handler(id) {
1539
- if (id.startsWith("/@slidev/") || id.includes("__slidev_")) return id;
1522
+ handler(id$2) {
1523
+ if (id$2.startsWith("/@slidev/") || id$2.includes("__slidev_")) return id$2;
1540
1524
  return null;
1541
1525
  }
1542
1526
  },
1543
- async load(id) {
1544
- const template = templates.find((i) => i.id === id);
1527
+ async load(id$2) {
1528
+ const template = templates.find((i) => i.id === id$2);
1545
1529
  if (template) return {
1546
1530
  code: await template.getContent.call(this, options),
1547
1531
  map: { mappings: "" }
1548
1532
  };
1549
- const matchFacade = id.match(regexSlideFacadeId);
1533
+ const matchFacade = id$2.match(regexSlideFacadeId);
1550
1534
  if (matchFacade) {
1551
1535
  const [, no, type] = matchFacade;
1552
1536
  const idx = +no - 1;
1553
1537
  const sourceId = JSON.stringify(sourceIds[type][idx]);
1554
1538
  return [`export * from ${sourceId}`, `export { default } from ${sourceId}`].join("\n");
1555
1539
  }
1556
- const matchSource = id.match(regexSlideSourceId);
1540
+ const matchSource = id$2.match(regexSlideSourceId);
1557
1541
  if (matchSource) {
1558
1542
  const [, no, type] = matchSource;
1559
1543
  const idx = +no - 1;
@@ -1616,7 +1600,7 @@ function createSlidesLoader(options, serverOptions) {
1616
1600
  };
1617
1601
  }
1618
1602
  }
1619
- if (data.markdownFiles[id]) return "";
1603
+ if (data.markdownFiles[id$2]) return "";
1620
1604
  }
1621
1605
  };
1622
1606
  function renderNote(text = "") {
@@ -1627,7 +1611,7 @@ function createSlidesLoader(options, serverOptions) {
1627
1611
  clickCount += Number(count);
1628
1612
  return `<span class="slidev-note-click-mark" data-clicks="${clickCount}"></span>`;
1629
1613
  });
1630
- const keys = Object.keys(notesAutoRuby).sort((b, a) => b.length - a.length).map((i) => /^[\w-]+$/.test(i) ? `\\b${i}\\b` : i);
1614
+ const keys = Object.keys(notesAutoRuby).sort((b, a) => b.length - a.length).map((i) => RE_WORD_CHARS_ONLY.test(i) ? `\\b${i}\\b` : i);
1631
1615
  if (keys.length) {
1632
1616
  const regex = new RegExp(`(${keys.join("|")})`, "g");
1633
1617
  md = md.replace(regex, (match) => {
@@ -1646,6 +1630,29 @@ function createSlidesLoader(options, serverOptions) {
1646
1630
  }
1647
1631
  }
1648
1632
 
1633
+ //#endregion
1634
+ //#region node/setups/transformers.ts
1635
+ async function setupTransformers(roots) {
1636
+ const returns = await loadSetups(roots, "transformers.ts", []);
1637
+ const result = {
1638
+ pre: [],
1639
+ preCodeblock: [],
1640
+ postCodeblock: [],
1641
+ post: [],
1642
+ codeblocks: []
1643
+ };
1644
+ for (const r of [...returns].reverse()) {
1645
+ if (r.pre) result.pre.push(...r.pre);
1646
+ if (r.preCodeblock) result.preCodeblock.push(...r.preCodeblock);
1647
+ }
1648
+ for (const r of returns) {
1649
+ if (r.postCodeblock) result.postCodeblock.push(...r.postCodeblock);
1650
+ if (r.post) result.post.push(...r.post);
1651
+ if (r.codeblocks) result.codeblocks.push(...r.codeblocks);
1652
+ }
1653
+ return result;
1654
+ }
1655
+
1649
1656
  //#endregion
1650
1657
  //#region ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=49e14003b6caa0b7d164cbe71da573809d375bab_0dce998d26f79c06311f476731d22630/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/image-size/specialCharacters.js
1651
1658
  var SpecialCharacters;
@@ -1865,11 +1872,11 @@ function isTodoItem(tokens, index) {
1865
1872
  }
1866
1873
  function todoify(token, options) {
1867
1874
  if (token.children == null) return;
1868
- const id = generateIdForToken(token);
1869
- token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id));
1875
+ const id$2 = generateIdForToken(token);
1876
+ token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id$2));
1870
1877
  token.children[1].content = token.children[1].content.replace(checkboxRegex, "");
1871
1878
  if (options.label) {
1872
- token.children.splice(1, 0, createLabelBeginToken(id));
1879
+ token.children.splice(1, 0, createLabelBeginToken(id$2));
1873
1880
  token.children.push(createLabelEndToken());
1874
1881
  }
1875
1882
  }
@@ -1877,17 +1884,17 @@ function generateIdForToken(token) {
1877
1884
  if (token.map) return `task-item-${token.map[0]}`;
1878
1885
  else return `task-item-${Math.ceil(Math.random() * (1e4 * 1e3) - 1e3)}`;
1879
1886
  }
1880
- function createCheckboxToken(token, enabled, id) {
1887
+ function createCheckboxToken(token, enabled, id$2) {
1881
1888
  const checkbox = new token_default("taskListItemCheckbox", "", 0);
1882
1889
  if (!enabled) checkbox.attrSet("disabled", "true");
1883
1890
  if (token.map) checkbox.attrSet("line", token.map[0].toString());
1884
- checkbox.attrSet("id", id);
1891
+ checkbox.attrSet("id", id$2);
1885
1892
  if (checkboxRegex.exec(token.content)?.[1].toLowerCase() === "x") checkbox.attrSet("checked", "true");
1886
1893
  return checkbox;
1887
1894
  }
1888
- function createLabelBeginToken(id) {
1895
+ function createLabelBeginToken(id$2) {
1889
1896
  const labelBeginToken = new token_default("taskListItemLabel_open", "", 1);
1890
- labelBeginToken.attrSet("id", id);
1897
+ labelBeginToken.attrSet("id", id$2);
1891
1898
  return labelBeginToken;
1892
1899
  }
1893
1900
  function createLabelEndToken() {
@@ -1907,62 +1914,139 @@ function startsWithTodoMarkdown(token) {
1907
1914
  }
1908
1915
 
1909
1916
  //#endregion
1910
- //#region node/syntax/markdown-it/markdown-it-escape-code.ts
1911
- function MarkdownItEscapeInlineCode(md) {
1912
- const codeInline = md.renderer.rules.code_inline;
1913
- md.renderer.rules.code_inline = async (tokens, idx, options, env, self) => {
1914
- return (await codeInline(tokens, idx, options, env, self)).replace(/^<code/, "<code v-pre");
1915
- };
1916
- }
1917
-
1918
- //#endregion
1919
- //#region node/syntax/markdown-it/markdown-it-shiki.ts
1920
- async function MarkdownItShiki({ data: { config }, mode, utils: { shiki, shikiOptions } }) {
1921
- async function getTwoslashTransformer() {
1922
- const [, , { transformerTwoslash }] = await Promise.all([
1923
- shiki.codeToHast("", {
1924
- lang: "js",
1925
- ...shikiOptions
1926
- }),
1927
- shiki.codeToHast("", {
1928
- lang: "ts",
1929
- ...shikiOptions
1930
- }),
1931
- import("@shikijs/vitepress-twoslash")
1932
- ]);
1933
- return transformerTwoslash({
1934
- explicitTrigger: true,
1935
- twoslashOptions: { handbookOptions: { noErrorValidation: true } }
1936
- });
1937
- }
1938
- const transformers = [
1939
- ...shikiOptions.transformers || [],
1940
- (config.twoslash === true || config.twoslash === mode) && await getTwoslashTransformer(),
1941
- {
1942
- pre(pre) {
1943
- this.addClassToHast(pre, "slidev-code");
1944
- delete pre.properties.tabindex;
1945
- },
1946
- postprocess(code) {
1947
- return escapeVueInCode(code);
1917
+ //#region node/syntax/codeblock/magic-move.ts
1918
+ const RE_MAGIC_MOVE_INFO = /^(?:md|markdown) magic-move\s*(?:\[([^\]]*)\])?\s*(\{[^}]*\})?/;
1919
+ const RE_CODE_BLOCK = /^```([\w'-]+)?(?:[ \t]*|[ \t][ \w\t'-]*)(?:\[([^\]]*)\])?[ \t]*(?:\{([\w*,|-]+)\}[ \t]*(\{[^}]*\})?([^\r\n]*))?\r?\n((?:(?!^```)[\s\S])*?)^```$/gm;
1920
+ const RE_LINES_TRUE = /\blines: *true\b/;
1921
+ const RE_LINES_FALSE = /\blines: *false\b/;
1922
+ function parseLineNumbersOption(options) {
1923
+ return RE_LINES_TRUE.test(options) ? true : RE_LINES_FALSE.test(options) ? false : void 0;
1924
+ }
1925
+ var magic_move_default = defineCodeblockTransformer(async ({ info, fence, code, options: { data: { config }, utils: { shikiOptions, shiki } } }) => {
1926
+ if (fence !== 4) return;
1927
+ const match = info.match(RE_MAGIC_MOVE_INFO);
1928
+ if (!match) return;
1929
+ const [, title = "", options = "{}"] = match;
1930
+ const defaultLineNumbers = parseLineNumbersOption(options) ?? config.lineNumbers;
1931
+ const matches = Array.from(code.matchAll(RE_CODE_BLOCK));
1932
+ if (!matches.length) throw new Error("Magic Move block must contain at least one code block");
1933
+ const ranges = matches.map((i) => normalizeRangeStr(i[3]));
1934
+ const steps = await Promise.all(matches.map(async (i) => {
1935
+ const lang = i[1];
1936
+ const lineNumbers = parseLineNumbersOption(i[4]) ?? defaultLineNumbers;
1937
+ const code$1 = i[6].trimEnd();
1938
+ const options$1 = {
1939
+ ...shikiOptions,
1940
+ lang
1941
+ };
1942
+ const { tokens, bg, fg: fg$1, rootStyle, themeName } = await shiki.codeToTokens(code$1, options$1);
1943
+ return {
1944
+ ...toKeyedTokens(code$1, tokens, JSON.stringify([lang, "themes" in options$1 ? options$1.themes : options$1.theme]), lineNumbers),
1945
+ bg,
1946
+ fg: fg$1,
1947
+ rootStyle,
1948
+ themeName,
1949
+ lang
1950
+ };
1951
+ }));
1952
+ return `<ShikiMagicMove v-bind="${options}" steps-lz="${lz.compressToBase64(JSON.stringify(steps))}" :title='${JSON.stringify(title)}' :step-ranges='${JSON.stringify(ranges)}' />`;
1953
+ });
1954
+
1955
+ //#endregion
1956
+ //#region node/syntax/codeblock/mermaid.ts
1957
+ const RE_MERMAID = /^mermaid\s*(\{[^\n]*\})?/;
1958
+ var mermaid_default = defineCodeblockTransformer(async ({ info, code }) => {
1959
+ const match = info.match(RE_MERMAID);
1960
+ if (!match) return;
1961
+ const [, options] = match;
1962
+ return `<Mermaid ${options ? `v-bind="${options}"` : ""} code-lz="${lz.compressToBase64(code.trim())}" />`;
1963
+ });
1964
+
1965
+ //#endregion
1966
+ //#region node/syntax/codeblock/monaco.ts
1967
+ const RE_MONACO = /^([\w'-]+)?\s*\{(monaco[\w-]*)\}\s*(\{[^}]*\})?(.*)$/;
1968
+ const RE_DIFF_SEPARATOR = /^\s*~~~\s*\n/m;
1969
+ var monaco_default = defineCodeblockTransformer(async ({ info, code, options: { data: { config }, mode } }) => {
1970
+ const match = info.match(RE_MONACO);
1971
+ if (!match) return;
1972
+ const [, lang = "", monaco, options] = match;
1973
+ if (!(config.monaco === true || config.monaco === mode)) return;
1974
+ let encoded;
1975
+ let diff = "";
1976
+ if (monaco === "monaco-diff") {
1977
+ const [original, modified] = code.split(RE_DIFF_SEPARATOR, 2);
1978
+ encoded = lz.compressToBase64(original);
1979
+ diff = modified === void 0 ? "" : `diff-lz="${lz.compressToBase64(modified)}"`;
1980
+ } else encoded = lz.compressToBase64(code);
1981
+ return `<Monaco ${options ? `v-bind="${options}"` : ""} ${monaco === "monaco-run" ? "runnable" : ""} lang="${lang}" code-lz="${encoded}" ${diff} />`;
1982
+ });
1983
+
1984
+ //#endregion
1985
+ //#region node/syntax/codeblock/plant-uml.ts
1986
+ const RE_PLANT_UML = /^plantuml\s*(\{[^\n]*\})?/;
1987
+ var plant_uml_default = defineCodeblockTransformer(async ({ info, code, options: { data: { config: { plantUmlServer } } } }) => {
1988
+ const match = info.match(RE_PLANT_UML);
1989
+ if (!match) return;
1990
+ const [, options] = match;
1991
+ return `<PlantUml ${options ? `v-bind="${options}"` : ""} code="${encode(code.trim())}"${plantUmlServer === void 0 ? "" : ` server=${JSON.stringify(plantUmlServer)}`} />`;
1992
+ });
1993
+
1994
+ //#endregion
1995
+ //#region node/syntax/codeblock/wrapper.ts
1996
+ const RE_BLOCK_INFO = /^([\w'-]+)?(?:[ \t]*|[ \t][ \w\t'-]*)(?:\[([^\]]*)\])?[ \t]*(?:\{([\w,|\-*]+)\})?[ \t]*(\{[^}]*\})?(.*)$/;
1997
+ var wrapper_default = defineCodeblockTransformer(async ({ info, renderHighlighted }) => {
1998
+ const [, lang = "", title = "", rangeStr = "", options, rest = ""] = info.match(RE_BLOCK_INFO) ?? [];
1999
+ const ranges = normalizeRangeStr(rangeStr);
2000
+ const optionsProp = options ? `v-bind="${options}"` : "";
2001
+ const code = await renderHighlighted({ info: `${lang} ${rest}` });
2002
+ return `<CodeBlockWrapper ${optionsProp} title=${JSON.stringify(title)} :ranges='${JSON.stringify(ranges)}'>${escapeVueInCode(code)}</CodeBlockWrapper>`;
2003
+ });
2004
+
2005
+ //#endregion
2006
+ //#region node/syntax/codeblock/index.ts
2007
+ function MarkdownItCodeblocks(md, options, extraTransformers) {
2008
+ const oldFence = md.renderer.rules.fence;
2009
+ md.renderer.rules.fence = async function(tokens, idx, renderOptions, env, slf) {
2010
+ const token = tokens[idx];
2011
+ const slideNo = env.id?.match(regexSlideSourceId);
2012
+ const ctx = {
2013
+ info: token.info.trim(),
2014
+ code: token.content,
2015
+ fence: token.markup.length,
2016
+ slide: slideNo ? options.data.slides[slideNo[1] - 1] : null,
2017
+ options,
2018
+ renderHighlighted(override) {
2019
+ if (override.info != null) token.info = override.info;
2020
+ if (override.code != null) token.content = override.code;
2021
+ return oldFence(tokens, idx, renderOptions, env, slf);
1948
2022
  }
2023
+ };
2024
+ const transformers = [
2025
+ ...extraTransformers,
2026
+ mermaid_default,
2027
+ plant_uml_default,
2028
+ magic_move_default,
2029
+ monaco_default,
2030
+ wrapper_default
2031
+ ];
2032
+ for (const transformer of transformers) {
2033
+ if (!transformer) continue;
2034
+ const res = await transformer(ctx);
2035
+ if (res != null) return ensureSuffix("\n", res);
1949
2036
  }
1950
- ].filter(isTruthy);
1951
- return fromAsyncCodeToHtml(shiki.codeToHtml, {
1952
- ...shikiOptions,
1953
- transformers
1954
- });
2037
+ throw new Error("Should not reach here");
2038
+ };
1955
2039
  }
1956
2040
 
1957
2041
  //#endregion
1958
- //#region node/syntax/markdown-it/markdown-it-v-drag.ts
2042
+ //#region node/syntax/drag.ts
1959
2043
  const dragComponentRegex = /<(v-?drag-?\w*)([\s>])/i;
1960
2044
  const dragDirectiveRegex = /(?<![</\w])v-drag(=".*?")?/i;
1961
2045
  function MarkdownItVDrag(md, markdownTransformMap) {
1962
2046
  const visited = /* @__PURE__ */ new WeakSet();
1963
2047
  const sourceMapConsumers = /* @__PURE__ */ new WeakMap();
1964
- function getSourceMapConsumer(id) {
1965
- const s = markdownTransformMap.get(id);
2048
+ function getSourceMapConsumer(id$2) {
2049
+ const s = id$2 && markdownTransformMap.get(id$2);
1966
2050
  if (!s) return void 0;
1967
2051
  let smc = sourceMapConsumers.get(s);
1968
2052
  if (smc) return smc;
@@ -1976,7 +2060,7 @@ function MarkdownItVDrag(md, markdownTransformMap) {
1976
2060
  }
1977
2061
  const _parse = md.parse;
1978
2062
  md.parse = function(src, env) {
1979
- const smc = getSourceMapConsumer(env.id);
2063
+ const smc = getSourceMapConsumer(env?.id);
1980
2064
  const toOriginalPos = smc ? (line) => smc.originalPositionFor({
1981
2065
  line: line + 1,
1982
2066
  column: 0
@@ -2014,211 +2098,126 @@ function MarkdownItVDrag(md, markdownTransformMap) {
2014
2098
  }
2015
2099
 
2016
2100
  //#endregion
2017
- //#region node/syntax/markdown-it/index.ts
2018
- async function useMarkdownItPlugins(md, options, markdownTransformMap) {
2019
- const { data: { features, config }, utils: { katexOptions } } = options;
2020
- if (config.highlighter === "shiki") md.use(await MarkdownItShiki(options));
2021
- md.use(MarkdownItLink);
2022
- md.use(MarkdownItEscapeInlineCode);
2023
- md.use(MarkdownItFootnote);
2024
- md.use(taskLists, {
2025
- enabled: true,
2026
- lineNumber: true,
2027
- label: true
2028
- });
2029
- if (features.katex) md.use(MarkdownItKatex, katexOptions);
2030
- md.use(MarkdownItVDrag, markdownTransformMap);
2031
- if (config.comark || config.mdc) md.use(MarkdownItComark);
2032
- }
2033
-
2034
- //#endregion
2035
- //#region node/setups/transformers.ts
2036
- async function setupTransformers(roots) {
2037
- const returns = await loadSetups(roots, "transformers.ts", []);
2038
- const result = {
2039
- pre: [],
2040
- preCodeblock: [],
2041
- postCodeblock: [],
2042
- post: []
2101
+ //#region node/syntax/escape-code.ts
2102
+ const RE_CODE_TAG_OPEN = /^<code/;
2103
+ function MarkdownItEscapeInlineCode(md) {
2104
+ const codeInline = md.renderer.rules.code_inline;
2105
+ md.renderer.rules.code_inline = async (tokens, idx, options, env, self) => {
2106
+ return (await codeInline(tokens, idx, options, env, self)).replace(RE_CODE_TAG_OPEN, "<code v-pre");
2043
2107
  };
2044
- for (const r of [...returns].reverse()) {
2045
- if (r.pre) result.pre.push(...r.pre);
2046
- if (r.preCodeblock) result.preCodeblock.push(...r.preCodeblock);
2047
- }
2048
- for (const r of returns) {
2049
- if (r.postCodeblock) result.postCodeblock.push(...r.postCodeblock);
2050
- if (r.post) result.post.push(...r.post);
2051
- }
2052
- return result;
2053
- }
2054
-
2055
- //#endregion
2056
- //#region node/syntax/transform/code-wrapper.ts
2057
- const reCodeBlock = /^```([\w'-]+)?(?:[ \t]*|[ \t][ \w\t'-]*)(?:\[([^\]]*)\])?[ \t]*(?:\{([\w*,|-]+)\}[ \t]*(\{[^}]*\})?([^\r\n]*))?\r?\n((?:(?!^```)[\s\S])*?)^```$/gm;
2058
- /**
2059
- * Transform code block with wrapper
2060
- */
2061
- function transformCodeWrapper(ctx) {
2062
- ctx.s.replace(reCodeBlock, (full, lang = "", title = "", rangeStr = "", options = "", attrs = "", code) => {
2063
- const ranges = normalizeRangeStr(rangeStr);
2064
- code = code.trimEnd();
2065
- options = options.trim() || "{}";
2066
- return `\n<CodeBlockWrapper v-bind="${options}" :title='${JSON.stringify(title)}' :ranges='${JSON.stringify(ranges)}'>\n\n\`\`\`${lang}${title ? ` [${title}]` : ""}${attrs ? ` ${attrs.trim()}` : ""}\n${code}\n\`\`\`\n\n</CodeBlockWrapper>`;
2067
- });
2068
- }
2069
-
2070
- //#endregion
2071
- //#region node/syntax/transform/in-page-css.ts
2072
- /**
2073
- * Transform <style> in markdown to scoped style with page selector
2074
- */
2075
- function transformPageCSS(ctx) {
2076
- const codeBlocks = getCodeBlocks(ctx.s.original);
2077
- const commentBlocks = getCommentBlocks(ctx.s.original);
2078
- ctx.s.replace(/(\n<style[^>]*>)([\s\S]+?)(<\/style>)/g, (full, start, css, end, index) => {
2079
- if (codeBlocks.isInsideCodeblocks(index)) return full;
2080
- if (commentBlocks.isInsideCommentBlocks(index)) return ``;
2081
- if (!start.includes("scoped")) start = start.replace("<style", "<style scoped");
2082
- return `${start}\n${css}${end}`;
2083
- });
2084
2108
  }
2085
2109
 
2086
2110
  //#endregion
2087
- //#region node/syntax/transform/katex-wrapper.ts
2088
- /**
2089
- * Wrapper KaTex syntax `$$...$$` for highlighting
2090
- */
2091
- function transformKaTexWrapper(ctx) {
2092
- ctx.s.replace(/^\$\$(?:\s*\{([\w*,|-]+)\}\s*?(?:(\{[^}]*\})\s*?)?)?\n(\S[\s\S]*?)^\$\$/gm, (full, rangeStr = "", options = "", code) => {
2093
- const ranges = !rangeStr.trim() ? [] : rangeStr.trim().split(/\|/g).map((i) => i.trim());
2094
- code = code.trimEnd();
2095
- options = options.trim() || "{}";
2096
- return `<KaTexBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>\n\n\$\$\n${code}\n\$\$\n</KaTexBlockWrapper>\n`;
2097
- });
2111
+ //#region node/syntax/scoped.ts
2112
+ const RE_STYLE_TAG_OPEN = /<style\b([^>]*)>/gi;
2113
+ const RE_SCOPED_ATTR = /\bscoped\b/i;
2114
+ function MarkdownItStyleScoped(md) {
2115
+ const addScoped = (html) => {
2116
+ return html.replace(RE_STYLE_TAG_OPEN, (match, attrs) => {
2117
+ if (RE_SCOPED_ATTR.test(attrs)) return match;
2118
+ return `<style scoped${attrs}>`;
2119
+ });
2120
+ };
2121
+ const htmlBlock = md.renderer.rules.html_block;
2122
+ md.renderer.rules.html_block = async (...args) => addScoped(await htmlBlock(...args));
2123
+ const htmlInline = md.renderer.rules.html_inline;
2124
+ md.renderer.rules.html_inline = async (...args) => addScoped(await htmlInline(...args));
2098
2125
  }
2099
2126
 
2100
2127
  //#endregion
2101
- //#region node/syntax/transform/magic-move.ts
2102
- const reMagicMoveBlock = /^````(?:md|markdown) magic-move(?: *\[([^\]]*)\])?(?: *(\{[^}]*\}))? *([^\n]*)\n([\s\S]+?)^````\s*?$/gm;
2103
- function parseLineNumbersOption(options) {
2104
- return /lines: *true/.test(options) ? true : /lines: *false/.test(options) ? false : void 0;
2105
- }
2106
- /**
2107
- * Transform magic-move code blocks
2108
- */
2109
- async function transformMagicMove(ctx) {
2110
- const { codeToTokens } = ctx.options.utils.shiki;
2111
- const replacements = [];
2112
- ctx.s.replace(reMagicMoveBlock, (full, title = "", options = "{}", _attrs = "", body, start) => {
2113
- const end = start + full.length;
2114
- replacements.push([
2115
- start,
2116
- end,
2117
- worker()
2128
+ //#region node/syntax/shiki.ts
2129
+ async function MarkdownItShiki({ data: { config }, mode, utils: { shiki, shikiOptions } }) {
2130
+ async function getTwoslashTransformer() {
2131
+ const [, , { transformerTwoslash }] = await Promise.all([
2132
+ shiki.codeToHast("", {
2133
+ lang: "js",
2134
+ ...shikiOptions
2135
+ }),
2136
+ shiki.codeToHast("", {
2137
+ lang: "ts",
2138
+ ...shikiOptions
2139
+ }),
2140
+ import("@shikijs/vitepress-twoslash")
2118
2141
  ]);
2119
- return "";
2120
- async function worker() {
2121
- const matches = Array.from(body.matchAll(reCodeBlock));
2122
- if (!matches.length) throw new Error("Magic Move block must contain at least one code block");
2123
- const defaultLineNumbers = parseLineNumbersOption(options) ?? ctx.options.data.config.lineNumbers;
2124
- const ranges = matches.map((i) => normalizeRangeStr(i[3]));
2125
- const steps = await Promise.all(matches.map(async (i) => {
2126
- const lang = i[1];
2127
- const lineNumbers = parseLineNumbersOption(i[4]) ?? defaultLineNumbers;
2128
- const code = i[6].trimEnd();
2129
- const options$1 = {
2130
- ...ctx.options.utils.shikiOptions,
2131
- lang
2132
- };
2133
- const { tokens, bg, fg: fg$1, rootStyle, themeName } = await codeToTokens(code, options$1);
2134
- return {
2135
- ...toKeyedTokens(code, tokens, JSON.stringify([lang, "themes" in options$1 ? options$1.themes : options$1.theme]), lineNumbers),
2136
- bg,
2137
- fg: fg$1,
2138
- rootStyle,
2139
- themeName,
2140
- lang
2141
- };
2142
- }));
2143
- return `<ShikiMagicMove v-bind="${options}" steps-lz="${lz.compressToBase64(JSON.stringify(steps))}" :title='${JSON.stringify(title)}' :step-ranges='${JSON.stringify(ranges)}' />`;
2144
- }
2145
- });
2146
- for (const [start, end, content] of replacements) ctx.s.overwrite(start, end, await content);
2147
- }
2148
-
2149
- //#endregion
2150
- //#region node/syntax/transform/mermaid.ts
2151
- /**
2152
- * Transform Mermaid code blocks (render done on client side)
2153
- */
2154
- function transformMermaid(ctx) {
2155
- ctx.s.replace(/^```mermaid *(\{[^\n]*\})?\n([\s\S]+?)\n```/gm, (full, options = "", code = "") => {
2156
- code = code.trim();
2157
- options = options.trim() || "{}";
2158
- return `<Mermaid code-lz="${lz.compressToBase64(code)}" v-bind="${options}" />`;
2159
- });
2160
- }
2161
-
2162
- //#endregion
2163
- //#region node/syntax/transform/monaco.ts
2164
- function transformMonaco(ctx) {
2165
- if (!(ctx.options.data.config.monaco === true || ctx.options.data.config.monaco === ctx.options.mode)) {
2166
- ctx.s.replace(/\{monaco([\w:,-]*)\}/g, "");
2167
- return;
2142
+ return transformerTwoslash({
2143
+ explicitTrigger: true,
2144
+ twoslashOptions: { handbookOptions: { noErrorValidation: true } }
2145
+ });
2168
2146
  }
2169
- ctx.s.replace(/^```(\w+) *\{monaco-diff\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^~~~ *\n([\s\S]+?)^```/gm, (full, lang = "ts", options = "{}", code, diff) => {
2170
- lang = lang.trim();
2171
- options = options.trim() || "{}";
2172
- return `<Monaco code-lz="${lz.compressToBase64(code)}" diff-lz="${lz.compressToBase64(diff)}" lang="${lang}" v-bind="${options}" />`;
2173
- });
2174
- ctx.s.replace(/^```(\w+) *\{monaco\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^```/gm, (full, lang = "ts", options = "{}", code) => {
2175
- lang = lang.trim();
2176
- options = options.trim() || "{}";
2177
- return `<Monaco code-lz="${lz.compressToBase64(code)}" lang="${lang}" v-bind="${options}" />`;
2178
- });
2179
- ctx.s.replace(/^```(\w+) *\{monaco-run\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^```/gm, (full, lang = "ts", options = "{}", code) => {
2180
- lang = lang.trim();
2181
- options = options.trim() || "{}";
2182
- return `<Monaco runnable code-lz="${lz.compressToBase64(code)}" lang="${lang}" v-bind="${options}" />`;
2147
+ const transformers = [
2148
+ ...shikiOptions.transformers || [],
2149
+ (config.twoslash === true || config.twoslash === mode) && await getTwoslashTransformer(),
2150
+ (config.twoslash === true || config.twoslash === mode) && transformerTwoslashConditional(),
2151
+ { pre(pre) {
2152
+ this.addClassToHast(pre, "slidev-code");
2153
+ delete pre.properties.tabindex;
2154
+ } }
2155
+ ].filter(isTruthy);
2156
+ return fromAsyncCodeToHtml(shiki.codeToHtml, {
2157
+ ...shikiOptions,
2158
+ transformers
2183
2159
  });
2184
2160
  }
2185
-
2186
- //#endregion
2187
- //#region node/syntax/transform/plant-uml.ts
2188
- function transformPlantUml(ctx) {
2189
- const server = ctx.options.data.config.plantUmlServer;
2190
- ctx.s.replace(/^```plantuml[^\n{}]*(\{[^}\n]*\})?\n([\s\S]+?)\n```/gm, (full, options = "", content = "") => {
2191
- const code = encode(content.trim());
2192
- options = options.trim() || "{}";
2193
- return `<PlantUml :code="'${code}'" :server="'${server}'" v-bind="${options}" />`;
2194
- });
2161
+ function transformerTwoslashConditional() {
2162
+ return {
2163
+ name: "slidev:twoslash-conditional",
2164
+ code: function applyConditionalFlag(node) {
2165
+ if (node.tagName === "v-menu") {
2166
+ if (node.properties[":shown"] === "true") node.properties[":shown"] = "$nav.currentPage === $page";
2167
+ } else for (const child of node.children) if (child.type === "element") applyConditionalFlag(child);
2168
+ }
2169
+ };
2195
2170
  }
2196
2171
 
2197
2172
  //#endregion
2198
- //#region node/syntax/transform/slot-sugar.ts
2199
- function transformSlotSugar(ctx) {
2200
- const linesWithNewline = ctx.s.original.split(/(\r?\n)/g);
2201
- const codeBlocks = getCodeBlocks(ctx.s.original);
2202
- const commentBlocks = getCommentBlocks(ctx.s.original);
2203
- const lines = [];
2204
- for (let i = 0; i < linesWithNewline.length; i += 2) {
2205
- const line = linesWithNewline[i];
2206
- const newline = linesWithNewline[i + 1] || "";
2207
- lines.push(line + newline);
2208
- }
2209
- let prevSlot = false;
2210
- let offset = 0;
2211
- lines.forEach((line) => {
2212
- const start = offset;
2213
- offset += line.length;
2214
- if (codeBlocks.isInsideCodeblocks(offset) || commentBlocks.isInsideCommentBlocks(offset)) return;
2215
- const match = line.match(/^::\s*([\w.\-:]+)\s*::(\s*)$/);
2216
- if (match) {
2217
- ctx.s.overwrite(start, offset - match[2].length, `${prevSlot ? "\n\n</template>\n" : "\n"}<template v-slot:${match[1]}="slotProps">\n`);
2218
- prevSlot = true;
2173
+ //#region node/syntax/slot-sugar.ts
2174
+ const RE_SLOT_MARKER = /^::\s*([\w.\-:]+)\s*::\s*$/;
2175
+ function MarkdownItSlotSugar(md) {
2176
+ md.block.ruler.before("fence", "slot_marker", (state, startLine, _endLine, silent) => {
2177
+ if (state.sCount[startLine] - state.blkIndent > 0) return false;
2178
+ const pos = state.bMarks[startLine] + state.tShift[startLine];
2179
+ const max = state.eMarks[startLine];
2180
+ const match = state.src.slice(pos, max).match(RE_SLOT_MARKER);
2181
+ if (!match) return false;
2182
+ if (silent) return true;
2183
+ const token = state.push("slot_marker", "", 0);
2184
+ token.meta = { slotName: match[1] };
2185
+ token.map = [startLine, startLine + 1];
2186
+ state.line = startLine + 1;
2187
+ state.env.hasSlotMarker = true;
2188
+ return true;
2189
+ }, { alt: [
2190
+ "paragraph",
2191
+ "reference",
2192
+ "blockquote",
2193
+ "list"
2194
+ ] });
2195
+ md.core.ruler.push("slot_sugar_compiler", (state) => {
2196
+ if (!state.env.hasSlotMarker) return;
2197
+ const tokens = state.tokens;
2198
+ const newTokens = [];
2199
+ let hasOpenSlot = false;
2200
+ for (let i = 0; i < tokens.length; i++) {
2201
+ const token = tokens[i];
2202
+ if (token.type === "slot_marker") {
2203
+ if (hasOpenSlot) {
2204
+ const closeHtml = new state.Token("html_block", "", 0);
2205
+ closeHtml.content = "\n</template>\n";
2206
+ newTokens.push(closeHtml);
2207
+ }
2208
+ const openHtml = new state.Token("html_block", "", 0);
2209
+ openHtml.content = `\n<template v-slot:${token.meta.slotName}="slotProps">\n`;
2210
+ newTokens.push(openHtml);
2211
+ hasOpenSlot = true;
2212
+ } else newTokens.push(token);
2213
+ }
2214
+ if (hasOpenSlot) {
2215
+ const closeHtml = new state.Token("html_block", "", 0);
2216
+ closeHtml.content = "\n</template>\n";
2217
+ newTokens.push(closeHtml);
2219
2218
  }
2219
+ state.tokens = newTokens;
2220
2220
  });
2221
- if (prevSlot) ctx.s.append("\n\n</template>");
2222
2221
  }
2223
2222
 
2224
2223
  //#endregion
@@ -2254,7 +2253,8 @@ function createMonacoWriterPlugin({ userRoot }) {
2254
2253
  }
2255
2254
 
2256
2255
  //#endregion
2257
- //#region node/syntax/transform/snippet.ts
2256
+ //#region node/syntax/snippet.ts
2257
+ const RE_NEWLINE = /\r?\n/;
2258
2258
  function dedent(text) {
2259
2259
  const lines = text.split("\n");
2260
2260
  const minIndentLength = lines.reduce((acc, line) => {
@@ -2327,76 +2327,96 @@ function findRegion(lines, regionName) {
2327
2327
  }
2328
2328
  return null;
2329
2329
  }
2330
- const reMonacoWrite = /^\{monaco-write\}/;
2331
- /**
2332
- * format: ">>> /path/to/file.extension#region language meta..."
2333
- * where #region, language and meta are optional
2334
- * meta should starts with {
2335
- * lang can contain special characters like C++, C#, F#, etc.
2336
- * path can be relative to the current file or absolute
2337
- * file extension is optional
2338
- * path can contain spaces and dots
2339
- *
2340
- * captures: ['/path/to/file.extension', '#region', 'language', '{meta}']
2341
- */
2342
- function transformSnippet({ s, slide, options }) {
2343
- const watchFiles = options.data.watchFiles;
2344
- const dir = path.dirname(slide.source?.filepath ?? options.entry ?? options.userRoot);
2345
- s.replace(/^<<<[ \t]*(\S.*?)(#[\w-]+)?[ \t]*(?:[ \t](\S+?))?[ \t]*(\{.*)?$/gm, (full, filepath = "", regionName = "", lang = "", meta = "") => {
2346
- const src = slash(/^@\//.test(filepath) ? path.resolve(options.userRoot, filepath.slice(2)) : path.resolve(dir, filepath));
2330
+ const RE_SNIPPET_IMPORT = /^<<<[ \t]*(\S.*?)(#[\w-]+)?[ \t]*(?:[ \t](\S+?))?[ \t]*(\{.*)?$/;
2331
+ function MarkdownItSnippet(md, { userRoot, data: { watchFiles, slides } }) {
2332
+ md.block.ruler.before("fence", "snippet_import", (state, startLine, _endLine, silent) => {
2333
+ const pos = state.bMarks[startLine] + state.tShift[startLine];
2334
+ const max = state.eMarks[startLine];
2335
+ const match = state.src.slice(pos, max).match(RE_SNIPPET_IMPORT);
2336
+ if (!match) return false;
2337
+ if (silent) return true;
2338
+ let [, filepath = "", regionName = "", lang = "", meta = ""] = match;
2339
+ const slideNo = state.env.id?.match(regexSlideSourceId);
2340
+ const slide = slideNo ? slides[slideNo[1] - 1] : null;
2341
+ if (!slide) {
2342
+ console.warn(yellow(`[markdown-it-snippet] Snippet syntax is not supported in ${state.env.id || "unknown source"}. Skipped.`));
2343
+ return false;
2344
+ }
2345
+ const dir = path.dirname(slide.source.filepath);
2346
+ const src = slash(filepath.startsWith("@/") ? path.resolve(userRoot, filepath.slice(2)) : path.resolve(dir, filepath));
2347
+ lang = lang.trim() || path.extname(filepath).slice(1);
2347
2348
  meta = meta.trim();
2348
- lang = lang.trim();
2349
- lang = lang || path.extname(filepath).slice(1);
2350
- const isAFile = fs.statSync(src).isFile();
2351
- if (!fs.existsSync(src) || !isAFile) throw new Error(isAFile ? `Code snippet path not found: ${src}` : `Invalid code snippet option`);
2349
+ if (!(fs.existsSync(src) && fs.statSync(src).isFile())) throw new Error(`Code snippet path not found: ${src}`);
2352
2350
  let content = fs.readFileSync(src, "utf8");
2353
2351
  if (regionName) {
2354
- const lines = content.split(/\r?\n/);
2352
+ const lines = content.split(RE_NEWLINE);
2355
2353
  const region = findRegion(lines, regionName.slice(1));
2356
2354
  if (region) content = dedent(lines.slice(region.start, region.end).filter((l) => !(region.re.start.test(l) || region.re.end.test(l))).join("\n"));
2357
2355
  }
2358
- if (meta.match(reMonacoWrite)) {
2356
+ if (meta.includes("{monaco-write}")) {
2359
2357
  monacoWriterWhitelist.add(filepath);
2360
2358
  lang = lang.trim();
2361
- meta = meta.replace(reMonacoWrite, "").trim() || "{}";
2359
+ meta = meta.replace("{monaco-write}", "").trim() || "{}";
2360
+ const safeFilepath = JSON.stringify(filepath).slice(1, -1);
2362
2361
  const encoded = lz.compressToBase64(content);
2363
- return `<Monaco writable=${JSON.stringify(filepath)} code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />`;
2362
+ const token = state.push("html_block", "", 0);
2363
+ token.content = `<Monaco writable="${safeFilepath}" code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />\n`;
2364
+ token.map = [startLine, startLine + 1];
2364
2365
  } else {
2365
2366
  watchFiles[src] ??= /* @__PURE__ */ new Set();
2366
2367
  watchFiles[src].add(slide.index);
2368
+ const token = state.push("fence", "code", 0);
2369
+ token.info = `${lang} ${meta}`.trim();
2370
+ token.content = content.endsWith("\n") ? content : `${content}\n`;
2371
+ token.map = [startLine, startLine + 1];
2367
2372
  }
2368
- return `\`\`\`${lang} ${meta}\n${content}\n\`\`\``;
2373
+ state.line = startLine + 1;
2374
+ return true;
2375
+ }, { alt: [
2376
+ "paragraph",
2377
+ "reference",
2378
+ "blockquote",
2379
+ "list"
2380
+ ] });
2381
+ }
2382
+
2383
+ //#endregion
2384
+ //#region node/syntax/index.ts
2385
+ async function useMarkdownItPlugins(md, options, markdownTransformMap, codeblockTransformers) {
2386
+ const { data: { features, config }, utils: { katexOptions } } = options;
2387
+ md.use(MarkdownItSnippet, options);
2388
+ md.use(await MarkdownItShiki(options));
2389
+ md.use(MarkdownItCodeblocks, options, codeblockTransformers);
2390
+ md.use(MarkdownItLink);
2391
+ md.use(MarkdownItEscapeInlineCode);
2392
+ md.use(MarkdownItFootnote);
2393
+ md.use(taskLists, {
2394
+ enabled: true,
2395
+ lineNumber: true,
2396
+ label: true
2369
2397
  });
2398
+ if (features.katex) md.use(MarkdownItKatex, katexOptions);
2399
+ md.use(MarkdownItVDrag, markdownTransformMap);
2400
+ md.use(MarkdownItSlotSugar);
2401
+ if (config.comark || config.mdc) md.use(MarkdownItComark);
2402
+ md.use(MarkdownItStyleScoped);
2403
+ md.use(MarkdownItGitHubAlerts);
2370
2404
  }
2371
2405
 
2372
2406
  //#endregion
2373
- //#region node/syntax/transform/index.ts
2374
- async function getMarkdownTransformers(options) {
2407
+ //#region node/vite/markdown.ts
2408
+ const RE_MD_EXT$1 = /\.md$/;
2409
+ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
2410
+ const markdownTransformMap = /* @__PURE__ */ new Map();
2375
2411
  const extras = await setupTransformers(options.roots);
2376
- return [
2412
+ const transformers = [
2377
2413
  ...extras.pre,
2378
- transformSnippet,
2379
- options.data.config.highlighter === "shiki" && transformMagicMove,
2380
2414
  ...extras.preCodeblock,
2381
- transformMermaid,
2382
- transformPlantUml,
2383
- options.data.features.monaco && transformMonaco,
2384
2415
  ...extras.postCodeblock,
2385
- transformCodeWrapper,
2386
- options.data.features.katex && transformKaTexWrapper,
2387
- transformPageCSS,
2388
- transformSlotSugar,
2389
2416
  ...extras.post
2390
2417
  ];
2391
- }
2392
-
2393
- //#endregion
2394
- //#region node/vite/markdown.ts
2395
- async function createMarkdownPlugin(options, { markdown: mdOptions }) {
2396
- const markdownTransformMap = /* @__PURE__ */ new Map();
2397
- const transformers = await getMarkdownTransformers(options);
2398
2418
  return Markdown({
2399
- include: [/\.md$/],
2419
+ include: [RE_MD_EXT$1],
2400
2420
  wrapperClasses: "",
2401
2421
  headEnabled: false,
2402
2422
  frontmatter: false,
@@ -2410,18 +2430,18 @@ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
2410
2430
  },
2411
2431
  ...mdOptions,
2412
2432
  async markdownSetup(md) {
2413
- await useMarkdownItPlugins(md, options, markdownTransformMap);
2433
+ await useMarkdownItPlugins(md, options, markdownTransformMap, extras.codeblocks);
2414
2434
  await mdOptions?.markdownSetup?.(md);
2415
2435
  },
2416
2436
  transforms: {
2417
2437
  ...mdOptions?.transforms,
2418
- async before(code, id) {
2419
- if (options.data.markdownFiles[id]) return "";
2420
- code = await mdOptions?.transforms?.before?.(code, id) ?? code;
2421
- const match = id.match(regexSlideSourceId);
2438
+ async before(code, id$2) {
2439
+ if (options.data.markdownFiles[id$2]) return "";
2440
+ code = await mdOptions?.transforms?.before?.(code, id$2) ?? code;
2441
+ const match = id$2.match(regexSlideSourceId);
2422
2442
  if (!match) return code;
2423
2443
  const s = new MagicString(code);
2424
- markdownTransformMap.set(id, s);
2444
+ markdownTransformMap.set(id$2, s);
2425
2445
  const ctx = {
2426
2446
  s,
2427
2447
  slide: options.data.slides[+match[1] - 1],
@@ -2443,13 +2463,13 @@ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
2443
2463
  function createMonacoTypesLoader({ userRoot, utils }) {
2444
2464
  return {
2445
2465
  name: "slidev:monaco-types-loader",
2446
- resolveId(id) {
2447
- if (id.startsWith("/@slidev-monaco-types/")) return id;
2466
+ resolveId(id$2) {
2467
+ if (id$2.startsWith("/@slidev-monaco-types/")) return id$2;
2448
2468
  return null;
2449
2469
  },
2450
- async load(id) {
2451
- if (!id.startsWith("/@slidev-monaco-types/")) return null;
2452
- const url = new URL(id, "http://localhost");
2470
+ async load(id$2) {
2471
+ if (!id$2.startsWith("/@slidev-monaco-types/")) return null;
2472
+ const url = new URL(id$2, "http://localhost");
2453
2473
  if (url.pathname === "/@slidev-monaco-types/resolve") {
2454
2474
  const query = new URLSearchParams(url.search);
2455
2475
  const pkg = query.get("pkg");
@@ -2502,9 +2522,9 @@ function createPatchMonacoSourceMapPlugin() {
2502
2522
  return {
2503
2523
  name: "slidev:patch-monaco-sourcemap",
2504
2524
  enforce: "pre",
2505
- load(id) {
2506
- if (!id.includes("node_modules/monaco-editor/esm/vs/base")) return;
2507
- return readFile(cleanUrl(id), "utf-8");
2525
+ load(id$2) {
2526
+ if (!id$2.includes("node_modules/monaco-editor/esm/vs/base")) return;
2527
+ return readFile(cleanUrl(id$2), "utf-8");
2508
2528
  }
2509
2529
  };
2510
2530
  }
@@ -2515,7 +2535,7 @@ async function createRemoteAssetsPlugin({ data: { config }, mode }, pluginOption
2515
2535
  if (!(config.remoteAssets === true || config.remoteAssets === mode)) return;
2516
2536
  const { VitePluginRemoteAssets, DefaultRules } = await import("vite-plugin-remote-assets");
2517
2537
  return VitePluginRemoteAssets({
2518
- resolveMode: (id) => id.endsWith("index.html") ? "relative" : "@fs",
2538
+ resolveMode: (id$2) => id$2.endsWith("index.html") ? "relative" : "@fs",
2519
2539
  awaitDownload: mode === "build",
2520
2540
  ...pluginOptions.remoteAssets,
2521
2541
  rules: [
@@ -2669,6 +2689,11 @@ async function createUnocssPlugin(options, pluginOptions) {
2669
2689
 
2670
2690
  //#endregion
2671
2691
  //#region node/vite/vue.ts
2692
+ const RE_VUE_EXT = /\.vue$/;
2693
+ const RE_VUE_QUERY_VUE = /\.vue\?vue/;
2694
+ const RE_VUE_QUERY_V = /\.vue\?v=/;
2695
+ const RE_MD_EXT = /\.md$/;
2696
+ const RE_MD_QUERY_VUE = /\.md\?vue/;
2672
2697
  const customElements = new Set([
2673
2698
  "annotation",
2674
2699
  "math",
@@ -2702,11 +2727,11 @@ async function createVuePlugin(_options, pluginOptions) {
2702
2727
  const { vue: vueOptions = {}, vuejsx: vuejsxOptions = {} } = pluginOptions;
2703
2728
  const VuePlugin = Vue({
2704
2729
  include: [
2705
- /\.vue$/,
2706
- /\.vue\?vue/,
2707
- /\.vue\?v=/,
2708
- /\.md$/,
2709
- /\.md\?vue/
2730
+ RE_VUE_EXT,
2731
+ RE_VUE_QUERY_VUE,
2732
+ RE_VUE_QUERY_V,
2733
+ RE_MD_EXT,
2734
+ RE_MD_QUERY_VUE
2710
2735
  ],
2711
2736
  exclude: [],
2712
2737
  ...vueOptions,