2026-05-31 13:54:31 +02:00

2694 lines
91 KiB
JavaScript

import {
createResolver,
getRoots,
isInstalledGlobally,
resolveEntry,
resolveImportPath,
resolveImportUrl,
toAtFS
} from "./chunk-UNQ5DBLZ.js";
// package.json
var version = "0.50.0";
// node/integrations/themes.ts
import { join } from "node:path";
import fs from "fs-extra";
import { satisfies } from "semver";
var officialThemes = {
"none": "",
"default": "@slidev/theme-default",
"seriph": "@slidev/theme-seriph",
"apple-basic": "@slidev/theme-apple-basic",
"shibainu": "@slidev/theme-shibainu",
"bricks": "@slidev/theme-bricks"
};
var resolveTheme = createResolver("theme", officialThemes);
async function getThemeMeta(name, root) {
const path4 = join(root, "package.json");
if (!fs.existsSync(path4))
return {};
const { slidev = {}, engines = {} } = await fs.readJSON(path4);
if (engines.slidev && !satisfies(version, engines.slidev, { includePrerelease: true }))
throw new Error(`[slidev] theme "${name}" requires Slidev version range "${engines.slidev}" but found "${version}"`);
return slidev;
}
// node/parser.ts
import * as parser from "@slidev/parser/fs";
// node/options.ts
import path3 from "node:path";
import { objectMap as objectMap2, uniq as uniq5 } from "@antfu/utils";
import Debug from "debug";
import fg4 from "fast-glob";
import mm from "micromatch";
// node/integrations/addons.ts
import { resolve } from "node:path";
import fs2 from "fs-extra";
import { satisfies as satisfies2 } from "semver";
async function resolveAddons(addonsInConfig) {
const { userRoot, userPkgJson } = await getRoots();
const resolved = [];
const resolveAddonNameAndRoot = createResolver("addon", {});
async function resolveAddon(name, parent) {
const [, pkgRoot] = await resolveAddonNameAndRoot(name, parent);
if (!pkgRoot)
return;
resolved.push(pkgRoot);
const { slidev = {}, engines = {} } = await fs2.readJSON(resolve(pkgRoot, "package.json"));
if (engines.slidev && !satisfies2(version, engines.slidev, { includePrerelease: true }))
throw new Error(`[slidev] addon "${name}" requires Slidev version range "${engines.slidev}" but found "${version}"`);
if (Array.isArray(slidev.addons))
await Promise.all(slidev.addons.map((addon) => resolveAddon(addon, pkgRoot)));
}
if (Array.isArray(addonsInConfig))
await Promise.all(addonsInConfig.map((addon) => resolveAddon(addon, userRoot)));
if (Array.isArray(userPkgJson.slidev?.addons))
await Promise.all(userPkgJson.slidev.addons.map((addon) => resolveAddon(addon, userRoot)));
return resolved;
}
// node/setups/indexHtml.ts
import { existsSync as existsSync8, readFileSync as readFileSync2 } from "node:fs";
import { join as join13 } from "node:path";
import { slash as slash4 } from "@antfu/utils";
import { white, yellow as yellow2 } from "kolorist";
import { escapeHtml } from "markdown-it/lib/common/utils.mjs";
// node/commands/shared.ts
import { existsSync as existsSync7 } from "node:fs";
import { join as join12 } from "node:path";
import MarkdownIt from "markdown-it";
import { loadConfigFromFile, mergeConfig as mergeConfig2 } from "vite";
// node/syntax/markdown-it/markdown-it-link.ts
function MarkdownItLink(md) {
const defaultRender = md.renderer.rules.link_open ?? ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));
md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
const token = tokens[idx];
const hrefIndex = token.attrIndex("href");
const attr = token.attrs?.[hrefIndex];
const href = attr?.[1] ?? "";
if ("./#".includes(href[0]) || /^\d+$/.test(href)) {
token.tag = "Link";
attr[0] = "to";
for (let i = idx + 1; i < tokens.length; i++) {
if (tokens[i].type === "link_close") {
tokens[i].tag = "Link";
break;
}
}
} else if (token.attrGet("target") == null) {
token.attrPush(["target", "_blank"]);
}
return defaultRender(tokens, idx, options, env, self);
};
}
// node/utils.ts
import { fileURLToPath } from "node:url";
import { createJiti } from "jiti";
import YAML from "yaml";
var jiti;
function loadModule(absolutePath) {
jiti ??= createJiti(fileURLToPath(import.meta.url));
return jiti.import(absolutePath);
}
function stringifyMarkdownTokens(tokens) {
return tokens.map((token) => token.children?.filter((t) => ["text", "code_inline"].includes(t.type) && !t.content.match(/^\s*$/)).map((t) => t.content.trim()).join(" ")).filter(Boolean).join(" ");
}
function generateGoogleFontsUrl(options) {
const weights = options.weights.flatMap((i) => options.italic ? [`0,${i}`, `1,${i}`] : [`${i}`]).sort().join(";");
const fonts = options.webfonts.map((i) => `family=${i.replace(/^(['"])(.*)\1$/, "$1").replace(/\s+/g, "+")}:${options.italic ? "ital," : ""}wght@${weights}`).join("&");
return `https://fonts.googleapis.com/css2?${fonts}&display=swap`;
}
function updateFrontmatterPatch(slide, frontmatter) {
const source = slide.source;
let doc = source.frontmatterDoc;
if (!doc) {
source.frontmatterStyle = "frontmatter";
source.frontmatterDoc = doc = new YAML.Document({});
}
for (const [key, value] of Object.entries(frontmatter)) {
slide.frontmatter[key] = source.frontmatter[key] = value;
if (value == null) {
doc.delete(key);
} else {
const valueNode = doc.createNode(value);
let found = false;
YAML.visit(doc.contents, {
Pair(_key, node, path4) {
if (path4.length === 1 && YAML.isScalar(node.key) && node.key.value === key) {
node.value = valueNode;
found = true;
return YAML.visit.BREAK;
}
}
});
if (!found) {
if (!YAML.isMap(doc.contents))
doc.contents = doc.createNode({});
doc.contents.add(
doc.createPair(key, valueNode)
);
}
}
}
}
function getBodyJson(req) {
return new Promise((resolve9, reject) => {
let body = "";
req.on("data", (chunk) => body += chunk);
req.on("error", reject);
req.on("end", () => {
try {
resolve9(JSON.parse(body) || {});
} catch (e) {
reject(e);
}
});
});
}
// node/vite/compilerFlagsVue.ts
import { objectEntries } from "@antfu/utils";
function createVueCompilerFlagsPlugin(options) {
const define = objectEntries(options.utils.define);
return {
name: "slidev:flags",
enforce: "pre",
transform(code, id) {
if (!id.match(/\.vue($|\?)/) && !id.includes("?vue&"))
return;
const original = code;
define.forEach(([from, to]) => {
code = code.replaceAll(from, to);
});
if (original !== code)
return code;
}
};
}
// node/vite/components.ts
import { join as join2 } from "node:path";
import IconsResolver from "unplugin-icons/resolver";
import Components from "unplugin-vue-components/vite";
function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
return Components({
extensions: ["vue", "md", "js", "ts", "jsx", "tsx"],
dirs: [
join2(clientRoot, "builtin"),
...roots.map((i) => join2(i, "components"))
],
include: [/\.vue$/, /\.vue\?vue/, /\.vue\?v=/, /\.md$/, /\.md\?vue/],
exclude: [],
resolvers: [
IconsResolver({
prefix: "",
customCollections: Object.keys(pluginOptions.icons?.customCollections || [])
})
],
dts: false,
...pluginOptions.components
});
}
// node/vite/common.ts
var regexSlideReqPath = /^\/__slidev\/slides\/(\d+)\.json$/;
var regexSlideFacadeId = /^\/@slidev\/slides\/(\d+)\/(md|frontmatter)($|\?)/;
var regexSlideSourceId = /__slidev_(\d+)\.(md|frontmatter)$/;
var templateInjectionMarker = "/* @slidev-injection */";
var templateImportContextUtils = `import { useSlideContext as _useSlideContext, frontmatterToProps as _frontmatterToProps } from "@slidev/client/context.ts"`;
var templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = _useSlideContext()`;
// node/vite/contextInjection.ts
function createContextInjectionPlugin() {
return {
name: "slidev:context-injection",
async transform(code, id) {
if (!id.endsWith(".vue") || id.includes("/@slidev/client/") || id.includes("/packages/client/"))
return;
if (code.includes(templateInjectionMarker) || code.includes("useSlideContext()"))
return code;
const imports = [
templateImportContextUtils,
templateInitContext,
templateInjectionMarker
];
const matchScript = code.match(/<script((?!setup).)*(setup)?.*>/);
if (matchScript && matchScript[2]) {
return code.replace(/(<script.*>)/g, `$1
${imports.join("\n")}
`);
} else if (matchScript && !matchScript[2]) {
const matchExport = code.match(/export\s+default\s+\{/);
if (matchExport) {
const exportIndex = (matchExport.index || 0) + matchExport[0].length;
let component = code.slice(exportIndex);
component = component.slice(0, component.indexOf("</script>"));
const scriptIndex = (matchScript.index || 0) + matchScript[0].length;
const provideImport = '\nimport { injectionSlidevContext } from "@slidev/client/constants.ts"\n';
code = `${code.slice(0, scriptIndex)}${provideImport}${code.slice(scriptIndex)}`;
let injectIndex = exportIndex + provideImport.length;
let injectObject = "$slidev: { from: injectionSlidevContext },";
const matchInject = component.match(/.*inject\s*:\s*([[{])/);
if (matchInject) {
injectIndex += (matchInject.index || 0) + matchInject[0].length;
if (matchInject[1] === "[") {
let injects = component.slice((matchInject.index || 0) + matchInject[0].length);
const injectEndIndex = injects.indexOf("]");
injects = injects.slice(0, injectEndIndex);
injectObject += injects.split(",").map((inject) => `${inject}: {from: ${inject}}`).join(",");
return `${code.slice(0, injectIndex - 1)}{
${injectObject}
}${code.slice(injectIndex + injectEndIndex + 1)}`;
} else {
return `${code.slice(0, injectIndex)}
${injectObject}
${code.slice(injectIndex)}`;
}
}
return `${code.slice(0, injectIndex)}
inject: { ${injectObject} },
${code.slice(injectIndex)}`;
}
}
return `<script setup>
${imports.join("\n")}
</script>
${code}`;
}
};
}
// node/vite/extendConfig.ts
import { join as join3 } from "node:path";
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
import { slash, uniq } from "@antfu/utils";
import { createResolve } from "mlly";
import { mergeConfig } from "vite";
var INCLUDE_GLOBAL = [
"@typescript/ata",
"file-saver",
"lz-string",
"prettier",
"recordrtc",
"typescript",
"yaml",
"html-to-image"
];
var INCLUDE_LOCAL = INCLUDE_GLOBAL.map((i) => `@slidev/cli > @slidev/client > ${i}`);
var EXCLUDE_GLOBAL = [
"@antfu/utils",
"@shikijs/monaco",
"@shikijs/vitepress-twoslash/client",
"@slidev/client",
"@slidev/client/constants",
"@slidev/client/context",
"@slidev/client/logic/dark",
"@slidev/parser",
"@slidev/parser/core",
"@slidev/rough-notation",
"@slidev/types",
"@unhead/vue",
"@unocss/reset",
"@vueuse/core",
"@vueuse/math",
"@vueuse/motion",
"@vueuse/shared",
"drauu",
"floating-vue",
"fuse.js",
"mermaid",
"monaco-editor",
"shiki-magic-move/vue",
"shiki",
"shiki/core",
"vue-demi",
"vue-router",
"vue"
];
var EXCLUDE_LOCAL = EXCLUDE_GLOBAL;
var ASYNC_MODULES = [
"file-saver",
"vue",
"@vue"
];
function createConfigPlugin(options) {
const resolveClientDep = createResolve({
// Same as Vite's default resolve conditions
conditions: ["import", "module", "browser", "default", options.mode === "build" ? "production" : "development"],
url: pathToFileURL(options.clientRoot)
});
return {
name: "slidev:config",
async config(config) {
const injection = {
define: options.utils.define,
resolve: {
alias: [
{
find: /^@slidev\/client$/,
replacement: `${toAtFS(options.clientRoot)}/index.ts`
},
{
find: /^@slidev\/client\/(.*)/,
replacement: `${toAtFS(options.clientRoot)}/$1`
},
{
find: /^#slidev\/(.*)/,
replacement: "/@slidev/$1"
},
{
find: "vue",
replacement: await resolveImportPath("vue/dist/vue.esm-bundler.js", true)
},
...isInstalledGlobally.value ? await Promise.all(INCLUDE_GLOBAL.map(async (dep) => ({
find: dep,
replacement: fileURLToPath2(await resolveClientDep(dep))
}))) : []
],
dedupe: ["vue"]
},
optimizeDeps: isInstalledGlobally.value ? {
exclude: EXCLUDE_GLOBAL,
include: INCLUDE_GLOBAL
} : {
// We need to specify the full deps path for non-hoisted modules
exclude: EXCLUDE_LOCAL,
include: INCLUDE_LOCAL
},
css: {
postcss: {
plugins: [
await import("postcss-nested").then((r) => (r.default || r)())
]
}
},
server: {
fs: {
strict: true,
allow: uniq([
options.userWorkspaceRoot,
options.clientRoot,
// Special case for PNPM global installation
isInstalledGlobally.value ? slash(options.cliRoot).replace(/\/\.pnpm\/.*$/gi, "") : options.cliRoot,
...options.roots
])
}
},
publicDir: join3(options.userRoot, "public"),
build: {
rollupOptions: {
output: {
chunkFileNames(chunkInfo) {
const DEFAULT = "assets/[name]-[hash].js";
if (chunkInfo.name.includes("/"))
return DEFAULT;
if (chunkInfo.moduleIds.filter((i) => isSlidevClient(i)).length > chunkInfo.moduleIds.length * 0.6)
return "assets/slidev/[name]-[hash].js";
if (chunkInfo.moduleIds.filter((i) => i.match(/\/monaco-editor(-core)?\//)).length > chunkInfo.moduleIds.length * 0.6)
return "assets/monaco/[name]-[hash].js";
return DEFAULT;
},
manualChunks(id) {
if (id.startsWith("/@slidev-monaco-types/") || id.includes("/@slidev/monaco-types") || id.endsWith("?monaco-types&raw"))
return "monaco/bundled-types";
if (id.includes("/shiki/") || id.includes("/@shikijs/"))
return `modules/shiki`;
if (id.startsWith("~icons/"))
return "modules/unplugin-icons";
const matchedAsyncModule = ASYNC_MODULES.find((i) => id.includes(`/node_modules/${i}`));
if (matchedAsyncModule)
return `modules/${matchedAsyncModule.replace("@", "").replace("/", "-")}`;
}
}
}
},
cacheDir: isInstalledGlobally.value ? join3(options.cliRoot, "node_modules/.vite") : void 0
};
function isSlidevClient(id) {
return id.includes("/@slidev/") || id.includes("/slidev/packages/client/") || id.includes("/@vueuse/");
}
return mergeConfig(injection, config);
},
configureServer(server) {
return () => {
server.middlewares.use(async (req, res, next) => {
if (req.url === "/index.html") {
res.setHeader("Content-Type", "text/html");
res.statusCode = 200;
res.end(options.utils.indexHtml);
return;
}
next();
});
};
}
};
}
// node/vite/hmrPatch.ts
function createHmrPatchPlugin() {
return {
name: "slidev:hmr-patch",
transform(code, id) {
if (!id.match(regexSlideSourceId))
return;
return code.replace("if (_rerender_only)", "if (false)");
}
};
}
// node/vite/icons.ts
import Icons from "unplugin-icons/vite";
function createIconsPlugin(options, pluginOptions) {
return Icons({
defaultClass: "slidev-icon",
collectionsNodeResolvePath: options.utils.iconsResolvePath,
...pluginOptions.icons
});
}
// node/vite/inspect.ts
async function createInspectPlugin(options, pluginOptions) {
if (!options.inspect)
return;
const { default: PluginInspect } = await import("vite-plugin-inspect");
return PluginInspect({
dev: true,
build: true,
...pluginOptions.inspect
});
}
// node/vite/layoutWrapper.ts
import { bold, gray, red, yellow } from "kolorist";
function createLayoutWrapperPlugin({ data, utils }) {
return {
name: "slidev:layout-wrapper",
async transform(code, id) {
const match = id.match(regexSlideSourceId);
if (!match)
return;
const [, no, type] = match;
if (type !== "md")
return;
const index = +no - 1;
const layouts = utils.getLayouts();
const rawLayoutName = data.slides[index]?.frontmatter?.layout ?? data.slides[0]?.frontmatter?.defaults?.layout;
let layoutName = rawLayoutName || (index === 0 ? "cover" : "default");
if (!layouts[layoutName]) {
console.error(red(`
Unknown layout "${bold(layoutName)}".${yellow(" Available layouts are:")}`) + Object.keys(layouts).map((i, idx) => (idx % 3 === 0 ? "\n " : "") + gray(i.padEnd(15, " "))).join(" "));
console.error();
layoutName = "default";
}
const setupTag = code.match(/^<script setup.*>/m);
if (!setupTag)
throw new Error(`[Slidev] Internal error: <script setup> block not found in slide ${index + 1}.`);
const templatePart = code.slice(0, setupTag.index);
const scriptPart = code.slice(setupTag.index);
const bodyStart = templatePart.indexOf("<template>") + 10;
const bodyEnd = templatePart.lastIndexOf("</template>");
let body = code.slice(bodyStart, bodyEnd).trim();
if (body.startsWith("<div>") && body.endsWith("</div>"))
body = body.slice(5, -6);
return [
templatePart.slice(0, bodyStart),
`<InjectedLayout v-bind="_frontmatterToProps($frontmatter,${index})">
${body}
</InjectedLayout>`,
templatePart.slice(bodyEnd),
scriptPart.slice(0, setupTag[0].length),
`import InjectedLayout from "${toAtFS(layouts[layoutName])}"`,
templateImportContextUtils,
templateInitContext,
"$clicksContext.setup()",
templateInjectionMarker,
scriptPart.slice(setupTag[0].length)
].join("\n");
}
};
}
// node/vite/loaders.ts
import { notNullish, range } from "@antfu/utils";
import * as parser2 from "@slidev/parser/fs";
import equal from "fast-deep-equal";
import YAML2 from "yaml";
// node/virtual/configs.ts
import { isString } from "@antfu/utils";
var templateConfigs = {
id: "/@slidev/configs",
getContent({ data, remote }) {
const config = {
...data.config,
remote,
slidesTitle: getSlideTitle(data)
};
if (isString(config.info))
config.info = sharedMd.render(config.info);
return `export default ${JSON.stringify(config)}`;
}
};
// node/virtual/deprecated.ts
var templateLegacyRoutes = {
id: "/@slidev/routes",
getContent() {
return [
`export { slides } from '#slidev/slides'`,
`console.warn('[slidev] #slidev/routes is deprecated, use #slidev/slides instead')`
].join("\n");
}
};
var templateLegacyTitles = {
id: "/@slidev/titles.md",
getContent() {
return `
<script setup lang="ts">
import TitleRenderer from '#slidev/title-renderer'
defineProps<{ no: number | string }>()
console.warn('/@slidev/titles.md is deprecated, import from #slidev/title-renderer instead')
</script>
<TitleRenderer :no="no" />
`;
}
};
// node/virtual/global-layers.ts
import { existsSync } from "node:fs";
import { join as join4 } from "node:path";
var templateGlobalLayers = {
id: `/@slidev/global-layers`,
getContent({ roots }) {
const imports = [];
let n = 0;
function getComponent(names) {
const components = roots.flatMap((root) => names.map((name) => join4(root, name))).filter((i) => existsSync(i));
imports.push(components.map((path4, i) => `import __n${n}_${i} from '${toAtFS(path4)}'`).join("\n"));
const render = components.map((_, i) => `h(__n${n}_${i})`).join(",");
n++;
return `{ render: () => [${render}] }`;
}
const globalTop = getComponent(["global.vue", "global-top.vue", "GlobalTop.vue"]);
const globalBottom = getComponent(["global-bottom.vue", "GlobalBottom.vue"]);
const slideTop = getComponent(["slide-top.vue", "SlideTop.vue"]);
const slideBottom = getComponent(["slide-bottom.vue", "SlideBottom.vue"]);
return [
imports.join("\n"),
`import { h } from 'vue'`,
`export const GlobalTop = ${globalTop}`,
`export const GlobalBottom = ${globalBottom}`,
`export const SlideTop = ${slideTop}`,
`export const SlideBottom = ${slideBottom}`
].join("\n");
}
};
// node/virtual/layouts.ts
import { objectMap } from "@antfu/utils";
var templateLayouts = {
id: "/@slidev/layouts",
getContent({ utils }) {
const imports = [];
const layouts = objectMap(
utils.getLayouts(),
(k, v) => {
imports.push(`import __layout_${k} from "${toAtFS(v)}"`);
return [k, `__layout_${k}`];
}
);
return [
imports.join("\n"),
`export default {
${Object.entries(layouts).map(([k, v]) => `"${k}": ${v}`).join(",\n")}
}`
].join("\n\n");
}
};
// node/virtual/monaco-deps.ts
import { resolve as resolve2 } from "node:path";
import { uniq as uniq2 } from "@antfu/utils";
var templateMonacoRunDeps = {
id: "/@slidev/monaco-run-deps",
async getContent({ userRoot, data }) {
if (!data.features.monaco)
return "";
const deps = uniq2(data.features.monaco.deps.concat(data.config.monacoTypesAdditionalPackages));
const importerPath = resolve2(userRoot, "./snippets/__importer__.ts");
let result = "";
for (let i = 0; i < deps.length; i++) {
const specifier = deps[i];
const resolved = await this.resolve(specifier, importerPath);
if (!resolved)
continue;
result += `import * as vendored${i} from ${JSON.stringify(resolved.id)}
`;
}
result += "export default {\n";
for (let i = 0; i < deps.length; i++)
result += `${JSON.stringify(deps[i])}: vendored${i},
`;
result += "}\n";
return result;
}
};
// node/virtual/monaco-types.ts
import { builtinModules } from "node:module";
import { join as join5, resolve as resolve3 } from "node:path";
import { uniq as uniq3 } from "@antfu/utils";
import fg from "fast-glob";
var templateMonacoTypes = {
id: "/@slidev/monaco-types",
getContent: async ({ userRoot, data, utils }) => {
if (!data.features.monaco)
return "";
const typesRoot = join5(userRoot, "snippets");
const files = await fg(["**/*.ts", "**/*.mts", "**/*.cts"], { cwd: typesRoot });
let result = 'import { addFile } from "@slidev/client/setup/monaco.ts"\n';
for (const file of files) {
const url = `${toAtFS(resolve3(typesRoot, file))}?monaco-types&raw`;
result += `addFile(() => import(${JSON.stringify(url)}), ${JSON.stringify(file)})
`;
}
function mapModuleNameToModule(moduleSpecifier) {
if (moduleSpecifier.startsWith("node:"))
return "node";
if (builtinModules.includes(moduleSpecifier))
return "node";
const mainPackageName = moduleSpecifier.split("/")[0];
if (builtinModules.includes(mainPackageName) && !mainPackageName.startsWith("@"))
return "node";
const [a = "", b = ""] = moduleSpecifier.split("/");
const moduleName = a.startsWith("@") ? `${a}/${b}` : a;
return moduleName;
}
let deps = [...data.config.monacoTypesAdditionalPackages];
if (data.config.monacoTypesSource === "local")
deps.push(...data.features.monaco.types);
deps = uniq3(deps.map((specifier) => {
if (specifier[0] === ".")
return "";
return mapModuleNameToModule(specifier);
}).filter(Boolean));
deps = deps.filter((pkg) => !utils.isMonacoTypesIgnored(pkg));
for (const pkg of deps) {
result += `import(${JSON.stringify(`/@slidev-monaco-types/resolve?${new URLSearchParams({ pkg })}`)})
`;
}
return result;
}
};
// node/virtual/nav-controls.ts
import { existsSync as existsSync2 } from "node:fs";
import { join as join6 } from "node:path";
var templateNavControls = {
id: "/@slidev/custom-nav-controls",
getContent({ roots }) {
const components = roots.flatMap((root) => {
return [
join6(root, "custom-nav-controls.vue"),
join6(root, "CustomNavControls.vue")
];
}).filter((i) => existsSync2(i));
const imports = components.map((i, idx) => `import __n${idx} from '${toAtFS(i)}'`).join("\n");
const render = components.map((i, idx) => `h(__n${idx})`).join(",");
return `${imports}
import { h } from 'vue'
export default {
render: () => [${render}],
}`;
}
};
// node/virtual/setups.ts
import { existsSync as existsSync3 } from "node:fs";
import { join as join7 } from "node:path";
function createSetupTemplate(name) {
return {
id: `/@slidev/setups/${name}`,
getContent({ roots }) {
const setups = roots.flatMap((i) => {
const path4 = join7(i, "setup", name);
return [".ts", ".mts", ".js", ".mjs"].map((ext) => path4 + ext);
}).filter((i) => existsSync3(i));
const imports = [];
setups.forEach((path4, idx) => {
imports.push(`import __n${idx} from '${toAtFS(path4)}'`);
});
imports.push(`export default [${setups.map((_, idx) => `__n${idx}`).join(",")}]`);
return imports.join("\n");
}
};
}
var setupModules = ["shiki", "code-runners", "monaco", "mermaid", "main", "root", "routes", "shortcuts", "context-menu"];
var templateSetups = setupModules.map(createSetupTemplate);
// node/virtual/shiki.ts
import { uniq as uniq4 } from "@antfu/utils";
var templateShiki = {
id: "/@slidev/shiki",
getContent: async ({ utils }) => {
const options = utils.shikiOptions;
const langs = await resolveLangs(options.langs || ["markdown", "vue", "javascript", "typescript", "html", "css"]);
const resolvedThemeOptions = "themes" in options ? {
themes: Object.fromEntries(await Promise.all(
Object.entries(options.themes).map(async ([name, value]) => [name, await resolveTheme2(value)])
))
} : {
theme: await resolveTheme2(options.theme || "vitesse-dark")
};
const themes = resolvedThemeOptions.themes ? Object.values(resolvedThemeOptions.themes) : [resolvedThemeOptions.theme];
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 };
async function normalizeGetter(p) {
const r = typeof p === "function" ? p() : p;
return r.default || r;
}
async function resolveLangs(langs2) {
const awaited = await Promise.all(langs2.map((lang) => normalizeGetter(lang)));
return uniq4(awaited.flat());
}
async function resolveTheme2(theme) {
return typeof theme === "string" ? theme : await normalizeGetter(theme);
}
const langsInit = await Promise.all(
langs.map(async (lang) => typeof lang === "string" ? `import('${await resolveImportUrl(`shiki/langs/${lang}.mjs`)}')` : JSON.stringify(lang))
);
const themesInit = await Promise.all(themes.map(async (theme) => typeof theme === "string" ? `import('${await resolveImportUrl(`shiki/themes/${theme}.mjs`)}')` : JSON.stringify(theme)));
const langNames = langs.flatMap((lang) => typeof lang === "string" ? lang : lang.name);
const lines = [];
lines.push(
`import { createHighlighterCore } from "${await resolveImportUrl("shiki/core")}"`,
`export { shikiToMonaco } from "${await resolveImportUrl("@shikijs/monaco")}"`,
`export const languages = ${JSON.stringify(langNames)}`,
`export const themes = ${JSON.stringify(themeOptionsNames.themes || themeOptionsNames.theme)}`,
"export const shiki = createHighlighterCore({",
` themes: [${themesInit.join(",")}],`,
` langs: [${langsInit.join(",")}],`,
` loadWasm: import('${await resolveImportUrl("shiki/wasm")}'),`,
"})",
"let highlight",
"export async function getHighlighter() {",
" if (highlight) return highlight",
" const highlighter = await shiki",
" highlight = (code, lang, options) => highlighter.codeToHtml(code, {",
" lang,",
` theme: ${JSON.stringify(themeOptionsNames.theme)},`,
` themes: ${JSON.stringify(themeOptionsNames.themes)},`,
" defaultColor: false,",
" ...options,",
" })",
" return highlight",
"}"
);
return lines.join("\n");
}
};
// node/virtual/slides.ts
var VIRTUAL_SLIDE_PREFIX = "/@slidev/slides/";
var templateSlides = {
id: "/@slidev/slides",
getContent({ data, utils }) {
const layouts = utils.getLayouts();
const statements = [
`import { defineAsyncComponent, shallowRef } from 'vue'`,
`import SlideError from '${layouts.error}'`,
`import SlideLoading from '@slidev/client/internals/SlideLoading.vue'`,
`const componentsCache = new Array(${data.slides.length})`,
`const getAsyncComponent = (idx, loader) => defineAsyncComponent({`,
` loader,`,
` delay: 300,`,
` loadingComponent: SlideLoading,`,
` errorComponent: SlideError,`,
` onError: e => console.error('Failed to load slide ' + (idx + 1), e) `,
`})`
];
const slides = data.slides.map((_, idx) => {
const no = idx + 1;
statements.push(
`import { meta as f${no} } from '${VIRTUAL_SLIDE_PREFIX}${no}/frontmatter'`,
// For some unknown reason, import error won't be caught by the error component. Catch it here.
`const load${no} = async () => {`,
` try { return componentsCache[${idx}] ??= await import('${VIRTUAL_SLIDE_PREFIX}${no}/md') }`,
` catch (e) { console.error('slide failed to load', e); return SlideError }`,
`}`
);
return `{ no: ${no}, meta: f${no}, load: load${no}, component: getAsyncComponent(${idx}, load${no}) }`;
});
return [
...statements,
`const data = [
${slides.join(",\n")}
]`,
`if (import.meta.hot) {`,
` import.meta.hot.data.slides ??= shallowRef()`,
` import.meta.hot.data.slides.value = data`,
` import.meta.hot.dispose(() => componentsCache.length = 0)`,
` import.meta.hot.accept()`,
`}`,
`export const slides = import.meta.hot ? import.meta.hot.data.slides : shallowRef(data)`
].join("\n");
}
};
// node/virtual/styles.ts
import { existsSync as existsSync4 } from "node:fs";
import { join as join8 } from "node:path";
var templateStyle = {
id: "/@slidev/styles",
async getContent({ data, clientRoot, roots }) {
function resolveUrlOfClient(name) {
return toAtFS(join8(clientRoot, name));
}
const imports = [
`import "${resolveUrlOfClient("styles/vars.css")}"`,
`import "${resolveUrlOfClient("styles/index.css")}"`,
`import "${resolveUrlOfClient("styles/code.css")}"`,
`import "${resolveUrlOfClient("styles/katex.css")}"`,
`import "${resolveUrlOfClient("styles/transitions.css")}"`
];
for (const root of roots) {
const styles = [
join8(root, "styles", "index.ts"),
join8(root, "styles", "index.js"),
join8(root, "styles", "index.css"),
join8(root, "styles.css"),
join8(root, "style.css")
];
for (const style of styles) {
if (existsSync4(style)) {
imports.push(`import "${toAtFS(style)}"`);
continue;
}
}
}
if (data.features.katex)
imports.push(`import "${await resolveImportUrl("katex/dist/katex.min.css")}"`);
if (data.config.highlighter === "shiki") {
imports.push(
`import "${await resolveImportUrl("@shikijs/vitepress-twoslash/style.css")}"`,
`import "${resolveUrlOfClient("styles/shiki-twoslash.css")}"`,
`import "${await resolveImportUrl("shiki-magic-move/style.css")}"`
);
}
imports.unshift(
`import "${await resolveImportUrl("@unocss/reset/tailwind.css")}"`,
'import "uno:preflights.css"',
'import "uno:typography.css"',
'import "uno:shortcuts.css"'
);
imports.push('import "uno.css"');
return imports.join("\n");
}
};
// node/virtual/titles.ts
var templateTitleRendererMd = {
id: "/@slidev/title-renderer.md",
getContent({ data }) {
const lines = data.slides.map(({ title }, i) => `<template ${i === 0 ? "v-if" : "v-else-if"}="no === ${i + 1}">
${title}
</template>`);
lines.push(
`<script setup lang="ts">`,
`import { useSlideContext } from '@slidev/client/context.ts'`,
`import { computed } from 'vue'`,
`const props = defineProps<{ no?: number | string }>()`,
`const { $page } = useSlideContext()`,
`const no = computed(() => +(props.no ?? $page.value))`,
`</script>`
);
return lines.join("\n");
}
};
var templateTitleRenderer = {
id: "/@slidev/title-renderer",
async getContent() {
return 'export { default } from "/@slidev/title-renderer.md"';
}
};
// node/virtual/index.ts
var templates = [
templateShiki,
templateMonacoTypes,
templateMonacoRunDeps,
templateConfigs,
templateStyle,
templateGlobalLayers,
templateNavControls,
templateSlides,
templateLayouts,
templateTitleRenderer,
templateTitleRendererMd,
...templateSetups,
// Deprecated
templateLegacyRoutes,
templateLegacyTitles
];
// node/vite/loaders.ts
function renderNote(text = "") {
let clickCount = 0;
const html = sharedMd.render(
text.replace(/\[click(?::(\d+))?\]/gi, (_, count = 1) => {
clickCount += Number(count);
return `<span class="slidev-note-click-mark" data-clicks="${clickCount}"></span>`;
})
);
return html;
}
function withRenderedNote(data) {
return {
...data,
noteHTML: renderNote(data?.note)
};
}
function createSlidesLoader(options, serverOptions) {
const { data, mode, utils } = options;
const hmrSlidesIndexes = /* @__PURE__ */ new Set();
let server;
let skipHmr = null;
let sourceIds = resolveSourceIds(data);
function resolveSourceIds(data2) {
const ids = {
md: [],
frontmatter: []
};
for (const type of ["md", "frontmatter"]) {
for (let i = 0; i < data2.slides.length; i++) {
ids[type].push(`${data2.slides[i].source.filepath}__slidev_${i + 1}.${type}`);
}
}
return ids;
}
function updateServerWatcher() {
if (!server)
return;
server.watcher.add(Object.keys(data.watchFiles));
}
function getFrontmatter(pageNo) {
return {
...data.headmatter?.defaults || {},
...data.slides[pageNo]?.frontmatter || {}
};
}
return {
name: "slidev:loader",
enforce: "pre",
configureServer(_server) {
server = _server;
updateServerWatcher();
server.middlewares.use(async (req, res, next) => {
const match = req.url?.match(regexSlideReqPath);
if (!match)
return next();
const [, no] = match;
const idx = Number.parseInt(no) - 1;
if (req.method === "GET") {
res.write(JSON.stringify(withRenderedNote(data.slides[idx])));
return res.end();
} else if (req.method === "POST") {
const body = await getBodyJson(req);
const slide = data.slides[idx];
if (body.content && body.content !== slide.source.content)
hmrSlidesIndexes.add(idx);
if (body.content)
slide.content = slide.source.content = body.content;
if (body.frontmatterRaw != null) {
if (body.frontmatterRaw.trim() === "") {
slide.source.frontmatterDoc = slide.source.frontmatterStyle = void 0;
} else {
const parsed = YAML2.parseDocument(body.frontmatterRaw);
if (parsed.errors.length)
console.error("ERROR when saving frontmatter", parsed.errors);
else
slide.source.frontmatterDoc = parsed;
}
}
if (body.note)
slide.note = slide.source.note = body.note;
if (body.frontmatter)
updateFrontmatterPatch(slide, body.frontmatter);
parser2.prettifySlide(slide.source);
const fileContent = await parser2.save(data.markdownFiles[slide.source.filepath]);
if (body.skipHmr) {
skipHmr = {
filePath: slide.source.filepath,
fileContent
};
server?.moduleGraph.invalidateModule(
server.moduleGraph.getModuleById(sourceIds.md[idx])
);
if (body.frontmatter) {
server?.moduleGraph.invalidateModule(
server.moduleGraph.getModuleById(sourceIds.frontmatter[idx])
);
}
}
res.statusCode = 200;
res.write(JSON.stringify(withRenderedNote(slide)));
return res.end();
}
next();
});
},
async handleHotUpdate(ctx) {
const forceChangedSlides = data.watchFiles[ctx.file];
if (!forceChangedSlides)
return;
for (const index of forceChangedSlides) {
hmrSlidesIndexes.add(index);
}
const newData = await serverOptions.loadData?.({
[ctx.file]: await ctx.read()
});
if (!newData)
return [];
if (skipHmr && newData.markdownFiles[skipHmr.filePath]?.raw === skipHmr.fileContent) {
skipHmr = null;
return [];
}
const moduleIds = /* @__PURE__ */ new Set();
const newSourceIds = resolveSourceIds(newData);
for (const type of ["md", "frontmatter"]) {
const old = sourceIds[type];
const newIds = newSourceIds[type];
for (let i = 0; i < newIds.length; i++) {
if (old[i] !== newIds[i]) {
moduleIds.add(`${VIRTUAL_SLIDE_PREFIX}${i + 1}/${type}`);
}
}
}
sourceIds = newSourceIds;
if (data.slides.length !== newData.slides.length) {
moduleIds.add(templateSlides.id);
}
if (!equal(data.headmatter.defaults, newData.headmatter.defaults)) {
moduleIds.add(templateSlides.id);
range(data.slides.length).map((i) => hmrSlidesIndexes.add(i));
}
if (!equal(data.config, newData.config))
moduleIds.add(templateConfigs.id);
if (!equal(data.features, newData.features)) {
setTimeout(() => {
ctx.server.hot.send({ type: "full-reload" });
}, 1);
}
const length = Math.min(data.slides.length, newData.slides.length);
for (let i = 0; i < length; i++) {
const a = data.slides[i];
const b = newData.slides[i];
if (!hmrSlidesIndexes.has(i) && a.content.trim() === b.content.trim() && a.title?.trim() === b.title?.trim() && equal(a.frontmatter, b.frontmatter)) {
if (a.note !== b.note) {
ctx.server.hot.send(
"slidev:update-note",
{
no: i + 1,
note: b.note || "",
noteHTML: renderNote(b.note || "")
}
);
}
continue;
}
ctx.server.hot.send(
"slidev:update-slide",
{
no: i + 1,
data: withRenderedNote(newData.slides[i])
}
);
hmrSlidesIndexes.add(i);
}
Object.assign(data, newData);
Object.assign(utils, createDataUtils(options));
if (hmrSlidesIndexes.size > 0)
moduleIds.add(templateTitleRendererMd.id);
const vueModules = Array.from(hmrSlidesIndexes).flatMap((idx) => {
const frontmatter = ctx.server.moduleGraph.getModuleById(sourceIds.frontmatter[idx]);
const main = ctx.server.moduleGraph.getModuleById(sourceIds.md[idx]);
const styles = main ? [...main.clientImportedModules].find((m) => m.id?.includes(`&type=style`)) : void 0;
return [
frontmatter,
main,
styles
];
});
hmrSlidesIndexes.clear();
const moduleEntries = [
...ctx.modules.filter((i) => i.id === templateMonacoRunDeps.id || i.id === templateMonacoTypes.id),
...vueModules,
...Array.from(moduleIds).map((id) => ctx.server.moduleGraph.getModuleById(id))
].filter(notNullish).filter((i) => !i.id?.startsWith("/@id/@vite-icons"));
updateServerWatcher();
return moduleEntries;
},
resolveId: {
order: "pre",
handler(id) {
if (id.startsWith("/@slidev/") || id.includes("__slidev_"))
return id;
return null;
}
},
async load(id) {
const template = templates.find((i) => i.id === id);
if (template) {
return {
code: await template.getContent.call(this, options),
map: { mappings: "" }
};
}
const matchFacade = id.match(regexSlideFacadeId);
if (matchFacade) {
const [, no, type] = matchFacade;
const idx = +no - 1;
const sourceId = JSON.stringify(sourceIds[type][idx]);
return [
`export * from ${sourceId}`,
`export { default } from ${sourceId}`
].join("\n");
}
const matchSource = id.match(regexSlideSourceId);
if (matchSource) {
const [, no, type] = matchSource;
const idx = +no - 1;
const slide = data.slides[idx];
if (!slide)
return;
if (type === "md") {
return {
code: slide.content,
map: { mappings: "" }
};
} else if (type === "frontmatter") {
const slideBase = {
...withRenderedNote(slide),
frontmatter: void 0,
source: void 0,
importChain: void 0,
// remove raw content in build, optimize the bundle size
...mode === "build" ? { raw: "", content: "", note: "" } : {}
};
const fontmatter = getFrontmatter(idx);
return {
code: [
"// @unocss-include",
'import { computed, reactive, shallowReactive } from "vue"',
`export const frontmatterData = ${JSON.stringify(fontmatter)}`,
// handle HMR, update frontmatter with update
"if (import.meta.hot) {",
" const firstLoad = !import.meta.hot.data.frontmatter",
" import.meta.hot.data.frontmatter ??= reactive(frontmatterData)",
" import.meta.hot.accept(({ frontmatterData: update }) => {",
" if (firstLoad) return",
" const frontmatter = import.meta.hot.data.frontmatter",
" Object.keys(frontmatter).forEach(key => {",
" if (!(key in update)) delete frontmatter[key]",
" })",
" Object.assign(frontmatter, update)",
" })",
"}",
"export const frontmatter = import.meta.hot ? import.meta.hot.data.frontmatter : reactive(frontmatterData)",
"export default frontmatter",
"export const meta = shallowReactive({",
" get layout(){ return frontmatter.layout },",
" get transition(){ return frontmatter.transition },",
" get class(){ return frontmatter.class },",
" get clicks(){ return frontmatter.clicks },",
" get name(){ return frontmatter.name },",
" get preload(){ return frontmatter.preload },",
// No need to be reactive, as it's only used once after reload
" slide: {",
` ...(${JSON.stringify(slideBase)}),`,
` frontmatter,`,
` filepath: ${JSON.stringify(mode === "dev" ? slide.source.filepath : "")},`,
` start: ${JSON.stringify(slide.source.start)},`,
` id: ${idx},`,
` no: ${no},`,
" },",
" __clicksContext: null,",
" __preloaded: false,",
"})"
].join("\n"),
map: { mappings: "" }
};
}
}
if (data.markdownFiles[id])
return "";
}
};
}
// node/vite/markdown.ts
import MagicString from "magic-string-stack";
import Markdown from "unplugin-vue-markdown/vite";
// ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.1.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/image-size/specialCharacters.js
var SpecialCharacters;
(function(SpecialCharacters2) {
SpecialCharacters2[SpecialCharacters2["EXCLAMATION_MARK"] = 33] = "EXCLAMATION_MARK";
SpecialCharacters2[SpecialCharacters2["OPENING_BRACKET"] = 91] = "OPENING_BRACKET";
SpecialCharacters2[SpecialCharacters2["OPENING_PARENTHESIS"] = 40] = "OPENING_PARENTHESIS";
SpecialCharacters2[SpecialCharacters2["WHITESPACE"] = 32] = "WHITESPACE";
SpecialCharacters2[SpecialCharacters2["NEW_LINE"] = 10] = "NEW_LINE";
SpecialCharacters2[SpecialCharacters2["EQUALS"] = 61] = "EQUALS";
SpecialCharacters2[SpecialCharacters2["LOWER_CASE_X"] = 120] = "LOWER_CASE_X";
SpecialCharacters2[SpecialCharacters2["NUMBER_ZERO"] = 48] = "NUMBER_ZERO";
SpecialCharacters2[SpecialCharacters2["NUMBER_NINE"] = 57] = "NUMBER_NINE";
SpecialCharacters2[SpecialCharacters2["PERCENTAGE"] = 37] = "PERCENTAGE";
SpecialCharacters2[SpecialCharacters2["CLOSING_PARENTHESIS"] = 41] = "CLOSING_PARENTHESIS";
})(SpecialCharacters || (SpecialCharacters = {}));
// ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=tuyuxytl56b2vxulpkzt2wf4o4_markdown-it@14.1.0/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/task-lists/index.js
import Token from "markdown-it/lib/token.mjs";
var checkboxRegex = /^ *\[([\sx])] /i;
function taskLists(md, options = { enabled: false, label: false, lineNumber: false }) {
md.core.ruler.after("inline", "task-lists", (state) => processToken(state, options));
md.renderer.rules.taskListItemCheckbox = (tokens) => {
const token = tokens[0];
const checkedAttribute = token.attrGet("checked") ? 'checked="" ' : "";
const disabledAttribute = token.attrGet("disabled") ? 'disabled="" ' : "";
const line = token.attrGet("line");
const idAttribute = `id="${token.attrGet("id")}" `;
const dataLineAttribute = line && options.lineNumber ? `data-line="${line}" ` : "";
return `<input class="task-list-item-checkbox" type="checkbox" ${checkedAttribute}${disabledAttribute}${dataLineAttribute}${idAttribute}/>`;
};
md.renderer.rules.taskListItemLabel_close = () => {
return "</label>";
};
md.renderer.rules.taskListItemLabel_open = (tokens) => {
const token = tokens[0];
const id = token.attrGet("id");
return `<label for="${id}">`;
};
}
function processToken(state, options) {
const allTokens = state.tokens;
for (let i = 2; i < allTokens.length; i++) {
if (!isTodoItem(allTokens, i)) {
continue;
}
todoify(allTokens[i], options);
allTokens[i - 2].attrJoin("class", `task-list-item ${options.enabled ? " enabled" : ""}`);
const parentToken = findParentToken(allTokens, i - 2);
if (parentToken) {
const classes = parentToken.attrGet("class") ?? "";
if (!classes.match(/(^| )contains-task-list/)) {
parentToken.attrJoin("class", "contains-task-list");
}
}
}
return false;
}
function findParentToken(tokens, index) {
const targetLevel = tokens[index].level - 1;
for (let currentTokenIndex = index - 1; currentTokenIndex >= 0; currentTokenIndex--) {
if (tokens[currentTokenIndex].level === targetLevel) {
return tokens[currentTokenIndex];
}
}
return void 0;
}
function isTodoItem(tokens, index) {
return isInline(tokens[index]) && isParagraph(tokens[index - 1]) && isListItem(tokens[index - 2]) && startsWithTodoMarkdown(tokens[index]);
}
function todoify(token, options) {
if (token.children == null) {
return;
}
const id = generateIdForToken(token);
token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id));
token.children[1].content = token.children[1].content.replace(checkboxRegex, "");
if (options.label) {
token.children.splice(1, 0, createLabelBeginToken(id));
token.children.push(createLabelEndToken());
}
}
function generateIdForToken(token) {
if (token.map) {
return `task-item-${token.map[0]}`;
} else {
return `task-item-${Math.ceil(Math.random() * (1e4 * 1e3) - 1e3)}`;
}
}
function createCheckboxToken(token, enabled, id) {
const checkbox = new Token("taskListItemCheckbox", "", 0);
if (!enabled) {
checkbox.attrSet("disabled", "true");
}
if (token.map) {
checkbox.attrSet("line", token.map[0].toString());
}
checkbox.attrSet("id", id);
const checkboxRegexResult = checkboxRegex.exec(token.content);
const isChecked = checkboxRegexResult?.[1].toLowerCase() === "x";
if (isChecked) {
checkbox.attrSet("checked", "true");
}
return checkbox;
}
function createLabelBeginToken(id) {
const labelBeginToken = new Token("taskListItemLabel_open", "", 1);
labelBeginToken.attrSet("id", id);
return labelBeginToken;
}
function createLabelEndToken() {
return new Token("taskListItemLabel_close", "", -1);
}
function isInline(token) {
return token.type === "inline";
}
function isParagraph(token) {
return token.type === "paragraph_open";
}
function isListItem(token) {
return token.type === "list_item_open";
}
function startsWithTodoMarkdown(token) {
return checkboxRegex.test(token.content);
}
// node/syntax/markdown-it/index.ts
import MarkdownItFootnote from "markdown-it-footnote";
import MarkdownItMdc from "markdown-it-mdc";
// node/setups/load.ts
import { resolve as resolve4 } from "node:path";
import { deepMergeWithArray } from "@antfu/utils";
import fs3 from "fs-extra";
async function loadSetups(roots, filename, args, extraLoader) {
const returns = [];
for (const root of roots) {
const path4 = resolve4(root, "setup", filename);
if (fs3.existsSync(path4)) {
const { default: setup } = await loadModule(path4);
const ret = await setup(...args);
if (ret)
returns.push(ret);
}
if (extraLoader)
returns.push(...await extraLoader(root));
}
return returns;
}
// node/setups/katex.ts
async function setupKatex(roots) {
const options = await loadSetups(roots, "katex.ts", []);
return Object.assign(
{ strict: false },
...options
);
}
// node/syntax/markdown-it/markdown-it-escape-code.ts
function MarkdownItEscapeInlineCode(md) {
const codeInline = md.renderer.rules.code_inline;
md.renderer.rules.code_inline = (tokens, idx, options, env, self) => {
const result = codeInline(tokens, idx, options, env, self);
return result.replace(/^<code/, "<code v-pre");
};
}
// node/syntax/markdown-it/markdown-it-katex.ts
import katex from "katex";
function isValidDelim(state, pos) {
const max = state.posMax;
let can_open = true;
let can_close = true;
const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
const nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
if (prevChar === 32 || prevChar === 9 || /* \t */
nextChar >= 48 && nextChar <= 57)
can_close = false;
if (nextChar === 32 || nextChar === 9)
can_open = false;
return {
can_open,
can_close
};
}
function math_inline(state, silent) {
let match, token, res, pos;
if (state.src[state.pos] !== "$")
return false;
res = isValidDelim(state, state.pos);
if (!res.can_open) {
if (!silent)
state.pending += "$";
state.pos += 1;
return true;
}
const start = state.pos + 1;
match = start;
while ((match = state.src.indexOf("$", match)) !== -1) {
pos = match - 1;
while (state.src[pos] === "\\") pos -= 1;
if ((match - pos) % 2 === 1)
break;
match += 1;
}
if (match === -1) {
if (!silent)
state.pending += "$";
state.pos = start;
return true;
}
if (match - start === 0) {
if (!silent)
state.pending += "$$";
state.pos = start + 1;
return true;
}
res = isValidDelim(state, match);
if (!res.can_close) {
if (!silent)
state.pending += "$";
state.pos = start;
return true;
}
if (!silent) {
token = state.push("math_inline", "math", 0);
token.markup = "$";
token.content = state.src.slice(start, match);
}
state.pos = match + 1;
return true;
}
function math_block(state, start, end, silent) {
let firstLine;
let lastLine;
let next;
let lastPos;
let found = false;
let pos = state.bMarks[start] + state.tShift[start];
let max = state.eMarks[start];
if (pos + 2 > max)
return false;
if (state.src.slice(pos, pos + 2) !== "$$")
return false;
pos += 2;
firstLine = state.src.slice(pos, max);
if (silent)
return true;
if (firstLine.trim().slice(-2) === "$$") {
firstLine = firstLine.trim().slice(0, -2);
found = true;
}
for (next = start; !found; ) {
next++;
if (next >= end)
break;
pos = state.bMarks[next] + state.tShift[next];
max = state.eMarks[next];
if (pos < max && state.tShift[next] < state.blkIndent) {
break;
}
if (state.src.slice(pos, max).trim().slice(-2) === "$$") {
lastPos = state.src.slice(0, max).lastIndexOf("$$");
lastLine = state.src.slice(pos, lastPos);
found = true;
}
}
state.line = next + 1;
const token = state.push("math_block", "math", 0);
token.block = true;
token.content = (firstLine && firstLine.trim() ? `${firstLine}
` : "") + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
token.map = [start, state.line];
token.markup = "$$";
return true;
}
function MarkdownItKatex(md, options) {
const katexInline = function(latex) {
options.displayMode = false;
try {
return katex.renderToString(latex, options);
} catch (error) {
if (options.throwOnError)
console.warn(error);
return latex;
}
};
const inlineRenderer = function(tokens, idx) {
return katexInline(tokens[idx].content);
};
const katexBlock = function(latex) {
options.displayMode = true;
try {
return `<p>${katex.renderToString(latex, options)}</p>`;
} catch (error) {
if (options.throwOnError)
console.warn(error);
return latex;
}
};
const blockRenderer = function(tokens, idx) {
return `${katexBlock(tokens[idx].content)}
`;
};
md.inline.ruler.after("escape", "math_inline", math_inline);
md.block.ruler.after("blockquote", "math_block", math_block, {
alt: ["paragraph", "reference", "blockquote", "list"]
});
md.renderer.rules.math_inline = inlineRenderer;
md.renderer.rules.math_block = blockRenderer;
}
// node/syntax/markdown-it/markdown-it-shiki.ts
import { isTruthy } from "@antfu/utils";
import { fromHighlighter } from "@shikijs/markdown-it/core";
// node/syntax/transform/utils.ts
function normalizeRangeStr(rangeStr = "") {
return !rangeStr.trim() ? [] : rangeStr.trim().split(/\|/g).map((i) => i.trim());
}
function getCodeBlocks(md) {
const codeblocks = Array.from(md.matchAll(/^```[\s\S]*?^```/gm)).map((m) => {
const start = m.index;
const end = m.index + m[0].length;
const startLine = md.slice(0, start).match(/\n/g)?.length || 0;
const endLine = md.slice(0, end).match(/\n/g)?.length || 0;
return [start, end, startLine, endLine];
});
return {
codeblocks,
isInsideCodeblocks(idx) {
return codeblocks.some(([s, e]) => s <= idx && idx <= e);
},
isLineInsideCodeblocks(line) {
return codeblocks.some(([, , s, e]) => s <= line && line <= e);
}
};
}
function escapeVueInCode(md) {
return md.replace(/\{\{/g, "&lbrace;&lbrace;");
}
// node/syntax/markdown-it/markdown-it-shiki.ts
async function MarkdownItShiki({ data: { config }, mode, utils }) {
const transformers = [
...utils.shikiOptions.transformers || [],
(config.twoslash === true || config.twoslash === mode) && (await import("@shikijs/vitepress-twoslash")).transformerTwoslash({
explicitTrigger: true,
twoslashOptions: {
handbookOptions: {
noErrorValidation: true
}
}
}),
{
pre(pre) {
this.addClassToHast(pre, "slidev-code");
delete pre.properties.tabindex;
},
postprocess(code) {
return escapeVueInCode(code);
}
}
].filter(isTruthy);
return fromHighlighter(utils.shiki, {
...utils.shikiOptions,
transformers
});
}
// node/syntax/markdown-it/markdown-it-v-drag.ts
import { SourceMapConsumer } from "source-map-js";
var dragComponentRegex = /<(v-?drag-?\w*)([\s>])/i;
var dragDirectiveRegex = /(?<![</\w])v-drag(=".*?")?/i;
function MarkdownItVDrag(md, markdownTransformMap) {
const visited = /* @__PURE__ */ new WeakSet();
const sourceMapConsumers = /* @__PURE__ */ new WeakMap();
function getSourceMapConsumer(id) {
const s = markdownTransformMap.get(id);
if (!s)
return void 0;
let smc = sourceMapConsumers.get(s);
if (smc)
return smc;
const sourceMap = s.generateMap();
smc = new SourceMapConsumer({
...sourceMap,
version: sourceMap.version.toString()
});
sourceMapConsumers.set(s, smc);
return smc;
}
const _parse = md.parse;
md.parse = function(src, env) {
const smc = getSourceMapConsumer(env.id);
const toOriginalPos = smc ? (line) => smc.originalPositionFor({ line: line + 1, column: 0 }).line - 1 : (line) => line;
function toMarkdownSource(map, idx) {
const start = toOriginalPos(map[0]);
const end = toOriginalPos(map[1]);
return `[${start},${Math.max(start + 1, end)},${idx}]`;
}
function replaceChildren(token, regex, replacement) {
for (const child of token.children ?? []) {
if (child.type === "html_block" || child.type === "html_inline") {
child.content = child.content.replace(regex, replacement);
}
replaceChildren(child, regex, replacement);
}
}
return _parse.call(this, src, env).map((token) => {
if (!["html_block", "html_inline", "inline"].includes(token.type) || !token.content.includes("drag") || visited.has(token))
return token;
token.content = token.content.replace(dragComponentRegex, (_, tag, space, idx) => {
const replacement = `<${tag} :markdownSource="${toMarkdownSource(token.map, idx)}"${space}`;
replaceChildren(token, dragComponentRegex, replacement);
return replacement;
}).replace(dragDirectiveRegex, (_, value, idx) => {
const replacement = `v-drag${value ?? ""} :markdownSource="${toMarkdownSource(token.map, idx)}"`;
replaceChildren(token, dragDirectiveRegex, replacement);
return replacement;
});
visited.add(token);
return token;
});
};
}
// node/syntax/markdown-it/index.ts
async function useMarkdownItPlugins(md, options, markdownTransformMap) {
const { roots, data: { features, config } } = options;
if (config.highlighter === "shiki") {
md.use(await MarkdownItShiki(options));
}
md.use(MarkdownItLink);
md.use(MarkdownItEscapeInlineCode);
md.use(MarkdownItFootnote);
md.use(taskLists, { enabled: true, lineNumber: true, label: true });
if (features.katex)
md.use(MarkdownItKatex, await setupKatex(roots));
md.use(MarkdownItVDrag, markdownTransformMap);
if (config.mdc)
md.use(MarkdownItMdc);
}
// node/setups/transformers.ts
async function setupTransformers(roots) {
const returns = await loadSetups(roots, "transformers.ts", []);
const result = {
pre: [],
preCodeblock: [],
postCodeblock: [],
post: []
};
for (const r of [...returns].reverse()) {
if (r.pre)
result.pre.push(...r.pre);
if (r.preCodeblock)
result.preCodeblock.push(...r.preCodeblock);
}
for (const r of returns) {
if (r.postCodeblock)
result.postCodeblock.push(...r.postCodeblock);
if (r.post)
result.post.push(...r.post);
}
return result;
}
// node/syntax/transform/code-wrapper.ts
var reCodeBlock = /^```([\w'-]+)?\s*(?:\{([\w*,|-]+)\}\s*?(\{[^}]*\})?([^\r\n]*))?\r?\n([ \t]*\S[\s\S]*?)^```$/gm;
function transformCodeWrapper(ctx) {
ctx.s.replace(
reCodeBlock,
(full, lang = "", rangeStr = "", options = "", attrs = "", code) => {
const ranges = normalizeRangeStr(rangeStr);
code = code.trimEnd();
options = options.trim() || "{}";
return `
<CodeBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>
\`\`\`${lang}${attrs}
${code}
\`\`\`
</CodeBlockWrapper>`;
}
);
}
// node/syntax/transform/in-page-css.ts
function transformPageCSS(ctx) {
const codeBlocks = getCodeBlocks(ctx.s.original);
ctx.s.replace(
/(\n<style[^>]*>)([\s\S]+?)(<\/style>)/g,
(full, start, css, end, index) => {
if (codeBlocks.isInsideCodeblocks(index))
return full;
if (!start.includes("scoped"))
start = start.replace("<style", "<style scoped");
return `${start}
${css}${end}`;
}
);
}
// node/syntax/transform/katex-wrapper.ts
function transformKaTexWrapper(ctx) {
ctx.s.replace(
/^\$\$(?:\s*\{([\w*,|-]+)\}\s*?(?:(\{[^}]*\})\s*?)?)?\n(\S[\s\S]*?)^\$\$/gm,
(full, rangeStr = "", options = "", code) => {
const ranges = !rangeStr.trim() ? [] : rangeStr.trim().split(/\|/g).map((i) => i.trim());
code = code.trimEnd();
options = options.trim() || "{}";
return `<KaTexBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>
$$
${code}
$$
</KaTexBlockWrapper>
`;
}
);
}
// node/syntax/transform/magic-move.ts
import lz from "lz-string";
import { codeToKeyedTokens } from "shiki-magic-move/core";
var reMagicMoveBlock = /^````(?:md|markdown) magic-move *(\{[^}]*\})?([^ \n]*)\n([\s\S]+?)^````$/gm;
function parseLineNumbersOption(options) {
return /lines: *true/.test(options) ? true : /lines: *false/.test(options) ? false : void 0;
}
function transformMagicMove(ctx) {
ctx.s.replace(
reMagicMoveBlock,
(full, options = "{}", _attrs = "", body) => {
const matches = Array.from(body.matchAll(reCodeBlock));
if (!matches.length)
throw new Error("Magic Move block must contain at least one code block");
const defaultLineNumbers = parseLineNumbersOption(options) ?? ctx.options.data.config.lineNumbers;
const ranges = matches.map((i) => normalizeRangeStr(i[2]));
const steps = matches.map((i) => {
const lineNumbers = parseLineNumbersOption(i[3]) ?? defaultLineNumbers;
return codeToKeyedTokens(ctx.options.utils.shiki, i[5].trimEnd(), {
...ctx.options.utils.shikiOptions,
lang: i[1]
}, lineNumbers);
});
const compressed = lz.compressToBase64(JSON.stringify(steps));
return `<ShikiMagicMove v-bind="${options}" steps-lz="${compressed}" :step-ranges='${JSON.stringify(ranges)}' />`;
}
);
}
// node/syntax/transform/mermaid.ts
import lz2 from "lz-string";
function transformMermaid(ctx) {
ctx.s.replace(
/^```mermaid *(\{[^\n]*\})?\n([\s\S]+?)\n```/gm,
(full, options = "", code = "") => {
code = code.trim();
options = options.trim() || "{}";
const encoded = lz2.compressToBase64(code);
return `<Mermaid code-lz="${encoded}" v-bind="${options}" />`;
}
);
}
// node/syntax/transform/monaco.ts
import lz3 from "lz-string";
function transformMonaco(ctx) {
const enabled = ctx.options.data.config.monaco === true || ctx.options.data.config.monaco === ctx.options.mode;
if (!enabled) {
ctx.s.replace(/\{monaco([\w:,-]*)\}/g, "");
return;
}
ctx.s.replace(
/^```(\w+) *\{monaco-diff\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^~~~ *\n([\s\S]+?)^```/gm,
(full, lang = "ts", options = "{}", code, diff) => {
lang = lang.trim();
options = options.trim() || "{}";
const encoded = lz3.compressToBase64(code);
const encodedDiff = lz3.compressToBase64(diff);
return `<Monaco code-lz="${encoded}" diff-lz="${encodedDiff}" lang="${lang}" v-bind="${options}" />`;
}
);
ctx.s.replace(
/^```(\w+) *\{monaco\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^```/gm,
(full, lang = "ts", options = "{}", code) => {
lang = lang.trim();
options = options.trim() || "{}";
const encoded = lz3.compressToBase64(code);
return `<Monaco code-lz="${encoded}" lang="${lang}" v-bind="${options}" />`;
}
);
ctx.s.replace(
/^```(\w+) *\{monaco-run\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^```/gm,
(full, lang = "ts", options = "{}", code) => {
lang = lang.trim();
options = options.trim() || "{}";
const encoded = lz3.compressToBase64(code);
return `<Monaco runnable code-lz="${encoded}" lang="${lang}" v-bind="${options}" />`;
}
);
}
// node/syntax/transform/plant-uml.ts
import { encode as encodePlantUml } from "plantuml-encoder";
function transformPlantUml(ctx) {
const server = ctx.options.data.config.plantUmlServer;
ctx.s.replace(
/^```plantuml[^\n{}]*(\{[^}\n]*\})?\n([\s\S]+?)\n```/gm,
(full, options = "", content = "") => {
const code = encodePlantUml(content.trim());
options = options.trim() || "{}";
return `<PlantUml :code="'${code}'" :server="'${server}'" v-bind="${options}" />`;
}
);
}
// node/syntax/transform/slot-sugar.ts
function transformSlotSugar(ctx) {
const linesWithNewline = ctx.s.original.split(/(\r?\n)/g);
const codeBlocks = getCodeBlocks(ctx.s.original);
const lines = [];
for (let i = 0; i < linesWithNewline.length; i += 2) {
const line = linesWithNewline[i];
const newline = linesWithNewline[i + 1] || "";
lines.push(line + newline);
}
let prevSlot = false;
let offset = 0;
lines.forEach((line) => {
const start = offset;
offset += line.length;
if (codeBlocks.isInsideCodeblocks(offset))
return;
const match = line.match(/^::\s*([\w.\-:]+)\s*::(\s*)$/);
if (match) {
ctx.s.overwrite(start, offset - match[2].length, `${prevSlot ? "\n\n</template>\n" : "\n"}<template v-slot:${match[1]}="slotProps">
`);
prevSlot = true;
}
});
if (prevSlot)
ctx.s.append("\n\n</template>");
}
// node/syntax/transform/snippet.ts
import path2 from "node:path";
import { slash as slash2 } from "@antfu/utils";
import fs5 from "fs-extra";
import lz4 from "lz-string";
// node/vite/monacoWrite.ts
import fs4 from "node:fs/promises";
import path from "node:path";
var monacoWriterWhitelist = /* @__PURE__ */ new Set();
function createMonacoWriterPlugin({ userRoot }) {
return {
name: "slidev:monaco-write",
apply: "serve",
configureServer(server) {
server.ws.on("connection", (socket) => {
socket.on("message", async (data) => {
let json;
try {
json = JSON.parse(data.toString());
} catch {
return;
}
if (json.type === "custom" && json.event === "slidev:monaco-write") {
const { file, content } = json.data;
if (!monacoWriterWhitelist.has(file)) {
console.error(`[Slidev] Unauthorized file write: ${file}`);
return;
}
const filepath = path.join(userRoot, file);
console.log("[Slidev] Writing file:", filepath);
await fs4.writeFile(filepath, content, "utf-8");
}
});
});
}
};
}
// node/syntax/transform/snippet.ts
function dedent(text) {
const lines = text.split("\n");
const minIndentLength = lines.reduce((acc, line) => {
for (let i = 0; i < line.length; i++) {
if (line[i] !== " " && line[i] !== " ")
return Math.min(i, acc);
}
return acc;
}, Number.POSITIVE_INFINITY);
if (minIndentLength < Number.POSITIVE_INFINITY)
return lines.map((x) => x.slice(minIndentLength)).join("\n");
return text;
}
function findRegion(lines, regionName) {
const regionRegexps = [
// javascript, typescript, java
[/^\/\/ ?#?region ([\w*-]+)$/, /^\/\/ ?#?endregion/],
// css, less, scss
[/^\/\* ?#region ([\w*-]+) ?\*\/$/, /^\/\* ?#endregion[\s\w*-]*\*\/$/],
// C, C++
[/^#pragma region ([\w*-]+)$/, /^#pragma endregion/],
// HTML, markdown
[/^<!-- #?region ([\w*-]+) -->$/, /^<!-- #?region[\s\w*-]*-->$/],
// Visual Basic
[/^#Region ([\w*-]+)$/, /^#End Region/],
// Bat
[/^::#region ([\w*-]+)$/, /^::#endregion/],
// C#, PHP, Powershell, Python, perl & misc
[/^# ?region ([\w*-]+)$/, /^# ?endregion/]
];
let endReg = null;
let start = -1;
for (const [lineId, line] of lines.entries()) {
if (endReg === null) {
for (const [startReg, end] of regionRegexps) {
const match = line.trim().match(startReg);
if (match && match[1] === regionName) {
start = lineId + 1;
endReg = end;
break;
}
}
} else if (endReg.test(line.trim())) {
return {
start,
end: lineId,
regexp: endReg
};
}
}
return null;
}
var reMonacoWrite = /^\{monaco-write\}/;
function transformSnippet({ s, slide, options }) {
const watchFiles = options.data.watchFiles;
const dir = path2.dirname(slide.source?.filepath ?? options.entry ?? options.userRoot);
s.replace(
// eslint-disable-next-line regexp/no-super-linear-backtracking
/^<<<\s*(\S.*?)(#[\w-]+)?\s*(?:\s(\S+?))?\s*(\{.*)?$/gm,
(full, filepath = "", regionName = "", lang = "", meta = "") => {
const src = slash2(
/^@\//.test(filepath) ? path2.resolve(options.userRoot, filepath.slice(2)) : path2.resolve(dir, filepath)
);
meta = meta.trim();
lang = lang.trim();
lang = lang || path2.extname(filepath).slice(1);
const isAFile = fs5.statSync(src).isFile();
if (!fs5.existsSync(src) || !isAFile) {
throw new Error(isAFile ? `Code snippet path not found: ${src}` : `Invalid code snippet option`);
}
let content = fs5.readFileSync(src, "utf8");
if (regionName) {
const lines = content.split(/\r?\n/);
const region = findRegion(lines, regionName.slice(1));
if (region) {
content = dedent(
lines.slice(region.start, region.end).filter((line) => !region.regexp.test(line.trim())).join("\n")
);
}
}
if (meta.match(reMonacoWrite)) {
monacoWriterWhitelist.add(filepath);
lang = lang.trim();
meta = meta.replace(reMonacoWrite, "").trim() || "{}";
const encoded = lz4.compressToBase64(content);
return `<Monaco writable=${JSON.stringify(filepath)} code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />`;
} else {
watchFiles[src] ??= /* @__PURE__ */ new Set();
watchFiles[src].add(slide.index);
}
return `\`\`\`${lang} ${meta}
${content}
\`\`\``;
}
);
}
// node/syntax/transform/index.ts
async function getMarkdownTransformers(options) {
const extras = await setupTransformers(options.roots);
return [
...extras.pre,
transformSnippet,
options.data.config.highlighter === "shiki" && transformMagicMove,
...extras.preCodeblock,
transformMermaid,
transformPlantUml,
options.data.features.monaco && transformMonaco,
...extras.postCodeblock,
transformCodeWrapper,
options.data.features.katex && transformKaTexWrapper,
transformPageCSS,
transformSlotSugar,
...extras.post
];
}
// node/vite/markdown.ts
async function createMarkdownPlugin(options, { markdown: mdOptions }) {
const markdownTransformMap = /* @__PURE__ */ new Map();
const transformers = await getMarkdownTransformers(options);
return Markdown({
include: [/\.md$/],
wrapperClasses: "",
headEnabled: false,
frontmatter: false,
escapeCodeTagInterpolation: false,
markdownItOptions: {
quotes: `""''`,
html: true,
xhtmlOut: true,
linkify: true,
...mdOptions?.markdownItOptions
},
...mdOptions,
async markdownItSetup(md) {
await useMarkdownItPlugins(md, options, markdownTransformMap);
await mdOptions?.markdownItSetup?.(md);
},
transforms: {
...mdOptions?.transforms,
before(code, id) {
if (options.data.markdownFiles[id])
return "";
code = mdOptions?.transforms?.before?.(code, id) ?? code;
const match = id.match(regexSlideSourceId);
if (!match)
return code;
const s = new MagicString(code);
markdownTransformMap.set(id, s);
const ctx = {
s,
slide: options.data.slides[+match[1] - 1],
options
};
for (const transformer of transformers) {
if (!transformer)
continue;
transformer(ctx);
if (!ctx.s.isEmpty())
ctx.s.commit();
}
return s.toString();
}
}
});
}
// node/vite/monacoTypes.ts
import fs6 from "node:fs/promises";
import { dirname, resolve as resolve5 } from "node:path";
import { slash as slash3 } from "@antfu/utils";
import fg2 from "fast-glob";
import { findDepPkgJsonPath } from "vitefu";
function createMonacoTypesLoader({ userRoot, utils }) {
return {
name: "slidev:monaco-types-loader",
resolveId(id) {
if (id.startsWith("/@slidev-monaco-types/"))
return id;
return null;
},
async load(id) {
if (!id.startsWith("/@slidev-monaco-types/"))
return null;
const url = new URL(id, "http://localhost");
if (url.pathname === "/@slidev-monaco-types/resolve") {
const query = new URLSearchParams(url.search);
const pkg = query.get("pkg");
const importer = query.get("importer") ?? userRoot;
const pkgJsonPath = await findDepPkgJsonPath(pkg, importer);
if (!pkgJsonPath)
throw new Error(`Package "${pkg}" not found in "${importer}"`);
const root = slash3(dirname(pkgJsonPath));
const pkgJson = JSON.parse(await fs6.readFile(pkgJsonPath, "utf-8"));
let deps = Object.keys(pkgJson.dependencies ?? {});
deps = deps.filter((pkg2) => !utils.isMonacoTypesIgnored(pkg2));
return [
`import "/@slidev-monaco-types/load?${new URLSearchParams({ root, name: pkgJson.name })}"`,
...deps.map((dep) => `import "/@slidev-monaco-types/resolve?${new URLSearchParams({ pkg: dep, importer: root })}"`)
].join("\n");
}
if (url.pathname === "/@slidev-monaco-types/load") {
const query = new URLSearchParams(url.search);
const root = query.get("root");
const name = query.get("name");
const files = await fg2(
[
"**/*.ts",
"**/*.mts",
"**/*.cts",
"package.json"
],
{
cwd: root,
followSymbolicLinks: true,
ignore: ["**/node_modules/**"]
}
);
if (!files.length)
return "/** No files found **/";
return [
'import { addFile } from "@slidev/client/setup/monaco.ts"',
...files.map((file) => `addFile(() => import(${JSON.stringify(`${toAtFS(resolve5(root, file))}?monaco-types&raw`)}), ${JSON.stringify(`node_modules/${name}/${file}`)})`)
].join("\n");
}
}
};
}
// node/vite/remoteAssets.ts
async function createRemoteAssetsPlugin({ data: { config }, mode }, pluginOptions) {
if (!(config.remoteAssets === true || config.remoteAssets === mode))
return;
const { VitePluginRemoteAssets, DefaultRules } = await import("vite-plugin-remote-assets");
return VitePluginRemoteAssets({
resolveMode: (id) => id.endsWith("index.html") ? "relative" : "@fs",
awaitDownload: mode === "build",
...pluginOptions.remoteAssets,
rules: [
...DefaultRules,
{
match: /\b(https?:\/\/image.unsplash\.com.*?)(?=[`'")\]])/gi,
ext: ".png"
},
...pluginOptions.remoteAssets?.rules ?? []
]
});
}
// node/vite/serverRef.ts
import ServerRef from "vite-plugin-vue-server-ref";
// node/integrations/drawings.ts
import { basename, dirname as dirname2, join as join9, resolve as resolve6 } from "node:path";
import fg3 from "fast-glob";
import fs7 from "fs-extra";
function resolveDrawingsDir(options) {
return options.data.config.drawings.persist ? resolve6(
dirname2(options.entry),
options.data.config.drawings.persist
) : void 0;
}
async function loadDrawings(options) {
const dir = resolveDrawingsDir(options);
if (!dir || !fs7.existsSync(dir))
return {};
const files = await fg3("*.svg", {
onlyFiles: true,
cwd: dir,
absolute: true,
suppressErrors: true
});
const obj = {};
await Promise.all(files.map(async (path4) => {
const num = +basename(path4, ".svg");
if (Number.isNaN(num))
return;
const content = await fs7.readFile(path4, "utf8");
const lines = content.split(/\n/g);
obj[num.toString()] = lines.slice(1, -1).join("\n");
}));
return obj;
}
async function writeDrawings(options, drawing) {
const dir = resolveDrawingsDir(options);
if (!dir)
return;
const width = options.data.config.canvasWidth;
const height = Math.round(width / options.data.config.aspectRatio);
const SVG_HEAD = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">`;
await fs7.ensureDir(dir);
return Promise.all(
Object.entries(drawing).map(async ([key, value]) => {
if (!value)
return;
const svg = `${SVG_HEAD}
${value}
</svg>`;
await fs7.writeFile(join9(dir, `${key}.svg`), svg, "utf-8");
})
);
}
// node/integrations/snapshots.ts
import { dirname as dirname3, join as join10, resolve as resolve7 } from "node:path";
import fs8 from "fs-extra";
function resolveSnapshotsDir(options) {
return resolve7(dirname3(options.entry), ".slidev/snapshots");
}
async function loadSnapshots(options) {
const dir = resolveSnapshotsDir(options);
const file = join10(dir, "snapshots.json");
if (!dir || !fs8.existsSync(file))
return {};
return JSON.parse(await fs8.readFile(file, "utf8"));
}
async function writeSnapshots(options, data) {
const dir = resolveSnapshotsDir(options);
if (!dir)
return;
await fs8.ensureDir(dir);
await fs8.writeFile(join10(dir, "snapshots.json"), JSON.stringify(data, null, 2), "utf-8");
}
// node/vite/serverRef.ts
async function createServerRefPlugin(options, pluginOptions) {
return ServerRef({
debug: false,
// process.env.NODE_ENV === 'development',
state: {
sync: false,
nav: {
page: 0,
clicks: 0
},
drawings: await loadDrawings(options),
snapshots: await loadSnapshots(options),
...pluginOptions.serverRef?.state
},
onChanged(key, data, patch, timestamp) {
pluginOptions.serverRef?.onChanged?.(key, data, patch, timestamp);
if (options.data.config.drawings.persist && key === "drawings")
writeDrawings(options, patch ?? data);
if (key === "snapshots")
writeSnapshots(options, data);
}
});
}
// node/vite/staticCopy.ts
import { existsSync as existsSync5 } from "node:fs";
import { join as join11 } from "node:path";
async function createStaticCopyPlugin({ themeRoots, addonRoots }, pluginOptions) {
const publicDirs = [...themeRoots, ...addonRoots].map((i) => join11(i, "public")).filter(existsSync5);
if (!publicDirs.length)
return;
const { viteStaticCopy } = await import("vite-plugin-static-copy");
return viteStaticCopy({
silent: true,
targets: publicDirs.map((dir) => ({
src: `${dir}/*`,
dest: "theme"
})),
...pluginOptions.staticCopy
});
}
// node/vite/unocss.ts
import UnoCSS from "unocss/vite";
// node/setups/unocss.ts
import { existsSync as existsSync6, readFileSync } from "node:fs";
import { resolve as resolve8 } from "node:path";
import { mergeConfigs, presetIcons } from "unocss";
async function setupUnocss({ clientRoot, roots, data, utils }) {
async function loadFileConfigs(root) {
return (await Promise.all([
resolve8(root, "uno.config.ts"),
resolve8(root, "unocss.config.ts")
].map(async (i) => {
if (!existsSync6(i))
return void 0;
const loaded = await loadModule(i);
return "default" in loaded ? loaded.default : loaded;
}))).filter((x) => !!x);
}
const configs = [
{
presets: [
presetIcons({
collectionsNodeResolvePath: utils.iconsResolvePath,
collections: {
slidev: {
logo: () => readFileSync(resolve8(clientRoot, "assets/logo.svg"), "utf-8")
}
}
})
]
},
...await loadFileConfigs(clientRoot),
...await loadSetups(roots, "unocss.ts", [], loadFileConfigs)
].filter(Boolean);
const config = mergeConfigs(configs);
config.theme ||= {};
config.theme.fontFamily ||= {};
config.theme.fontFamily.sans ||= data.config.fonts.sans.join(",");
config.theme.fontFamily.mono ||= data.config.fonts.mono.join(",");
config.theme.fontFamily.serif ||= data.config.fonts.serif.join(",");
return config;
}
// node/vite/unocss.ts
async function createUnocssPlugin(options, pluginOptions) {
return UnoCSS({
configFile: false,
...await setupUnocss(options),
...pluginOptions.unocss
});
}
// node/vite/vue.ts
import Vue from "@vitejs/plugin-vue";
import VueJsx from "@vitejs/plugin-vue-jsx";
var customElements = /* @__PURE__ */ new Set([
// katex
"annotation",
"math",
"menclose",
"mfrac",
"mglyph",
"mi",
"mlabeledtr",
"mn",
"mo",
"mover",
"mpadded",
"mphantom",
"mroot",
"mrow",
"mspace",
"msqrt",
"mstyle",
"msub",
"msubsup",
"msup",
"mtable",
"mtd",
"mtext",
"mtr",
"munder",
"munderover",
"semantics"
]);
async function createVuePlugin(_options, pluginOptions) {
const {
vue: vueOptions = {},
vuejsx: vuejsxOptions = {}
} = pluginOptions;
const VuePlugin = Vue({
include: [/\.vue$/, /\.vue\?vue/, /\.vue\?v=/, /\.md$/, /\.md\?vue/],
exclude: [],
...vueOptions,
template: {
...vueOptions?.template,
compilerOptions: {
...vueOptions?.template?.compilerOptions,
isCustomElement(tag) {
return customElements.has(tag) || vueOptions?.template?.compilerOptions?.isCustomElement?.(tag);
}
}
}
});
const VueJsxPlugin = VueJsx(vuejsxOptions);
return [
VueJsxPlugin,
VuePlugin
];
}
// node/vite/index.ts
function ViteSlidevPlugin(options, pluginOptions = {}, serverOptions = {}) {
return Promise.all([
createSlidesLoader(options, serverOptions),
createMarkdownPlugin(options, pluginOptions),
createLayoutWrapperPlugin(options),
createContextInjectionPlugin(),
createVuePlugin(options, pluginOptions),
createHmrPatchPlugin(),
createComponentsPlugin(options, pluginOptions),
createIconsPlugin(options, pluginOptions),
createRemoteAssetsPlugin(options, pluginOptions),
createServerRefPlugin(options, pluginOptions),
createConfigPlugin(options),
createMonacoTypesLoader(options),
createMonacoWriterPlugin(options),
createVueCompilerFlagsPlugin(options),
createUnocssPlugin(options, pluginOptions),
createStaticCopyPlugin(options, pluginOptions),
createInspectPlugin(options, pluginOptions)
]);
}
// node/commands/shared.ts
var sharedMd = MarkdownIt({ html: true });
sharedMd.use(MarkdownItLink);
function getSlideTitle(data) {
const tokens = sharedMd.parseInline(data.config.title, {});
const title = stringifyMarkdownTokens(tokens);
const slideTitle = data.config.titleTemplate.replace("%s", title);
return slideTitle === "Slidev - Slidev" ? "Slidev" : slideTitle;
}
async function resolveViteConfigs(options, baseConfig, overrideConfigs, command, serverOptions) {
const configEnv = {
mode: command === "build" ? "production" : "development",
command
};
const files = options.roots.map((i) => join12(i, "vite.config.ts"));
for (const file of files) {
if (!existsSync7(file))
continue;
const viteConfig = await loadConfigFromFile(configEnv, file);
if (!viteConfig?.config)
continue;
baseConfig = mergeConfig2(baseConfig, viteConfig.config);
}
baseConfig = mergeConfig2(baseConfig, overrideConfigs);
baseConfig = mergeConfig2(baseConfig, {
configFile: false,
root: options.userRoot,
plugins: await ViteSlidevPlugin(options, baseConfig.slidev, serverOptions),
define: {
// Fixes Vue production mode breaking PDF Export #1245
__VUE_PROD_DEVTOOLS__: false
}
});
return baseConfig;
}
// node/setups/indexHtml.ts
function toAttrValue(unsafe) {
return JSON.stringify(escapeHtml(String(unsafe)));
}
function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data }) {
let main = readFileSync2(join13(clientRoot, "index.html"), "utf-8");
let head = "";
let body = "";
const { info, author, keywords } = data.headmatter;
head += [
`<meta name="slidev:version" content="${version}">`,
mode === "dev" && `<meta charset="slidev:entry" content="${slash4(entry)}">`,
`<link rel="icon" href="${data.config.favicon}">`,
`<title>${getSlideTitle(data)}</title>`,
info && `<meta name="description" content=${toAttrValue(info)}>`,
author && `<meta name="author" content=${toAttrValue(author)}>`,
keywords && `<meta name="keywords" content=${toAttrValue(Array.isArray(keywords) ? keywords.join(", ") : keywords)}>`
].filter(Boolean).join("\n");
for (const root of roots) {
const path4 = join13(root, "index.html");
if (!existsSync8(path4))
continue;
const index = readFileSync2(path4, "utf-8");
if (root === userRoot && index.includes("<!DOCTYPE")) {
console.error(yellow2(`[Slidev] Ignored provided index.html with doctype declaration. (${white(path4)})`));
console.error(yellow2("This file may be generated by Slidev, please remove it from your project."));
continue;
}
head += `
${(index.match(/<head>([\s\S]*?)<\/head>/i)?.[1] || "").trim()}`;
body += `
${(index.match(/<body>([\s\S]*?)<\/body>/i)?.[1] || "").trim()}`;
}
if (data.features.tweet)
body += '\n<script async src="https://platform.twitter.com/widgets.js"></script>';
if (data.config.fonts.webfonts.length && data.config.fonts.provider !== "none")
head += `
<link rel="stylesheet" href="${generateGoogleFontsUrl(data.config.fonts)}" type="text/css">`;
if (data.headmatter.lang)
main = main.replace('<html lang="en">', `<html lang="${data.headmatter.lang}">`);
main = main.replace("__ENTRY__", toAtFS(join13(clientRoot, "main.ts"))).replace("<!-- head -->", head).replace("<!-- body -->", body);
return main;
}
// node/setups/shiki.ts
import fs9 from "node:fs/promises";
import { bundledLanguages, createHighlighter } from "shiki";
var cachedRoots;
var cachedShiki;
async function setupShiki(roots) {
if (cachedRoots === roots)
return cachedShiki;
cachedShiki?.shiki.dispose();
const options = await loadSetups(
roots,
"shiki.ts",
[{
/** @deprecated */
async loadTheme(path4) {
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.");
return JSON.parse(await fs9.readFile(path4, "utf-8"));
}
}]
);
const mergedOptions = Object.assign({}, ...options);
if ("theme" in mergedOptions && "themes" in mergedOptions)
delete mergedOptions.theme;
if (mergedOptions.theme && typeof mergedOptions.theme !== "string" && !mergedOptions.theme.name && !mergedOptions.theme.tokenColors) {
mergedOptions.themes = mergedOptions.theme;
delete mergedOptions.theme;
}
if (!mergedOptions.theme && !mergedOptions.themes) {
mergedOptions.themes = {
dark: "vitesse-dark",
light: "vitesse-light"
};
}
if (mergedOptions.themes)
mergedOptions.defaultColor = false;
const shiki = await createHighlighter({
...mergedOptions,
langs: mergedOptions.langs ?? Object.keys(bundledLanguages),
themes: "themes" in mergedOptions ? Object.values(mergedOptions.themes) : [mergedOptions.theme]
});
cachedRoots = roots;
return cachedShiki = {
shiki,
shikiOptions: mergedOptions
};
}
// node/options.ts
var debug = Debug("slidev:options");
async function resolveOptions(entryOptions, mode) {
const entry = await resolveEntry(entryOptions.entry);
const rootsInfo = await getRoots(entry);
const loaded = await parser.load(rootsInfo.userRoot, entry, void 0, mode);
let themeRaw = entryOptions.theme || loaded.headmatter.theme;
themeRaw = themeRaw === null ? "none" : themeRaw || "default";
const [theme, themeRoot] = await resolveTheme(themeRaw, entry);
const themeRoots = themeRoot ? [themeRoot] : [];
const themeMeta = themeRoot ? await getThemeMeta(theme, themeRoot) : void 0;
const config = parser.resolveConfig(loaded.headmatter, themeMeta, entryOptions.entry);
const addonRoots = await resolveAddons(config.addons);
const roots = uniq5([...themeRoots, ...addonRoots, rootsInfo.userRoot]);
if (entryOptions.download)
config.download ||= entryOptions.download;
debug({
...rootsInfo,
...entryOptions,
config,
mode,
entry,
themeRaw,
theme,
themeRoots,
addonRoots,
roots
});
const data = {
...loaded,
config,
themeMeta
};
const resolved = {
...rootsInfo,
...entryOptions,
data,
mode,
entry,
themeRaw,
theme,
themeRoots,
addonRoots,
roots
};
return {
...resolved,
utils: await createDataUtils(resolved)
};
}
async function createDataUtils(resolved) {
const monacoTypesIgnorePackagesMatches = (resolved.data.config.monacoTypesIgnorePackages || []).map((i) => mm.matcher(i));
let _layouts_cache_time = 0;
let _layouts_cache = {};
return {
...await setupShiki(resolved.roots),
indexHtml: setupIndexHtml(resolved),
define: getDefine(resolved),
iconsResolvePath: [resolved.clientRoot, ...resolved.roots].reverse(),
isMonacoTypesIgnored: (pkg) => monacoTypesIgnorePackagesMatches.some((i) => i(pkg)),
getLayouts: () => {
const now = Date.now();
if (now - _layouts_cache_time < 2e3)
return _layouts_cache;
const layouts = {};
for (const root of [resolved.clientRoot, ...resolved.roots]) {
const layoutPaths = fg4.sync("layouts/**/*.{vue,ts}", {
cwd: root,
absolute: true,
suppressErrors: true
});
for (const layoutPath of layoutPaths) {
const layoutName = path3.basename(layoutPath).replace(/\.\w+$/, "");
layouts[layoutName] = layoutPath;
}
}
_layouts_cache_time = now;
_layouts_cache = layouts;
return layouts;
}
};
}
function getDefine(options) {
const matchMode = (mode) => mode === true || mode === options.mode;
return objectMap2(
{
__DEV__: options.mode === "dev",
__SLIDEV_CLIENT_ROOT__: toAtFS(options.clientRoot),
__SLIDEV_HASH_ROUTE__: options.data.config.routerMode === "hash",
__SLIDEV_FEATURE_DRAWINGS__: matchMode(options.data.config.drawings.enabled),
__SLIDEV_FEATURE_EDITOR__: options.mode === "dev" && options.data.config.editor !== false,
__SLIDEV_FEATURE_DRAWINGS_PERSIST__: !!options.data.config.drawings.persist,
__SLIDEV_FEATURE_RECORD__: matchMode(options.data.config.record),
__SLIDEV_FEATURE_PRESENTER__: matchMode(options.data.config.presenter),
__SLIDEV_FEATURE_PRINT__: options.mode === "export" || options.mode === "build" && [true, "true", "auto"].includes(options.data.config.download),
__SLIDEV_FEATURE_BROWSER_EXPORTER__: matchMode(options.data.config.browserExporter),
__SLIDEV_FEATURE_WAKE_LOCK__: matchMode(options.data.config.wakeLock),
__SLIDEV_HAS_SERVER__: options.mode !== "build"
},
(v, k) => [v, JSON.stringify(k)]
);
}
export {
version,
resolveAddons,
resolveTheme,
getThemeMeta,
parser,
loadSetups,
resolveOptions,
createDataUtils,
ViteSlidevPlugin,
resolveViteConfigs
};