@slidev/cli 0.48.0-beta.15 → 0.48.0-beta.17

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