@slidev/cli 52.14.2 → 52.15.1

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,12 +1,12 @@
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
- import path, { basename, dirname, join, relative, resolve } from "node:path";
3
+ import path, { basename, dirname, join, relative, resolve, win32 } from "node:path";
4
4
  import { loadConfigFromFile, mergeConfig } from "vite";
5
5
  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,21 @@ 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;
73
+ const RE_WINDOWS_DRIVE = /^[A-Z]:\//i;
66
74
  let jiti;
67
75
  function loadModule(absolutePath) {
68
76
  jiti ??= createJiti(fileURLToPath(import.meta.url), { moduleCache: false });
69
77
  return jiti.import(absolutePath);
70
78
  }
71
79
  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(" ");
80
+ 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
81
  }
74
82
  function generateFontParams(options) {
75
83
  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("&");
84
+ return options.webfonts.map((i) => `family=${i.replace(RE_QUOTED_STRING, "$1").replace(RE_WHITESPACE, "+")}:${options.italic ? "ital," : ""}wght@${weights}`).join("&");
77
85
  }
78
86
  function generateGoogleFontsUrl(options) {
79
87
  return `https://fonts.googleapis.com/css2?${generateFontParams(options)}&display=swap`;
@@ -124,12 +132,21 @@ function getBodyJson(req) {
124
132
  });
125
133
  });
126
134
  }
127
- function makeAbsoluteImportGlob(userRoot, globs, options = {}) {
128
- const relativeGlobs = globs.map((glob) => `./${slash(relative(userRoot, glob))}`);
135
+ function getImportGlobRelativePath(from, to) {
136
+ const normalizedFrom = slash(from);
137
+ const normalizedTo = slash(to);
138
+ return slash(RE_WINDOWS_DRIVE.test(normalizedFrom) || RE_WINDOWS_DRIVE.test(normalizedTo) ? win32.relative(normalizedFrom, normalizedTo) : relative(normalizedFrom, normalizedTo));
139
+ }
140
+ function makeAbsoluteImportGlob(self, globs, options = {}, baseRoot) {
141
+ const root = baseRoot && globs.some((glob) => RE_WINDOWS_DRIVE.test(slash(glob))) ? baseRoot : void 0;
142
+ const relativeGlobs = globs.map((glob) => {
143
+ const relativeGlob = getImportGlobRelativePath(root ?? self, glob);
144
+ return root && !relativeGlob.startsWith(".") && !RE_WINDOWS_DRIVE.test(relativeGlob) ? `./${relativeGlob}` : relativeGlob;
145
+ });
129
146
  const opts = {
130
147
  eager: true,
131
148
  exhaustive: true,
132
- base: "/",
149
+ ...root ? { base: "/" } : {},
133
150
  ...options
134
151
  };
135
152
  return `import.meta.glob(${JSON.stringify(relativeGlobs)}, ${JSON.stringify(opts)})`;
@@ -155,6 +172,7 @@ async function setupVitePlugins(options) {
155
172
 
156
173
  //#endregion
157
174
  //#region node/vite/compilerFlagsVue.ts
175
+ const RE_VUE_FILE = /\.vue(?:$|\?)/;
158
176
  /**
159
177
  * Replace compiler flags like `__DEV__` in Vue SFC
160
178
  */
@@ -163,8 +181,8 @@ function createVueCompilerFlagsPlugin(options) {
163
181
  return {
164
182
  name: "slidev:flags",
165
183
  enforce: "pre",
166
- transform: { handler(code, id) {
167
- if (!id.match(/\.vue($|\?)/) && !id.includes("?vue&")) return;
184
+ transform: { handler(code, id$2) {
185
+ if (!RE_VUE_FILE.test(id$2) && !id$2.includes("?vue&")) return;
168
186
  const original = code;
169
187
  define.forEach(([from, to]) => {
170
188
  code = code.replaceAll(from, to);
@@ -176,6 +194,11 @@ function createVueCompilerFlagsPlugin(options) {
176
194
 
177
195
  //#endregion
178
196
  //#region node/vite/components.ts
197
+ const RE_VUE_EXT$1 = /\.vue$/;
198
+ const RE_VUE_QUERY_VUE$1 = /\.vue\?vue/;
199
+ const RE_VUE_QUERY_V$1 = /\.vue\?v=/;
200
+ const RE_MD_EXT$2 = /\.md$/;
201
+ const RE_MD_QUERY_VUE$1 = /\.md\?vue/;
179
202
  function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
180
203
  return Components({
181
204
  extensions: [
@@ -189,11 +212,11 @@ function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
189
212
  dirs: [join(clientRoot, "builtin"), ...roots.map((i) => join(i, "components"))],
190
213
  globsExclude: [],
191
214
  include: [
192
- /\.vue$/,
193
- /\.vue\?vue/,
194
- /\.vue\?v=/,
195
- /\.md$/,
196
- /\.md\?vue/
215
+ RE_VUE_EXT$1,
216
+ RE_VUE_QUERY_VUE$1,
217
+ RE_VUE_QUERY_V$1,
218
+ RE_MD_EXT$2,
219
+ RE_MD_QUERY_VUE$1
197
220
  ],
198
221
  exclude: [],
199
222
  resolvers: [IconsResolver({
@@ -216,14 +239,16 @@ const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $pa
216
239
 
217
240
  //#endregion
218
241
  //#region node/vite/contextInjection.ts
242
+ const RE_EXPORT_DEFAULT_OBJECT = /export\s+default\s+\{/;
243
+ const RE_INJECT_OPTION = /.*inject\s*:\s*([[{])/;
219
244
  /**
220
245
  * Inject `$slidev` into the script block of a Vue component
221
246
  */
222
247
  function createContextInjectionPlugin() {
223
248
  return {
224
249
  name: "slidev:context-injection",
225
- transform: { async handler(code, id) {
226
- if (!id.endsWith(".vue") || id.includes("/@slidev/client/") || id.includes("/packages/client/")) return;
250
+ transform: { async handler(code, id$2) {
251
+ if (!id$2.endsWith(".vue") || id$2.includes("/@slidev/client/") || id$2.includes("/packages/client/")) return;
227
252
  if (code.includes(templateInjectionMarker) || code.includes("useSlideContext()")) return code;
228
253
  const imports = [
229
254
  templateImportContextUtils,
@@ -237,7 +262,7 @@ function createContextInjectionPlugin() {
237
262
  const setupTagEnd = (setupScriptMatch.index || 0) + setupTag.length;
238
263
  return `${code.slice(0, setupTagEnd)}\n${imports.join("\n")}\n${code.slice(setupTagEnd)}`;
239
264
  } else if (!setupScriptMatch && matchScripts.length === 1) {
240
- const matchExport = code.match(/export\s+default\s+\{/);
265
+ const matchExport = code.match(RE_EXPORT_DEFAULT_OBJECT);
241
266
  if (matchExport) {
242
267
  const exportIndex = (matchExport.index || 0) + matchExport[0].length;
243
268
  let component = code.slice(exportIndex);
@@ -248,7 +273,7 @@ import { injectionSlidevContext } from "@slidev/client/constants.ts"
248
273
  ${code.slice(scriptIndex)}`;
249
274
  let injectIndex = exportIndex + 70;
250
275
  let injectObject = "$slidev: { from: injectionSlidevContext },";
251
- const matchInject = component.match(/.*inject\s*:\s*([[{])/);
276
+ const matchInject = component.match(RE_INJECT_OPTION);
252
277
  if (matchInject) {
253
278
  injectIndex += (matchInject.index || 0) + matchInject[0].length;
254
279
  if (matchInject[1] === "[") {
@@ -269,6 +294,10 @@ ${code.slice(scriptIndex)}`;
269
294
 
270
295
  //#endregion
271
296
  //#region node/vite/extendConfig.ts
297
+ const RE_SLIDEV_CLIENT = /^@slidev\/client$/;
298
+ const RE_SLIDEV_CLIENT_SUBPATH = /^@slidev\/client\/(.*)/;
299
+ const RE_SLIDEV_VIRTUAL = /^#slidev\/(.*)/;
300
+ const RE_MONACO_EDITOR = /\/monaco-editor(?:-core)?\//;
272
301
  const INCLUDE_GLOBAL = [
273
302
  "@typescript/ata",
274
303
  "file-saver",
@@ -335,15 +364,15 @@ function createConfigPlugin(options) {
335
364
  resolve: {
336
365
  alias: [
337
366
  {
338
- find: /^@slidev\/client$/,
367
+ find: RE_SLIDEV_CLIENT,
339
368
  replacement: `${toAtFS(options.clientRoot)}/index.ts`
340
369
  },
341
370
  {
342
- find: /^@slidev\/client\/(.*)/,
371
+ find: RE_SLIDEV_CLIENT_SUBPATH,
343
372
  replacement: `${toAtFS(options.clientRoot)}/$1`
344
373
  },
345
374
  {
346
- find: /^#slidev\/(.*)/,
375
+ find: RE_SLIDEV_VIRTUAL,
347
376
  replacement: "/@slidev/$1"
348
377
  },
349
378
  {
@@ -380,21 +409,21 @@ function createConfigPlugin(options) {
380
409
  const DEFAULT = "assets/[name]-[hash].js";
381
410
  if (chunkInfo.name.includes("/")) return DEFAULT;
382
411
  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";
412
+ if (chunkInfo.moduleIds.filter((i) => i.match(RE_MONACO_EDITOR)).length > chunkInfo.moduleIds.length * .6) return "assets/monaco/[name]-[hash].js";
384
413
  return DEFAULT;
385
414
  },
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}`));
415
+ manualChunks(id$2) {
416
+ if (id$2.startsWith("/@slidev-monaco-types/") || id$2.includes("/@slidev/monaco-types") || id$2.endsWith("?monaco-types&raw")) return "monaco/bundled-types";
417
+ if (id$2.includes("/shiki/") || id$2.includes("/@shikijs/")) return `modules/shiki`;
418
+ if (id$2.startsWith("~icons/")) return "modules/unplugin-icons";
419
+ const matchedAsyncModule = ASYNC_MODULES.find((i) => id$2.includes(`/node_modules/${i}`));
391
420
  if (matchedAsyncModule) return `modules/${matchedAsyncModule.replace("@", "").replace("/", "-")}`;
392
421
  }
393
422
  } } },
394
423
  cacheDir: isInstalledGlobally.value ? join(options.cliRoot, "node_modules/.vite") : void 0
395
424
  };
396
- function isSlidevClient(id) {
397
- return id.includes("/@slidev/") || id.includes("/slidev/packages/client/") || id.includes("/@vueuse/");
425
+ function isSlidevClient(id$2) {
426
+ return id$2.includes("/@slidev/") || id$2.includes("/slidev/packages/client/") || id$2.includes("/@vueuse/");
398
427
  }
399
428
  return mergeConfig(injection, config);
400
429
  },
@@ -426,8 +455,8 @@ function createHmrPatchPlugin() {
426
455
  name: "slidev:hmr-patch",
427
456
  transform: {
428
457
  filter: { id: { include: regexSlideSourceId } },
429
- handler(code, id) {
430
- if (!id.match(regexSlideSourceId)) return;
458
+ handler(code, id$2) {
459
+ if (!id$2.match(regexSlideSourceId)) return;
431
460
  return code.replace("if (_rerender_only)", "if (false)");
432
461
  }
433
462
  }
@@ -458,13 +487,14 @@ async function createInspectPlugin(options, pluginOptions) {
458
487
 
459
488
  //#endregion
460
489
  //#region node/vite/layoutWrapper.ts
490
+ const RE_SCRIPT_SETUP_TAG = /^<script setup.*>/m;
461
491
  function createLayoutWrapperPlugin({ data, utils }) {
462
492
  return {
463
493
  name: "slidev:layout-wrapper",
464
494
  transform: {
465
495
  filter: { id: { include: regexSlideSourceId } },
466
- async handler(code, id) {
467
- const match = id.match(regexSlideSourceId);
496
+ async handler(code, id$2) {
497
+ const match = id$2.match(regexSlideSourceId);
468
498
  if (!match) return;
469
499
  const [, no, type] = match;
470
500
  if (type !== "md") return;
@@ -476,7 +506,7 @@ function createLayoutWrapperPlugin({ data, utils }) {
476
506
  console.error();
477
507
  layoutName = "default";
478
508
  }
479
- const setupTag = code.match(/^<script setup.*>/m);
509
+ const setupTag = code.match(RE_SCRIPT_SETUP_TAG);
480
510
  if (!setupTag) throw new Error(`[Slidev] Internal error: <script setup> block not found in slide ${index + 1}.`);
481
511
  const templatePart = code.slice(0, setupTag.index);
482
512
  const scriptPart = code.slice(setupTag.index);
@@ -503,7 +533,7 @@ function createLayoutWrapperPlugin({ data, utils }) {
503
533
 
504
534
  //#endregion
505
535
  //#region package.json
506
- var version = "52.14.2";
536
+ var version = "52.15.1";
507
537
 
508
538
  //#endregion
509
539
  //#region node/integrations/addons.ts
@@ -545,6 +575,8 @@ async function getThemeMeta(name, root) {
545
575
 
546
576
  //#endregion
547
577
  //#region node/setups/indexHtml.ts
578
+ const RE_TRAILING_SLASH = /\/$/;
579
+ const RE_BODY_CONTENT = /<body>([\s\S]*?)<\/body>/i;
548
580
  function escapeHtml(str) {
549
581
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
550
582
  }
@@ -555,7 +587,7 @@ function collectPreloadImages(data, base) {
555
587
  if (data.config.preloadImages === false) return [];
556
588
  const seen = /* @__PURE__ */ new Set();
557
589
  const links = [];
558
- const basePrefix = base ? base.replace(/\/$/, "") : "";
590
+ const basePrefix = base ? base.replace(RE_TRAILING_SLASH, "") : "";
559
591
  for (const slide of data.slides) {
560
592
  const images = slide.images || slide.source?.images;
561
593
  if (!images?.length) continue;
@@ -585,10 +617,11 @@ async function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data,
585
617
  console.error(yellow("This file may be generated by Slidev, please remove it from your project."));
586
618
  continue;
587
619
  }
588
- inputs.push(extractUnheadInputFromHtml(html).input);
589
- body += `\n${(html.match(/<body>([\s\S]*?)<\/body>/i)?.[1] || "").trim()}`;
620
+ inputs.push(parseHtmlForUnheadExtraction(html).input);
621
+ body += `\n${(html.match(RE_BODY_CONTENT)?.[1] || "").trim()}`;
590
622
  }
591
623
  if (data.features.tweet) body += "\n<script async src=\"https://platform.twitter.com/widgets.js\"><\/script>";
624
+ if (data.features.bluesky) body += "\n<script async src=\"https://embed.bsky.app/static/embed.js\" charset=\"utf-8\"><\/script>";
592
625
  const webFontsLink = [];
593
626
  if (data.config.fonts.webfonts.length) {
594
627
  const { provider } = data.config.fonts;
@@ -799,6 +832,7 @@ async function setupShiki(roots) {
799
832
 
800
833
  //#endregion
801
834
  //#region node/options.ts
835
+ const RE_FILE_EXTENSION = /\.\w+$/;
802
836
  const debug = createDebug("slidev:options");
803
837
  async function resolveOptions(entryOptions, mode) {
804
838
  const entry = await resolveEntry(entryOptions.entry);
@@ -817,6 +851,7 @@ async function resolveOptions(entryOptions, mode) {
817
851
  rootsInfo.userRoot
818
852
  ]);
819
853
  if (entryOptions.download) config.download ||= entryOptions.download;
854
+ if (entryOptions.routerMode) config.routerMode = entryOptions.routerMode;
820
855
  debug({
821
856
  ...rootsInfo,
822
857
  ...entryOptions,
@@ -875,7 +910,7 @@ async function createDataUtils(resolved) {
875
910
  suppressErrors: true
876
911
  })));
877
912
  for (const layoutPath of layoutPaths.flat(1)) {
878
- const layoutName = path.basename(layoutPath).replace(/\.\w+$/, "");
913
+ const layoutName = path.basename(layoutPath).replace(RE_FILE_EXTENSION, "");
879
914
  layouts[layoutName] = layoutPath;
880
915
  }
881
916
  return layouts;
@@ -906,52 +941,10 @@ function getDefine(options) {
906
941
  }
907
942
 
908
943
  //#endregion
909
- //#region node/syntax/transform/utils.ts
944
+ //#region node/syntax/utils.ts
910
945
  function normalizeRangeStr(rangeStr = "") {
911
946
  return !rangeStr.trim() ? [] : rangeStr.trim().split(/\|/g).map((i) => i.trim());
912
947
  }
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
948
  /**
956
949
  * Escape `{{` in code block to prevent Vue interpret it, #99, #1316
957
950
  */
@@ -960,7 +953,8 @@ function escapeVueInCode(md) {
960
953
  }
961
954
 
962
955
  //#endregion
963
- //#region node/syntax/markdown-it/markdown-it-katex.ts
956
+ //#region node/syntax/katex.ts
957
+ const RE_KATEX_BLOCK_INFO = /^\{([\w*,|-]+)\}\s*(\{[^}]*\})?/;
964
958
  function isValidDelim(state, pos) {
965
959
  const max = state.posMax;
966
960
  let can_open = true;
@@ -1015,7 +1009,7 @@ function math_inline(state, silent) {
1015
1009
  state.pos = match + 1;
1016
1010
  return true;
1017
1011
  }
1018
- function math_block(state, start, end, silent) {
1012
+ const math_block = function(state, start, end, silent) {
1019
1013
  let firstLine;
1020
1014
  let lastLine;
1021
1015
  let next;
@@ -1026,11 +1020,13 @@ function math_block(state, start, end, silent) {
1026
1020
  if (pos + 2 > max) return false;
1027
1021
  if (state.src.slice(pos, pos + 2) !== "$$") return false;
1028
1022
  pos += 2;
1029
- firstLine = state.src.slice(pos, max);
1023
+ firstLine = state.src.slice(pos, max).trim();
1030
1024
  if (silent) return true;
1031
- if (firstLine.trim().slice(-2) === "$$") {
1032
- firstLine = firstLine.trim().slice(0, -2);
1025
+ let singleLine = false;
1026
+ if (firstLine.slice(-2) === "$$") {
1027
+ firstLine = firstLine.slice(0, -2).trim();
1033
1028
  found = true;
1029
+ singleLine = true;
1034
1030
  }
1035
1031
  for (next = start; !found;) {
1036
1032
  next++;
@@ -1047,11 +1043,15 @@ function math_block(state, start, end, silent) {
1047
1043
  state.line = next + 1;
1048
1044
  const token = state.push("math_block", "math", 0);
1049
1045
  token.block = true;
1050
- token.content = (firstLine && firstLine.trim() ? `${firstLine}\n` : "") + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
1046
+ if (singleLine) token.content = firstLine;
1047
+ else {
1048
+ token.info = firstLine;
1049
+ token.content = state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
1050
+ }
1051
1051
  token.map = [start, state.line];
1052
1052
  token.markup = "$$";
1053
1053
  return true;
1054
- }
1054
+ };
1055
1055
  function MarkdownItKatex(md, options) {
1056
1056
  const katexInline = function(latex) {
1057
1057
  options.displayMode = false;
@@ -1075,7 +1075,10 @@ function MarkdownItKatex(md, options) {
1075
1075
  }
1076
1076
  };
1077
1077
  const blockRenderer = function(tokens, idx) {
1078
- return `${katexBlock(tokens[idx].content)}\n`;
1078
+ const token = tokens[idx];
1079
+ const [, rangeStr, options$1] = RE_KATEX_BLOCK_INFO.exec(token.info) || [];
1080
+ const ranges = normalizeRangeStr(rangeStr);
1081
+ return `<KaTexBlockWrapper ${options$1 ? `v-bind="${options$1}"` : ""} :ranges='${JSON.stringify(ranges)}'>${katexBlock(tokens[idx].content)}</KaTexBlockWrapper>\n`;
1079
1082
  };
1080
1083
  md.inline.ruler.after("escape", "math_inline", math_inline);
1081
1084
  md.block.ruler.after("blockquote", "math_block", math_block, { alt: [
@@ -1088,6 +1091,23 @@ function MarkdownItKatex(md, options) {
1088
1091
  md.renderer.rules.math_block = blockRenderer;
1089
1092
  }
1090
1093
 
1094
+ //#endregion
1095
+ //#region node/virtual/conditional-styles.ts
1096
+ const id$1 = "/@slidev/conditional-styles";
1097
+ const templateConditionalStyles = {
1098
+ id: id$1,
1099
+ async getContent({ data, roots, userRoot }) {
1100
+ const imports = [];
1101
+ for (const root of roots) imports.push(makeAbsoluteImportGlob(id$1, [
1102
+ join(root, "styles/index.{ts,js,css}"),
1103
+ join(root, "styles.{ts,js,css}"),
1104
+ join(root, "style.{ts,js,css}")
1105
+ ], {}, userRoot));
1106
+ if (data.features.katex) imports.push(`import "${await resolveImportUrl("katex/dist/katex.min.css")}"`);
1107
+ return imports.join("\n");
1108
+ }
1109
+ };
1110
+
1091
1111
  //#endregion
1092
1112
  //#region node/virtual/configs.ts
1093
1113
  const templateConfigs = {
@@ -1138,15 +1158,16 @@ console.warn('/@slidev/titles.md is deprecated, import from #slidev/title-render
1138
1158
 
1139
1159
  //#endregion
1140
1160
  //#region node/virtual/global-layers.ts
1161
+ const id = `/@slidev/global-layers`;
1141
1162
  const templateGlobalLayers = {
1142
- id: `/@slidev/global-layers`,
1143
- getContent({ userRoot, roots }) {
1163
+ id,
1164
+ getContent({ roots, userRoot }) {
1144
1165
  function* getComponent(name, names) {
1145
1166
  yield `const ${name}Components = [\n`;
1146
1167
  for (const root of roots) {
1147
1168
  const globs = names.map((name$1) => join(root, `${name$1}.{ts,js,vue}`));
1148
1169
  yield " Object.values(";
1149
- yield makeAbsoluteImportGlob(userRoot, globs, { import: "default" });
1170
+ yield makeAbsoluteImportGlob(id, globs, { import: "default" }, userRoot);
1150
1171
  yield ")[0],\n";
1151
1172
  }
1152
1173
  yield `].filter(Boolean)\n`;
@@ -1262,11 +1283,12 @@ export default {
1262
1283
  //#endregion
1263
1284
  //#region node/virtual/setups.ts
1264
1285
  function createSetupTemplate(name) {
1286
+ const id$2 = `/@slidev/setups/${name}`;
1265
1287
  return {
1266
- id: `/@slidev/setups/${name}`,
1267
- getContent({ userRoot, roots }) {
1288
+ id: id$2,
1289
+ getContent({ roots, userRoot }) {
1268
1290
  return `export default [${roots.map((root) => {
1269
- return `Object.values(${makeAbsoluteImportGlob(userRoot, [join(root, `setup/${name}.{ts,js,mts,mjs}`)], { import: "default" })})[0]`;
1291
+ return `Object.values(${makeAbsoluteImportGlob(id$2, [join(root, `setup/${name}.{ts,js,mts,mjs}`)], { import: "default" }, userRoot)})[0]`;
1270
1292
  }).join(", ")}].filter(Boolean)`;
1271
1293
  }
1272
1294
  };
@@ -1323,34 +1345,6 @@ const templateSlides = {
1323
1345
  }
1324
1346
  };
1325
1347
 
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
1348
  //#endregion
1355
1349
  //#region node/virtual/titles.ts
1356
1350
  const templateTitleRendererMd = {
@@ -1374,7 +1368,7 @@ const templates = [
1374
1368
  templateMonacoTypes,
1375
1369
  templateMonacoRunDeps,
1376
1370
  templateConfigs,
1377
- templateStyle,
1371
+ templateConditionalStyles,
1378
1372
  templateGlobalLayers,
1379
1373
  templateNavControls,
1380
1374
  templateSlides,
@@ -1388,6 +1382,7 @@ const templates = [
1388
1382
 
1389
1383
  //#endregion
1390
1384
  //#region node/vite/loaders.ts
1385
+ const RE_WORD_CHARS_ONLY = /^[\w-]+$/;
1391
1386
  function createSlidesLoader(options, serverOptions) {
1392
1387
  const { data, mode, utils, withoutNotes } = options;
1393
1388
  const notesMd = MarkdownExit({ html: true });
@@ -1528,32 +1523,32 @@ function createSlidesLoader(options, serverOptions) {
1528
1523
  const moduleEntries = [
1529
1524
  ...ctx.modules.filter((i) => i.id === templateMonacoRunDeps.id || i.id === templateMonacoTypes.id),
1530
1525
  ...vueModules,
1531
- ...Array.from(moduleIds).map((id) => ctx.server.moduleGraph.getModuleById(id))
1526
+ ...Array.from(moduleIds).map((id$2) => ctx.server.moduleGraph.getModuleById(id$2))
1532
1527
  ].filter(notNullish).filter((i) => !i.id?.startsWith("/@id/@vite-icons"));
1533
1528
  updateServerWatcher();
1534
1529
  return moduleEntries;
1535
1530
  },
1536
1531
  resolveId: {
1537
1532
  order: "pre",
1538
- handler(id) {
1539
- if (id.startsWith("/@slidev/") || id.includes("__slidev_")) return id;
1533
+ handler(id$2) {
1534
+ if (id$2.startsWith("/@slidev/") || id$2.includes("__slidev_")) return id$2;
1540
1535
  return null;
1541
1536
  }
1542
1537
  },
1543
- async load(id) {
1544
- const template = templates.find((i) => i.id === id);
1538
+ async load(id$2) {
1539
+ const template = templates.find((i) => i.id === id$2);
1545
1540
  if (template) return {
1546
1541
  code: await template.getContent.call(this, options),
1547
1542
  map: { mappings: "" }
1548
1543
  };
1549
- const matchFacade = id.match(regexSlideFacadeId);
1544
+ const matchFacade = id$2.match(regexSlideFacadeId);
1550
1545
  if (matchFacade) {
1551
1546
  const [, no, type] = matchFacade;
1552
1547
  const idx = +no - 1;
1553
1548
  const sourceId = JSON.stringify(sourceIds[type][idx]);
1554
1549
  return [`export * from ${sourceId}`, `export { default } from ${sourceId}`].join("\n");
1555
1550
  }
1556
- const matchSource = id.match(regexSlideSourceId);
1551
+ const matchSource = id$2.match(regexSlideSourceId);
1557
1552
  if (matchSource) {
1558
1553
  const [, no, type] = matchSource;
1559
1554
  const idx = +no - 1;
@@ -1616,7 +1611,7 @@ function createSlidesLoader(options, serverOptions) {
1616
1611
  };
1617
1612
  }
1618
1613
  }
1619
- if (data.markdownFiles[id]) return "";
1614
+ if (data.markdownFiles[id$2]) return "";
1620
1615
  }
1621
1616
  };
1622
1617
  function renderNote(text = "") {
@@ -1627,7 +1622,7 @@ function createSlidesLoader(options, serverOptions) {
1627
1622
  clickCount += Number(count);
1628
1623
  return `<span class="slidev-note-click-mark" data-clicks="${clickCount}"></span>`;
1629
1624
  });
1630
- const keys = Object.keys(notesAutoRuby).sort((b, a) => b.length - a.length).map((i) => /^[\w-]+$/.test(i) ? `\\b${i}\\b` : i);
1625
+ 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
1626
  if (keys.length) {
1632
1627
  const regex = new RegExp(`(${keys.join("|")})`, "g");
1633
1628
  md = md.replace(regex, (match) => {
@@ -1646,6 +1641,29 @@ function createSlidesLoader(options, serverOptions) {
1646
1641
  }
1647
1642
  }
1648
1643
 
1644
+ //#endregion
1645
+ //#region node/setups/transformers.ts
1646
+ async function setupTransformers(roots) {
1647
+ const returns = await loadSetups(roots, "transformers.ts", []);
1648
+ const result = {
1649
+ pre: [],
1650
+ preCodeblock: [],
1651
+ postCodeblock: [],
1652
+ post: [],
1653
+ codeblocks: []
1654
+ };
1655
+ for (const r of [...returns].reverse()) {
1656
+ if (r.pre) result.pre.push(...r.pre);
1657
+ if (r.preCodeblock) result.preCodeblock.push(...r.preCodeblock);
1658
+ }
1659
+ for (const r of returns) {
1660
+ if (r.postCodeblock) result.postCodeblock.push(...r.postCodeblock);
1661
+ if (r.post) result.post.push(...r.post);
1662
+ if (r.codeblocks) result.codeblocks.push(...r.codeblocks);
1663
+ }
1664
+ return result;
1665
+ }
1666
+
1649
1667
  //#endregion
1650
1668
  //#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
1669
  var SpecialCharacters;
@@ -1865,11 +1883,11 @@ function isTodoItem(tokens, index) {
1865
1883
  }
1866
1884
  function todoify(token, options) {
1867
1885
  if (token.children == null) return;
1868
- const id = generateIdForToken(token);
1869
- token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id));
1886
+ const id$2 = generateIdForToken(token);
1887
+ token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id$2));
1870
1888
  token.children[1].content = token.children[1].content.replace(checkboxRegex, "");
1871
1889
  if (options.label) {
1872
- token.children.splice(1, 0, createLabelBeginToken(id));
1890
+ token.children.splice(1, 0, createLabelBeginToken(id$2));
1873
1891
  token.children.push(createLabelEndToken());
1874
1892
  }
1875
1893
  }
@@ -1877,17 +1895,17 @@ function generateIdForToken(token) {
1877
1895
  if (token.map) return `task-item-${token.map[0]}`;
1878
1896
  else return `task-item-${Math.ceil(Math.random() * (1e4 * 1e3) - 1e3)}`;
1879
1897
  }
1880
- function createCheckboxToken(token, enabled, id) {
1898
+ function createCheckboxToken(token, enabled, id$2) {
1881
1899
  const checkbox = new token_default("taskListItemCheckbox", "", 0);
1882
1900
  if (!enabled) checkbox.attrSet("disabled", "true");
1883
1901
  if (token.map) checkbox.attrSet("line", token.map[0].toString());
1884
- checkbox.attrSet("id", id);
1902
+ checkbox.attrSet("id", id$2);
1885
1903
  if (checkboxRegex.exec(token.content)?.[1].toLowerCase() === "x") checkbox.attrSet("checked", "true");
1886
1904
  return checkbox;
1887
1905
  }
1888
- function createLabelBeginToken(id) {
1906
+ function createLabelBeginToken(id$2) {
1889
1907
  const labelBeginToken = new token_default("taskListItemLabel_open", "", 1);
1890
- labelBeginToken.attrSet("id", id);
1908
+ labelBeginToken.attrSet("id", id$2);
1891
1909
  return labelBeginToken;
1892
1910
  }
1893
1911
  function createLabelEndToken() {
@@ -1907,62 +1925,139 @@ function startsWithTodoMarkdown(token) {
1907
1925
  }
1908
1926
 
1909
1927
  //#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);
1928
+ //#region node/syntax/codeblock/magic-move.ts
1929
+ const RE_MAGIC_MOVE_INFO = /^(?:md|markdown) magic-move\s*(?:\[([^\]]*)\])?\s*(\{[^}]*\})?/;
1930
+ const RE_CODE_BLOCK = /^```([\w'-]+)?(?:[ \t]*|[ \t][ \w\t'-]*)(?:\[([^\]]*)\])?[ \t]*(?:\{([\w*,|-]+)\}[ \t]*(\{[^}]*\})?([^\r\n]*))?\r?\n((?:(?!^```)[\s\S])*?)^```$/gm;
1931
+ const RE_LINES_TRUE = /\blines: *true\b/;
1932
+ const RE_LINES_FALSE = /\blines: *false\b/;
1933
+ function parseLineNumbersOption(options) {
1934
+ return RE_LINES_TRUE.test(options) ? true : RE_LINES_FALSE.test(options) ? false : void 0;
1935
+ }
1936
+ var magic_move_default = defineCodeblockTransformer(async ({ info, fence, code, options: { data: { config }, utils: { shikiOptions, shiki } } }) => {
1937
+ if (fence !== 4) return;
1938
+ const match = info.match(RE_MAGIC_MOVE_INFO);
1939
+ if (!match) return;
1940
+ const [, title = "", options = "{}"] = match;
1941
+ const defaultLineNumbers = parseLineNumbersOption(options) ?? config.lineNumbers;
1942
+ const matches = Array.from(code.matchAll(RE_CODE_BLOCK));
1943
+ if (!matches.length) throw new Error("Magic Move block must contain at least one code block");
1944
+ const ranges = matches.map((i) => normalizeRangeStr(i[3]));
1945
+ const steps = await Promise.all(matches.map(async (i) => {
1946
+ const lang = i[1];
1947
+ const lineNumbers = parseLineNumbersOption(i[4]) ?? defaultLineNumbers;
1948
+ const code$1 = i[6].trimEnd();
1949
+ const options$1 = {
1950
+ ...shikiOptions,
1951
+ lang
1952
+ };
1953
+ const { tokens, bg, fg: fg$1, rootStyle, themeName } = await shiki.codeToTokens(code$1, options$1);
1954
+ return {
1955
+ ...toKeyedTokens(code$1, tokens, JSON.stringify([lang, "themes" in options$1 ? options$1.themes : options$1.theme]), lineNumbers),
1956
+ bg,
1957
+ fg: fg$1,
1958
+ rootStyle,
1959
+ themeName,
1960
+ lang
1961
+ };
1962
+ }));
1963
+ return `<ShikiMagicMove v-bind="${options}" steps-lz="${lz.compressToBase64(JSON.stringify(steps))}" :title='${JSON.stringify(title)}' :step-ranges='${JSON.stringify(ranges)}' />`;
1964
+ });
1965
+
1966
+ //#endregion
1967
+ //#region node/syntax/codeblock/mermaid.ts
1968
+ const RE_MERMAID = /^mermaid\s*(\{[^\n]*\})?/;
1969
+ var mermaid_default = defineCodeblockTransformer(async ({ info, code }) => {
1970
+ const match = info.match(RE_MERMAID);
1971
+ if (!match) return;
1972
+ const [, options] = match;
1973
+ return `<Mermaid ${options ? `v-bind="${options}"` : ""} code-lz="${lz.compressToBase64(code.trim())}" />`;
1974
+ });
1975
+
1976
+ //#endregion
1977
+ //#region node/syntax/codeblock/monaco.ts
1978
+ const RE_MONACO = /^([\w'-]+)?\s*\{(monaco[\w-]*)\}\s*(\{[^}]*\})?(.*)$/;
1979
+ const RE_DIFF_SEPARATOR = /^\s*~~~\s*\n/m;
1980
+ var monaco_default = defineCodeblockTransformer(async ({ info, code, options: { data: { config }, mode } }) => {
1981
+ const match = info.match(RE_MONACO);
1982
+ if (!match) return;
1983
+ const [, lang = "", monaco, options] = match;
1984
+ if (!(config.monaco === true || config.monaco === mode)) return;
1985
+ let encoded;
1986
+ let diff = "";
1987
+ if (monaco === "monaco-diff") {
1988
+ const [original, modified] = code.split(RE_DIFF_SEPARATOR, 2);
1989
+ encoded = lz.compressToBase64(original);
1990
+ diff = modified === void 0 ? "" : `diff-lz="${lz.compressToBase64(modified)}"`;
1991
+ } else encoded = lz.compressToBase64(code);
1992
+ return `<Monaco ${options ? `v-bind="${options}"` : ""} ${monaco === "monaco-run" ? "runnable" : ""} lang="${lang}" code-lz="${encoded}" ${diff} />`;
1993
+ });
1994
+
1995
+ //#endregion
1996
+ //#region node/syntax/codeblock/plant-uml.ts
1997
+ const RE_PLANT_UML = /^plantuml\s*(\{[^\n]*\})?/;
1998
+ var plant_uml_default = defineCodeblockTransformer(async ({ info, code, options: { data: { config: { plantUmlServer } } } }) => {
1999
+ const match = info.match(RE_PLANT_UML);
2000
+ if (!match) return;
2001
+ const [, options] = match;
2002
+ return `<PlantUml ${options ? `v-bind="${options}"` : ""} code="${encode(code.trim())}"${plantUmlServer === void 0 ? "" : ` server=${JSON.stringify(plantUmlServer)}`} />`;
2003
+ });
2004
+
2005
+ //#endregion
2006
+ //#region node/syntax/codeblock/wrapper.ts
2007
+ const RE_BLOCK_INFO = /^([\w'-]+)?(?:[ \t]*|[ \t][ \w\t'-]*)(?:\[([^\]]*)\])?[ \t]*(?:\{([\w,|\-*]+)\})?[ \t]*(\{[^}]*\})?(.*)$/;
2008
+ var wrapper_default = defineCodeblockTransformer(async ({ info, renderHighlighted }) => {
2009
+ const [, lang = "", title = "", rangeStr = "", options, rest = ""] = info.match(RE_BLOCK_INFO) ?? [];
2010
+ const ranges = normalizeRangeStr(rangeStr);
2011
+ const optionsProp = options ? `v-bind="${options}"` : "";
2012
+ const code = await renderHighlighted({ info: `${lang} ${rest}` });
2013
+ return `<CodeBlockWrapper ${optionsProp} title=${JSON.stringify(title)} :ranges='${JSON.stringify(ranges)}'>${escapeVueInCode(code)}</CodeBlockWrapper>`;
2014
+ });
2015
+
2016
+ //#endregion
2017
+ //#region node/syntax/codeblock/index.ts
2018
+ function MarkdownItCodeblocks(md, options, extraTransformers) {
2019
+ const oldFence = md.renderer.rules.fence;
2020
+ md.renderer.rules.fence = async function(tokens, idx, renderOptions, env, slf) {
2021
+ const token = tokens[idx];
2022
+ const slideNo = env.id?.match(regexSlideSourceId);
2023
+ const ctx = {
2024
+ info: token.info.trim(),
2025
+ code: token.content,
2026
+ fence: token.markup.length,
2027
+ slide: slideNo ? options.data.slides[slideNo[1] - 1] : null,
2028
+ options,
2029
+ renderHighlighted(override) {
2030
+ if (override.info != null) token.info = override.info;
2031
+ if (override.code != null) token.content = override.code;
2032
+ return oldFence(tokens, idx, renderOptions, env, slf);
1948
2033
  }
2034
+ };
2035
+ const transformers = [
2036
+ ...extraTransformers,
2037
+ mermaid_default,
2038
+ plant_uml_default,
2039
+ magic_move_default,
2040
+ monaco_default,
2041
+ wrapper_default
2042
+ ];
2043
+ for (const transformer of transformers) {
2044
+ if (!transformer) continue;
2045
+ const res = await transformer(ctx);
2046
+ if (res != null) return ensureSuffix("\n", res);
1949
2047
  }
1950
- ].filter(isTruthy);
1951
- return fromAsyncCodeToHtml(shiki.codeToHtml, {
1952
- ...shikiOptions,
1953
- transformers
1954
- });
2048
+ throw new Error("Should not reach here");
2049
+ };
1955
2050
  }
1956
2051
 
1957
2052
  //#endregion
1958
- //#region node/syntax/markdown-it/markdown-it-v-drag.ts
2053
+ //#region node/syntax/drag.ts
1959
2054
  const dragComponentRegex = /<(v-?drag-?\w*)([\s>])/i;
1960
2055
  const dragDirectiveRegex = /(?<![</\w])v-drag(=".*?")?/i;
1961
2056
  function MarkdownItVDrag(md, markdownTransformMap) {
1962
2057
  const visited = /* @__PURE__ */ new WeakSet();
1963
2058
  const sourceMapConsumers = /* @__PURE__ */ new WeakMap();
1964
- function getSourceMapConsumer(id) {
1965
- const s = markdownTransformMap.get(id);
2059
+ function getSourceMapConsumer(id$2) {
2060
+ const s = id$2 && markdownTransformMap.get(id$2);
1966
2061
  if (!s) return void 0;
1967
2062
  let smc = sourceMapConsumers.get(s);
1968
2063
  if (smc) return smc;
@@ -1976,7 +2071,7 @@ function MarkdownItVDrag(md, markdownTransformMap) {
1976
2071
  }
1977
2072
  const _parse = md.parse;
1978
2073
  md.parse = function(src, env) {
1979
- const smc = getSourceMapConsumer(env.id);
2074
+ const smc = getSourceMapConsumer(env?.id);
1980
2075
  const toOriginalPos = smc ? (line) => smc.originalPositionFor({
1981
2076
  line: line + 1,
1982
2077
  column: 0
@@ -2014,211 +2109,126 @@ function MarkdownItVDrag(md, markdownTransformMap) {
2014
2109
  }
2015
2110
 
2016
2111
  //#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: []
2112
+ //#region node/syntax/escape-code.ts
2113
+ const RE_CODE_TAG_OPEN = /^<code/;
2114
+ function MarkdownItEscapeInlineCode(md) {
2115
+ const codeInline = md.renderer.rules.code_inline;
2116
+ md.renderer.rules.code_inline = async (tokens, idx, options, env, self) => {
2117
+ return (await codeInline(tokens, idx, options, env, self)).replace(RE_CODE_TAG_OPEN, "<code v-pre");
2043
2118
  };
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
2119
  }
2085
2120
 
2086
2121
  //#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
- });
2122
+ //#region node/syntax/scoped.ts
2123
+ const RE_STYLE_TAG_OPEN = /<style\b([^>]*)>/gi;
2124
+ const RE_SCOPED_ATTR = /\bscoped\b/i;
2125
+ function MarkdownItStyleScoped(md) {
2126
+ const addScoped = (html) => {
2127
+ return html.replace(RE_STYLE_TAG_OPEN, (match, attrs) => {
2128
+ if (RE_SCOPED_ATTR.test(attrs)) return match;
2129
+ return `<style scoped${attrs}>`;
2130
+ });
2131
+ };
2132
+ const htmlBlock = md.renderer.rules.html_block;
2133
+ md.renderer.rules.html_block = async (...args) => addScoped(await htmlBlock(...args));
2134
+ const htmlInline = md.renderer.rules.html_inline;
2135
+ md.renderer.rules.html_inline = async (...args) => addScoped(await htmlInline(...args));
2098
2136
  }
2099
2137
 
2100
2138
  //#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()
2139
+ //#region node/syntax/shiki.ts
2140
+ async function MarkdownItShiki({ data: { config }, mode, utils: { shiki, shikiOptions } }) {
2141
+ async function getTwoslashTransformer() {
2142
+ const [, , { transformerTwoslash }] = await Promise.all([
2143
+ shiki.codeToHast("", {
2144
+ lang: "js",
2145
+ ...shikiOptions
2146
+ }),
2147
+ shiki.codeToHast("", {
2148
+ lang: "ts",
2149
+ ...shikiOptions
2150
+ }),
2151
+ import("@shikijs/vitepress-twoslash")
2118
2152
  ]);
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;
2153
+ return transformerTwoslash({
2154
+ explicitTrigger: true,
2155
+ twoslashOptions: { handbookOptions: { noErrorValidation: true } }
2156
+ });
2168
2157
  }
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}" />`;
2158
+ const transformers = [
2159
+ ...shikiOptions.transformers || [],
2160
+ (config.twoslash === true || config.twoslash === mode) && await getTwoslashTransformer(),
2161
+ (config.twoslash === true || config.twoslash === mode) && transformerTwoslashConditional(),
2162
+ { pre(pre) {
2163
+ this.addClassToHast(pre, "slidev-code");
2164
+ delete pre.properties.tabindex;
2165
+ } }
2166
+ ].filter(isTruthy);
2167
+ return fromAsyncCodeToHtml(shiki.codeToHtml, {
2168
+ ...shikiOptions,
2169
+ transformers
2183
2170
  });
2184
2171
  }
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
- });
2172
+ function transformerTwoslashConditional() {
2173
+ return {
2174
+ name: "slidev:twoslash-conditional",
2175
+ code: function applyConditionalFlag(node) {
2176
+ if (node.tagName === "v-menu") {
2177
+ if (node.properties[":shown"] === "true") node.properties[":shown"] = "$nav.currentPage === $page";
2178
+ } else for (const child of node.children) if (child.type === "element") applyConditionalFlag(child);
2179
+ }
2180
+ };
2195
2181
  }
2196
2182
 
2197
2183
  //#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;
2184
+ //#region node/syntax/slot-sugar.ts
2185
+ const RE_SLOT_MARKER = /^::\s*([\w.\-:]+)\s*::\s*$/;
2186
+ function MarkdownItSlotSugar(md) {
2187
+ md.block.ruler.before("fence", "slot_marker", (state, startLine, _endLine, silent) => {
2188
+ if (state.sCount[startLine] - state.blkIndent > 0) return false;
2189
+ const pos = state.bMarks[startLine] + state.tShift[startLine];
2190
+ const max = state.eMarks[startLine];
2191
+ const match = state.src.slice(pos, max).match(RE_SLOT_MARKER);
2192
+ if (!match) return false;
2193
+ if (silent) return true;
2194
+ const token = state.push("slot_marker", "", 0);
2195
+ token.meta = { slotName: match[1] };
2196
+ token.map = [startLine, startLine + 1];
2197
+ state.line = startLine + 1;
2198
+ state.env.hasSlotMarker = true;
2199
+ return true;
2200
+ }, { alt: [
2201
+ "paragraph",
2202
+ "reference",
2203
+ "blockquote",
2204
+ "list"
2205
+ ] });
2206
+ md.core.ruler.push("slot_sugar_compiler", (state) => {
2207
+ if (!state.env.hasSlotMarker) return;
2208
+ const tokens = state.tokens;
2209
+ const newTokens = [];
2210
+ let hasOpenSlot = false;
2211
+ for (let i = 0; i < tokens.length; i++) {
2212
+ const token = tokens[i];
2213
+ if (token.type === "slot_marker") {
2214
+ if (hasOpenSlot) {
2215
+ const closeHtml = new state.Token("html_block", "", 0);
2216
+ closeHtml.content = "\n</template>\n";
2217
+ newTokens.push(closeHtml);
2218
+ }
2219
+ const openHtml = new state.Token("html_block", "", 0);
2220
+ openHtml.content = `\n<template v-slot:${token.meta.slotName}="slotProps">\n`;
2221
+ newTokens.push(openHtml);
2222
+ hasOpenSlot = true;
2223
+ } else newTokens.push(token);
2224
+ }
2225
+ if (hasOpenSlot) {
2226
+ const closeHtml = new state.Token("html_block", "", 0);
2227
+ closeHtml.content = "\n</template>\n";
2228
+ newTokens.push(closeHtml);
2219
2229
  }
2230
+ state.tokens = newTokens;
2220
2231
  });
2221
- if (prevSlot) ctx.s.append("\n\n</template>");
2222
2232
  }
2223
2233
 
2224
2234
  //#endregion
@@ -2254,7 +2264,8 @@ function createMonacoWriterPlugin({ userRoot }) {
2254
2264
  }
2255
2265
 
2256
2266
  //#endregion
2257
- //#region node/syntax/transform/snippet.ts
2267
+ //#region node/syntax/snippet.ts
2268
+ const RE_NEWLINE = /\r?\n/;
2258
2269
  function dedent(text) {
2259
2270
  const lines = text.split("\n");
2260
2271
  const minIndentLength = lines.reduce((acc, line) => {
@@ -2327,76 +2338,96 @@ function findRegion(lines, regionName) {
2327
2338
  }
2328
2339
  return null;
2329
2340
  }
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));
2341
+ const RE_SNIPPET_IMPORT = /^<<<[ \t]*(\S.*?)(#[\w-]+)?[ \t]*(?:[ \t](\S+?))?[ \t]*(\{.*)?$/;
2342
+ function MarkdownItSnippet(md, { userRoot, data: { watchFiles, slides } }) {
2343
+ md.block.ruler.before("fence", "snippet_import", (state, startLine, _endLine, silent) => {
2344
+ const pos = state.bMarks[startLine] + state.tShift[startLine];
2345
+ const max = state.eMarks[startLine];
2346
+ const match = state.src.slice(pos, max).match(RE_SNIPPET_IMPORT);
2347
+ if (!match) return false;
2348
+ if (silent) return true;
2349
+ let [, filepath = "", regionName = "", lang = "", meta = ""] = match;
2350
+ const slideNo = state.env.id?.match(regexSlideSourceId);
2351
+ const slide = slideNo ? slides[slideNo[1] - 1] : null;
2352
+ if (!slide) {
2353
+ console.warn(yellow(`[markdown-it-snippet] Snippet syntax is not supported in ${state.env.id || "unknown source"}. Skipped.`));
2354
+ return false;
2355
+ }
2356
+ const dir = path.dirname(slide.source.filepath);
2357
+ const src = slash(filepath.startsWith("@/") ? path.resolve(userRoot, filepath.slice(2)) : path.resolve(dir, filepath));
2358
+ lang = lang.trim() || path.extname(filepath).slice(1);
2347
2359
  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`);
2360
+ if (!(fs.existsSync(src) && fs.statSync(src).isFile())) throw new Error(`Code snippet path not found: ${src}`);
2352
2361
  let content = fs.readFileSync(src, "utf8");
2353
2362
  if (regionName) {
2354
- const lines = content.split(/\r?\n/);
2363
+ const lines = content.split(RE_NEWLINE);
2355
2364
  const region = findRegion(lines, regionName.slice(1));
2356
2365
  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
2366
  }
2358
- if (meta.match(reMonacoWrite)) {
2367
+ if (meta.includes("{monaco-write}")) {
2359
2368
  monacoWriterWhitelist.add(filepath);
2360
2369
  lang = lang.trim();
2361
- meta = meta.replace(reMonacoWrite, "").trim() || "{}";
2370
+ meta = meta.replace("{monaco-write}", "").trim() || "{}";
2371
+ const safeFilepath = JSON.stringify(filepath).slice(1, -1);
2362
2372
  const encoded = lz.compressToBase64(content);
2363
- return `<Monaco writable=${JSON.stringify(filepath)} code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />`;
2373
+ const token = state.push("html_block", "", 0);
2374
+ token.content = `<Monaco writable="${safeFilepath}" code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />\n`;
2375
+ token.map = [startLine, startLine + 1];
2364
2376
  } else {
2365
2377
  watchFiles[src] ??= /* @__PURE__ */ new Set();
2366
2378
  watchFiles[src].add(slide.index);
2379
+ const token = state.push("fence", "code", 0);
2380
+ token.info = `${lang} ${meta}`.trim();
2381
+ token.content = content.endsWith("\n") ? content : `${content}\n`;
2382
+ token.map = [startLine, startLine + 1];
2367
2383
  }
2368
- return `\`\`\`${lang} ${meta}\n${content}\n\`\`\``;
2384
+ state.line = startLine + 1;
2385
+ return true;
2386
+ }, { alt: [
2387
+ "paragraph",
2388
+ "reference",
2389
+ "blockquote",
2390
+ "list"
2391
+ ] });
2392
+ }
2393
+
2394
+ //#endregion
2395
+ //#region node/syntax/index.ts
2396
+ async function useMarkdownItPlugins(md, options, markdownTransformMap, codeblockTransformers) {
2397
+ const { data: { features, config }, utils: { katexOptions } } = options;
2398
+ md.use(MarkdownItSnippet, options);
2399
+ md.use(await MarkdownItShiki(options));
2400
+ md.use(MarkdownItCodeblocks, options, codeblockTransformers);
2401
+ md.use(MarkdownItLink);
2402
+ md.use(MarkdownItEscapeInlineCode);
2403
+ md.use(MarkdownItFootnote);
2404
+ md.use(taskLists, {
2405
+ enabled: true,
2406
+ lineNumber: true,
2407
+ label: true
2369
2408
  });
2409
+ if (features.katex) md.use(MarkdownItKatex, katexOptions);
2410
+ md.use(MarkdownItVDrag, markdownTransformMap);
2411
+ md.use(MarkdownItSlotSugar);
2412
+ if (config.comark || config.mdc) md.use(MarkdownItComark);
2413
+ md.use(MarkdownItStyleScoped);
2414
+ md.use(MarkdownItGitHubAlerts);
2370
2415
  }
2371
2416
 
2372
2417
  //#endregion
2373
- //#region node/syntax/transform/index.ts
2374
- async function getMarkdownTransformers(options) {
2418
+ //#region node/vite/markdown.ts
2419
+ const RE_MD_EXT$1 = /\.md$/;
2420
+ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
2421
+ const markdownTransformMap = /* @__PURE__ */ new Map();
2375
2422
  const extras = await setupTransformers(options.roots);
2376
- return [
2423
+ const transformers = [
2377
2424
  ...extras.pre,
2378
- transformSnippet,
2379
- options.data.config.highlighter === "shiki" && transformMagicMove,
2380
2425
  ...extras.preCodeblock,
2381
- transformMermaid,
2382
- transformPlantUml,
2383
- options.data.features.monaco && transformMonaco,
2384
2426
  ...extras.postCodeblock,
2385
- transformCodeWrapper,
2386
- options.data.features.katex && transformKaTexWrapper,
2387
- transformPageCSS,
2388
- transformSlotSugar,
2389
2427
  ...extras.post
2390
2428
  ];
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
2429
  return Markdown({
2399
- include: [/\.md$/],
2430
+ include: [RE_MD_EXT$1],
2400
2431
  wrapperClasses: "",
2401
2432
  headEnabled: false,
2402
2433
  frontmatter: false,
@@ -2410,18 +2441,18 @@ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
2410
2441
  },
2411
2442
  ...mdOptions,
2412
2443
  async markdownSetup(md) {
2413
- await useMarkdownItPlugins(md, options, markdownTransformMap);
2444
+ await useMarkdownItPlugins(md, options, markdownTransformMap, extras.codeblocks);
2414
2445
  await mdOptions?.markdownSetup?.(md);
2415
2446
  },
2416
2447
  transforms: {
2417
2448
  ...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);
2449
+ async before(code, id$2) {
2450
+ if (options.data.markdownFiles[id$2]) return "";
2451
+ code = await mdOptions?.transforms?.before?.(code, id$2) ?? code;
2452
+ const match = id$2.match(regexSlideSourceId);
2422
2453
  if (!match) return code;
2423
2454
  const s = new MagicString(code);
2424
- markdownTransformMap.set(id, s);
2455
+ markdownTransformMap.set(id$2, s);
2425
2456
  const ctx = {
2426
2457
  s,
2427
2458
  slide: options.data.slides[+match[1] - 1],
@@ -2443,13 +2474,13 @@ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
2443
2474
  function createMonacoTypesLoader({ userRoot, utils }) {
2444
2475
  return {
2445
2476
  name: "slidev:monaco-types-loader",
2446
- resolveId(id) {
2447
- if (id.startsWith("/@slidev-monaco-types/")) return id;
2477
+ resolveId(id$2) {
2478
+ if (id$2.startsWith("/@slidev-monaco-types/")) return id$2;
2448
2479
  return null;
2449
2480
  },
2450
- async load(id) {
2451
- if (!id.startsWith("/@slidev-monaco-types/")) return null;
2452
- const url = new URL(id, "http://localhost");
2481
+ async load(id$2) {
2482
+ if (!id$2.startsWith("/@slidev-monaco-types/")) return null;
2483
+ const url = new URL(id$2, "http://localhost");
2453
2484
  if (url.pathname === "/@slidev-monaco-types/resolve") {
2454
2485
  const query = new URLSearchParams(url.search);
2455
2486
  const pkg = query.get("pkg");
@@ -2502,9 +2533,9 @@ function createPatchMonacoSourceMapPlugin() {
2502
2533
  return {
2503
2534
  name: "slidev:patch-monaco-sourcemap",
2504
2535
  enforce: "pre",
2505
- load(id) {
2506
- if (!id.includes("node_modules/monaco-editor/esm/vs/base")) return;
2507
- return readFile(cleanUrl(id), "utf-8");
2536
+ load(id$2) {
2537
+ if (!id$2.includes("node_modules/monaco-editor/esm/vs/base")) return;
2538
+ return readFile(cleanUrl(id$2), "utf-8");
2508
2539
  }
2509
2540
  };
2510
2541
  }
@@ -2515,7 +2546,7 @@ async function createRemoteAssetsPlugin({ data: { config }, mode }, pluginOption
2515
2546
  if (!(config.remoteAssets === true || config.remoteAssets === mode)) return;
2516
2547
  const { VitePluginRemoteAssets, DefaultRules } = await import("vite-plugin-remote-assets");
2517
2548
  return VitePluginRemoteAssets({
2518
- resolveMode: (id) => id.endsWith("index.html") ? "relative" : "@fs",
2549
+ resolveMode: (id$2) => id$2.endsWith("index.html") ? "relative" : "@fs",
2519
2550
  awaitDownload: mode === "build",
2520
2551
  ...pluginOptions.remoteAssets,
2521
2552
  rules: [
@@ -2669,6 +2700,11 @@ async function createUnocssPlugin(options, pluginOptions) {
2669
2700
 
2670
2701
  //#endregion
2671
2702
  //#region node/vite/vue.ts
2703
+ const RE_VUE_EXT = /\.vue$/;
2704
+ const RE_VUE_QUERY_VUE = /\.vue\?vue/;
2705
+ const RE_VUE_QUERY_V = /\.vue\?v=/;
2706
+ const RE_MD_EXT = /\.md$/;
2707
+ const RE_MD_QUERY_VUE = /\.md\?vue/;
2672
2708
  const customElements = new Set([
2673
2709
  "annotation",
2674
2710
  "math",
@@ -2702,11 +2738,11 @@ async function createVuePlugin(_options, pluginOptions) {
2702
2738
  const { vue: vueOptions = {}, vuejsx: vuejsxOptions = {} } = pluginOptions;
2703
2739
  const VuePlugin = Vue({
2704
2740
  include: [
2705
- /\.vue$/,
2706
- /\.vue\?vue/,
2707
- /\.vue\?v=/,
2708
- /\.md$/,
2709
- /\.md\?vue/
2741
+ RE_VUE_EXT,
2742
+ RE_VUE_QUERY_VUE,
2743
+ RE_VUE_QUERY_V,
2744
+ RE_MD_EXT,
2745
+ RE_MD_QUERY_VUE
2710
2746
  ],
2711
2747
  exclude: [],
2712
2748
  ...vueOptions,