@slidev/cli 0.48.0-beta.14 → 0.48.0-beta.16

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.
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  resolveImportPath,
6
6
  toAtFS
7
- } from "./chunk-7HOZGSL4.mjs";
7
+ } from "./chunk-G3BP3FUT.mjs";
8
8
  import {
9
9
  __commonJS,
10
10
  __toESM
@@ -2496,7 +2496,7 @@ var require_semver2 = __commonJS({
2496
2496
  });
2497
2497
 
2498
2498
  // package.json
2499
- var version = "0.48.0-beta.14";
2499
+ var version = "0.48.0-beta.16";
2500
2500
 
2501
2501
  // node/common.ts
2502
2502
  import { existsSync, promises as fs } from "node:fs";
@@ -2563,9 +2563,9 @@ async function mergeViteConfigs({ roots, entry }, viteConfig, config, command) {
2563
2563
  }
2564
2564
 
2565
2565
  // node/plugins/preset.ts
2566
- import { join as join7 } from "node:path";
2566
+ import { join as join6 } from "node:path";
2567
2567
  import { existsSync as existsSync3 } from "node:fs";
2568
- import process3 from "node:process";
2568
+ import process2 from "node:process";
2569
2569
  import { fileURLToPath } from "node:url";
2570
2570
  import Vue from "@vitejs/plugin-vue";
2571
2571
  import VueJsx from "@vitejs/plugin-vue-jsx";
@@ -2645,6 +2645,11 @@ var EXCLUDE = [
2645
2645
  "vue-demi",
2646
2646
  "vue"
2647
2647
  ];
2648
+ var ASYNC_MODULES = [
2649
+ "file-saver",
2650
+ "vue",
2651
+ "@vue"
2652
+ ];
2648
2653
  function createConfigPlugin(options) {
2649
2654
  return {
2650
2655
  name: "slidev:config",
@@ -2680,8 +2685,40 @@ function createConfigPlugin(options) {
2680
2685
  ])
2681
2686
  }
2682
2687
  },
2683
- publicDir: join3(options.userRoot, "public")
2688
+ publicDir: join3(options.userRoot, "public"),
2689
+ build: {
2690
+ rollupOptions: {
2691
+ output: {
2692
+ chunkFileNames(chunkInfo) {
2693
+ const DEFAULT = "[name]-[hash].js";
2694
+ if (chunkInfo.name.includes("/") || chunkInfo.name === "entry")
2695
+ return DEFAULT;
2696
+ if (chunkInfo.moduleIds.filter((i) => isSlidevClient(i)).length > chunkInfo.moduleIds.length * 0.6)
2697
+ return "slidev/[name]-[hash].js";
2698
+ if (chunkInfo.moduleIds.filter((i) => i.includes("/monaco-editor/")).length > chunkInfo.moduleIds.length * 0.6)
2699
+ return "monaco/[name]-[hash].js";
2700
+ return DEFAULT;
2701
+ },
2702
+ manualChunks(id) {
2703
+ if (id.startsWith("/@slidev-monaco-types/") || id.includes("/@slidev/monaco-types") || id.match(/(\.d\.[mc]?ts|package.json)\?raw$/))
2704
+ return "monaco/bundled-types";
2705
+ if (id.includes("/shiki/") || id.includes("/@shikijs/"))
2706
+ return `modules/shiki`;
2707
+ if (id.startsWith("~icons/"))
2708
+ return "modules/unplugin-icons";
2709
+ if (id.includes("/client/main.ts"))
2710
+ return "entry";
2711
+ const matchedAsyncModule = ASYNC_MODULES.find((i) => id.includes(`/node_modules/${i}`));
2712
+ if (matchedAsyncModule)
2713
+ return `modules/${matchedAsyncModule.replace("@", "").replace("/", "-")}`;
2714
+ }
2715
+ }
2716
+ }
2717
+ }
2684
2718
  };
2719
+ function isSlidevClient(id) {
2720
+ return id.includes("/@slidev/") || id.includes("/slidev/packages/client/") || id.includes("/@vueuse/");
2721
+ }
2685
2722
  if (isInstalledGlobally) {
2686
2723
  injection.cacheDir = join3(options.cliRoot, "node_modules/.vite");
2687
2724
  injection.root = options.cliRoot;
@@ -2718,1504 +2755,1560 @@ function getDefine(options) {
2718
2755
  }
2719
2756
 
2720
2757
  // node/plugins/loaders.ts
2721
- import { basename as basename2, join as join4 } from "node:path";
2722
- import { isString, isTruthy, notNullish, objectMap, range } from "@antfu/utils";
2758
+ import { basename as basename2, join as join4, resolve as resolve2 } from "node:path";
2759
+ import { builtinModules } from "node:module";
2760
+ import { isString, isTruthy as isTruthy2, notNullish, objectMap, range } from "@antfu/utils";
2723
2761
  import fg2 from "fast-glob";
2724
- import fs3 from "fs-extra";
2725
- import Markdown from "markdown-it";
2762
+ import fs5 from "fs-extra";
2763
+ import Markdown2 from "markdown-it";
2726
2764
  import { bold, gray, red, yellow } from "kolorist";
2727
- import mila from "markdown-it-link-attributes";
2765
+ import mila2 from "markdown-it-link-attributes";
2728
2766
  import * as parser from "@slidev/parser/fs";
2729
2767
  import equal from "fast-deep-equal";
2730
- var regexId = /^\/\@slidev\/slide\/(\d+)\.(md|json)(?:\?import)?$/;
2731
- var regexIdQuery = /(\d+?)\.(md|json|frontmatter)$/;
2732
- var templateInjectionMarker = "/* @slidev-injection */";
2733
- var templateImportContextUtils = `import {
2734
- useSlideContext,
2735
- provideFrontmatter as _provideFrontmatter,
2736
- frontmatterToProps as _frontmatterToProps,
2737
- } from "@slidev/client/context.ts"`.replace(/\n\s*/g, " ");
2738
- var templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = useSlideContext()`;
2739
- function getBodyJson(req) {
2740
- return new Promise((resolve3, reject) => {
2741
- let body = "";
2742
- req.on("data", (chunk) => body += chunk);
2743
- req.on("error", reject);
2744
- req.on("end", () => {
2745
- try {
2746
- resolve3(JSON.parse(body) || {});
2747
- } catch (e) {
2748
- reject(e);
2768
+
2769
+ // node/plugins/markdown.ts
2770
+ import fs4 from "node:fs/promises";
2771
+ import Markdown from "unplugin-vue-markdown/vite";
2772
+ import { isTruthy, slash } from "@antfu/utils";
2773
+
2774
+ // ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.0.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/image-size/specialCharacters.js
2775
+ var SpecialCharacters;
2776
+ (function(SpecialCharacters2) {
2777
+ SpecialCharacters2[SpecialCharacters2["EXCLAMATION_MARK"] = 33] = "EXCLAMATION_MARK";
2778
+ SpecialCharacters2[SpecialCharacters2["OPENING_BRACKET"] = 91] = "OPENING_BRACKET";
2779
+ SpecialCharacters2[SpecialCharacters2["OPENING_PARENTHESIS"] = 40] = "OPENING_PARENTHESIS";
2780
+ SpecialCharacters2[SpecialCharacters2["WHITESPACE"] = 32] = "WHITESPACE";
2781
+ SpecialCharacters2[SpecialCharacters2["NEW_LINE"] = 10] = "NEW_LINE";
2782
+ SpecialCharacters2[SpecialCharacters2["EQUALS"] = 61] = "EQUALS";
2783
+ SpecialCharacters2[SpecialCharacters2["LOWER_CASE_X"] = 120] = "LOWER_CASE_X";
2784
+ SpecialCharacters2[SpecialCharacters2["NUMBER_ZERO"] = 48] = "NUMBER_ZERO";
2785
+ SpecialCharacters2[SpecialCharacters2["NUMBER_NINE"] = 57] = "NUMBER_NINE";
2786
+ SpecialCharacters2[SpecialCharacters2["PERCENTAGE"] = 37] = "PERCENTAGE";
2787
+ SpecialCharacters2[SpecialCharacters2["CLOSING_PARENTHESIS"] = 41] = "CLOSING_PARENTHESIS";
2788
+ })(SpecialCharacters || (SpecialCharacters = {}));
2789
+
2790
+ // ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.0.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/task-lists/index.js
2791
+ import Token from "markdown-it/lib/token.mjs";
2792
+ var checkboxRegex = /^ *\[([\sx])] /i;
2793
+ function taskLists(md2, options = { enabled: false, label: false, lineNumber: false }) {
2794
+ md2.core.ruler.after("inline", "task-lists", (state) => processToken(state, options));
2795
+ md2.renderer.rules.taskListItemCheckbox = (tokens) => {
2796
+ const token = tokens[0];
2797
+ const checkedAttribute = token.attrGet("checked") ? 'checked="" ' : "";
2798
+ const disabledAttribute = token.attrGet("disabled") ? 'disabled="" ' : "";
2799
+ const line = token.attrGet("line");
2800
+ const idAttribute = `id="${token.attrGet("id")}" `;
2801
+ const dataLineAttribute = line && options.lineNumber ? `data-line="${line}" ` : "";
2802
+ return `<input class="task-list-item-checkbox" type="checkbox" ${checkedAttribute}${disabledAttribute}${dataLineAttribute}${idAttribute}/>`;
2803
+ };
2804
+ md2.renderer.rules.taskListItemLabel_close = () => {
2805
+ return "</label>";
2806
+ };
2807
+ md2.renderer.rules.taskListItemLabel_open = (tokens) => {
2808
+ const token = tokens[0];
2809
+ const id = token.attrGet("id");
2810
+ return `<label for="${id}">`;
2811
+ };
2812
+ }
2813
+ function processToken(state, options) {
2814
+ const allTokens = state.tokens;
2815
+ for (let i = 2; i < allTokens.length; i++) {
2816
+ if (!isTodoItem(allTokens, i)) {
2817
+ continue;
2818
+ }
2819
+ todoify(allTokens[i], options);
2820
+ allTokens[i - 2].attrJoin("class", `task-list-item ${options.enabled ? " enabled" : ""}`);
2821
+ const parentToken = findParentToken(allTokens, i - 2);
2822
+ if (parentToken) {
2823
+ const classes = parentToken.attrGet("class") ?? "";
2824
+ if (!classes.match(/(^| )contains-task-list/)) {
2825
+ parentToken.attrJoin("class", "contains-task-list");
2749
2826
  }
2750
- });
2751
- });
2827
+ }
2828
+ }
2829
+ return false;
2752
2830
  }
2753
- var md = Markdown({ html: true });
2754
- md.use(mila, {
2755
- attrs: {
2756
- target: "_blank",
2757
- rel: "noopener"
2831
+ function findParentToken(tokens, index) {
2832
+ const targetLevel = tokens[index].level - 1;
2833
+ for (let currentTokenIndex = index - 1; currentTokenIndex >= 0; currentTokenIndex--) {
2834
+ if (tokens[currentTokenIndex].level === targetLevel) {
2835
+ return tokens[currentTokenIndex];
2836
+ }
2758
2837
  }
2759
- });
2760
- function renderNote(text = "") {
2761
- let clickCount = 0;
2762
- const html = md.render(
2763
- text.replace(/\[click(?::(\d+))?\]/gi, (_, count = 1) => {
2764
- clickCount += Number(count);
2765
- return `<span class="slidev-note-click-mark" data-clicks="${clickCount}"></span>`;
2766
- })
2767
- );
2768
- return html;
2838
+ return void 0;
2769
2839
  }
2770
- function withRenderedNote(data) {
2840
+ function isTodoItem(tokens, index) {
2841
+ return isInline(tokens[index]) && isParagraph(tokens[index - 1]) && isListItem(tokens[index - 2]) && startsWithTodoMarkdown(tokens[index]);
2842
+ }
2843
+ function todoify(token, options) {
2844
+ if (token.children == null) {
2845
+ return;
2846
+ }
2847
+ const id = generateIdForToken(token);
2848
+ token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id));
2849
+ token.children[1].content = token.children[1].content.replace(checkboxRegex, "");
2850
+ if (options.label) {
2851
+ token.children.splice(1, 0, createLabelBeginToken(id));
2852
+ token.children.push(createLabelEndToken());
2853
+ }
2854
+ }
2855
+ function generateIdForToken(token) {
2856
+ if (token.map) {
2857
+ return `task-item-${token.map[0]}`;
2858
+ } else {
2859
+ return `task-item-${Math.ceil(Math.random() * (1e4 * 1e3) - 1e3)}`;
2860
+ }
2861
+ }
2862
+ function createCheckboxToken(token, enabled, id) {
2863
+ const checkbox = new Token("taskListItemCheckbox", "", 0);
2864
+ if (!enabled) {
2865
+ checkbox.attrSet("disabled", "true");
2866
+ }
2867
+ if (token.map) {
2868
+ checkbox.attrSet("line", token.map[0].toString());
2869
+ }
2870
+ checkbox.attrSet("id", id);
2871
+ const checkboxRegexResult = checkboxRegex.exec(token.content);
2872
+ const isChecked = checkboxRegexResult?.[1].toLowerCase() === "x";
2873
+ if (isChecked) {
2874
+ checkbox.attrSet("checked", "true");
2875
+ }
2876
+ return checkbox;
2877
+ }
2878
+ function createLabelBeginToken(id) {
2879
+ const labelBeginToken = new Token("taskListItemLabel_open", "", 1);
2880
+ labelBeginToken.attrSet("id", id);
2881
+ return labelBeginToken;
2882
+ }
2883
+ function createLabelEndToken() {
2884
+ return new Token("taskListItemLabel_close", "", -1);
2885
+ }
2886
+ function isInline(token) {
2887
+ return token.type === "inline";
2888
+ }
2889
+ function isParagraph(token) {
2890
+ return token.type === "paragraph_open";
2891
+ }
2892
+ function isListItem(token) {
2893
+ return token.type === "list_item_open";
2894
+ }
2895
+ function startsWithTodoMarkdown(token) {
2896
+ return checkboxRegex.test(token.content);
2897
+ }
2898
+
2899
+ // ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.0.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/toc/plugin.js
2900
+ import { Optional } from "@mrdrogdrog/optional";
2901
+
2902
+ // node/plugins/markdown.ts
2903
+ import { encode as encodePlantUml } from "plantuml-encoder";
2904
+ import Mdc from "markdown-it-mdc";
2905
+ import { codeToKeyedTokens, createMagicMoveMachine } from "shiki-magic-move/core";
2906
+ import mila from "markdown-it-link-attributes";
2907
+ import mif from "markdown-it-footnote";
2908
+ import lz from "lz-string";
2909
+
2910
+ // node/plugins/markdown-it-katex.ts
2911
+ import katex from "katex";
2912
+ function isValidDelim(state, pos) {
2913
+ const max = state.posMax;
2914
+ let can_open = true;
2915
+ let can_close = true;
2916
+ const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
2917
+ const nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
2918
+ if (prevChar === 32 || prevChar === 9 || /* \t */
2919
+ nextChar >= 48 && nextChar <= 57)
2920
+ can_close = false;
2921
+ if (nextChar === 32 || nextChar === 9)
2922
+ can_open = false;
2771
2923
  return {
2772
- ...data,
2773
- noteHTML: renderNote(data?.note)
2924
+ can_open,
2925
+ can_close
2774
2926
  };
2775
2927
  }
2776
- function createSlidesLoader({ data, clientRoot, roots, remote, mode }, pluginOptions, serverOptions) {
2777
- const slidePrefix = "/@slidev/slides/";
2778
- const hmrPages = /* @__PURE__ */ new Set();
2779
- let server;
2780
- let _layouts_cache_time = 0;
2781
- let _layouts_cache = {};
2782
- return [
2783
- {
2784
- name: "slidev:loader",
2785
- configureServer(_server) {
2786
- server = _server;
2787
- updateServerWatcher();
2788
- server.middlewares.use(async (req, res, next) => {
2789
- const match = req.url?.match(regexId);
2790
- if (!match)
2791
- return next();
2792
- const [, no, type] = match;
2793
- const idx = Number.parseInt(no);
2794
- if (type === "json" && req.method === "GET") {
2795
- res.write(JSON.stringify(withRenderedNote(data.slides[idx])));
2796
- return res.end();
2797
- }
2798
- if (type === "json" && req.method === "POST") {
2799
- const body = await getBodyJson(req);
2800
- const slide = data.slides[idx];
2801
- if (body.content && body.content !== slide.source.content)
2802
- hmrPages.add(idx);
2803
- Object.assign(slide.source, body);
2804
- parser.prettifySlide(slide.source);
2805
- await parser.save(data.markdownFiles[slide.source.filepath]);
2806
- res.statusCode = 200;
2807
- res.write(JSON.stringify(withRenderedNote(slide)));
2808
- return res.end();
2809
- }
2810
- next();
2811
- });
2812
- },
2813
- async handleHotUpdate(ctx) {
2814
- if (!data.watchFiles.includes(ctx.file))
2815
- return;
2816
- await ctx.read();
2817
- const newData = await serverOptions.loadData?.();
2818
- if (!newData)
2819
- return [];
2820
- const moduleIds = /* @__PURE__ */ new Set();
2821
- if (data.slides.length !== newData.slides.length) {
2822
- moduleIds.add("/@slidev/routes");
2823
- range(newData.slides.length).map((i) => hmrPages.add(i));
2824
- }
2825
- if (!equal(data.headmatter.defaults, newData.headmatter.defaults)) {
2826
- moduleIds.add("/@slidev/routes");
2827
- range(data.slides.length).map((i) => hmrPages.add(i));
2828
- }
2829
- if (!equal(data.config, newData.config))
2830
- moduleIds.add("/@slidev/configs");
2831
- if (!equal(data.features, newData.features)) {
2832
- setTimeout(() => {
2833
- ctx.server.ws.send({ type: "full-reload" });
2834
- }, 1);
2835
- }
2836
- const length = Math.max(data.slides.length, newData.slides.length);
2837
- for (let i = 0; i < length; i++) {
2838
- const a = data.slides[i];
2839
- const b = newData.slides[i];
2840
- if (a?.content.trim() === b?.content.trim() && a?.title?.trim() === b?.title?.trim() && equal(a.frontmatter, b.frontmatter) && Object.entries(a.snippetsUsed ?? {}).every(([file, oldContent]) => {
2841
- try {
2842
- const newContent = fs3.readFileSync(file, "utf-8");
2843
- return oldContent === newContent;
2844
- } catch {
2845
- return false;
2846
- }
2847
- })) {
2848
- if (a?.note !== b?.note) {
2849
- ctx.server.ws.send({
2850
- type: "custom",
2851
- event: "slidev-update-note",
2852
- data: {
2853
- id: i,
2854
- note: b.note || "",
2855
- noteHTML: renderNote(b.note || "")
2856
- }
2857
- });
2858
- }
2859
- continue;
2860
- }
2861
- ctx.server.ws.send({
2862
- type: "custom",
2863
- event: "slidev-update",
2864
- data: {
2865
- id: i,
2866
- data: withRenderedNote(newData.slides[i])
2867
- }
2868
- });
2869
- hmrPages.add(i);
2870
- }
2871
- Object.assign(data, newData);
2872
- if (hmrPages.size > 0)
2873
- moduleIds.add("/@slidev/titles.md");
2874
- const vueModules = Array.from(hmrPages).flatMap((i) => [
2875
- ctx.server.moduleGraph.getModuleById(`${slidePrefix}${i + 1}.frontmatter`),
2876
- ctx.server.moduleGraph.getModuleById(`${slidePrefix}${i + 1}.md`)
2877
- ]);
2878
- hmrPages.clear();
2879
- const moduleEntries = [
2880
- ...vueModules,
2881
- ...Array.from(moduleIds).map((id) => ctx.server.moduleGraph.getModuleById(id))
2882
- ].filter(notNullish).filter((i) => !i.id?.startsWith("/@id/@vite-icons"));
2883
- updateServerWatcher();
2884
- return moduleEntries;
2885
- },
2886
- resolveId(id) {
2887
- if (id.startsWith(slidePrefix) || id.startsWith("/@slidev/"))
2888
- return id;
2889
- return null;
2890
- },
2891
- load(id) {
2892
- if (id === "/@slidev/routes")
2893
- return generateRoutes();
2894
- if (id === "/@slidev/layouts")
2895
- return generateLayouts();
2896
- if (id === "/@slidev/styles")
2897
- return generateUserStyles();
2898
- if (id === "/@slidev/monaco-types")
2899
- return generateMonacoTypes();
2900
- if (id === "/@slidev/configs")
2901
- return generateConfigs();
2902
- if (id === "/@slidev/global-components/top")
2903
- return generateGlobalComponents("top");
2904
- if (id === "/@slidev/global-components/bottom")
2905
- return generateGlobalComponents("bottom");
2906
- if (id === "/@slidev/custom-nav-controls")
2907
- return generateCustomNavControls();
2908
- if (id === "/@slidev/titles.md") {
2909
- return {
2910
- code: data.slides.map(({ title }, i) => `<template ${i === 0 ? "v-if" : "v-else-if"}="+no === ${i + 1}">
2911
-
2912
- ${title}
2913
-
2914
- </template>`).join(""),
2915
- map: { mappings: "" }
2916
- };
2917
- }
2918
- if (id.startsWith(slidePrefix)) {
2919
- const remaning = id.slice(slidePrefix.length);
2920
- const match = remaning.match(regexIdQuery);
2921
- if (match) {
2922
- const [, no, type] = match;
2923
- const pageNo = Number.parseInt(no) - 1;
2924
- const slide = data.slides[pageNo];
2925
- if (!slide)
2926
- return;
2927
- if (type === "md") {
2928
- return {
2929
- code: slide?.content,
2930
- map: { mappings: "" }
2931
- };
2932
- } else if (type === "frontmatter") {
2933
- const slideBase = {
2934
- ...withRenderedNote(slide),
2935
- frontmatter: void 0,
2936
- source: void 0,
2937
- // remove raw content in build, optimize the bundle size
2938
- ...mode === "build" ? { raw: "", content: "", note: "" } : {}
2939
- };
2940
- const fontmatter = getFrontmatter(pageNo);
2941
- return {
2942
- code: [
2943
- "// @unocss-include",
2944
- 'import { reactive, computed } from "vue"',
2945
- `export const frontmatter = reactive(${JSON.stringify(fontmatter)})`,
2946
- `export const meta = reactive({
2947
- layout: computed(() => frontmatter.layout),
2948
- transition: computed(() => frontmatter.transition),
2949
- class: computed(() => frontmatter.class),
2950
- clicks: computed(() => frontmatter.clicks),
2951
- name: computed(() => frontmatter.name),
2952
- preload: computed(() => frontmatter.preload),
2953
- slide: {
2954
- ...(${JSON.stringify(slideBase)}),
2955
- frontmatter,
2956
- filepath: ${JSON.stringify(slide.source.filepath)},
2957
- start: ${JSON.stringify(slide.source.start)},
2958
- id: ${pageNo},
2959
- no: ${no},
2960
- },
2961
- __clicksContext: null,
2962
- __preloaded: false,
2963
- })`,
2964
- "export default frontmatter",
2965
- // handle HMR, update frontmatter with update
2966
- "if (import.meta.hot) {",
2967
- " import.meta.hot.accept(({ frontmatter: update }) => {",
2968
- " if(!update) return",
2969
- " Object.keys(frontmatter).forEach(key => {",
2970
- " if (!(key in update)) delete frontmatter[key]",
2971
- " })",
2972
- " Object.assign(frontmatter, update)",
2973
- " })",
2974
- "}"
2975
- ].join("\n"),
2976
- map: { mappings: "" }
2977
- };
2978
- }
2979
- }
2980
- return {
2981
- code: "",
2982
- map: { mappings: "" }
2983
- };
2984
- }
2985
- }
2986
- },
2987
- {
2988
- name: "slidev:layout-transform:pre",
2989
- enforce: "pre",
2990
- async transform(code, id) {
2991
- if (!id.startsWith(slidePrefix))
2992
- return;
2993
- const remaning = id.slice(slidePrefix.length);
2994
- const match = remaning.match(regexIdQuery);
2995
- if (!match)
2996
- return;
2997
- const [, no, type] = match;
2998
- if (type !== "md")
2999
- return;
3000
- const pageNo = Number.parseInt(no) - 1;
3001
- return transformMarkdown(code, pageNo);
3002
- }
3003
- },
3004
- {
3005
- name: "slidev:context-transform:pre",
3006
- enforce: "pre",
3007
- async transform(code, id) {
3008
- if (!id.endsWith(".vue") || id.includes("/@slidev/client/") || id.includes("/packages/client/"))
3009
- return;
3010
- return transformVue(code);
3011
- }
3012
- },
3013
- {
3014
- name: "slidev:title-transform:pre",
3015
- enforce: "pre",
3016
- transform(code, id) {
3017
- if (id !== "/@slidev/titles.md")
3018
- return;
3019
- return transformTitles(code);
3020
- }
3021
- },
3022
- {
3023
- name: "slidev:slide-transform:post",
3024
- enforce: "post",
3025
- transform(code, id) {
3026
- if (!id.match(/\/@slidev\/slides\/\d+\.md($|\?)/))
3027
- return;
3028
- const replaced = code.replace("if (_rerender_only)", "if (false)");
3029
- if (replaced !== code)
3030
- return replaced;
3031
- }
3032
- },
3033
- {
3034
- name: "slidev:index-html-transform",
3035
- transformIndexHtml() {
3036
- const { info, author, keywords } = data.headmatter;
3037
- return [
3038
- {
3039
- tag: "title",
3040
- children: getTitle()
3041
- },
3042
- info && {
3043
- tag: "meta",
3044
- attrs: {
3045
- name: "description",
3046
- content: info
3047
- }
3048
- },
3049
- author && {
3050
- tag: "meta",
3051
- attrs: {
3052
- name: "author",
3053
- content: author
3054
- }
3055
- },
3056
- keywords && {
3057
- tag: "meta",
3058
- attrs: {
3059
- name: "keywords",
3060
- content: Array.isArray(keywords) ? keywords.join(", ") : keywords
3061
- }
3062
- }
3063
- ].filter(isTruthy);
3064
- }
3065
- }
3066
- ];
3067
- function updateServerWatcher() {
3068
- if (!server)
3069
- return;
3070
- server.watcher.add(data.watchFiles);
2928
+ function math_inline(state, silent) {
2929
+ let match, token, res, pos;
2930
+ if (state.src[state.pos] !== "$")
2931
+ return false;
2932
+ res = isValidDelim(state, state.pos);
2933
+ if (!res.can_open) {
2934
+ if (!silent)
2935
+ state.pending += "$";
2936
+ state.pos += 1;
2937
+ return true;
3071
2938
  }
3072
- function getFrontmatter(pageNo) {
3073
- return {
3074
- ...data.headmatter?.defaults || {},
3075
- ...data.slides[pageNo]?.frontmatter || {}
3076
- };
2939
+ const start = state.pos + 1;
2940
+ match = start;
2941
+ while ((match = state.src.indexOf("$", match)) !== -1) {
2942
+ pos = match - 1;
2943
+ while (state.src[pos] === "\\")
2944
+ pos -= 1;
2945
+ if ((match - pos) % 2 === 1)
2946
+ break;
2947
+ match += 1;
3077
2948
  }
3078
- async function transformMarkdown(code, pageNo) {
3079
- const layouts = await getLayouts();
3080
- const frontmatter = getFrontmatter(pageNo);
3081
- let layoutName = frontmatter?.layout || (pageNo === 0 ? "cover" : "default");
3082
- if (!layouts[layoutName]) {
3083
- console.error(red(`
3084
- Unknown layout "${bold(layoutName)}".${yellow(" Available layouts are:")}`) + Object.keys(layouts).map((i, idx) => (idx % 3 === 0 ? "\n " : "") + gray(i.padEnd(15, " "))).join(" "));
3085
- console.error();
3086
- layoutName = "default";
3087
- }
3088
- delete frontmatter.title;
3089
- const imports = [
3090
- `import InjectedLayout from "${toAtFS(layouts[layoutName])}"`,
3091
- `import frontmatter from "${toAtFS(`${slidePrefix + (pageNo + 1)}.frontmatter`)}"`,
3092
- templateImportContextUtils,
3093
- "_provideFrontmatter(frontmatter)",
3094
- templateInitContext,
3095
- templateInjectionMarker
3096
- ];
3097
- code = code.replace(/(<script setup.*>)/g, `$1
3098
- ${imports.join("\n")}
3099
- `);
3100
- const injectA = code.indexOf("<template>") + "<template>".length;
3101
- const injectB = code.lastIndexOf("</template>");
3102
- let body = code.slice(injectA, injectB).trim();
3103
- if (body.startsWith("<div>") && body.endsWith("</div>"))
3104
- body = body.slice(5, -6);
3105
- code = `${code.slice(0, injectA)}
3106
- <InjectedLayout v-bind="_frontmatterToProps(frontmatter,${pageNo})">
3107
- ${body}
3108
- </InjectedLayout>
3109
- ${code.slice(injectB)}`;
3110
- return code;
3111
- }
3112
- function transformVue(code) {
3113
- if (code.includes(templateInjectionMarker) || code.includes("useSlideContext()"))
3114
- return code;
3115
- const imports = [
3116
- templateImportContextUtils,
3117
- templateInitContext,
3118
- templateInjectionMarker
3119
- ];
3120
- const matchScript = code.match(/<script((?!setup).)*(setup)?.*>/);
3121
- if (matchScript && matchScript[2]) {
3122
- return code.replace(/(<script.*>)/g, `$1
3123
- ${imports.join("\n")}
3124
- `);
3125
- } else if (matchScript && !matchScript[2]) {
3126
- const matchExport = code.match(/export\s+default\s+{/);
3127
- if (matchExport) {
3128
- const exportIndex = (matchExport.index || 0) + matchExport[0].length;
3129
- let component = code.slice(exportIndex);
3130
- component = component.slice(0, component.indexOf("</script>"));
3131
- const scriptIndex = (matchScript.index || 0) + matchScript[0].length;
3132
- const provideImport = '\nimport { injectionSlidevContext } from "@slidev/client/constants.ts"\n';
3133
- code = `${code.slice(0, scriptIndex)}${provideImport}${code.slice(scriptIndex)}`;
3134
- let injectIndex = exportIndex + provideImport.length;
3135
- let injectObject = "$slidev: { from: injectionSlidevContext },";
3136
- const matchInject = component.match(/.*inject\s*:\s*([\[{])/);
3137
- if (matchInject) {
3138
- injectIndex += (matchInject.index || 0) + matchInject[0].length;
3139
- if (matchInject[1] === "[") {
3140
- let injects = component.slice((matchInject.index || 0) + matchInject[0].length);
3141
- const injectEndIndex = injects.indexOf("]");
3142
- injects = injects.slice(0, injectEndIndex);
3143
- injectObject += injects.split(",").map((inject) => `${inject}: {from: ${inject}}`).join(",");
3144
- return `${code.slice(0, injectIndex - 1)}{
3145
- ${injectObject}
3146
- }${code.slice(injectIndex + injectEndIndex + 1)}`;
3147
- } else {
3148
- return `${code.slice(0, injectIndex)}
3149
- ${injectObject}
3150
- ${code.slice(injectIndex)}`;
3151
- }
3152
- }
3153
- return `${code.slice(0, injectIndex)}
3154
- inject: { ${injectObject} },
3155
- ${code.slice(injectIndex)}`;
3156
- }
3157
- }
3158
- return `<script setup>
3159
- ${imports.join("\n")}
3160
- </script>
3161
- ${code}`;
2949
+ if (match === -1) {
2950
+ if (!silent)
2951
+ state.pending += "$";
2952
+ state.pos = start;
2953
+ return true;
3162
2954
  }
3163
- function transformTitles(code) {
3164
- return code.replace(/<template>\s*<div>\s*<p>/, "<template>").replace(/<\/p>\s*<\/div>\s*<\/template>/, "</template>").replace(/<script\ssetup>/, `<script setup lang="ts">
3165
- defineProps<{ no: number | string }>()`);
2955
+ if (match - start === 0) {
2956
+ if (!silent)
2957
+ state.pending += "$$";
2958
+ state.pos = start + 1;
2959
+ return true;
3166
2960
  }
3167
- async function getLayouts() {
3168
- const now = Date.now();
3169
- if (now - _layouts_cache_time < 2e3)
3170
- return _layouts_cache;
3171
- const layouts = {};
3172
- for (const root of [...roots, clientRoot]) {
3173
- const layoutPaths = await fg2("layouts/**/*.{vue,ts}", {
3174
- cwd: root,
3175
- absolute: true,
3176
- suppressErrors: true
3177
- });
3178
- for (const layoutPath of layoutPaths) {
3179
- const layout = basename2(layoutPath).replace(/\.\w+$/, "");
3180
- if (layouts[layout])
3181
- continue;
3182
- layouts[layout] = layoutPath;
3183
- }
3184
- }
3185
- _layouts_cache_time = now;
3186
- _layouts_cache = layouts;
3187
- return layouts;
2961
+ res = isValidDelim(state, match);
2962
+ if (!res.can_close) {
2963
+ if (!silent)
2964
+ state.pending += "$";
2965
+ state.pos = start;
2966
+ return true;
3188
2967
  }
3189
- async function resolveUrl(id) {
3190
- return toAtFS(await resolveImportPath(id, true));
2968
+ if (!silent) {
2969
+ token = state.push("math_inline", "math", 0);
2970
+ token.markup = "$";
2971
+ token.content = state.src.slice(start, match);
3191
2972
  }
3192
- function resolveUrlOfClient(name) {
3193
- return toAtFS(join4(clientRoot, name));
2973
+ state.pos = match + 1;
2974
+ return true;
2975
+ }
2976
+ function math_block(state, start, end, silent) {
2977
+ let firstLine;
2978
+ let lastLine;
2979
+ let next;
2980
+ let lastPos;
2981
+ let found = false;
2982
+ let pos = state.bMarks[start] + state.tShift[start];
2983
+ let max = state.eMarks[start];
2984
+ if (pos + 2 > max)
2985
+ return false;
2986
+ if (state.src.slice(pos, pos + 2) !== "$$")
2987
+ return false;
2988
+ pos += 2;
2989
+ firstLine = state.src.slice(pos, max);
2990
+ if (silent)
2991
+ return true;
2992
+ if (firstLine.trim().slice(-2) === "$$") {
2993
+ firstLine = firstLine.trim().slice(0, -2);
2994
+ found = true;
3194
2995
  }
3195
- async function generateUserStyles() {
3196
- const imports = [
3197
- `import "${resolveUrlOfClient("styles/vars.css")}"`,
3198
- `import "${resolveUrlOfClient("styles/index.css")}"`,
3199
- `import "${resolveUrlOfClient("styles/code.css")}"`,
3200
- `import "${resolveUrlOfClient("styles/katex.css")}"`,
3201
- `import "${resolveUrlOfClient("styles/transitions.css")}"`
3202
- ];
3203
- for (const root of roots) {
3204
- const styles = [
3205
- join4(root, "styles", "index.ts"),
3206
- join4(root, "styles", "index.js"),
3207
- join4(root, "styles", "index.css"),
3208
- join4(root, "styles.css"),
3209
- join4(root, "style.css")
3210
- ];
3211
- for (const style of styles) {
3212
- if (fs3.existsSync(style)) {
3213
- imports.push(`import "${toAtFS(style)}"`);
3214
- continue;
3215
- }
3216
- }
3217
- }
3218
- if (data.features.katex)
3219
- imports.push(`import "${await resolveUrl("katex/dist/katex.min.css")}"`);
3220
- if (data.config.highlighter === "shiki") {
3221
- imports.push(
3222
- `import "${await resolveUrl("@shikijs/vitepress-twoslash/style.css")}"`,
3223
- `import "${resolveUrlOfClient("styles/shiki-twoslash.css")}"`
3224
- );
3225
- }
3226
- if (data.config.css === "unocss") {
3227
- imports.unshift(
3228
- `import "${await resolveUrl("@unocss/reset/tailwind.css")}"`,
3229
- 'import "uno:preflights.css"',
3230
- 'import "uno:typography.css"',
3231
- 'import "uno:shortcuts.css"'
3232
- );
3233
- imports.push('import "uno.css"');
2996
+ for (next = start; !found; ) {
2997
+ next++;
2998
+ if (next >= end)
2999
+ break;
3000
+ pos = state.bMarks[next] + state.tShift[next];
3001
+ max = state.eMarks[next];
3002
+ if (pos < max && state.tShift[next] < state.blkIndent) {
3003
+ break;
3234
3004
  }
3235
- return imports.join("\n");
3236
- }
3237
- async function generateMonacoTypes() {
3238
- return `void 0; ${parser.scanMonacoModules(data.slides.map((s) => s.source.raw).join()).map((i) => `import('/@slidev-monaco-types/${i}')`).join("\n")}`;
3239
- }
3240
- async function generateLayouts() {
3241
- const imports = [];
3242
- const layouts = objectMap(
3243
- await getLayouts(),
3244
- (k, v) => {
3245
- imports.push(`import __layout_${k} from "${toAtFS(v)}"`);
3246
- return [k, `__layout_${k}`];
3247
- }
3248
- );
3249
- return [
3250
- imports.join("\n"),
3251
- `export default {
3252
- ${Object.entries(layouts).map(([k, v]) => `"${k}": ${v}`).join(",\n")}
3253
- }`
3254
- ].join("\n\n");
3255
- }
3256
- async function generateRoutes() {
3257
- const imports = [];
3258
- const redirects = [];
3259
- const layouts = await getLayouts();
3260
- imports.push(
3261
- `import { markRaw } from 'vue'`,
3262
- `import __layout__end from '${layouts.end}'`
3263
- );
3264
- let no = 1;
3265
- const routes = data.slides.map((i, idx) => {
3266
- imports.push(`import n${no} from '${slidePrefix}${idx + 1}.md'`);
3267
- imports.push(`import { meta as f${no} } from '${slidePrefix}${idx + 1}.frontmatter'`);
3268
- const route = `{ path: '${no}', name: 'page-${no}', component: n${no}, meta: f${no} }`;
3269
- if (i.frontmatter?.routeAlias)
3270
- redirects.push(`{ path: '${i.frontmatter?.routeAlias}', redirect: { path: '${no}' } }`);
3271
- no += 1;
3272
- return route;
3273
- });
3274
- const routesStr = `export const rawRoutes = [
3275
- ${routes.join(",\n")}
3276
- ].map(markRaw)`;
3277
- const redirectsStr = `export const redirects = [
3278
- ${redirects.join(",\n")}
3279
- ].map(markRaw)`;
3280
- return [...imports, routesStr, redirectsStr].join("\n");
3281
- }
3282
- function getTitle() {
3283
- if (isString(data.config.title)) {
3284
- const tokens = md.parseInline(data.config.title, {});
3285
- return stringifyMarkdownTokens(tokens);
3005
+ if (state.src.slice(pos, max).trim().slice(-2) === "$$") {
3006
+ lastPos = state.src.slice(0, max).lastIndexOf("$$");
3007
+ lastLine = state.src.slice(pos, lastPos);
3008
+ found = true;
3286
3009
  }
3287
- return data.config.title;
3288
- }
3289
- function generateConfigs() {
3290
- const config = {
3291
- ...data.config,
3292
- remote,
3293
- title: getTitle()
3294
- };
3295
- if (isString(config.info))
3296
- config.info = md.render(config.info);
3297
- return `export default ${JSON.stringify(config)}`;
3298
- }
3299
- async function generateGlobalComponents(layer) {
3300
- const components = roots.flatMap((root) => {
3301
- if (layer === "top") {
3302
- return [
3303
- join4(root, "global.vue"),
3304
- join4(root, "global-top.vue"),
3305
- join4(root, "GlobalTop.vue")
3306
- ];
3307
- } else {
3308
- return [
3309
- join4(root, "global-bottom.vue"),
3310
- join4(root, "GlobalBottom.vue")
3311
- ];
3312
- }
3313
- }).filter((i) => fs3.existsSync(i));
3314
- const imports = components.map((i, idx) => `import __n${idx} from '${toAtFS(i)}'`).join("\n");
3315
- const render = components.map((i, idx) => `h(__n${idx})`).join(",");
3316
- return `
3317
- ${imports}
3318
- import { h } from 'vue'
3319
- export default {
3320
- render() {
3321
- return [${render}]
3322
3010
  }
3011
+ state.line = next + 1;
3012
+ const token = state.push("math_block", "math", 0);
3013
+ token.block = true;
3014
+ token.content = (firstLine && firstLine.trim() ? `${firstLine}
3015
+ ` : "") + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
3016
+ token.map = [start, state.line];
3017
+ token.markup = "$$";
3018
+ return true;
3323
3019
  }
3020
+ function math_plugin(md2, options) {
3021
+ options = options || {};
3022
+ const katexInline = function(latex) {
3023
+ options.displayMode = false;
3024
+ try {
3025
+ return katex.renderToString(latex, options);
3026
+ } catch (error) {
3027
+ if (options.throwOnError)
3028
+ console.warn(error);
3029
+ return latex;
3030
+ }
3031
+ };
3032
+ const inlineRenderer = function(tokens, idx) {
3033
+ return katexInline(tokens[idx].content);
3034
+ };
3035
+ const katexBlock = function(latex) {
3036
+ options.displayMode = true;
3037
+ try {
3038
+ return `<p>${katex.renderToString(latex, options)}</p>`;
3039
+ } catch (error) {
3040
+ if (options.throwOnError)
3041
+ console.warn(error);
3042
+ return latex;
3043
+ }
3044
+ };
3045
+ const blockRenderer = function(tokens, idx) {
3046
+ return `${katexBlock(tokens[idx].content)}
3324
3047
  `;
3048
+ };
3049
+ md2.inline.ruler.after("escape", "math_inline", math_inline);
3050
+ md2.block.ruler.after("blockquote", "math_block", math_block, {
3051
+ alt: ["paragraph", "reference", "blockquote", "list"]
3052
+ });
3053
+ md2.renderer.rules.math_inline = inlineRenderer;
3054
+ md2.renderer.rules.math_block = blockRenderer;
3055
+ }
3056
+
3057
+ // node/plugins/markdown-it-prism.ts
3058
+ import { createRequire } from "node:module";
3059
+ import Prism from "prismjs";
3060
+ import loadLanguages from "prismjs/components/index.js";
3061
+ import * as htmlparser2 from "htmlparser2";
3062
+ var require2 = createRequire(import.meta.url);
3063
+ var Tag = class {
3064
+ tagname;
3065
+ attributes;
3066
+ constructor(tagname, attributes) {
3067
+ this.tagname = tagname;
3068
+ this.attributes = attributes;
3325
3069
  }
3326
- async function generateCustomNavControls() {
3327
- const components = roots.flatMap((root) => {
3328
- return [
3329
- join4(root, "custom-nav-controls.vue"),
3330
- join4(root, "CustomNavControls.vue")
3331
- ];
3332
- }).filter((i) => fs3.existsSync(i));
3333
- const imports = components.map((i, idx) => `import __n${idx} from '${toAtFS(i)}'`).join("\n");
3334
- const render = components.map((i, idx) => `h(__n${idx})`).join(",");
3335
- return `
3336
- ${imports}
3337
- import { h } from 'vue'
3338
- export default {
3339
- render() {
3340
- return [${render}]
3070
+ asOpen() {
3071
+ return `<${this.tagname} ${Object.entries(this.attributes).map(([key, value]) => `${key}="${value}"`).join(" ")}>`;
3072
+ }
3073
+ asClosed() {
3074
+ return `</${this.tagname}>`;
3075
+ }
3076
+ };
3077
+ var DEFAULTS = {
3078
+ plugins: [],
3079
+ init: () => {
3080
+ },
3081
+ defaultLanguageForUnknown: void 0,
3082
+ defaultLanguageForUnspecified: void 0,
3083
+ defaultLanguage: void 0
3084
+ };
3085
+ function loadPrismLang(lang) {
3086
+ if (!lang)
3087
+ return void 0;
3088
+ let langObject = Prism.languages[lang];
3089
+ if (langObject === void 0) {
3090
+ loadLanguages([lang]);
3091
+ langObject = Prism.languages[lang];
3341
3092
  }
3093
+ return langObject;
3342
3094
  }
3343
- `;
3095
+ function loadPrismPlugin(name) {
3096
+ try {
3097
+ require2(`prismjs/plugins/${name}/prism-${name}`);
3098
+ } catch (e) {
3099
+ throw new Error(`Cannot load Prism plugin "${name}". Please check the spelling.`);
3344
3100
  }
3345
3101
  }
3346
-
3347
- // node/plugins/monacoTransform.ts
3348
- import { dirname as dirname2, join as join5 } from "node:path";
3349
- import fs4 from "node:fs/promises";
3350
- import process2 from "node:process";
3351
- import { slash } from "@antfu/utils";
3352
- import { findDepPkgJsonPath } from "vitefu";
3353
- async function getPackageData(pkg) {
3354
- const pkgJsonPath = await findDepPkgJsonPath(pkg, process2.cwd());
3355
- if (!pkgJsonPath)
3356
- return;
3357
- const pkgJson = JSON.parse(await fs4.readFile(pkgJsonPath, "utf-8"));
3358
- const typePath = pkgJson.types || pkgJson.typings;
3359
- if (!typePath)
3360
- return;
3361
- return [dirname2(pkgJsonPath), pkgJson, typePath];
3102
+ function selectLanguage(options, lang) {
3103
+ let langToUse = lang;
3104
+ if (langToUse === "" && options.defaultLanguageForUnspecified !== void 0)
3105
+ langToUse = options.defaultLanguageForUnspecified;
3106
+ let prismLang = loadPrismLang(langToUse);
3107
+ if (prismLang === void 0 && options.defaultLanguageForUnknown !== void 0) {
3108
+ langToUse = options.defaultLanguageForUnknown;
3109
+ prismLang = loadPrismLang(langToUse);
3110
+ }
3111
+ return [langToUse, prismLang];
3362
3112
  }
3363
- function createMonacoTypesLoader() {
3364
- return {
3365
- name: "slidev:monaco-types-loader",
3366
- resolveId(id) {
3367
- if (id.startsWith("/@slidev-monaco-types/"))
3368
- return id;
3369
- return null;
3113
+ function highlight(markdownit, options, text, lang) {
3114
+ const [langToUse, prismLang] = selectLanguage(options, lang);
3115
+ let code = text.trimEnd();
3116
+ code = prismLang ? highlightPrism(code, prismLang, langToUse) : markdownit.utils.escapeHtml(code);
3117
+ code = code.split(/\r?\n/g).map((line) => `<span class="line">${line}</span>`).join("\n");
3118
+ const classAttribute = langToUse ? ` class="slidev-code ${markdownit.options.langPrefix}${markdownit.utils.escapeHtml(langToUse)}"` : "";
3119
+ return escapeVueInCode(`<pre${classAttribute}><code>${code}</code></pre>`);
3120
+ }
3121
+ function highlightPrism(code, prismLang, langToUse) {
3122
+ const openTags = [];
3123
+ const parser2 = new htmlparser2.Parser({
3124
+ onopentag(tagname, attributes) {
3125
+ openTags.push(new Tag(tagname, attributes));
3370
3126
  },
3371
- async load(id) {
3372
- const match = id.match(/^\/\@slidev-monaco-types\/(.*)$/);
3373
- if (match) {
3374
- const pkg = match[1];
3375
- const packageData = await getPackageData(pkg) || await getPackageData(`@types/${pkg}`);
3376
- if (!packageData)
3377
- return;
3378
- const [pkgDir, pkgJson, typePath] = packageData;
3379
- return [
3380
- "import * as monaco from 'monaco-editor'",
3381
- `import Type from "${slash(join5(pkgDir, typePath))}?raw"`,
3382
- ...Object.keys(pkgJson.dependencies || {}).map((i) => `import "/@slidev-monaco-types/${i}"`),
3383
- `monaco.languages.typescript.typescriptDefaults.addExtraLib(\`declare module "${pkg}" { \${Type} }\`)`
3384
- ].join("\n");
3385
- }
3127
+ onclosetag() {
3128
+ openTags.pop();
3386
3129
  }
3387
- };
3130
+ });
3131
+ code = Prism.highlight(code, prismLang, langToUse);
3132
+ code = code.split(/\r?\n/g).map((line) => {
3133
+ const prefix = openTags.map((tag) => tag.asOpen()).join("");
3134
+ parser2.write(line);
3135
+ const postfix = openTags.reverse().map((tag) => tag.asClosed()).join("");
3136
+ return prefix + line + postfix;
3137
+ }).join("\n");
3138
+ parser2.end();
3139
+ return code;
3388
3140
  }
3389
-
3390
- // node/plugins/setupClient.ts
3391
- import { existsSync as existsSync2 } from "node:fs";
3392
- import { join as join6, resolve as resolve2 } from "node:path";
3393
- import { slash as slash2, uniq as uniq2 } from "@antfu/utils";
3394
- function createClientSetupPlugin({ themeRoots, addonRoots, userRoot, clientRoot }) {
3395
- const setupEntry = slash2(resolve2(clientRoot, "setup"));
3396
- return {
3397
- name: "slidev:setup",
3398
- enforce: "pre",
3399
- async transform(code, id) {
3400
- if (id.startsWith(setupEntry)) {
3401
- let getInjections2 = function(isAwait = false, isChained = false) {
3402
- return injections.join("\n").replace(/:AWAIT:/g, isAwait ? "await " : "").replace(/(,\s*)?:LAST:/g, isChained ? "$1injection_return" : "");
3403
- };
3404
- var getInjections = getInjections2;
3405
- const name = id.slice(setupEntry.length + 1).replace(/\?.*$/, "");
3406
- const imports = [];
3407
- const injections = [];
3408
- const setups = uniq2([
3409
- ...themeRoots,
3410
- ...addonRoots,
3411
- userRoot
3412
- ]).map((i) => join6(i, "setup", name));
3413
- setups.forEach((path2, idx) => {
3414
- if (!existsSync2(path2))
3415
- return;
3416
- imports.push(`import __n${idx} from '${toAtFS(path2)}'`);
3417
- let fn = `:AWAIT:__n${idx}`;
3418
- if (/\binjection_return\b/g.test(code))
3419
- fn = `injection_return = ${fn}`;
3420
- if (/\binjection_arg\b/g.test(code)) {
3421
- fn += "(";
3422
- const matches = Array.from(code.matchAll(/\binjection_arg(_\d+)?\b/g));
3423
- const dedupedMatches = Array.from(new Set(matches.map((m) => m[0])));
3424
- fn += dedupedMatches.join(", ");
3425
- fn += ", :LAST:)";
3426
- } else {
3427
- fn += "(:LAST:)";
3428
- }
3429
- injections.push(
3430
- `// ${path2}`,
3431
- fn
3432
- );
3433
- });
3434
- code = code.replace("/* __imports__ */", imports.join("\n"));
3435
- code = code.replace("/* __injections__ */", getInjections2());
3436
- code = code.replace("/* __async_injections__ */", getInjections2(true));
3437
- code = code.replace("/* __chained_injections__ */", getInjections2(false, true));
3438
- code = code.replace("/* __chained_async_injections__ */", getInjections2(true, true));
3439
- return code;
3440
- }
3441
- return null;
3442
- }
3443
- };
3141
+ function checkLanguageOption(options, optionName) {
3142
+ const language = options[optionName];
3143
+ if (language !== void 0 && loadPrismLang(language) === void 0)
3144
+ throw new Error(`Bad option ${optionName}: There is no Prism language '${language}'.`);
3145
+ }
3146
+ function markdownItPrism(markdownit, useroptions) {
3147
+ const options = Object.assign({}, DEFAULTS, useroptions);
3148
+ checkLanguageOption(options, "defaultLanguage");
3149
+ checkLanguageOption(options, "defaultLanguageForUnknown");
3150
+ checkLanguageOption(options, "defaultLanguageForUnspecified");
3151
+ options.defaultLanguageForUnknown = options.defaultLanguageForUnknown || options.defaultLanguage;
3152
+ options.defaultLanguageForUnspecified = options.defaultLanguageForUnspecified || options.defaultLanguage;
3153
+ options.plugins.forEach(loadPrismPlugin);
3154
+ options.init(Prism);
3155
+ markdownit.options.highlight = (text, lang) => highlight(markdownit, options, text, lang);
3444
3156
  }
3445
3157
 
3446
- // node/plugins/markdown.ts
3447
- import fs6 from "node:fs/promises";
3448
- import Markdown2 from "unplugin-vue-markdown/vite";
3449
- import * as base64 from "js-base64";
3450
- import { slash as slash3 } from "@antfu/utils";
3451
- import { hash as getHash } from "ohash";
3452
- import mila2 from "markdown-it-link-attributes";
3453
- import mif from "markdown-it-footnote";
3454
-
3455
- // ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.0.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/image-size/specialCharacters.js
3456
- var SpecialCharacters;
3457
- (function(SpecialCharacters2) {
3458
- SpecialCharacters2[SpecialCharacters2["EXCLAMATION_MARK"] = 33] = "EXCLAMATION_MARK";
3459
- SpecialCharacters2[SpecialCharacters2["OPENING_BRACKET"] = 91] = "OPENING_BRACKET";
3460
- SpecialCharacters2[SpecialCharacters2["OPENING_PARENTHESIS"] = 40] = "OPENING_PARENTHESIS";
3461
- SpecialCharacters2[SpecialCharacters2["WHITESPACE"] = 32] = "WHITESPACE";
3462
- SpecialCharacters2[SpecialCharacters2["NEW_LINE"] = 10] = "NEW_LINE";
3463
- SpecialCharacters2[SpecialCharacters2["EQUALS"] = 61] = "EQUALS";
3464
- SpecialCharacters2[SpecialCharacters2["LOWER_CASE_X"] = 120] = "LOWER_CASE_X";
3465
- SpecialCharacters2[SpecialCharacters2["NUMBER_ZERO"] = 48] = "NUMBER_ZERO";
3466
- SpecialCharacters2[SpecialCharacters2["NUMBER_NINE"] = 57] = "NUMBER_NINE";
3467
- SpecialCharacters2[SpecialCharacters2["PERCENTAGE"] = 37] = "PERCENTAGE";
3468
- SpecialCharacters2[SpecialCharacters2["CLOSING_PARENTHESIS"] = 41] = "CLOSING_PARENTHESIS";
3469
- })(SpecialCharacters || (SpecialCharacters = {}));
3470
-
3471
- // ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.0.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/task-lists/index.js
3472
- import Token from "markdown-it/lib/token.mjs";
3473
- var checkboxRegex = /^ *\[([\sx])] /i;
3474
- function taskLists(md2, options = { enabled: false, label: false, lineNumber: false }) {
3475
- md2.core.ruler.after("inline", "task-lists", (state) => processToken(state, options));
3476
- md2.renderer.rules.taskListItemCheckbox = (tokens) => {
3477
- const token = tokens[0];
3478
- const checkedAttribute = token.attrGet("checked") ? 'checked="" ' : "";
3479
- const disabledAttribute = token.attrGet("disabled") ? 'disabled="" ' : "";
3480
- const line = token.attrGet("line");
3481
- const idAttribute = `id="${token.attrGet("id")}" `;
3482
- const dataLineAttribute = line && options.lineNumber ? `data-line="${line}" ` : "";
3483
- return `<input class="task-list-item-checkbox" type="checkbox" ${checkedAttribute}${disabledAttribute}${dataLineAttribute}${idAttribute}/>`;
3484
- };
3485
- md2.renderer.rules.taskListItemLabel_close = () => {
3486
- return "</label>";
3487
- };
3488
- md2.renderer.rules.taskListItemLabel_open = (tokens) => {
3489
- const token = tokens[0];
3490
- const id = token.attrGet("id");
3491
- return `<label for="${id}">`;
3492
- };
3493
- }
3494
- function processToken(state, options) {
3495
- const allTokens = state.tokens;
3496
- for (let i = 2; i < allTokens.length; i++) {
3497
- if (!isTodoItem(allTokens, i)) {
3498
- continue;
3158
+ // node/plugins/transformSnippet.ts
3159
+ import path from "node:path";
3160
+ import fs3 from "fs-extra";
3161
+ function dedent(text) {
3162
+ const lines = text.split("\n");
3163
+ const minIndentLength = lines.reduce((acc, line) => {
3164
+ for (let i = 0; i < line.length; i++) {
3165
+ if (line[i] !== " " && line[i] !== " ")
3166
+ return Math.min(i, acc);
3499
3167
  }
3500
- todoify(allTokens[i], options);
3501
- allTokens[i - 2].attrJoin("class", `task-list-item ${options.enabled ? " enabled" : ""}`);
3502
- const parentToken = findParentToken(allTokens, i - 2);
3503
- if (parentToken) {
3504
- const classes = parentToken.attrGet("class") ?? "";
3505
- if (!classes.match(/(^| )contains-task-list/)) {
3506
- parentToken.attrJoin("class", "contains-task-list");
3168
+ return acc;
3169
+ }, Number.POSITIVE_INFINITY);
3170
+ if (minIndentLength < Number.POSITIVE_INFINITY)
3171
+ return lines.map((x) => x.slice(minIndentLength)).join("\n");
3172
+ return text;
3173
+ }
3174
+ function testLine(line, regexp, regionName, end = false) {
3175
+ const [full, tag, name] = regexp.exec(line.trim()) || [];
3176
+ return full && tag && name === regionName && tag.match(end ? /^[Ee]nd ?[rR]egion$/ : /^[rR]egion$/);
3177
+ }
3178
+ function findRegion(lines, regionName) {
3179
+ const regionRegexps = [
3180
+ /^\/\/ ?#?((?:end)?region) ([\w*-]+)$/,
3181
+ // javascript, typescript, java
3182
+ /^\/\* ?#((?:end)?region) ([\w*-]+) ?\*\/$/,
3183
+ // css, less, scss
3184
+ /^#pragma ((?:end)?region) ([\w*-]+)$/,
3185
+ // C, C++
3186
+ /^<!-- #?((?:end)?region) ([\w*-]+) -->$/,
3187
+ // HTML, markdown
3188
+ /^#((?:End )Region) ([\w*-]+)$/,
3189
+ // Visual Basic
3190
+ /^::#((?:end)region) ([\w*-]+)$/,
3191
+ // Bat
3192
+ /^# ?((?:end)?region) ([\w*-]+)$/
3193
+ // C#, PHP, Powershell, Python, perl & misc
3194
+ ];
3195
+ let regexp = null;
3196
+ let start = -1;
3197
+ for (const [lineId, line] of lines.entries()) {
3198
+ if (regexp === null) {
3199
+ for (const reg of regionRegexps) {
3200
+ if (testLine(line, reg, regionName)) {
3201
+ start = lineId + 1;
3202
+ regexp = reg;
3203
+ break;
3204
+ }
3507
3205
  }
3206
+ } else if (testLine(line, regexp, regionName, true)) {
3207
+ return { start, end: lineId, regexp };
3508
3208
  }
3509
3209
  }
3510
- return false;
3210
+ return null;
3511
3211
  }
3512
- function findParentToken(tokens, index) {
3513
- const targetLevel = tokens[index].level - 1;
3514
- for (let currentTokenIndex = index - 1; currentTokenIndex >= 0; currentTokenIndex--) {
3515
- if (tokens[currentTokenIndex].level === targetLevel) {
3516
- return tokens[currentTokenIndex];
3212
+ function transformSnippet(md2, options, id) {
3213
+ const slideId = id.match(/(\d+)\.md$/)?.[1];
3214
+ if (!slideId)
3215
+ return md2;
3216
+ const data = options.data;
3217
+ const slideInfo = data.slides[+slideId - 1];
3218
+ const dir = path.dirname(slideInfo.source?.filepath ?? options.entry ?? options.userRoot);
3219
+ return md2.replace(
3220
+ /^<<< *(.+?)(#[\w-]+)? *(?: (\S+?))? *(\{.*)?$/mg,
3221
+ (full, filepath = "", regionName = "", lang = "", meta = "") => {
3222
+ const firstLine = `\`\`\`${lang || path.extname(filepath).slice(1)} ${meta}`;
3223
+ const src = /^\@[\/]/.test(filepath) ? path.resolve(options.userRoot, filepath.slice(2)) : path.resolve(dir, filepath);
3224
+ data.watchFiles.push(src);
3225
+ const isAFile = fs3.statSync(src).isFile();
3226
+ if (!fs3.existsSync(src) || !isAFile) {
3227
+ throw new Error(isAFile ? `Code snippet path not found: ${src}` : `Invalid code snippet option`);
3228
+ }
3229
+ let content = fs3.readFileSync(src, "utf8");
3230
+ slideInfo.snippetsUsed ??= {};
3231
+ slideInfo.snippetsUsed[src] = content;
3232
+ if (regionName) {
3233
+ const lines = content.split(/\r?\n/);
3234
+ const region = findRegion(lines, regionName.slice(1));
3235
+ if (region) {
3236
+ content = dedent(
3237
+ lines.slice(region.start, region.end).filter((line) => !region.regexp.test(line.trim())).join("\n")
3238
+ );
3239
+ }
3240
+ }
3241
+ return `${firstLine}
3242
+ ${content}
3243
+ \`\`\``;
3517
3244
  }
3518
- }
3519
- return void 0;
3520
- }
3521
- function isTodoItem(tokens, index) {
3522
- return isInline(tokens[index]) && isParagraph(tokens[index - 1]) && isListItem(tokens[index - 2]) && startsWithTodoMarkdown(tokens[index]);
3523
- }
3524
- function todoify(token, options) {
3525
- if (token.children == null) {
3526
- return;
3527
- }
3528
- const id = generateIdForToken(token);
3529
- token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id));
3530
- token.children[1].content = token.children[1].content.replace(checkboxRegex, "");
3531
- if (options.label) {
3532
- token.children.splice(1, 0, createLabelBeginToken(id));
3533
- token.children.push(createLabelEndToken());
3534
- }
3245
+ );
3535
3246
  }
3536
- function generateIdForToken(token) {
3537
- if (token.map) {
3538
- return `task-item-${token.map[0]}`;
3247
+
3248
+ // node/plugins/markdown.ts
3249
+ var shiki;
3250
+ var shikiOptions;
3251
+ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
3252
+ const { data: { config }, roots, mode, entry, clientRoot } = options;
3253
+ const setups = [];
3254
+ const entryPath = slash(entry);
3255
+ if (config.highlighter === "shiki") {
3256
+ const [
3257
+ options2,
3258
+ { getHighlighter, bundledLanguages },
3259
+ markdownItShiki,
3260
+ transformerTwoslash
3261
+ ] = await Promise.all([
3262
+ loadShikiSetups(clientRoot, roots),
3263
+ import("shiki").then(({ getHighlighter: getHighlighter2, bundledLanguages: bundledLanguages2 }) => ({ bundledLanguages: bundledLanguages2, getHighlighter: getHighlighter2 })),
3264
+ import("@shikijs/markdown-it/core").then(({ fromHighlighter }) => fromHighlighter),
3265
+ import("@shikijs/vitepress-twoslash").then(({ transformerTwoslash: transformerTwoslash2 }) => transformerTwoslash2)
3266
+ ]);
3267
+ shikiOptions = options2;
3268
+ shiki = await getHighlighter({
3269
+ ...options2,
3270
+ langs: options2.langs ?? Object.keys(bundledLanguages),
3271
+ themes: "themes" in options2 ? Object.values(options2.themes) : [options2.theme]
3272
+ });
3273
+ const transformers = [
3274
+ ...options2.transformers || [],
3275
+ transformerTwoslash({
3276
+ explicitTrigger: true,
3277
+ twoslashOptions: {
3278
+ handbookOptions: {
3279
+ noErrorValidation: true
3280
+ }
3281
+ }
3282
+ }),
3283
+ {
3284
+ pre(pre) {
3285
+ this.addClassToHast(pre, "slidev-code");
3286
+ delete pre.properties.tabindex;
3287
+ },
3288
+ postprocess(code) {
3289
+ return escapeVueInCode(code);
3290
+ }
3291
+ }
3292
+ ];
3293
+ const plugin = markdownItShiki(shiki, {
3294
+ ...options2,
3295
+ transformers
3296
+ });
3297
+ setups.push((md2) => md2.use(plugin));
3539
3298
  } else {
3540
- return `task-item-${Math.ceil(Math.random() * (1e4 * 1e3) - 1e3)}`;
3299
+ setups.push((md2) => md2.use(markdownItPrism));
3541
3300
  }
3301
+ if (config.mdc)
3302
+ setups.push((md2) => md2.use(Mdc));
3303
+ const KatexOptions = await loadSetups(options.clientRoot, roots, "katex.ts", {}, { strict: false }, false);
3304
+ return Markdown({
3305
+ include: [/\.md$/],
3306
+ wrapperClasses: "",
3307
+ headEnabled: false,
3308
+ frontmatter: false,
3309
+ escapeCodeTagInterpolation: false,
3310
+ markdownItOptions: {
3311
+ quotes: `""''`,
3312
+ html: true,
3313
+ xhtmlOut: true,
3314
+ linkify: true,
3315
+ ...mdOptions?.markdownItOptions
3316
+ },
3317
+ ...mdOptions,
3318
+ markdownItSetup(md2) {
3319
+ md2.use(mila, {
3320
+ attrs: {
3321
+ target: "_blank",
3322
+ rel: "noopener"
3323
+ }
3324
+ });
3325
+ md2.use(mif);
3326
+ md2.use(taskLists, { enabled: true, lineNumber: true, label: true });
3327
+ md2.use(math_plugin, KatexOptions);
3328
+ setups.forEach((i) => i(md2));
3329
+ mdOptions?.markdownItSetup?.(md2);
3330
+ },
3331
+ transforms: {
3332
+ before(code, id) {
3333
+ if (id === entryPath)
3334
+ return "";
3335
+ const monaco = config.monaco === true || config.monaco === mode ? transformMarkdownMonaco : truncateMancoMark;
3336
+ if (config.highlighter === "shiki")
3337
+ code = transformMagicMove(code, shiki, shikiOptions);
3338
+ code = transformSlotSugar(code);
3339
+ code = transformSnippet(code, options, id);
3340
+ code = transformMermaid(code);
3341
+ code = transformPlantUml(code, config.plantUmlServer);
3342
+ code = monaco(code);
3343
+ code = transformHighlighter(code);
3344
+ code = transformPageCSS(code, id);
3345
+ code = transformKaTex(code);
3346
+ return code;
3347
+ }
3348
+ }
3349
+ });
3542
3350
  }
3543
- function createCheckboxToken(token, enabled, id) {
3544
- const checkbox = new Token("taskListItemCheckbox", "", 0);
3545
- if (!enabled) {
3546
- checkbox.attrSet("disabled", "true");
3547
- }
3548
- if (token.map) {
3549
- checkbox.attrSet("line", token.map[0].toString());
3550
- }
3551
- checkbox.attrSet("id", id);
3552
- const checkboxRegexResult = checkboxRegex.exec(token.content);
3553
- const isChecked = checkboxRegexResult?.[1].toLowerCase() === "x";
3554
- if (isChecked) {
3555
- checkbox.attrSet("checked", "true");
3556
- }
3557
- return checkbox;
3558
- }
3559
- function createLabelBeginToken(id) {
3560
- const labelBeginToken = new Token("taskListItemLabel_open", "", 1);
3561
- labelBeginToken.attrSet("id", id);
3562
- return labelBeginToken;
3351
+ function transformKaTex(md2) {
3352
+ return md2.replace(
3353
+ /^\$\$(?:\s*{([\d\w*,\|-]+)}\s*?({.*?})?\s*?)?\n([\s\S]+?)^\$\$/mg,
3354
+ (full, rangeStr = "", options = "", code) => {
3355
+ const ranges = rangeStr.split(/\|/g).map((i) => i.trim());
3356
+ code = code.trimEnd();
3357
+ options = options.trim() || "{}";
3358
+ return `<KaTexBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>
3359
+
3360
+ $$
3361
+ ${code}
3362
+ $$
3363
+ </KaTexBlockWrapper>
3364
+ `;
3365
+ }
3366
+ );
3563
3367
  }
3564
- function createLabelEndToken() {
3565
- return new Token("taskListItemLabel_close", "", -1);
3368
+ function transformMarkdownMonaco(md2) {
3369
+ md2 = md2.replace(
3370
+ /^```(\w+?)\s*{monaco-diff}\s*?({.*?})?\s*?\n([\s\S]+?)^~~~\s*?\n([\s\S]+?)^```/mg,
3371
+ (full, lang = "ts", options = "{}", code, diff) => {
3372
+ lang = lang.trim();
3373
+ options = options.trim() || "{}";
3374
+ const encoded = lz.compressToBase64(code);
3375
+ const encodedDiff = lz.compressToBase64(diff);
3376
+ return `<Monaco code-lz="${encoded}" diff-lz="${encodedDiff}" lang="${lang}" v-bind="${options}" />`;
3377
+ }
3378
+ );
3379
+ md2 = md2.replace(
3380
+ /^```(\w+?)\s*{monaco}\s*?({.*?})?\s*?\n([\s\S]+?)^```/mg,
3381
+ (full, lang = "ts", options = "{}", code) => {
3382
+ lang = lang.trim();
3383
+ options = options.trim() || "{}";
3384
+ const encoded = lz.compressToBase64(code);
3385
+ return `<Monaco code-lz="${encoded}" lang="${lang}" v-bind="${options}" />`;
3386
+ }
3387
+ );
3388
+ return md2;
3566
3389
  }
3567
- function isInline(token) {
3568
- return token.type === "inline";
3390
+ function scanMonacoModules(md2) {
3391
+ const typeModules = /* @__PURE__ */ new Set();
3392
+ md2.replace(
3393
+ /^```(\w+?)\s*{monaco([\w:,-]*)}[\s\n]*([\s\S]+?)^```/mg,
3394
+ (full, lang = "ts", options, code) => {
3395
+ options = options || "";
3396
+ lang = lang.trim();
3397
+ if (lang === "ts" || lang === "typescript") {
3398
+ Array.from(code.matchAll(/\s+from\s+(["'])([\/\w@-]+)\1/g)).map((i) => i[2]).filter(isTruthy).map((i) => typeModules.add(i));
3399
+ }
3400
+ return "";
3401
+ }
3402
+ );
3403
+ return Array.from(typeModules);
3569
3404
  }
3570
- function isParagraph(token) {
3571
- return token.type === "paragraph_open";
3405
+ function truncateMancoMark(md2) {
3406
+ return md2.replace(/{monaco.*?}/g, "");
3572
3407
  }
3573
- function isListItem(token) {
3574
- return token.type === "list_item_open";
3408
+ function transformSlotSugar(md2) {
3409
+ const lines = md2.split(/\r?\n/g);
3410
+ let prevSlot = false;
3411
+ const { isLineInsideCodeblocks } = getCodeBlocks(md2);
3412
+ lines.forEach((line, idx) => {
3413
+ if (isLineInsideCodeblocks(idx))
3414
+ return;
3415
+ const match = line.trimEnd().match(/^::\s*([\w\.\-\:]+)\s*::$/);
3416
+ if (match) {
3417
+ lines[idx] = `${prevSlot ? "\n\n</template>\n" : "\n"}<template v-slot:${match[1]}="slotProps">
3418
+ `;
3419
+ prevSlot = true;
3420
+ }
3421
+ });
3422
+ if (prevSlot)
3423
+ lines[lines.length - 1] += "\n\n</template>";
3424
+ return lines.join("\n");
3575
3425
  }
3576
- function startsWithTodoMarkdown(token) {
3577
- return checkboxRegex.test(token.content);
3426
+ var reMagicMoveBlock = /^````(?:md|markdown) magic-move(?:[ ]*?({.*?})?([^\n]*?))?\n([\s\S]+?)^````$/mg;
3427
+ var reCodeBlock = /^```(\w+?)(?:\s*{([\d\w*,\|-]+)}\s*?({.*?})?(.*?))?\n([\s\S]+?)^```$/mg;
3428
+ function transformMagicMove(md2, shiki2, shikiOptions2) {
3429
+ return md2.replace(
3430
+ reMagicMoveBlock,
3431
+ (full, _options = "", _attrs = "", body) => {
3432
+ if (!shiki2 || !shikiOptions2)
3433
+ throw new Error("Shiki is required for Magic Move. You may need to set `highlighter: shiki` in your Slidev config.");
3434
+ const matches = Array.from(body.matchAll(reCodeBlock));
3435
+ if (!matches.length)
3436
+ throw new Error("Magic Move block must contain at least one code block");
3437
+ const langs = new Set(matches.map((i) => i[1]));
3438
+ if (langs.size > 1)
3439
+ throw new Error(`Magic Move block must contain code blocks with the same language, got ${Array.from(langs).join(", ")}`);
3440
+ const lang = Array.from(langs)[0];
3441
+ const magicMove = createMagicMoveMachine(
3442
+ (code) => codeToKeyedTokens(shiki2, code, {
3443
+ ...shikiOptions2,
3444
+ lang
3445
+ })
3446
+ );
3447
+ const steps = matches.map((i) => magicMove.commit((i[5] || "").trimEnd()));
3448
+ const compressed = lz.compressToBase64(JSON.stringify(steps));
3449
+ return `<ShikiMagicMove steps-lz="${compressed}" />`;
3450
+ }
3451
+ );
3578
3452
  }
3453
+ function transformHighlighter(md2) {
3454
+ return md2.replace(
3455
+ reCodeBlock,
3456
+ (full, lang = "", rangeStr = "", options = "", attrs = "", code) => {
3457
+ const ranges = rangeStr.split(/\|/g).map((i) => i.trim());
3458
+ code = code.trimEnd();
3459
+ options = options.trim() || "{}";
3460
+ return `
3461
+ <CodeBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>
3579
3462
 
3580
- // ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.0.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/toc/plugin.js
3581
- import { Optional } from "@mrdrogdrog/optional";
3582
-
3583
- // node/plugins/markdown.ts
3584
- import { encode as encode2 } from "plantuml-encoder";
3585
- import Mdc from "markdown-it-mdc";
3586
- import { codeToKeyedTokens, createMagicMoveMachine } from "shiki-magic-move/core";
3463
+ \`\`\`${lang}${attrs}
3464
+ ${code}
3465
+ \`\`\`
3587
3466
 
3588
- // node/plugins/markdown-it-katex.ts
3589
- import katex from "katex";
3590
- function isValidDelim(state, pos) {
3591
- const max = state.posMax;
3592
- let can_open = true;
3593
- let can_close = true;
3594
- const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
3595
- const nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
3596
- if (prevChar === 32 || prevChar === 9 || /* \t */
3597
- nextChar >= 48 && nextChar <= 57)
3598
- can_close = false;
3599
- if (nextChar === 32 || nextChar === 9)
3600
- can_open = false;
3467
+ </CodeBlockWrapper>`;
3468
+ }
3469
+ );
3470
+ }
3471
+ function getCodeBlocks(md2) {
3472
+ const codeblocks = Array.from(md2.matchAll(/^```[\s\S]*?^```/mg)).map((m) => {
3473
+ const start = m.index;
3474
+ const end = m.index + m[0].length;
3475
+ const startLine = md2.slice(0, start).match(/\n/g)?.length || 0;
3476
+ const endLine = md2.slice(0, end).match(/\n/g)?.length || 0;
3477
+ return [start, end, startLine, endLine];
3478
+ });
3601
3479
  return {
3602
- can_open,
3603
- can_close
3480
+ codeblocks,
3481
+ isInsideCodeblocks(idx) {
3482
+ return codeblocks.some(([s, e]) => s <= idx && idx <= e);
3483
+ },
3484
+ isLineInsideCodeblocks(line) {
3485
+ return codeblocks.some(([, , s, e]) => s <= line && line <= e);
3486
+ }
3604
3487
  };
3605
3488
  }
3606
- function math_inline(state, silent) {
3607
- let match, token, res, pos;
3608
- if (state.src[state.pos] !== "$")
3609
- return false;
3610
- res = isValidDelim(state, state.pos);
3611
- if (!res.can_open) {
3612
- if (!silent)
3613
- state.pending += "$";
3614
- state.pos += 1;
3615
- return true;
3616
- }
3617
- const start = state.pos + 1;
3618
- match = start;
3619
- while ((match = state.src.indexOf("$", match)) !== -1) {
3620
- pos = match - 1;
3621
- while (state.src[pos] === "\\")
3622
- pos -= 1;
3623
- if ((match - pos) % 2 === 1)
3624
- break;
3625
- match += 1;
3626
- }
3627
- if (match === -1) {
3628
- if (!silent)
3629
- state.pending += "$";
3630
- state.pos = start;
3631
- return true;
3632
- }
3633
- if (match - start === 0) {
3634
- if (!silent)
3635
- state.pending += "$$";
3636
- state.pos = start + 1;
3637
- return true;
3638
- }
3639
- res = isValidDelim(state, match);
3640
- if (!res.can_close) {
3641
- if (!silent)
3642
- state.pending += "$";
3643
- state.pos = start;
3644
- return true;
3489
+ function transformPageCSS(md2, id) {
3490
+ const page = id.match(/(\d+)\.md$/)?.[1];
3491
+ if (!page)
3492
+ return md2;
3493
+ const { isInsideCodeblocks } = getCodeBlocks(md2);
3494
+ const result = md2.replace(
3495
+ /(\n<style[^>]*?>)([\s\S]+?)(<\/style>)/g,
3496
+ (full, start, css, end, index) => {
3497
+ if (index < 0 || isInsideCodeblocks(index))
3498
+ return full;
3499
+ if (!start.includes("scoped"))
3500
+ start = start.replace("<style", "<style scoped");
3501
+ return `${start}
3502
+ ${css}${end}`;
3503
+ }
3504
+ );
3505
+ return result;
3506
+ }
3507
+ function transformMermaid(md2) {
3508
+ return md2.replace(/^```mermaid\s*?({.*?})?\n([\s\S]+?)\n```/mg, (full, options = "", code = "") => {
3509
+ code = code.trim();
3510
+ options = options.trim() || "{}";
3511
+ const encoded = lz.compressToBase64(code);
3512
+ return `<Mermaid code-lz="${encoded}" v-bind="${options}" />`;
3513
+ });
3514
+ }
3515
+ function transformPlantUml(md2, server) {
3516
+ return md2.replace(/^```plantuml\s*?({.*?})?\n([\s\S]+?)\n```/mg, (full, options = "", content = "") => {
3517
+ const code = encodePlantUml(content.trim());
3518
+ options = options.trim() || "{}";
3519
+ return `<PlantUml :code="'${code}'" :server="'${server}'" v-bind="${options}" />`;
3520
+ });
3521
+ }
3522
+ function escapeVueInCode(md2) {
3523
+ return md2.replace(/{{/g, "&lbrace;&lbrace;");
3524
+ }
3525
+ async function loadShikiSetups(clientRoot, roots) {
3526
+ const result = await loadSetups(
3527
+ clientRoot,
3528
+ roots,
3529
+ "shiki.ts",
3530
+ {
3531
+ /** @deprecated */
3532
+ async loadTheme(path2) {
3533
+ console.warn("[slidev] `loadTheme` in `setup/shiki.ts` is deprecated. Pass directly the theme name it's supported by Shiki. For custom themes, load it manually via `JSON.parse(fs.readFileSync(path, 'utf-8'))` and pass the raw JSON object instead.");
3534
+ return JSON.parse(await fs4.readFile(path2, "utf-8"));
3535
+ }
3536
+ },
3537
+ {},
3538
+ false
3539
+ );
3540
+ if ("theme" in result && "themes" in result)
3541
+ delete result.theme;
3542
+ if (result.theme && typeof result.theme !== "string" && !result.theme.name && !result.theme.tokenColors) {
3543
+ result.themes = result.theme;
3544
+ delete result.theme;
3645
3545
  }
3646
- if (!silent) {
3647
- token = state.push("math_inline", "math", 0);
3648
- token.markup = "$";
3649
- token.content = state.src.slice(start, match);
3546
+ if (!result.theme && !result.themes) {
3547
+ result.themes = {
3548
+ dark: "vitesse-dark",
3549
+ light: "vitesse-light"
3550
+ };
3650
3551
  }
3651
- state.pos = match + 1;
3652
- return true;
3552
+ if (result.themes)
3553
+ result.defaultColor = false;
3554
+ return result;
3653
3555
  }
3654
- function math_block(state, start, end, silent) {
3655
- let firstLine;
3656
- let lastLine;
3657
- let next;
3658
- let lastPos;
3659
- let found = false;
3660
- let pos = state.bMarks[start] + state.tShift[start];
3661
- let max = state.eMarks[start];
3662
- if (pos + 2 > max)
3663
- return false;
3664
- if (state.src.slice(pos, pos + 2) !== "$$")
3665
- return false;
3666
- pos += 2;
3667
- firstLine = state.src.slice(pos, max);
3668
- if (silent)
3669
- return true;
3670
- if (firstLine.trim().slice(-2) === "$$") {
3671
- firstLine = firstLine.trim().slice(0, -2);
3672
- found = true;
3673
- }
3674
- for (next = start; !found; ) {
3675
- next++;
3676
- if (next >= end)
3677
- break;
3678
- pos = state.bMarks[next] + state.tShift[next];
3679
- max = state.eMarks[next];
3680
- if (pos < max && state.tShift[next] < state.blkIndent) {
3681
- break;
3682
- }
3683
- if (state.src.slice(pos, max).trim().slice(-2) === "$$") {
3684
- lastPos = state.src.slice(0, max).lastIndexOf("$$");
3685
- lastLine = state.src.slice(pos, lastPos);
3686
- found = true;
3687
- }
3556
+
3557
+ // node/plugins/loaders.ts
3558
+ var regexId = /^\/\@slidev\/slide\/(\d+)\.(md|json)(?:\?import)?$/;
3559
+ var regexIdQuery = /(\d+?)\.(md|json|frontmatter)$/;
3560
+ var templateInjectionMarker = "/* @slidev-injection */";
3561
+ var templateImportContextUtils = `import {
3562
+ useSlideContext,
3563
+ provideFrontmatter as _provideFrontmatter,
3564
+ frontmatterToProps as _frontmatterToProps,
3565
+ } from "@slidev/client/context.ts"`.replace(/\n\s*/g, " ");
3566
+ var templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = useSlideContext()`;
3567
+ function getBodyJson(req) {
3568
+ return new Promise((resolve5, reject) => {
3569
+ let body = "";
3570
+ req.on("data", (chunk) => body += chunk);
3571
+ req.on("error", reject);
3572
+ req.on("end", () => {
3573
+ try {
3574
+ resolve5(JSON.parse(body) || {});
3575
+ } catch (e) {
3576
+ reject(e);
3577
+ }
3578
+ });
3579
+ });
3580
+ }
3581
+ var md = Markdown2({ html: true });
3582
+ md.use(mila2, {
3583
+ attrs: {
3584
+ target: "_blank",
3585
+ rel: "noopener"
3688
3586
  }
3689
- state.line = next + 1;
3690
- const token = state.push("math_block", "math", 0);
3691
- token.block = true;
3692
- token.content = (firstLine && firstLine.trim() ? `${firstLine}
3693
- ` : "") + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
3694
- token.map = [start, state.line];
3695
- token.markup = "$$";
3696
- return true;
3587
+ });
3588
+ function renderNote(text = "") {
3589
+ let clickCount = 0;
3590
+ const html = md.render(
3591
+ text.replace(/\[click(?::(\d+))?\]/gi, (_, count = 1) => {
3592
+ clickCount += Number(count);
3593
+ return `<span class="slidev-note-click-mark" data-clicks="${clickCount}"></span>`;
3594
+ })
3595
+ );
3596
+ return html;
3697
3597
  }
3698
- function math_plugin(md2, options) {
3699
- options = options || {};
3700
- const katexInline = function(latex) {
3701
- options.displayMode = false;
3702
- try {
3703
- return katex.renderToString(latex, options);
3704
- } catch (error) {
3705
- if (options.throwOnError)
3706
- console.warn(error);
3707
- return latex;
3708
- }
3709
- };
3710
- const inlineRenderer = function(tokens, idx) {
3711
- return katexInline(tokens[idx].content);
3712
- };
3713
- const katexBlock = function(latex) {
3714
- options.displayMode = true;
3715
- try {
3716
- return `<p>${katex.renderToString(latex, options)}</p>`;
3717
- } catch (error) {
3718
- if (options.throwOnError)
3719
- console.warn(error);
3720
- return latex;
3721
- }
3722
- };
3723
- const blockRenderer = function(tokens, idx) {
3724
- return `${katexBlock(tokens[idx].content)}
3725
- `;
3598
+ function withRenderedNote(data) {
3599
+ return {
3600
+ ...data,
3601
+ noteHTML: renderNote(data?.note)
3726
3602
  };
3727
- md2.inline.ruler.after("escape", "math_inline", math_inline);
3728
- md2.block.ruler.after("blockquote", "math_block", math_block, {
3729
- alt: ["paragraph", "reference", "blockquote", "list"]
3730
- });
3731
- md2.renderer.rules.math_inline = inlineRenderer;
3732
- md2.renderer.rules.math_block = blockRenderer;
3733
3603
  }
3604
+ function createSlidesLoader({ data, clientRoot, roots, remote, mode, userRoot }, pluginOptions, serverOptions) {
3605
+ const slidePrefix = "/@slidev/slides/";
3606
+ const hmrPages = /* @__PURE__ */ new Set();
3607
+ let server;
3608
+ let _layouts_cache_time = 0;
3609
+ let _layouts_cache = {};
3610
+ return [
3611
+ {
3612
+ name: "slidev:loader",
3613
+ configureServer(_server) {
3614
+ server = _server;
3615
+ updateServerWatcher();
3616
+ server.middlewares.use(async (req, res, next) => {
3617
+ const match = req.url?.match(regexId);
3618
+ if (!match)
3619
+ return next();
3620
+ const [, no, type] = match;
3621
+ const idx = Number.parseInt(no);
3622
+ if (type === "json" && req.method === "GET") {
3623
+ res.write(JSON.stringify(withRenderedNote(data.slides[idx])));
3624
+ return res.end();
3625
+ }
3626
+ if (type === "json" && req.method === "POST") {
3627
+ const body = await getBodyJson(req);
3628
+ const slide = data.slides[idx];
3629
+ if (body.content && body.content !== slide.source.content)
3630
+ hmrPages.add(idx);
3631
+ Object.assign(slide.source, body);
3632
+ parser.prettifySlide(slide.source);
3633
+ await parser.save(data.markdownFiles[slide.source.filepath]);
3634
+ res.statusCode = 200;
3635
+ res.write(JSON.stringify(withRenderedNote(slide)));
3636
+ return res.end();
3637
+ }
3638
+ next();
3639
+ });
3640
+ },
3641
+ async handleHotUpdate(ctx) {
3642
+ if (!data.watchFiles.includes(ctx.file))
3643
+ return;
3644
+ await ctx.read();
3645
+ const newData = await serverOptions.loadData?.();
3646
+ if (!newData)
3647
+ return [];
3648
+ const moduleIds = /* @__PURE__ */ new Set();
3649
+ if (data.slides.length !== newData.slides.length) {
3650
+ moduleIds.add("/@slidev/routes");
3651
+ range(newData.slides.length).map((i) => hmrPages.add(i));
3652
+ }
3653
+ if (!equal(data.headmatter.defaults, newData.headmatter.defaults)) {
3654
+ moduleIds.add("/@slidev/routes");
3655
+ range(data.slides.length).map((i) => hmrPages.add(i));
3656
+ }
3657
+ if (!equal(data.config, newData.config))
3658
+ moduleIds.add("/@slidev/configs");
3659
+ if (!equal(data.features, newData.features)) {
3660
+ setTimeout(() => {
3661
+ ctx.server.ws.send({ type: "full-reload" });
3662
+ }, 1);
3663
+ }
3664
+ const length = Math.max(data.slides.length, newData.slides.length);
3665
+ for (let i = 0; i < length; i++) {
3666
+ const a = data.slides[i];
3667
+ const b = newData.slides[i];
3668
+ if (a?.content.trim() === b?.content.trim() && a?.title?.trim() === b?.title?.trim() && equal(a.frontmatter, b.frontmatter) && Object.entries(a.snippetsUsed ?? {}).every(([file, oldContent]) => {
3669
+ try {
3670
+ const newContent = fs5.readFileSync(file, "utf-8");
3671
+ return oldContent === newContent;
3672
+ } catch {
3673
+ return false;
3674
+ }
3675
+ })) {
3676
+ if (a?.note !== b?.note) {
3677
+ ctx.server.ws.send({
3678
+ type: "custom",
3679
+ event: "slidev-update-note",
3680
+ data: {
3681
+ id: i,
3682
+ note: b.note || "",
3683
+ noteHTML: renderNote(b.note || "")
3684
+ }
3685
+ });
3686
+ }
3687
+ continue;
3688
+ }
3689
+ ctx.server.ws.send({
3690
+ type: "custom",
3691
+ event: "slidev-update",
3692
+ data: {
3693
+ id: i,
3694
+ data: withRenderedNote(newData.slides[i])
3695
+ }
3696
+ });
3697
+ hmrPages.add(i);
3698
+ }
3699
+ Object.assign(data, newData);
3700
+ if (hmrPages.size > 0)
3701
+ moduleIds.add("/@slidev/titles.md");
3702
+ const vueModules = Array.from(hmrPages).flatMap((i) => [
3703
+ ctx.server.moduleGraph.getModuleById(`${slidePrefix}${i + 1}.frontmatter`),
3704
+ ctx.server.moduleGraph.getModuleById(`${slidePrefix}${i + 1}.md`)
3705
+ ]);
3706
+ hmrPages.clear();
3707
+ const moduleEntries = [
3708
+ ...vueModules,
3709
+ ...Array.from(moduleIds).map((id) => ctx.server.moduleGraph.getModuleById(id))
3710
+ ].filter(notNullish).filter((i) => !i.id?.startsWith("/@id/@vite-icons"));
3711
+ updateServerWatcher();
3712
+ return moduleEntries;
3713
+ },
3714
+ resolveId(id) {
3715
+ if (id.startsWith(slidePrefix) || id.startsWith("/@slidev/"))
3716
+ return id;
3717
+ return null;
3718
+ },
3719
+ load(id) {
3720
+ if (id === "/@slidev/routes")
3721
+ return generateRoutes();
3722
+ if (id === "/@slidev/layouts")
3723
+ return generateLayouts();
3724
+ if (id === "/@slidev/styles")
3725
+ return generateUserStyles();
3726
+ if (id === "/@slidev/monaco-types")
3727
+ return generateMonacoTypes();
3728
+ if (id === "/@slidev/configs")
3729
+ return generateConfigs();
3730
+ if (id === "/@slidev/global-components/top")
3731
+ return generateGlobalComponents("top");
3732
+ if (id === "/@slidev/global-components/bottom")
3733
+ return generateGlobalComponents("bottom");
3734
+ if (id === "/@slidev/custom-nav-controls")
3735
+ return generateCustomNavControls();
3736
+ if (id === "/@slidev/shiki")
3737
+ return generteShikiBundle();
3738
+ if (id === "/@slidev/titles.md") {
3739
+ return {
3740
+ code: data.slides.map(({ title }, i) => `<template ${i === 0 ? "v-if" : "v-else-if"}="+no === ${i + 1}">
3734
3741
 
3735
- // node/plugins/markdown-it-prism.ts
3736
- import { createRequire } from "node:module";
3737
- import Prism from "prismjs";
3738
- import loadLanguages from "prismjs/components/index.js";
3739
- import * as htmlparser2 from "htmlparser2";
3740
- var require2 = createRequire(import.meta.url);
3741
- var Tag = class {
3742
- tagname;
3743
- attributes;
3744
- constructor(tagname, attributes) {
3745
- this.tagname = tagname;
3746
- this.attributes = attributes;
3747
- }
3748
- asOpen() {
3749
- return `<${this.tagname} ${Object.entries(this.attributes).map(([key, value]) => `${key}="${value}"`).join(" ")}>`;
3742
+ ${title}
3743
+
3744
+ </template>`).join(""),
3745
+ map: { mappings: "" }
3746
+ };
3747
+ }
3748
+ if (id.startsWith(slidePrefix)) {
3749
+ const remaning = id.slice(slidePrefix.length);
3750
+ const match = remaning.match(regexIdQuery);
3751
+ if (match) {
3752
+ const [, no, type] = match;
3753
+ const pageNo = Number.parseInt(no) - 1;
3754
+ const slide = data.slides[pageNo];
3755
+ if (!slide)
3756
+ return;
3757
+ if (type === "md") {
3758
+ return {
3759
+ code: slide?.content,
3760
+ map: { mappings: "" }
3761
+ };
3762
+ } else if (type === "frontmatter") {
3763
+ const slideBase = {
3764
+ ...withRenderedNote(slide),
3765
+ frontmatter: void 0,
3766
+ source: void 0,
3767
+ // remove raw content in build, optimize the bundle size
3768
+ ...mode === "build" ? { raw: "", content: "", note: "" } : {}
3769
+ };
3770
+ const fontmatter = getFrontmatter(pageNo);
3771
+ return {
3772
+ code: [
3773
+ "// @unocss-include",
3774
+ 'import { reactive, computed } from "vue"',
3775
+ `export const frontmatter = reactive(${JSON.stringify(fontmatter)})`,
3776
+ `export const meta = reactive({
3777
+ layout: computed(() => frontmatter.layout),
3778
+ transition: computed(() => frontmatter.transition),
3779
+ class: computed(() => frontmatter.class),
3780
+ clicks: computed(() => frontmatter.clicks),
3781
+ name: computed(() => frontmatter.name),
3782
+ preload: computed(() => frontmatter.preload),
3783
+ slide: {
3784
+ ...(${JSON.stringify(slideBase)}),
3785
+ frontmatter,
3786
+ filepath: ${JSON.stringify(slide.source.filepath)},
3787
+ start: ${JSON.stringify(slide.source.start)},
3788
+ id: ${pageNo},
3789
+ no: ${no},
3790
+ },
3791
+ __clicksContext: null,
3792
+ __preloaded: false,
3793
+ })`,
3794
+ "export default frontmatter",
3795
+ // handle HMR, update frontmatter with update
3796
+ "if (import.meta.hot) {",
3797
+ " import.meta.hot.accept(({ frontmatter: update }) => {",
3798
+ " if(!update) return",
3799
+ " Object.keys(frontmatter).forEach(key => {",
3800
+ " if (!(key in update)) delete frontmatter[key]",
3801
+ " })",
3802
+ " Object.assign(frontmatter, update)",
3803
+ " })",
3804
+ "}"
3805
+ ].join("\n"),
3806
+ map: { mappings: "" }
3807
+ };
3808
+ }
3809
+ }
3810
+ return {
3811
+ code: "",
3812
+ map: { mappings: "" }
3813
+ };
3814
+ }
3815
+ }
3816
+ },
3817
+ {
3818
+ name: "slidev:layout-transform:pre",
3819
+ enforce: "pre",
3820
+ async transform(code, id) {
3821
+ if (!id.startsWith(slidePrefix))
3822
+ return;
3823
+ const remaning = id.slice(slidePrefix.length);
3824
+ const match = remaning.match(regexIdQuery);
3825
+ if (!match)
3826
+ return;
3827
+ const [, no, type] = match;
3828
+ if (type !== "md")
3829
+ return;
3830
+ const pageNo = Number.parseInt(no) - 1;
3831
+ return transformMarkdown(code, pageNo);
3832
+ }
3833
+ },
3834
+ {
3835
+ name: "slidev:context-transform:pre",
3836
+ enforce: "pre",
3837
+ async transform(code, id) {
3838
+ if (!id.endsWith(".vue") || id.includes("/@slidev/client/") || id.includes("/packages/client/"))
3839
+ return;
3840
+ return transformVue(code);
3841
+ }
3842
+ },
3843
+ {
3844
+ name: "slidev:title-transform:pre",
3845
+ enforce: "pre",
3846
+ transform(code, id) {
3847
+ if (id !== "/@slidev/titles.md")
3848
+ return;
3849
+ return transformTitles(code);
3850
+ }
3851
+ },
3852
+ {
3853
+ name: "slidev:slide-transform:post",
3854
+ enforce: "post",
3855
+ transform(code, id) {
3856
+ if (!id.match(/\/@slidev\/slides\/\d+\.md($|\?)/))
3857
+ return;
3858
+ const replaced = code.replace("if (_rerender_only)", "if (false)");
3859
+ if (replaced !== code)
3860
+ return replaced;
3861
+ }
3862
+ },
3863
+ {
3864
+ name: "slidev:index-html-transform",
3865
+ transformIndexHtml() {
3866
+ const { info, author, keywords } = data.headmatter;
3867
+ return [
3868
+ {
3869
+ tag: "title",
3870
+ children: getTitle()
3871
+ },
3872
+ info && {
3873
+ tag: "meta",
3874
+ attrs: {
3875
+ name: "description",
3876
+ content: info
3877
+ }
3878
+ },
3879
+ author && {
3880
+ tag: "meta",
3881
+ attrs: {
3882
+ name: "author",
3883
+ content: author
3884
+ }
3885
+ },
3886
+ keywords && {
3887
+ tag: "meta",
3888
+ attrs: {
3889
+ name: "keywords",
3890
+ content: Array.isArray(keywords) ? keywords.join(", ") : keywords
3891
+ }
3892
+ }
3893
+ ].filter(isTruthy2);
3894
+ }
3895
+ }
3896
+ ];
3897
+ function updateServerWatcher() {
3898
+ if (!server)
3899
+ return;
3900
+ server.watcher.add(data.watchFiles);
3750
3901
  }
3751
- asClosed() {
3752
- return `</${this.tagname}>`;
3902
+ function getFrontmatter(pageNo) {
3903
+ return {
3904
+ ...data.headmatter?.defaults || {},
3905
+ ...data.slides[pageNo]?.frontmatter || {}
3906
+ };
3753
3907
  }
3754
- };
3755
- var DEFAULTS = {
3756
- plugins: [],
3757
- init: () => {
3758
- },
3759
- defaultLanguageForUnknown: void 0,
3760
- defaultLanguageForUnspecified: void 0,
3761
- defaultLanguage: void 0
3762
- };
3763
- function loadPrismLang(lang) {
3764
- if (!lang)
3765
- return void 0;
3766
- let langObject = Prism.languages[lang];
3767
- if (langObject === void 0) {
3768
- loadLanguages([lang]);
3769
- langObject = Prism.languages[lang];
3908
+ async function transformMarkdown(code, pageNo) {
3909
+ const layouts = await getLayouts();
3910
+ const frontmatter = getFrontmatter(pageNo);
3911
+ let layoutName = frontmatter?.layout || (pageNo === 0 ? "cover" : "default");
3912
+ if (!layouts[layoutName]) {
3913
+ console.error(red(`
3914
+ Unknown layout "${bold(layoutName)}".${yellow(" Available layouts are:")}`) + Object.keys(layouts).map((i, idx) => (idx % 3 === 0 ? "\n " : "") + gray(i.padEnd(15, " "))).join(" "));
3915
+ console.error();
3916
+ layoutName = "default";
3917
+ }
3918
+ delete frontmatter.title;
3919
+ const imports = [
3920
+ `import InjectedLayout from "${toAtFS(layouts[layoutName])}"`,
3921
+ `import frontmatter from "${toAtFS(`${slidePrefix + (pageNo + 1)}.frontmatter`)}"`,
3922
+ templateImportContextUtils,
3923
+ "_provideFrontmatter(frontmatter)",
3924
+ templateInitContext,
3925
+ templateInjectionMarker
3926
+ ];
3927
+ code = code.replace(/(<script setup.*>)/g, `$1
3928
+ ${imports.join("\n")}
3929
+ `);
3930
+ const injectA = code.indexOf("<template>") + "<template>".length;
3931
+ const injectB = code.lastIndexOf("</template>");
3932
+ let body = code.slice(injectA, injectB).trim();
3933
+ if (body.startsWith("<div>") && body.endsWith("</div>"))
3934
+ body = body.slice(5, -6);
3935
+ code = `${code.slice(0, injectA)}
3936
+ <InjectedLayout v-bind="_frontmatterToProps(frontmatter,${pageNo})">
3937
+ ${body}
3938
+ </InjectedLayout>
3939
+ ${code.slice(injectB)}`;
3940
+ return code;
3770
3941
  }
3771
- return langObject;
3772
- }
3773
- function loadPrismPlugin(name) {
3774
- try {
3775
- require2(`prismjs/plugins/${name}/prism-${name}`);
3776
- } catch (e) {
3777
- throw new Error(`Cannot load Prism plugin "${name}". Please check the spelling.`);
3942
+ function transformVue(code) {
3943
+ if (code.includes(templateInjectionMarker) || code.includes("useSlideContext()"))
3944
+ return code;
3945
+ const imports = [
3946
+ templateImportContextUtils,
3947
+ templateInitContext,
3948
+ templateInjectionMarker
3949
+ ];
3950
+ const matchScript = code.match(/<script((?!setup).)*(setup)?.*>/);
3951
+ if (matchScript && matchScript[2]) {
3952
+ return code.replace(/(<script.*>)/g, `$1
3953
+ ${imports.join("\n")}
3954
+ `);
3955
+ } else if (matchScript && !matchScript[2]) {
3956
+ const matchExport = code.match(/export\s+default\s+{/);
3957
+ if (matchExport) {
3958
+ const exportIndex = (matchExport.index || 0) + matchExport[0].length;
3959
+ let component = code.slice(exportIndex);
3960
+ component = component.slice(0, component.indexOf("</script>"));
3961
+ const scriptIndex = (matchScript.index || 0) + matchScript[0].length;
3962
+ const provideImport = '\nimport { injectionSlidevContext } from "@slidev/client/constants.ts"\n';
3963
+ code = `${code.slice(0, scriptIndex)}${provideImport}${code.slice(scriptIndex)}`;
3964
+ let injectIndex = exportIndex + provideImport.length;
3965
+ let injectObject = "$slidev: { from: injectionSlidevContext },";
3966
+ const matchInject = component.match(/.*inject\s*:\s*([\[{])/);
3967
+ if (matchInject) {
3968
+ injectIndex += (matchInject.index || 0) + matchInject[0].length;
3969
+ if (matchInject[1] === "[") {
3970
+ let injects = component.slice((matchInject.index || 0) + matchInject[0].length);
3971
+ const injectEndIndex = injects.indexOf("]");
3972
+ injects = injects.slice(0, injectEndIndex);
3973
+ injectObject += injects.split(",").map((inject) => `${inject}: {from: ${inject}}`).join(",");
3974
+ return `${code.slice(0, injectIndex - 1)}{
3975
+ ${injectObject}
3976
+ }${code.slice(injectIndex + injectEndIndex + 1)}`;
3977
+ } else {
3978
+ return `${code.slice(0, injectIndex)}
3979
+ ${injectObject}
3980
+ ${code.slice(injectIndex)}`;
3981
+ }
3982
+ }
3983
+ return `${code.slice(0, injectIndex)}
3984
+ inject: { ${injectObject} },
3985
+ ${code.slice(injectIndex)}`;
3986
+ }
3987
+ }
3988
+ return `<script setup>
3989
+ ${imports.join("\n")}
3990
+ </script>
3991
+ ${code}`;
3778
3992
  }
3779
- }
3780
- function selectLanguage(options, lang) {
3781
- let langToUse = lang;
3782
- if (langToUse === "" && options.defaultLanguageForUnspecified !== void 0)
3783
- langToUse = options.defaultLanguageForUnspecified;
3784
- let prismLang = loadPrismLang(langToUse);
3785
- if (prismLang === void 0 && options.defaultLanguageForUnknown !== void 0) {
3786
- langToUse = options.defaultLanguageForUnknown;
3787
- prismLang = loadPrismLang(langToUse);
3993
+ function transformTitles(code) {
3994
+ return code.replace(/<template>\s*<div>\s*<p>/, "<template>").replace(/<\/p>\s*<\/div>\s*<\/template>/, "</template>").replace(/<script\ssetup>/, `<script setup lang="ts">
3995
+ defineProps<{ no: number | string }>()`);
3788
3996
  }
3789
- return [langToUse, prismLang];
3790
- }
3791
- function highlight(markdownit, options, text, lang) {
3792
- const [langToUse, prismLang] = selectLanguage(options, lang);
3793
- let code = text.trimEnd();
3794
- code = prismLang ? highlightPrism(code, prismLang, langToUse) : markdownit.utils.escapeHtml(code);
3795
- code = code.split(/\r?\n/g).map((line) => `<span class="line">${line}</span>`).join("\n");
3796
- const classAttribute = langToUse ? ` class="slidev-code ${markdownit.options.langPrefix}${markdownit.utils.escapeHtml(langToUse)}"` : "";
3797
- return escapeVueInCode(`<pre${classAttribute}><code>${code}</code></pre>`);
3798
- }
3799
- function highlightPrism(code, prismLang, langToUse) {
3800
- const openTags = [];
3801
- const parser2 = new htmlparser2.Parser({
3802
- onopentag(tagname, attributes) {
3803
- openTags.push(new Tag(tagname, attributes));
3804
- },
3805
- onclosetag() {
3806
- openTags.pop();
3807
- }
3808
- });
3809
- code = Prism.highlight(code, prismLang, langToUse);
3810
- code = code.split(/\r?\n/g).map((line) => {
3811
- const prefix = openTags.map((tag) => tag.asOpen()).join("");
3812
- parser2.write(line);
3813
- const postfix = openTags.reverse().map((tag) => tag.asClosed()).join("");
3814
- return prefix + line + postfix;
3815
- }).join("\n");
3816
- parser2.end();
3817
- return code;
3818
- }
3819
- function checkLanguageOption(options, optionName) {
3820
- const language = options[optionName];
3821
- if (language !== void 0 && loadPrismLang(language) === void 0)
3822
- throw new Error(`Bad option ${optionName}: There is no Prism language '${language}'.`);
3823
- }
3824
- function markdownItPrism(markdownit, useroptions) {
3825
- const options = Object.assign({}, DEFAULTS, useroptions);
3826
- checkLanguageOption(options, "defaultLanguage");
3827
- checkLanguageOption(options, "defaultLanguageForUnknown");
3828
- checkLanguageOption(options, "defaultLanguageForUnspecified");
3829
- options.defaultLanguageForUnknown = options.defaultLanguageForUnknown || options.defaultLanguage;
3830
- options.defaultLanguageForUnspecified = options.defaultLanguageForUnspecified || options.defaultLanguage;
3831
- options.plugins.forEach(loadPrismPlugin);
3832
- options.init(Prism);
3833
- markdownit.options.highlight = (text, lang) => highlight(markdownit, options, text, lang);
3834
- }
3835
-
3836
- // node/plugins/transformSnippet.ts
3837
- import path from "node:path";
3838
- import fs5 from "fs-extra";
3839
- function dedent(text) {
3840
- const lines = text.split("\n");
3841
- const minIndentLength = lines.reduce((acc, line) => {
3842
- for (let i = 0; i < line.length; i++) {
3843
- if (line[i] !== " " && line[i] !== " ")
3844
- return Math.min(i, acc);
3845
- }
3846
- return acc;
3847
- }, Number.POSITIVE_INFINITY);
3848
- if (minIndentLength < Number.POSITIVE_INFINITY)
3849
- return lines.map((x) => x.slice(minIndentLength)).join("\n");
3850
- return text;
3851
- }
3852
- function testLine(line, regexp, regionName, end = false) {
3853
- const [full, tag, name] = regexp.exec(line.trim()) || [];
3854
- return full && tag && name === regionName && tag.match(end ? /^[Ee]nd ?[rR]egion$/ : /^[rR]egion$/);
3855
- }
3856
- function findRegion(lines, regionName) {
3857
- const regionRegexps = [
3858
- /^\/\/ ?#?((?:end)?region) ([\w*-]+)$/,
3859
- // javascript, typescript, java
3860
- /^\/\* ?#((?:end)?region) ([\w*-]+) ?\*\/$/,
3861
- // css, less, scss
3862
- /^#pragma ((?:end)?region) ([\w*-]+)$/,
3863
- // C, C++
3864
- /^<!-- #?((?:end)?region) ([\w*-]+) -->$/,
3865
- // HTML, markdown
3866
- /^#((?:End )Region) ([\w*-]+)$/,
3867
- // Visual Basic
3868
- /^::#((?:end)region) ([\w*-]+)$/,
3869
- // Bat
3870
- /^# ?((?:end)?region) ([\w*-]+)$/
3871
- // C#, PHP, Powershell, Python, perl & misc
3872
- ];
3873
- let regexp = null;
3874
- let start = -1;
3875
- for (const [lineId, line] of lines.entries()) {
3876
- if (regexp === null) {
3877
- for (const reg of regionRegexps) {
3878
- if (testLine(line, reg, regionName)) {
3879
- start = lineId + 1;
3880
- regexp = reg;
3881
- break;
3882
- }
3997
+ async function getLayouts() {
3998
+ const now = Date.now();
3999
+ if (now - _layouts_cache_time < 2e3)
4000
+ return _layouts_cache;
4001
+ const layouts = {};
4002
+ for (const root of [...roots, clientRoot]) {
4003
+ const layoutPaths = await fg2("layouts/**/*.{vue,ts}", {
4004
+ cwd: root,
4005
+ absolute: true,
4006
+ suppressErrors: true
4007
+ });
4008
+ for (const layoutPath of layoutPaths) {
4009
+ const layout = basename2(layoutPath).replace(/\.\w+$/, "");
4010
+ if (layouts[layout])
4011
+ continue;
4012
+ layouts[layout] = layoutPath;
3883
4013
  }
3884
- } else if (testLine(line, regexp, regionName, true)) {
3885
- return { start, end: lineId, regexp };
3886
4014
  }
4015
+ _layouts_cache_time = now;
4016
+ _layouts_cache = layouts;
4017
+ return layouts;
3887
4018
  }
3888
- return null;
3889
- }
3890
- function transformSnippet(md2, options, id) {
3891
- const slideId = id.match(/(\d+)\.md$/)?.[1];
3892
- if (!slideId)
3893
- return md2;
3894
- const data = options.data;
3895
- const slideInfo = data.slides[+slideId - 1];
3896
- const dir = path.dirname(slideInfo.source?.filepath ?? options.entry ?? options.userRoot);
3897
- return md2.replace(
3898
- /^<<< *(.+?)(#[\w-]+)? *(?: (\S+?))? *(\{.*)?$/mg,
3899
- (full, filepath = "", regionName = "", lang = "", meta = "") => {
3900
- const firstLine = `\`\`\`${lang || path.extname(filepath).slice(1)} ${meta}`;
3901
- const src = /^\@[\/]/.test(filepath) ? path.resolve(options.userRoot, filepath.slice(2)) : path.resolve(dir, filepath);
3902
- data.watchFiles.push(src);
3903
- const isAFile = fs5.statSync(src).isFile();
3904
- if (!fs5.existsSync(src) || !isAFile) {
3905
- throw new Error(isAFile ? `Code snippet path not found: ${src}` : `Invalid code snippet option`);
3906
- }
3907
- let content = fs5.readFileSync(src, "utf8");
3908
- slideInfo.snippetsUsed ??= {};
3909
- slideInfo.snippetsUsed[src] = content;
3910
- if (regionName) {
3911
- const lines = content.split(/\r?\n/);
3912
- const region = findRegion(lines, regionName.slice(1));
3913
- if (region) {
3914
- content = dedent(
3915
- lines.slice(region.start, region.end).filter((line) => !region.regexp.test(line.trim())).join("\n")
3916
- );
4019
+ async function resolveUrl(id) {
4020
+ return toAtFS(await resolveImportPath(id, true));
4021
+ }
4022
+ function resolveUrlOfClient(name) {
4023
+ return toAtFS(join4(clientRoot, name));
4024
+ }
4025
+ async function generateUserStyles() {
4026
+ const imports = [
4027
+ `import "${resolveUrlOfClient("styles/vars.css")}"`,
4028
+ `import "${resolveUrlOfClient("styles/index.css")}"`,
4029
+ `import "${resolveUrlOfClient("styles/code.css")}"`,
4030
+ `import "${resolveUrlOfClient("styles/katex.css")}"`,
4031
+ `import "${resolveUrlOfClient("styles/transitions.css")}"`,
4032
+ `import "${resolveUrlOfClient("styles/monaco.css")}"`
4033
+ ];
4034
+ for (const root of roots) {
4035
+ const styles = [
4036
+ join4(root, "styles", "index.ts"),
4037
+ join4(root, "styles", "index.js"),
4038
+ join4(root, "styles", "index.css"),
4039
+ join4(root, "styles.css"),
4040
+ join4(root, "style.css")
4041
+ ];
4042
+ for (const style of styles) {
4043
+ if (fs5.existsSync(style)) {
4044
+ imports.push(`import "${toAtFS(style)}"`);
4045
+ continue;
3917
4046
  }
3918
4047
  }
3919
- return `${firstLine}
3920
- ${content}
3921
- \`\`\``;
3922
4048
  }
3923
- );
3924
- }
3925
-
3926
- // node/plugins/markdown.ts
3927
- var shiki;
3928
- var shikiOptions;
3929
- async function createMarkdownPlugin(options, { markdown: mdOptions }) {
3930
- const { data: { config }, roots, mode, entry, clientRoot } = options;
3931
- const setups = [];
3932
- const entryPath = slash3(entry);
3933
- if (config.highlighter === "shiki") {
3934
- const [
3935
- options2,
3936
- { getHighlighter, bundledLanguages },
3937
- markdownItShiki,
3938
- transformerTwoslash
3939
- ] = await Promise.all([
3940
- loadShikiSetups(clientRoot, roots),
3941
- import("shiki").then(({ getHighlighter: getHighlighter2, bundledLanguages: bundledLanguages2 }) => ({ bundledLanguages: bundledLanguages2, getHighlighter: getHighlighter2 })),
3942
- import("@shikijs/markdown-it/core").then(({ fromHighlighter }) => fromHighlighter),
3943
- import("@shikijs/vitepress-twoslash").then(({ transformerTwoslash: transformerTwoslash2 }) => transformerTwoslash2)
3944
- ]);
3945
- shikiOptions = options2;
3946
- shiki = await getHighlighter({
3947
- ...options2,
3948
- langs: options2.langs ?? Object.keys(bundledLanguages),
3949
- themes: "themes" in options2 ? Object.values(options2.themes) : [options2.theme]
3950
- });
3951
- const transformers = [
3952
- ...options2.transformers || [],
3953
- transformerTwoslash({
3954
- explicitTrigger: true,
3955
- twoslashOptions: {
3956
- handbookOptions: {
3957
- noErrorValidation: true
3958
- }
3959
- }
3960
- }),
3961
- {
3962
- pre(pre) {
3963
- this.addClassToHast(pre, "slidev-code");
3964
- delete pre.properties.tabindex;
3965
- },
3966
- postprocess(code) {
3967
- return escapeVueInCode(code);
3968
- }
4049
+ if (data.features.katex)
4050
+ imports.push(`import "${await resolveUrl("katex/dist/katex.min.css")}"`);
4051
+ if (data.config.highlighter === "shiki") {
4052
+ imports.push(
4053
+ `import "${await resolveUrl("@shikijs/vitepress-twoslash/style.css")}"`,
4054
+ `import "${resolveUrlOfClient("styles/shiki-twoslash.css")}"`
4055
+ );
4056
+ }
4057
+ if (data.config.css === "unocss") {
4058
+ imports.unshift(
4059
+ `import "${await resolveUrl("@unocss/reset/tailwind.css")}"`,
4060
+ 'import "uno:preflights.css"',
4061
+ 'import "uno:typography.css"',
4062
+ 'import "uno:shortcuts.css"'
4063
+ );
4064
+ imports.push('import "uno.css"');
4065
+ }
4066
+ return imports.join("\n");
4067
+ }
4068
+ async function generateMonacoTypes() {
4069
+ const typesRoot = join4(userRoot, "snippets");
4070
+ const files = await fg2(["**/*.ts", "**/*.mts", "**/*.cts"], { cwd: typesRoot });
4071
+ let result = [
4072
+ 'import * as monaco from "monaco-editor"',
4073
+ "async function addFile(mod, path) {",
4074
+ " const code = (await mod).default",
4075
+ ' monaco.languages.typescript.typescriptDefaults.addExtraLib(code, "file:///" + path)',
4076
+ ' monaco.editor.createModel(code, "javascript", monaco.Uri.file(path))',
4077
+ "}"
4078
+ ].join("\n");
4079
+ for (const file of files) {
4080
+ const url = `${toAtFS(resolve2(typesRoot, file))}?raw`;
4081
+ result += `addFile(import(${JSON.stringify(url)}), ${JSON.stringify(file)})
4082
+ `;
4083
+ }
4084
+ const deps = data.config.monacoTypesAdditionalPackages;
4085
+ if (data.config.monacoTypesSource === "local")
4086
+ deps.push(...scanMonacoModules(data.slides.map((s) => s.source.raw).join()));
4087
+ function mapModuleNameToModule(moduleSpecifier) {
4088
+ if (moduleSpecifier.startsWith("node:"))
4089
+ return "node";
4090
+ if (builtinModules.includes(moduleSpecifier))
4091
+ return "node";
4092
+ const mainPackageName = moduleSpecifier.split("/")[0];
4093
+ if (builtinModules.includes(mainPackageName) && !mainPackageName.startsWith("@"))
4094
+ return "node";
4095
+ const [a = "", b = ""] = moduleSpecifier.split("/");
4096
+ const moduleName = a.startsWith("@") ? `${a}/${b}` : a;
4097
+ return moduleName;
4098
+ }
4099
+ for (const specifier of deps) {
4100
+ if (specifier[0] === ".")
4101
+ continue;
4102
+ const moduleName = mapModuleNameToModule(specifier);
4103
+ result += `import(${JSON.stringify(`/@slidev-monaco-types/resolve/${moduleName}`)})
4104
+ `;
4105
+ }
4106
+ return result;
4107
+ }
4108
+ async function generateLayouts() {
4109
+ const imports = [];
4110
+ const layouts = objectMap(
4111
+ await getLayouts(),
4112
+ (k, v) => {
4113
+ imports.push(`import __layout_${k} from "${toAtFS(v)}"`);
4114
+ return [k, `__layout_${k}`];
3969
4115
  }
3970
- ];
3971
- const plugin = markdownItShiki(shiki, {
3972
- ...options2,
3973
- transformers
4116
+ );
4117
+ return [
4118
+ imports.join("\n"),
4119
+ `export default {
4120
+ ${Object.entries(layouts).map(([k, v]) => `"${k}": ${v}`).join(",\n")}
4121
+ }`
4122
+ ].join("\n\n");
4123
+ }
4124
+ async function generateRoutes() {
4125
+ const imports = [];
4126
+ const redirects = [];
4127
+ const layouts = await getLayouts();
4128
+ imports.push(
4129
+ `import { markRaw } from 'vue'`,
4130
+ `import __layout__end from '${layouts.end}'`
4131
+ );
4132
+ let no = 1;
4133
+ const routes = data.slides.map((i, idx) => {
4134
+ imports.push(`import n${no} from '${slidePrefix}${idx + 1}.md'`);
4135
+ imports.push(`import { meta as f${no} } from '${slidePrefix}${idx + 1}.frontmatter'`);
4136
+ const route = `{ path: '${no}', name: 'page-${no}', component: n${no}, meta: f${no} }`;
4137
+ if (i.frontmatter?.routeAlias)
4138
+ redirects.push(`{ path: '${i.frontmatter?.routeAlias}', redirect: { path: '${no}' } }`);
4139
+ no += 1;
4140
+ return route;
3974
4141
  });
3975
- setups.push((md2) => md2.use(plugin));
3976
- } else {
3977
- setups.push((md2) => md2.use(markdownItPrism));
4142
+ const routesStr = `export const rawRoutes = [
4143
+ ${routes.join(",\n")}
4144
+ ].map(markRaw)`;
4145
+ const redirectsStr = `export const redirects = [
4146
+ ${redirects.join(",\n")}
4147
+ ].map(markRaw)`;
4148
+ return [...imports, routesStr, redirectsStr].join("\n");
3978
4149
  }
3979
- if (config.mdc)
3980
- setups.push((md2) => md2.use(Mdc));
3981
- const KatexOptions = await loadSetups(options.clientRoot, roots, "katex.ts", {}, { strict: false }, false);
3982
- return Markdown2({
3983
- include: [/\.md$/],
3984
- wrapperClasses: "",
3985
- headEnabled: false,
3986
- frontmatter: false,
3987
- escapeCodeTagInterpolation: false,
3988
- markdownItOptions: {
3989
- quotes: `""''`,
3990
- html: true,
3991
- xhtmlOut: true,
3992
- linkify: true,
3993
- ...mdOptions?.markdownItOptions
3994
- },
3995
- ...mdOptions,
3996
- markdownItSetup(md2) {
3997
- md2.use(mila2, {
3998
- attrs: {
3999
- target: "_blank",
4000
- rel: "noopener"
4001
- }
4002
- });
4003
- md2.use(mif);
4004
- md2.use(taskLists, { enabled: true, lineNumber: true, label: true });
4005
- md2.use(math_plugin, KatexOptions);
4006
- setups.forEach((i) => i(md2));
4007
- mdOptions?.markdownItSetup?.(md2);
4008
- },
4009
- transforms: {
4010
- before(code, id) {
4011
- if (id === entryPath)
4012
- return "";
4013
- const monaco = config.monaco === true || config.monaco === mode ? transformMarkdownMonaco : truncateMancoMark;
4014
- if (config.highlighter === "shiki")
4015
- code = transformMagicMove(code, shiki, shikiOptions);
4016
- code = transformSlotSugar(code);
4017
- code = transformSnippet(code, options, id);
4018
- code = transformMermaid(code);
4019
- code = transformPlantUml(code, config.plantUmlServer);
4020
- code = monaco(code);
4021
- code = transformHighlighter(code);
4022
- code = transformPageCSS(code, id);
4023
- code = transformKaTex(code);
4024
- return code;
4025
- }
4150
+ function getTitle() {
4151
+ if (isString(data.config.title)) {
4152
+ const tokens = md.parseInline(data.config.title, {});
4153
+ return stringifyMarkdownTokens(tokens);
4026
4154
  }
4027
- });
4155
+ return data.config.title;
4156
+ }
4157
+ function generateConfigs() {
4158
+ const config = {
4159
+ ...data.config,
4160
+ remote,
4161
+ title: getTitle()
4162
+ };
4163
+ if (isString(config.info))
4164
+ config.info = md.render(config.info);
4165
+ return `export default ${JSON.stringify(config)}`;
4166
+ }
4167
+ async function generateGlobalComponents(layer) {
4168
+ const components = roots.flatMap((root) => {
4169
+ if (layer === "top") {
4170
+ return [
4171
+ join4(root, "global.vue"),
4172
+ join4(root, "global-top.vue"),
4173
+ join4(root, "GlobalTop.vue")
4174
+ ];
4175
+ } else {
4176
+ return [
4177
+ join4(root, "global-bottom.vue"),
4178
+ join4(root, "GlobalBottom.vue")
4179
+ ];
4180
+ }
4181
+ }).filter((i) => fs5.existsSync(i));
4182
+ const imports = components.map((i, idx) => `import __n${idx} from '${toAtFS(i)}'`).join("\n");
4183
+ const render = components.map((i, idx) => `h(__n${idx})`).join(",");
4184
+ return `
4185
+ ${imports}
4186
+ import { h } from 'vue'
4187
+ export default {
4188
+ render() {
4189
+ return [${render}]
4190
+ }
4028
4191
  }
4029
- function transformKaTex(md2) {
4030
- return md2.replace(/^\$\$(?:\s*{([\d\w*,\|-]+)}\s*?({.*?})?\s*?)?\n([\s\S]+?)^\$\$/mg, (full, rangeStr = "", options = "", code) => {
4031
- const ranges = rangeStr.split(/\|/g).map((i) => i.trim());
4032
- code = code.trimEnd();
4033
- options = options.trim() || "{}";
4034
- return `<KaTexBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>
4035
-
4036
- $$
4037
- ${code}
4038
- $$
4039
- </KaTexBlockWrapper>
4040
4192
  `;
4041
- });
4042
- }
4043
- function transformMarkdownMonaco(md2) {
4044
- md2 = md2.replace(/^```(\w+?)\s*{monaco-diff}\s*?({.*?})?\s*?\n([\s\S]+?)^~~~\s*?\n([\s\S]+?)^```/mg, (full, lang = "ts", options = "{}", code, diff) => {
4045
- lang = lang.trim();
4046
- options = options.trim() || "{}";
4047
- const encoded = base64.encode(code, true);
4048
- const encodedDiff = base64.encode(diff, true);
4049
- return `<Monaco :code="'${encoded}'" :diff="'${encodedDiff}'" lang="${lang}" v-bind="${options}" />`;
4050
- });
4051
- md2 = md2.replace(/^```(\w+?)\s*{monaco}\s*?({.*?})?\s*?\n([\s\S]+?)^```/mg, (full, lang = "ts", options = "{}", code) => {
4052
- lang = lang.trim();
4053
- options = options.trim() || "{}";
4054
- const encoded = base64.encode(code, true);
4055
- return `<Monaco :code="'${encoded}'" lang="${lang}" v-bind="${options}" />`;
4056
- });
4057
- return md2;
4058
- }
4059
- function truncateMancoMark(md2) {
4060
- return md2.replace(/{monaco.*?}/g, "");
4193
+ }
4194
+ async function generateCustomNavControls() {
4195
+ const components = roots.flatMap((root) => {
4196
+ return [
4197
+ join4(root, "custom-nav-controls.vue"),
4198
+ join4(root, "CustomNavControls.vue")
4199
+ ];
4200
+ }).filter((i) => fs5.existsSync(i));
4201
+ const imports = components.map((i, idx) => `import __n${idx} from '${toAtFS(i)}'`).join("\n");
4202
+ const render = components.map((i, idx) => `h(__n${idx})`).join(",");
4203
+ return `
4204
+ ${imports}
4205
+ import { h } from 'vue'
4206
+ export default {
4207
+ render() {
4208
+ return [${render}]
4209
+ }
4061
4210
  }
4062
- function transformSlotSugar(md2) {
4063
- const lines = md2.split(/\r?\n/g);
4064
- let prevSlot = false;
4065
- const { isLineInsideCodeblocks } = getCodeBlocks(md2);
4066
- lines.forEach((line, idx) => {
4067
- if (isLineInsideCodeblocks(idx))
4068
- return;
4069
- const match = line.trimEnd().match(/^::\s*([\w\.\-\:]+)\s*::$/);
4070
- if (match) {
4071
- lines[idx] = `${prevSlot ? "\n\n</template>\n" : "\n"}<template v-slot:${match[1]}="slotProps">
4072
4211
  `;
4073
- prevSlot = true;
4212
+ }
4213
+ async function generteShikiBundle() {
4214
+ const options = await loadShikiSetups(clientRoot, roots);
4215
+ const langs = await resolveLangs(options.langs || ["javascript", "typescript", "html", "css"]);
4216
+ const resolvedThemeOptions = "themes" in options ? {
4217
+ themes: Object.fromEntries(await Promise.all(
4218
+ Object.entries(options.themes).map(async ([name, value]) => [name, await resolveTheme(value)])
4219
+ ))
4220
+ } : {
4221
+ theme: await resolveTheme(options.theme || "vitesse-dark")
4222
+ };
4223
+ const themes = resolvedThemeOptions.themes ? Object.values(resolvedThemeOptions.themes) : [resolvedThemeOptions.theme];
4224
+ const themeOptionsNames = resolvedThemeOptions.themes ? { themes: Object.fromEntries(Object.entries(resolvedThemeOptions.themes).map(([name, value]) => [name, typeof value === "string" ? value : value.name])) } : { theme: typeof resolvedThemeOptions.theme === "string" ? resolvedThemeOptions.theme : resolvedThemeOptions.theme.name };
4225
+ async function normalizeGetter(p) {
4226
+ return Promise.resolve(typeof p === "function" ? p() : p).then((r) => r.default || r);
4074
4227
  }
4075
- });
4076
- if (prevSlot)
4077
- lines[lines.length - 1] += "\n\n</template>";
4078
- return lines.join("\n");
4079
- }
4080
- var reMagicMoveBlock = /^````(?:md|markdown) magic-move(?:[ ]*?({.*?})?([^\n]*?))?\n([\s\S]+?)^````$/mg;
4081
- var reCodeBlock = /^```(\w+?)(?:\s*{([\d\w*,\|-]+)}\s*?({.*?})?(.*?))?\n([\s\S]+?)^```$/mg;
4082
- function transformMagicMove(md2, shiki2, shikiOptions2) {
4083
- const scripts = [];
4084
- let count = 0;
4085
- md2 = md2.replace(
4086
- reMagicMoveBlock,
4087
- (full, _options = "", _attrs = "", body) => {
4088
- if (!shiki2 || !shikiOptions2)
4089
- throw new Error("Shiki is required for Magic Move. You may need to set `highlighter: shiki` in your Slidev config.");
4090
- const matches = Array.from(body.matchAll(reCodeBlock));
4091
- if (!matches.length)
4092
- throw new Error("Magic Move block must contain at least one code block");
4093
- const langs = new Set(matches.map((i) => i[1]));
4094
- if (langs.size > 1)
4095
- throw new Error(`Magic Move block must contain code blocks with the same language, got ${Array.from(langs).join(", ")}`);
4096
- const lang = Array.from(langs)[0];
4097
- const magicMove = createMagicMoveMachine(
4098
- (code) => codeToKeyedTokens(shiki2, code, {
4099
- ...shikiOptions2,
4100
- lang
4101
- })
4102
- );
4103
- const steps = matches.map((i) => magicMove.commit((i[5] || "").trimEnd()));
4104
- const id = `__magicMoveSteps_${getHash(body)}_${count++}`;
4105
- scripts.push(`const ${id} = Object.freeze(${JSON.stringify(steps)})`);
4106
- return `<ShikiMagicMove :steps='${id}' />`;
4228
+ async function resolveLangs(langs2) {
4229
+ return Array.from(new Set((await Promise.all(
4230
+ langs2.map(async (lang) => await normalizeGetter(lang).then((r) => Array.isArray(r) ? r : [r]))
4231
+ )).flat()));
4107
4232
  }
4108
- );
4109
- if (scripts.length)
4110
- md2 = `<script setup>
4111
- ${scripts.join("\n")}</script>
4112
-
4113
- ${md2}`;
4114
- return md2;
4115
- }
4116
- function transformHighlighter(md2) {
4117
- return md2.replace(
4118
- reCodeBlock,
4119
- (full, lang = "", rangeStr = "", options = "", attrs = "", code) => {
4120
- const ranges = rangeStr.split(/\|/g).map((i) => i.trim());
4121
- code = code.trimEnd();
4122
- options = options.trim() || "{}";
4123
- return `
4124
- <CodeBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>
4125
-
4126
- \`\`\`${lang}${attrs}
4127
- ${code}
4128
- \`\`\`
4129
-
4130
- </CodeBlockWrapper>`;
4233
+ async function resolveTheme(theme) {
4234
+ return typeof theme === "string" ? theme : await normalizeGetter(theme);
4131
4235
  }
4132
- );
4236
+ const langsInit = await Promise.all(
4237
+ langs.map(async (lang) => typeof lang === "string" ? `import('${await resolveUrl(`shiki/langs/${lang}.mjs`)}')` : JSON.stringify(lang))
4238
+ );
4239
+ const themesInit = await Promise.all(themes.map(async (theme) => typeof theme === "string" ? `import('${await resolveUrl(`shiki/themes/${theme}.mjs`)}')` : JSON.stringify(theme)));
4240
+ const langNames = langs.flatMap((lang) => typeof lang === "string" ? lang : lang.name);
4241
+ const lines = [];
4242
+ lines.push(
4243
+ `import { getHighlighterCore } from "${await resolveUrl("shiki/core")}"`,
4244
+ `export { shikiToMonaco } from "${await resolveUrl("@shikijs/monaco")}"`,
4245
+ `export const languages = ${JSON.stringify(langNames)}`,
4246
+ `export const themes = ${JSON.stringify(themeOptionsNames.themes || themeOptionsNames.theme)}`,
4247
+ "export const shiki = getHighlighterCore({",
4248
+ ` themes: [${themesInit.join(",")}],`,
4249
+ ` langs: [${langsInit.join(",")}],`,
4250
+ ` loadWasm: import('${await resolveUrl("shiki/wasm")}'),`,
4251
+ "})"
4252
+ );
4253
+ return lines.join("\n");
4254
+ }
4133
4255
  }
4134
- function getCodeBlocks(md2) {
4135
- const codeblocks = Array.from(md2.matchAll(/^```[\s\S]*?^```/mg)).map((m) => {
4136
- const start = m.index;
4137
- const end = m.index + m[0].length;
4138
- const startLine = md2.slice(0, start).match(/\n/g)?.length || 0;
4139
- const endLine = md2.slice(0, end).match(/\n/g)?.length || 0;
4140
- return [start, end, startLine, endLine];
4141
- });
4256
+
4257
+ // node/plugins/setupClient.ts
4258
+ import { existsSync as existsSync2 } from "node:fs";
4259
+ import { join as join5, resolve as resolve3 } from "node:path";
4260
+ import { slash as slash2, uniq as uniq2 } from "@antfu/utils";
4261
+ function createClientSetupPlugin({ themeRoots, addonRoots, userRoot, clientRoot }) {
4262
+ const setupEntry = slash2(resolve3(clientRoot, "setup"));
4142
4263
  return {
4143
- codeblocks,
4144
- isInsideCodeblocks(idx) {
4145
- return codeblocks.some(([s, e]) => s <= idx && idx <= e);
4146
- },
4147
- isLineInsideCodeblocks(line) {
4148
- return codeblocks.some(([, , s, e]) => s <= line && line <= e);
4264
+ name: "slidev:setup",
4265
+ enforce: "pre",
4266
+ async transform(code, id) {
4267
+ if (id.startsWith(setupEntry)) {
4268
+ let getInjections2 = function(isAwait = false, isChained = false) {
4269
+ return injections.join("\n").replace(/:AWAIT:/g, isAwait ? "await " : "").replace(/(,\s*)?:LAST:/g, isChained ? "$1injection_return" : "");
4270
+ };
4271
+ var getInjections = getInjections2;
4272
+ const name = id.slice(setupEntry.length + 1).replace(/\?.*$/, "");
4273
+ const imports = [];
4274
+ const injections = [];
4275
+ const setups = uniq2([
4276
+ ...themeRoots,
4277
+ ...addonRoots,
4278
+ userRoot
4279
+ ]).map((i) => join5(i, "setup", name));
4280
+ setups.forEach((path2, idx) => {
4281
+ if (!existsSync2(path2))
4282
+ return;
4283
+ imports.push(`import __n${idx} from '${toAtFS(path2)}'`);
4284
+ let fn = `:AWAIT:__n${idx}`;
4285
+ if (/\binjection_return\b/g.test(code))
4286
+ fn = `injection_return = ${fn}`;
4287
+ if (/\binjection_arg\b/g.test(code)) {
4288
+ fn += "(";
4289
+ const matches = Array.from(code.matchAll(/\binjection_arg(_\d+)?\b/g));
4290
+ const dedupedMatches = Array.from(new Set(matches.map((m) => m[0])));
4291
+ fn += dedupedMatches.join(", ");
4292
+ fn += ", :LAST:)";
4293
+ } else {
4294
+ fn += "(:LAST:)";
4295
+ }
4296
+ injections.push(
4297
+ `// ${path2}`,
4298
+ fn
4299
+ );
4300
+ });
4301
+ code = code.replace("/* __imports__ */", imports.join("\n"));
4302
+ code = code.replace("/* __injections__ */", getInjections2());
4303
+ code = code.replace("/* __async_injections__ */", getInjections2(true));
4304
+ code = code.replace("/* __chained_injections__ */", getInjections2(false, true));
4305
+ code = code.replace("/* __chained_async_injections__ */", getInjections2(true, true));
4306
+ return code;
4307
+ }
4308
+ return null;
4149
4309
  }
4150
4310
  };
4151
4311
  }
4152
- function transformPageCSS(md2, id) {
4153
- const page = id.match(/(\d+)\.md$/)?.[1];
4154
- if (!page)
4155
- return md2;
4156
- const { isInsideCodeblocks } = getCodeBlocks(md2);
4157
- const result = md2.replace(
4158
- /(\n<style[^>]*?>)([\s\S]+?)(<\/style>)/g,
4159
- (full, start, css, end, index) => {
4160
- if (index < 0 || isInsideCodeblocks(index))
4161
- return full;
4162
- if (!start.includes("scoped"))
4163
- start = start.replace("<style", "<style scoped");
4164
- return `${start}
4165
- ${css}${end}`;
4166
- }
4167
- );
4168
- return result;
4169
- }
4170
- function transformMermaid(md2) {
4171
- return md2.replace(/^```mermaid\s*?({.*?})?\n([\s\S]+?)\n```/mg, (full, options = "", code = "") => {
4172
- code = code.trim();
4173
- options = options.trim() || "{}";
4174
- const encoded = base64.encode(code, true);
4175
- return `<Mermaid :code="'${encoded}'" v-bind="${options}" />`;
4176
- });
4177
- }
4178
- function transformPlantUml(md2, server) {
4179
- return md2.replace(/^```plantuml\s*?({.*?})?\n([\s\S]+?)\n```/mg, (full, options = "", content = "") => {
4180
- const code = encode2(content.trim());
4181
- options = options.trim() || "{}";
4182
- return `<PlantUml :code="'${code}'" :server="'${server}'" v-bind="${options}" />`;
4183
- });
4184
- }
4185
- function escapeVueInCode(md2) {
4186
- return md2.replace(/{{/g, "&lbrace;&lbrace;");
4187
- }
4188
- async function loadShikiSetups(clientRoot, roots) {
4189
- const result = await loadSetups(
4190
- clientRoot,
4191
- roots,
4192
- "shiki.ts",
4193
- {
4194
- /** @deprecated */
4195
- async loadTheme(path2) {
4196
- console.warn("[slidev] `loadTheme` in `setup/shiki.ts` is deprecated. Pass directly the theme name it's supported by Shiki. For custom themes, load it manually via `JSON.parse(fs.readFileSync(path, 'utf-8'))` and pass the raw JSON object instead.");
4197
- return JSON.parse(await fs6.readFile(path2, "utf-8"));
4198
- }
4199
- },
4200
- {},
4201
- false
4202
- );
4203
- if ("theme" in result && "themes" in result)
4204
- delete result.theme;
4205
- if (result.theme && typeof result.theme !== "string" && !result.theme.name && !result.theme.tokenColors) {
4206
- result.themes = result.theme;
4207
- delete result.theme;
4208
- }
4209
- if (!result.theme && !result.themes) {
4210
- result.themes = {
4211
- dark: "vitesse-dark",
4212
- light: "vitesse-light"
4213
- };
4214
- }
4215
- if (result.themes)
4216
- result.defaultColor = false;
4217
- return result;
4218
- }
4219
4312
 
4220
4313
  // node/plugins/patchTransform.ts
4221
4314
  import { objectEntries } from "@antfu/utils";
@@ -4239,6 +4332,72 @@ function createFixPlugins(options) {
4239
4332
  ];
4240
4333
  }
4241
4334
 
4335
+ // node/plugins/monacoTypes.ts
4336
+ import fs6 from "node:fs/promises";
4337
+ import { dirname as dirname2, resolve as resolve4 } from "node:path";
4338
+ import { slash as slash3 } from "@antfu/utils";
4339
+ import fg3 from "fast-glob";
4340
+ import { findDepPkgJsonPath } from "vitefu";
4341
+ function createMonacoTypesLoader({ userRoot }) {
4342
+ const resolvedDepsMap = {};
4343
+ return {
4344
+ name: "slidev:monaco-types-loader",
4345
+ resolveId(id) {
4346
+ if (id.startsWith("/@slidev-monaco-types/"))
4347
+ return id;
4348
+ return null;
4349
+ },
4350
+ async load(id) {
4351
+ const matchResolve = id.match(/^\/\@slidev-monaco-types\/resolve\/(.*?)(?:\?importer=(.*))?$/);
4352
+ if (matchResolve) {
4353
+ const [_, pkg, importer = userRoot] = matchResolve;
4354
+ const resolvedDeps = resolvedDepsMap[importer] ??= /* @__PURE__ */ new Set();
4355
+ if (resolvedDeps.has(pkg))
4356
+ return "";
4357
+ resolvedDeps.add(pkg);
4358
+ const pkgJsonPath = await findDepPkgJsonPath(pkg, importer);
4359
+ if (!pkgJsonPath)
4360
+ throw new Error(`Package "${pkg}" not found in "${importer}"`);
4361
+ const root = dirname2(pkgJsonPath);
4362
+ const pkgJson = JSON.parse(await fs6.readFile(pkgJsonPath, "utf-8"));
4363
+ const deps = pkgJson.dependencies ?? {};
4364
+ return [
4365
+ `import "/@slidev-monaco-types/load/${slash3(root)}&name=${pkgJson.name}"`,
4366
+ ...Object.keys(deps).map((dep) => `import "/@slidev-monaco-types/resolve/${dep}?importer=${slash3(root)}"`)
4367
+ ].join("\n");
4368
+ }
4369
+ const matchLoad = id.match(/^\/\@slidev-monaco-types\/load(\/.*?)&name=(.*)$/);
4370
+ if (matchLoad) {
4371
+ const [_, root, name] = matchLoad;
4372
+ const files = await fg3(
4373
+ [
4374
+ "**/*.d.ts",
4375
+ "**/*.d.mts",
4376
+ "**/*.d.cts",
4377
+ "package.json"
4378
+ ],
4379
+ {
4380
+ cwd: root,
4381
+ followSymbolicLinks: true
4382
+ }
4383
+ );
4384
+ if (!files.length)
4385
+ return "";
4386
+ return [
4387
+ "import * as monaco from 'monaco-editor'",
4388
+ "async function addFile(mod, subPath) {",
4389
+ " const code = (await mod).default",
4390
+ ` const path = ${JSON.stringify(`/node_modules/${name}/`)} + subPath`,
4391
+ ' monaco.languages.typescript.typescriptDefaults.addExtraLib(code, "file://" + path)',
4392
+ ' monaco.editor.createModel(code, "javascript", monaco.Uri.file(path))',
4393
+ "}",
4394
+ ...files.map((file) => `addFile(import(${JSON.stringify(`${toAtFS(resolve4(root, file))}?raw`)}), ${JSON.stringify(file)})`)
4395
+ ].join("\n");
4396
+ }
4397
+ }
4398
+ };
4399
+ }
4400
+
4242
4401
  // node/plugins/preset.ts
4243
4402
  var customElements = /* @__PURE__ */ new Set([
4244
4403
  // katex
@@ -4302,7 +4461,7 @@ async function ViteSlidevPlugin(options, pluginOptions, serverOptions = {}) {
4302
4461
  const VueJsxPlugin = VueJsx(vuejsxOptions);
4303
4462
  const MarkdownPlugin = await createMarkdownPlugin(options, pluginOptions);
4304
4463
  const drawingData = await loadDrawings(options);
4305
- const publicRoots = [...themeRoots, ...addonRoots].map((i) => join7(i, "public")).filter(existsSync3);
4464
+ const publicRoots = [...themeRoots, ...addonRoots].map((i) => join6(i, "public")).filter(existsSync3);
4306
4465
  const plugins = [
4307
4466
  MarkdownPlugin,
4308
4467
  VueJsxPlugin,
@@ -4311,11 +4470,11 @@ async function ViteSlidevPlugin(options, pluginOptions, serverOptions = {}) {
4311
4470
  Components({
4312
4471
  extensions: ["vue", "md", "js", "ts", "jsx", "tsx"],
4313
4472
  dirs: [
4314
- join7(options.clientRoot, "builtin"),
4315
- ...roots.map((i) => join7(i, "components")),
4473
+ join6(options.clientRoot, "builtin"),
4474
+ ...roots.map((i) => join6(i, "components")),
4316
4475
  "src/components",
4317
4476
  "components",
4318
- join7(process3.cwd(), "components")
4477
+ join6(process2.cwd(), "components")
4319
4478
  ],
4320
4479
  include: [/\.vue$/, /\.vue\?vue/, /\.vue\?v=/, /\.md$/, /\.md\?vue/],
4321
4480
  exclude: [],
@@ -4346,7 +4505,7 @@ async function ViteSlidevPlugin(options, pluginOptions, serverOptions = {}) {
4346
4505
  ...remoteAssetsOptions
4347
4506
  })) : null,
4348
4507
  ServerRef({
4349
- debug: process3.env.NODE_ENV === "development",
4508
+ debug: process2.env.NODE_ENV === "development",
4350
4509
  state: {
4351
4510
  sync: false,
4352
4511
  nav: {
@@ -4366,7 +4525,7 @@ async function ViteSlidevPlugin(options, pluginOptions, serverOptions = {}) {
4366
4525
  }),
4367
4526
  createConfigPlugin(options),
4368
4527
  createClientSetupPlugin(options),
4369
- createMonacoTypesLoader(),
4528
+ createMonacoTypesLoader(options),
4370
4529
  createFixPlugins(options),
4371
4530
  publicRoots.length ? import("vite-plugin-static-copy").then((r) => r.viteStaticCopy({
4372
4531
  silent: true,