@slidev/cli 51.6.0 → 51.6.2
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.
- package/dist/build-oRYT3yXD.js +62 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +518 -594
- package/dist/export-D1Zqc-n1.js +433 -0
- package/dist/index.d.ts +12 -7
- package/dist/index.js +5 -17
- package/dist/resolver-BShaA6qw.js +155 -0
- package/dist/serve-D6FyjIir.js +14 -0
- package/dist/shared-D3tuRsUE.js +2475 -0
- package/package.json +23 -23
- package/dist/build-Z7GTSSXD.js +0 -77
- package/dist/chunk-BCA62OS7.js +0 -29
- package/dist/chunk-TJFRPB4N.js +0 -190
- package/dist/chunk-Y2DHOFI5.js +0 -2761
- package/dist/export-5TR5BGLG.js +0 -520
|
@@ -0,0 +1,2475 @@
|
|
|
1
|
+
import { createResolver, getRoots, isInstalledGlobally, resolveEntry, resolveImportPath, resolveImportUrl, toAtFS } from "./resolver-BShaA6qw.js";
|
|
2
|
+
import { builtinModules } from "node:module";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path, { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import { bold, gray, red, white, yellow } from "ansis";
|
|
6
|
+
import equal from "fast-deep-equal";
|
|
7
|
+
import { loadConfigFromFile, mergeConfig } from "vite";
|
|
8
|
+
import fs$1, { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import MarkdownIt from "markdown-it";
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
|
+
import { createJiti } from "jiti";
|
|
12
|
+
import YAML from "yaml";
|
|
13
|
+
import { isString, isTruthy, notNullish, objectEntries, objectMap, range, slash, uniq } from "@antfu/utils";
|
|
14
|
+
import IconsResolver from "unplugin-icons/resolver";
|
|
15
|
+
import Components from "unplugin-vue-components/vite";
|
|
16
|
+
import { createResolve } from "mlly";
|
|
17
|
+
import { findDepPkgJsonPath } from "vitefu";
|
|
18
|
+
import Icons from "unplugin-icons/vite";
|
|
19
|
+
import * as parser$1 from "@slidev/parser/fs";
|
|
20
|
+
import * as parser from "@slidev/parser/fs";
|
|
21
|
+
import Debug from "debug";
|
|
22
|
+
import fg from "fast-glob";
|
|
23
|
+
import pm from "picomatch";
|
|
24
|
+
import { satisfies } from "semver";
|
|
25
|
+
import { escapeHtml } from "markdown-it/lib/common/utils.mjs";
|
|
26
|
+
import { createHead, extractUnheadInputFromHtml, transformHtmlTemplate } from "unhead/server";
|
|
27
|
+
import { bundledLanguages, createHighlighter } from "shiki";
|
|
28
|
+
import katex from "katex";
|
|
29
|
+
import MagicString from "magic-string-stack";
|
|
30
|
+
import Markdown from "unplugin-vue-markdown/vite";
|
|
31
|
+
import Token from "markdown-it/lib/token.mjs";
|
|
32
|
+
import MarkdownItFootnote from "markdown-it-footnote";
|
|
33
|
+
import MarkdownItMdc from "markdown-it-mdc";
|
|
34
|
+
import { fromHighlighter } from "@shikijs/markdown-it/core";
|
|
35
|
+
import { SourceMapConsumer } from "source-map-js";
|
|
36
|
+
import lz from "lz-string";
|
|
37
|
+
import { codeToKeyedTokens } from "shiki-magic-move/core";
|
|
38
|
+
import { encode } from "plantuml-encoder";
|
|
39
|
+
import ServerRef from "vite-plugin-vue-server-ref";
|
|
40
|
+
import UnoCSS from "unocss/vite";
|
|
41
|
+
import { mergeConfigs, presetIcons } from "unocss";
|
|
42
|
+
import Vue from "@vitejs/plugin-vue";
|
|
43
|
+
import VueJsx from "@vitejs/plugin-vue-jsx";
|
|
44
|
+
|
|
45
|
+
//#region package.json
|
|
46
|
+
var version = "51.6.2";
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region node/syntax/markdown-it/markdown-it-link.ts
|
|
50
|
+
function MarkdownItLink(md) {
|
|
51
|
+
const defaultRender = md.renderer.rules.link_open ?? ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));
|
|
52
|
+
md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
|
|
53
|
+
const token = tokens[idx];
|
|
54
|
+
const hrefIndex = token.attrIndex("href");
|
|
55
|
+
const attr = token.attrs?.[hrefIndex];
|
|
56
|
+
const href = attr?.[1] ?? "";
|
|
57
|
+
if ("./#".includes(href[0]) || /^\d+$/.test(href)) {
|
|
58
|
+
token.tag = "Link";
|
|
59
|
+
attr[0] = "to";
|
|
60
|
+
for (let i = idx + 1; i < tokens.length; i++) if (tokens[i].type === "link_close") {
|
|
61
|
+
tokens[i].tag = "Link";
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
} else if (token.attrGet("target") == null) token.attrPush(["target", "_blank"]);
|
|
65
|
+
return defaultRender(tokens, idx, options, env, self);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region node/utils.ts
|
|
71
|
+
let jiti;
|
|
72
|
+
function loadModule(absolutePath) {
|
|
73
|
+
jiti ??= createJiti(fileURLToPath(import.meta.url), { moduleCache: false });
|
|
74
|
+
return jiti.import(absolutePath);
|
|
75
|
+
}
|
|
76
|
+
function stringifyMarkdownTokens(tokens) {
|
|
77
|
+
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(" ");
|
|
78
|
+
}
|
|
79
|
+
function generateFontParams(options) {
|
|
80
|
+
const weights = options.weights.flatMap((i) => options.italic ? [`0,${i}`, `1,${i}`] : [`${i}`]).sort().join(";");
|
|
81
|
+
const fontParams = options.webfonts.map((i) => `family=${i.replace(/^(['"])(.*)\1$/, "$1").replace(/\s+/g, "+")}:${options.italic ? "ital," : ""}wght@${weights}`).join("&");
|
|
82
|
+
return fontParams;
|
|
83
|
+
}
|
|
84
|
+
function generateGoogleFontsUrl(options) {
|
|
85
|
+
return `https://fonts.googleapis.com/css2?${generateFontParams(options)}&display=swap`;
|
|
86
|
+
}
|
|
87
|
+
function generateCoollabsFontsUrl(options) {
|
|
88
|
+
return `https://api.fonts.coollabs.io/fonts?${generateFontParams(options)}&display=swap`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Update frontmatter patch and preserve the comments
|
|
92
|
+
*/
|
|
93
|
+
function updateFrontmatterPatch(source, frontmatter) {
|
|
94
|
+
let doc = source.frontmatterDoc;
|
|
95
|
+
if (!doc) {
|
|
96
|
+
source.frontmatterStyle = "frontmatter";
|
|
97
|
+
source.frontmatterDoc = doc = new YAML.Document({});
|
|
98
|
+
}
|
|
99
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
100
|
+
source.frontmatter[key] = value;
|
|
101
|
+
if (value == null) doc.delete(key);
|
|
102
|
+
else {
|
|
103
|
+
const valueNode = doc.createNode(value);
|
|
104
|
+
let found = false;
|
|
105
|
+
YAML.visit(doc.contents, { Pair(_key, node, path$1) {
|
|
106
|
+
if (path$1.length === 1 && YAML.isScalar(node.key) && node.key.value === key) {
|
|
107
|
+
node.value = valueNode;
|
|
108
|
+
found = true;
|
|
109
|
+
return YAML.visit.BREAK;
|
|
110
|
+
}
|
|
111
|
+
} });
|
|
112
|
+
if (!found) {
|
|
113
|
+
if (!YAML.isMap(doc.contents)) doc.contents = doc.createNode({});
|
|
114
|
+
doc.contents.add(doc.createPair(key, valueNode));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function getBodyJson(req) {
|
|
120
|
+
return new Promise((resolve$2, reject) => {
|
|
121
|
+
let body = "";
|
|
122
|
+
req.on("data", (chunk) => body += chunk);
|
|
123
|
+
req.on("error", reject);
|
|
124
|
+
req.on("end", () => {
|
|
125
|
+
try {
|
|
126
|
+
resolve$2(JSON.parse(body) || {});
|
|
127
|
+
} catch (e) {
|
|
128
|
+
reject(e);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region node/vite/compilerFlagsVue.ts
|
|
136
|
+
/**
|
|
137
|
+
* Replace compiler flags like `__DEV__` in Vue SFC
|
|
138
|
+
*/
|
|
139
|
+
function createVueCompilerFlagsPlugin(options) {
|
|
140
|
+
const define = objectEntries(options.utils.define);
|
|
141
|
+
return {
|
|
142
|
+
name: "slidev:flags",
|
|
143
|
+
enforce: "pre",
|
|
144
|
+
transform(code, id) {
|
|
145
|
+
if (!id.match(/\.vue($|\?)/) && !id.includes("?vue&")) return;
|
|
146
|
+
const original = code;
|
|
147
|
+
define.forEach(([from, to]) => {
|
|
148
|
+
code = code.replaceAll(from, to);
|
|
149
|
+
});
|
|
150
|
+
if (original !== code) return code;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region node/vite/components.ts
|
|
157
|
+
function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
|
|
158
|
+
return Components({
|
|
159
|
+
extensions: [
|
|
160
|
+
"vue",
|
|
161
|
+
"md",
|
|
162
|
+
"js",
|
|
163
|
+
"ts",
|
|
164
|
+
"jsx",
|
|
165
|
+
"tsx"
|
|
166
|
+
],
|
|
167
|
+
dirs: [join(clientRoot, "builtin"), ...roots.map((i) => join(i, "components"))],
|
|
168
|
+
globsExclude: [],
|
|
169
|
+
include: [
|
|
170
|
+
/\.vue$/,
|
|
171
|
+
/\.vue\?vue/,
|
|
172
|
+
/\.vue\?v=/,
|
|
173
|
+
/\.md$/,
|
|
174
|
+
/\.md\?vue/
|
|
175
|
+
],
|
|
176
|
+
exclude: [],
|
|
177
|
+
resolvers: [IconsResolver({
|
|
178
|
+
prefix: "",
|
|
179
|
+
customCollections: Object.keys(pluginOptions.icons?.customCollections || [])
|
|
180
|
+
})],
|
|
181
|
+
dts: false,
|
|
182
|
+
...pluginOptions.components
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region node/vite/common.ts
|
|
188
|
+
const regexSlideReqPath = /^\/__slidev\/slides\/(\d+)\.json$/;
|
|
189
|
+
const regexSlideFacadeId = /^\/@slidev\/slides\/(\d+)\/(md|frontmatter)($|\?)/;
|
|
190
|
+
const regexSlideSourceId = /__slidev_(\d+)\.(md|frontmatter)$/;
|
|
191
|
+
const templateInjectionMarker = "/* @slidev-injection */";
|
|
192
|
+
const templateImportContextUtils = `import { useSlideContext as _useSlideContext, frontmatterToProps as _frontmatterToProps } from "@slidev/client/context.ts"`;
|
|
193
|
+
const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = _useSlideContext()`;
|
|
194
|
+
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region node/vite/contextInjection.ts
|
|
197
|
+
/**
|
|
198
|
+
* Inject `$slidev` into the script block of a Vue component
|
|
199
|
+
*/
|
|
200
|
+
function createContextInjectionPlugin() {
|
|
201
|
+
return {
|
|
202
|
+
name: "slidev:context-injection",
|
|
203
|
+
async transform(code, id) {
|
|
204
|
+
if (!id.endsWith(".vue") || id.includes("/@slidev/client/") || id.includes("/packages/client/")) return;
|
|
205
|
+
if (code.includes(templateInjectionMarker) || code.includes("useSlideContext()")) return code;
|
|
206
|
+
const imports = [
|
|
207
|
+
templateImportContextUtils,
|
|
208
|
+
templateInitContext,
|
|
209
|
+
templateInjectionMarker
|
|
210
|
+
];
|
|
211
|
+
const matchScript = code.match(/<script((?!setup).)*(setup)?.*>/);
|
|
212
|
+
if (matchScript && matchScript[2]) return code.replace(/(<script.*>)/g, `$1\n${imports.join("\n")}\n`);
|
|
213
|
+
else if (matchScript && !matchScript[2]) {
|
|
214
|
+
const matchExport = code.match(/export\s+default\s+\{/);
|
|
215
|
+
if (matchExport) {
|
|
216
|
+
const exportIndex = (matchExport.index || 0) + matchExport[0].length;
|
|
217
|
+
let component = code.slice(exportIndex);
|
|
218
|
+
component = component.slice(0, component.indexOf("</script>"));
|
|
219
|
+
const scriptIndex = (matchScript.index || 0) + matchScript[0].length;
|
|
220
|
+
const provideImport = "\nimport { injectionSlidevContext } from \"@slidev/client/constants.ts\"\n";
|
|
221
|
+
code = `${code.slice(0, scriptIndex)}${provideImport}${code.slice(scriptIndex)}`;
|
|
222
|
+
let injectIndex = exportIndex + provideImport.length;
|
|
223
|
+
let injectObject = "$slidev: { from: injectionSlidevContext },";
|
|
224
|
+
const matchInject = component.match(/.*inject\s*:\s*([[{])/);
|
|
225
|
+
if (matchInject) {
|
|
226
|
+
injectIndex += (matchInject.index || 0) + matchInject[0].length;
|
|
227
|
+
if (matchInject[1] === "[") {
|
|
228
|
+
let injects = component.slice((matchInject.index || 0) + matchInject[0].length);
|
|
229
|
+
const injectEndIndex = injects.indexOf("]");
|
|
230
|
+
injects = injects.slice(0, injectEndIndex);
|
|
231
|
+
injectObject += injects.split(",").map((inject) => `${inject}: {from: ${inject}}`).join(",");
|
|
232
|
+
return `${code.slice(0, injectIndex - 1)}{\n${injectObject}\n}${code.slice(injectIndex + injectEndIndex + 1)}`;
|
|
233
|
+
} else return `${code.slice(0, injectIndex)}\n${injectObject}\n${code.slice(injectIndex)}`;
|
|
234
|
+
}
|
|
235
|
+
return `${code.slice(0, injectIndex)}\ninject: { ${injectObject} },\n${code.slice(injectIndex)}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return `<script setup>\n${imports.join("\n")}\n</script>\n${code}`;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region node/vite/extendConfig.ts
|
|
245
|
+
const INCLUDE_GLOBAL = [
|
|
246
|
+
"@typescript/ata",
|
|
247
|
+
"file-saver",
|
|
248
|
+
"lz-string",
|
|
249
|
+
"prettier",
|
|
250
|
+
"recordrtc",
|
|
251
|
+
"typescript",
|
|
252
|
+
"yaml"
|
|
253
|
+
];
|
|
254
|
+
const INCLUDE_LOCAL = INCLUDE_GLOBAL.map((i) => `@slidev/cli > @slidev/client > ${i}`);
|
|
255
|
+
const EXCLUDE_GLOBAL = [
|
|
256
|
+
"@antfu/utils",
|
|
257
|
+
"@shikijs/monaco",
|
|
258
|
+
"@shikijs/vitepress-twoslash/client",
|
|
259
|
+
"@slidev/client",
|
|
260
|
+
"@slidev/client/constants",
|
|
261
|
+
"@slidev/client/context",
|
|
262
|
+
"@slidev/client/logic/dark",
|
|
263
|
+
"@slidev/parser",
|
|
264
|
+
"@slidev/parser/core",
|
|
265
|
+
"@slidev/rough-notation",
|
|
266
|
+
"@slidev/types",
|
|
267
|
+
"@unhead/vue",
|
|
268
|
+
"@unocss/reset",
|
|
269
|
+
"@vueuse/core",
|
|
270
|
+
"@vueuse/math",
|
|
271
|
+
"@vueuse/motion",
|
|
272
|
+
"@vueuse/shared",
|
|
273
|
+
"drauu",
|
|
274
|
+
"floating-vue",
|
|
275
|
+
"fuse.js",
|
|
276
|
+
"mermaid",
|
|
277
|
+
"monaco-editor",
|
|
278
|
+
"shiki-magic-move/vue",
|
|
279
|
+
"shiki",
|
|
280
|
+
"shiki/core",
|
|
281
|
+
"vue-demi",
|
|
282
|
+
"vue-router",
|
|
283
|
+
"vue"
|
|
284
|
+
];
|
|
285
|
+
const EXCLUDE_LOCAL = EXCLUDE_GLOBAL;
|
|
286
|
+
const ASYNC_MODULES = [
|
|
287
|
+
"file-saver",
|
|
288
|
+
"vue",
|
|
289
|
+
"@vue"
|
|
290
|
+
];
|
|
291
|
+
function createConfigPlugin(options) {
|
|
292
|
+
const resolveClientDep = createResolve({
|
|
293
|
+
conditions: [
|
|
294
|
+
"import",
|
|
295
|
+
"module",
|
|
296
|
+
"browser",
|
|
297
|
+
"default",
|
|
298
|
+
options.mode === "build" ? "production" : "development"
|
|
299
|
+
],
|
|
300
|
+
url: pathToFileURL(options.clientRoot)
|
|
301
|
+
});
|
|
302
|
+
return {
|
|
303
|
+
name: "slidev:config",
|
|
304
|
+
async config(config) {
|
|
305
|
+
const injection = {
|
|
306
|
+
define: options.utils.define,
|
|
307
|
+
resolve: {
|
|
308
|
+
alias: [
|
|
309
|
+
{
|
|
310
|
+
find: /^@slidev\/client$/,
|
|
311
|
+
replacement: `${toAtFS(options.clientRoot)}/index.ts`
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
find: /^@slidev\/client\/(.*)/,
|
|
315
|
+
replacement: `${toAtFS(options.clientRoot)}/$1`
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
find: /^#slidev\/(.*)/,
|
|
319
|
+
replacement: "/@slidev/$1"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
find: "vue",
|
|
323
|
+
replacement: await resolveImportPath("vue/dist/vue.esm-bundler.js", true)
|
|
324
|
+
},
|
|
325
|
+
...isInstalledGlobally.value ? await Promise.all(INCLUDE_GLOBAL.map(async (dep) => ({
|
|
326
|
+
find: dep,
|
|
327
|
+
replacement: fileURLToPath(await resolveClientDep(dep))
|
|
328
|
+
}))) : []
|
|
329
|
+
],
|
|
330
|
+
dedupe: ["vue"]
|
|
331
|
+
},
|
|
332
|
+
optimizeDeps: isInstalledGlobally.value ? {
|
|
333
|
+
exclude: EXCLUDE_GLOBAL,
|
|
334
|
+
include: INCLUDE_GLOBAL
|
|
335
|
+
} : {
|
|
336
|
+
exclude: EXCLUDE_LOCAL,
|
|
337
|
+
include: INCLUDE_LOCAL
|
|
338
|
+
},
|
|
339
|
+
css: { postcss: { plugins: [await import("postcss-nested").then((r) => (r.default || r)())] } },
|
|
340
|
+
server: { fs: {
|
|
341
|
+
strict: true,
|
|
342
|
+
allow: uniq([
|
|
343
|
+
options.userWorkspaceRoot,
|
|
344
|
+
options.clientRoot,
|
|
345
|
+
isInstalledGlobally.value ? slash(options.cliRoot).replace(/\/\.pnpm\/.*$/gi, "") : options.cliRoot,
|
|
346
|
+
...options.roots
|
|
347
|
+
])
|
|
348
|
+
} },
|
|
349
|
+
publicDir: join(options.userRoot, "public"),
|
|
350
|
+
build: { rollupOptions: { output: {
|
|
351
|
+
chunkFileNames(chunkInfo) {
|
|
352
|
+
const DEFAULT = "assets/[name]-[hash].js";
|
|
353
|
+
if (chunkInfo.name.includes("/")) return DEFAULT;
|
|
354
|
+
if (chunkInfo.moduleIds.filter((i) => isSlidevClient(i)).length > chunkInfo.moduleIds.length * .6) return "assets/slidev/[name]-[hash].js";
|
|
355
|
+
if (chunkInfo.moduleIds.filter((i) => i.match(/\/monaco-editor(-core)?\//)).length > chunkInfo.moduleIds.length * .6) return "assets/monaco/[name]-[hash].js";
|
|
356
|
+
return DEFAULT;
|
|
357
|
+
},
|
|
358
|
+
manualChunks(id) {
|
|
359
|
+
if (id.startsWith("/@slidev-monaco-types/") || id.includes("/@slidev/monaco-types") || id.endsWith("?monaco-types&raw")) return "monaco/bundled-types";
|
|
360
|
+
if (id.includes("/shiki/") || id.includes("/@shikijs/")) return `modules/shiki`;
|
|
361
|
+
if (id.startsWith("~icons/")) return "modules/unplugin-icons";
|
|
362
|
+
const matchedAsyncModule = ASYNC_MODULES.find((i) => id.includes(`/node_modules/${i}`));
|
|
363
|
+
if (matchedAsyncModule) return `modules/${matchedAsyncModule.replace("@", "").replace("/", "-")}`;
|
|
364
|
+
}
|
|
365
|
+
} } },
|
|
366
|
+
cacheDir: isInstalledGlobally.value ? join(options.cliRoot, "node_modules/.vite") : void 0
|
|
367
|
+
};
|
|
368
|
+
function isSlidevClient(id) {
|
|
369
|
+
return id.includes("/@slidev/") || id.includes("/slidev/packages/client/") || id.includes("/@vueuse/");
|
|
370
|
+
}
|
|
371
|
+
return mergeConfig(injection, config);
|
|
372
|
+
},
|
|
373
|
+
configureServer(server) {
|
|
374
|
+
return () => {
|
|
375
|
+
server.middlewares.use(async (req, res, next) => {
|
|
376
|
+
if (req.url === "/index.html") {
|
|
377
|
+
res.setHeader("Content-Type", "text/html");
|
|
378
|
+
res.statusCode = 200;
|
|
379
|
+
res.end(options.utils.indexHtml);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
next();
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region node/vite/hmrPatch.ts
|
|
391
|
+
/**
|
|
392
|
+
* force reload slide component to ensure v-click resolves correctly
|
|
393
|
+
*/
|
|
394
|
+
function createHmrPatchPlugin() {
|
|
395
|
+
return {
|
|
396
|
+
name: "slidev:hmr-patch",
|
|
397
|
+
transform(code, id) {
|
|
398
|
+
if (!id.match(regexSlideSourceId)) return;
|
|
399
|
+
return code.replace("if (_rerender_only)", "if (false)");
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region node/vite/icons.ts
|
|
406
|
+
function createIconsPlugin(options, pluginOptions) {
|
|
407
|
+
return Icons({
|
|
408
|
+
defaultClass: "slidev-icon",
|
|
409
|
+
collectionsNodeResolvePath: options.utils.iconsResolvePath,
|
|
410
|
+
...pluginOptions.icons
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region node/vite/inspect.ts
|
|
416
|
+
async function createInspectPlugin(options, pluginOptions) {
|
|
417
|
+
if (!options.inspect) return;
|
|
418
|
+
const { default: PluginInspect } = await import("vite-plugin-inspect");
|
|
419
|
+
return PluginInspect({
|
|
420
|
+
dev: true,
|
|
421
|
+
build: true,
|
|
422
|
+
...pluginOptions.inspect
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region node/vite/layoutWrapper.ts
|
|
428
|
+
function createLayoutWrapperPlugin({ data, utils }) {
|
|
429
|
+
return {
|
|
430
|
+
name: "slidev:layout-wrapper",
|
|
431
|
+
async transform(code, id) {
|
|
432
|
+
const match = id.match(regexSlideSourceId);
|
|
433
|
+
if (!match) return;
|
|
434
|
+
const [, no, type] = match;
|
|
435
|
+
if (type !== "md") return;
|
|
436
|
+
const index = +no - 1;
|
|
437
|
+
const layouts = utils.getLayouts();
|
|
438
|
+
const rawLayoutName = data.slides[index]?.frontmatter?.layout ?? data.slides[0]?.frontmatter?.defaults?.layout;
|
|
439
|
+
let layoutName = rawLayoutName || (index === 0 ? "cover" : "default");
|
|
440
|
+
if (!layouts[layoutName]) {
|
|
441
|
+
console.error(red(`\nUnknown layout "${bold(layoutName)}".${yellow(" Available layouts are:")}`) + Object.keys(layouts).map((i, idx) => (idx % 3 === 0 ? "\n " : "") + gray(i.padEnd(15, " "))).join(" "));
|
|
442
|
+
console.error();
|
|
443
|
+
layoutName = "default";
|
|
444
|
+
}
|
|
445
|
+
const setupTag = code.match(/^<script setup.*>/m);
|
|
446
|
+
if (!setupTag) throw new Error(`[Slidev] Internal error: <script setup> block not found in slide ${index + 1}.`);
|
|
447
|
+
const templatePart = code.slice(0, setupTag.index);
|
|
448
|
+
const scriptPart = code.slice(setupTag.index);
|
|
449
|
+
const bodyStart = templatePart.indexOf("<template>") + 10;
|
|
450
|
+
const bodyEnd = templatePart.lastIndexOf("</template>");
|
|
451
|
+
let body = code.slice(bodyStart, bodyEnd).trim();
|
|
452
|
+
if (body.startsWith("<div>") && body.endsWith("</div>")) body = body.slice(5, -6);
|
|
453
|
+
return [
|
|
454
|
+
templatePart.slice(0, bodyStart),
|
|
455
|
+
`<InjectedLayout v-bind="_frontmatterToProps($frontmatter,${index})">\n${body}\n</InjectedLayout>`,
|
|
456
|
+
templatePart.slice(bodyEnd),
|
|
457
|
+
scriptPart.slice(0, setupTag[0].length),
|
|
458
|
+
`import InjectedLayout from "${toAtFS(layouts[layoutName])}"`,
|
|
459
|
+
templateImportContextUtils,
|
|
460
|
+
templateInitContext,
|
|
461
|
+
"$clicksContext.setup()",
|
|
462
|
+
templateInjectionMarker,
|
|
463
|
+
scriptPart.slice(setupTag[0].length)
|
|
464
|
+
].join("\n");
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
//#endregion
|
|
470
|
+
//#region node/integrations/addons.ts
|
|
471
|
+
async function resolveAddons(addonsInConfig) {
|
|
472
|
+
const { userRoot, userPkgJson } = await getRoots();
|
|
473
|
+
const resolved = [];
|
|
474
|
+
const resolveAddonNameAndRoot = createResolver("addon", {});
|
|
475
|
+
async function resolveAddon(name, parent) {
|
|
476
|
+
const [, pkgRoot] = await resolveAddonNameAndRoot(name, parent);
|
|
477
|
+
if (!pkgRoot) return;
|
|
478
|
+
resolved.push(pkgRoot);
|
|
479
|
+
const { slidev = {}, engines = {} } = JSON.parse(await fs.readFile(resolve(pkgRoot, "package.json"), "utf-8"));
|
|
480
|
+
if (engines.slidev && !satisfies(version, engines.slidev, { includePrerelease: true })) throw new Error(`[slidev] addon "${name}" requires Slidev version range "${engines.slidev}" but found "${version}"`);
|
|
481
|
+
if (Array.isArray(slidev.addons)) await Promise.all(slidev.addons.map((addon) => resolveAddon(addon, pkgRoot)));
|
|
482
|
+
}
|
|
483
|
+
if (Array.isArray(addonsInConfig)) await Promise.all(addonsInConfig.map((addon) => resolveAddon(addon, userRoot)));
|
|
484
|
+
if (Array.isArray(userPkgJson.slidev?.addons)) await Promise.all(userPkgJson.slidev.addons.map((addon) => resolveAddon(addon, userRoot)));
|
|
485
|
+
return resolved;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region node/integrations/themes.ts
|
|
490
|
+
const officialThemes = {
|
|
491
|
+
"none": "",
|
|
492
|
+
"default": "@slidev/theme-default",
|
|
493
|
+
"seriph": "@slidev/theme-seriph",
|
|
494
|
+
"apple-basic": "@slidev/theme-apple-basic",
|
|
495
|
+
"shibainu": "@slidev/theme-shibainu",
|
|
496
|
+
"bricks": "@slidev/theme-bricks"
|
|
497
|
+
};
|
|
498
|
+
const resolveTheme = createResolver("theme", officialThemes);
|
|
499
|
+
async function getThemeMeta(name, root) {
|
|
500
|
+
const path$1 = join(root, "package.json");
|
|
501
|
+
if (!existsSync(path$1)) return {};
|
|
502
|
+
const { slidev = {}, engines = {} } = JSON.parse(await fs.readFile(path$1, "utf-8"));
|
|
503
|
+
if (engines.slidev && !satisfies(version, engines.slidev, { includePrerelease: true })) throw new Error(`[slidev] theme "${name}" requires Slidev version range "${engines.slidev}" but found "${version}"`);
|
|
504
|
+
return slidev;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
//#endregion
|
|
508
|
+
//#region node/setups/indexHtml.ts
|
|
509
|
+
function toAttrValue(unsafe) {
|
|
510
|
+
return JSON.stringify(escapeHtml(String(unsafe)));
|
|
511
|
+
}
|
|
512
|
+
async function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data, base }) {
|
|
513
|
+
let main = readFileSync(join(clientRoot, "index.html"), "utf-8");
|
|
514
|
+
let body = "";
|
|
515
|
+
const inputs = [];
|
|
516
|
+
for (const root of roots) {
|
|
517
|
+
const path$1 = join(root, "index.html");
|
|
518
|
+
if (!existsSync(path$1)) continue;
|
|
519
|
+
const html$1 = readFileSync(path$1, "utf-8");
|
|
520
|
+
if (root === userRoot && html$1.includes("<!DOCTYPE")) {
|
|
521
|
+
console.error(yellow(`[Slidev] Ignored provided index.html with doctype declaration. (${white(path$1)})`));
|
|
522
|
+
console.error(yellow("This file may be generated by Slidev, please remove it from your project."));
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
inputs.push(extractUnheadInputFromHtml(html$1).input);
|
|
526
|
+
body += `\n${(html$1.match(/<body>([\s\S]*?)<\/body>/i)?.[1] || "").trim()}`;
|
|
527
|
+
}
|
|
528
|
+
if (data.features.tweet) body += "\n<script async src=\"https://platform.twitter.com/widgets.js\"></script>";
|
|
529
|
+
const webFontsLink = [];
|
|
530
|
+
if (data.config.fonts.webfonts.length) {
|
|
531
|
+
const { provider } = data.config.fonts;
|
|
532
|
+
if (provider === "google") webFontsLink.push({
|
|
533
|
+
rel: "stylesheet",
|
|
534
|
+
href: generateGoogleFontsUrl(data.config.fonts),
|
|
535
|
+
type: "text/css"
|
|
536
|
+
});
|
|
537
|
+
else if (provider === "coollabs") webFontsLink.push({
|
|
538
|
+
rel: "stylesheet",
|
|
539
|
+
href: generateCoollabsFontsUrl(data.config.fonts),
|
|
540
|
+
type: "text/css"
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
const { info, author, keywords } = data.headmatter;
|
|
544
|
+
const seoMeta = data.headmatter.seoMeta ?? {};
|
|
545
|
+
const title = getSlideTitle(data);
|
|
546
|
+
const description = info ? toAttrValue(info) : null;
|
|
547
|
+
const unhead = createHead({ init: [{
|
|
548
|
+
htmlAttrs: data.headmatter.lang ? { lang: data.headmatter.lang } : void 0,
|
|
549
|
+
title,
|
|
550
|
+
link: [data.config.favicon ? {
|
|
551
|
+
rel: "icon",
|
|
552
|
+
href: data.config.favicon
|
|
553
|
+
} : null, ...webFontsLink].filter((x) => x),
|
|
554
|
+
meta: [
|
|
555
|
+
{
|
|
556
|
+
property: "slidev:version",
|
|
557
|
+
content: version
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
charset: "slidev:entry",
|
|
561
|
+
content: mode === "dev" && slash(entry)
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
name: "description",
|
|
565
|
+
content: description
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
name: "author",
|
|
569
|
+
content: author ? toAttrValue(author) : null
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: "keywords",
|
|
573
|
+
content: keywords ? toAttrValue(Array.isArray(keywords) ? keywords.join(", ") : keywords) : null
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
property: "og:title",
|
|
577
|
+
content: seoMeta.ogTitle || title
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
property: "og:description",
|
|
581
|
+
content: seoMeta.ogDescription || description
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
property: "og:image",
|
|
585
|
+
content: seoMeta.ogImage
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
property: "og:url",
|
|
589
|
+
content: seoMeta.ogUrl
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
property: "twitter:card",
|
|
593
|
+
content: seoMeta.twitterCard
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
property: "twitter:site",
|
|
597
|
+
content: seoMeta.twitterSite
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
property: "twitter:title",
|
|
601
|
+
content: seoMeta.twitterTitle
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
property: "twitter:description",
|
|
605
|
+
content: seoMeta.twitterDescription
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
property: "twitter:image",
|
|
609
|
+
content: seoMeta.twitterImage
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
property: "twitter:url",
|
|
613
|
+
content: seoMeta.twitterUrl
|
|
614
|
+
}
|
|
615
|
+
].filter((x) => x.content)
|
|
616
|
+
}, ...inputs] });
|
|
617
|
+
const baseInDev = mode === "dev" && base ? base.slice(0, -1) : "";
|
|
618
|
+
main = main.replace("__ENTRY__", baseInDev + toAtFS(join(clientRoot, "main.ts"))).replace("<!-- body -->", body);
|
|
619
|
+
const html = await transformHtmlTemplate(unhead, main);
|
|
620
|
+
return html;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
//#endregion
|
|
624
|
+
//#region node/setups/load.ts
|
|
625
|
+
async function loadSetups(roots, filename, args, extraLoader) {
|
|
626
|
+
const returns = [];
|
|
627
|
+
for (const root of roots) {
|
|
628
|
+
const path$1 = resolve(root, "setup", filename);
|
|
629
|
+
if (existsSync(path$1)) {
|
|
630
|
+
const { default: setup } = await loadModule(path$1);
|
|
631
|
+
const ret = await setup(...args);
|
|
632
|
+
if (ret) returns.push(ret);
|
|
633
|
+
}
|
|
634
|
+
if (extraLoader) returns.push(...await extraLoader(root));
|
|
635
|
+
}
|
|
636
|
+
return returns;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
//#endregion
|
|
640
|
+
//#region node/setups/katex.ts
|
|
641
|
+
async function setupKatex(roots) {
|
|
642
|
+
const options = await loadSetups(roots, "katex.ts", []);
|
|
643
|
+
return Object.assign({ strict: false }, ...options);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region node/setups/shiki.ts
|
|
648
|
+
let cachedRoots;
|
|
649
|
+
let cachedShiki;
|
|
650
|
+
async function setupShiki(roots) {
|
|
651
|
+
if (cachedRoots === roots) return cachedShiki;
|
|
652
|
+
cachedShiki?.shiki.dispose();
|
|
653
|
+
const options = await loadSetups(roots, "shiki.ts", [{ async loadTheme(path$1) {
|
|
654
|
+
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.");
|
|
655
|
+
return JSON.parse(await fs.readFile(path$1, "utf-8"));
|
|
656
|
+
} }]);
|
|
657
|
+
const mergedOptions = Object.assign({}, ...options);
|
|
658
|
+
if ("theme" in mergedOptions && "themes" in mergedOptions) delete mergedOptions.theme;
|
|
659
|
+
if (mergedOptions.theme && typeof mergedOptions.theme !== "string" && !mergedOptions.theme.name && !mergedOptions.theme.tokenColors) {
|
|
660
|
+
mergedOptions.themes = mergedOptions.theme;
|
|
661
|
+
delete mergedOptions.theme;
|
|
662
|
+
}
|
|
663
|
+
if (!mergedOptions.theme && !mergedOptions.themes) mergedOptions.themes = {
|
|
664
|
+
dark: "vitesse-dark",
|
|
665
|
+
light: "vitesse-light"
|
|
666
|
+
};
|
|
667
|
+
if (mergedOptions.themes) mergedOptions.defaultColor = false;
|
|
668
|
+
const shiki = await createHighlighter({
|
|
669
|
+
...mergedOptions,
|
|
670
|
+
langs: mergedOptions.langs ?? Object.keys(bundledLanguages),
|
|
671
|
+
themes: "themes" in mergedOptions ? Object.values(mergedOptions.themes) : [mergedOptions.theme]
|
|
672
|
+
});
|
|
673
|
+
cachedRoots = roots;
|
|
674
|
+
return cachedShiki = {
|
|
675
|
+
shiki,
|
|
676
|
+
shikiOptions: mergedOptions
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
//#endregion
|
|
681
|
+
//#region node/options.ts
|
|
682
|
+
const debug = Debug("slidev:options");
|
|
683
|
+
async function resolveOptions(entryOptions, mode) {
|
|
684
|
+
const entry = await resolveEntry(entryOptions.entry);
|
|
685
|
+
const rootsInfo = await getRoots(entry);
|
|
686
|
+
const loaded = await parser$1.load(rootsInfo.userRoot, entry, void 0, mode);
|
|
687
|
+
let themeRaw = entryOptions.theme || loaded.headmatter.theme;
|
|
688
|
+
themeRaw = themeRaw === null ? "none" : themeRaw || "default";
|
|
689
|
+
const [theme, themeRoot] = await resolveTheme(themeRaw, entry);
|
|
690
|
+
const themeRoots = themeRoot ? [themeRoot] : [];
|
|
691
|
+
const themeMeta = themeRoot ? await getThemeMeta(theme, themeRoot) : void 0;
|
|
692
|
+
const config = parser$1.resolveConfig(loaded.headmatter, themeMeta, entryOptions.entry);
|
|
693
|
+
const addonRoots = await resolveAddons(config.addons);
|
|
694
|
+
const roots = uniq([
|
|
695
|
+
...themeRoots,
|
|
696
|
+
...addonRoots,
|
|
697
|
+
rootsInfo.userRoot
|
|
698
|
+
]);
|
|
699
|
+
if (entryOptions.download) config.download ||= entryOptions.download;
|
|
700
|
+
debug({
|
|
701
|
+
...rootsInfo,
|
|
702
|
+
...entryOptions,
|
|
703
|
+
config,
|
|
704
|
+
mode,
|
|
705
|
+
entry,
|
|
706
|
+
themeRaw,
|
|
707
|
+
theme,
|
|
708
|
+
themeRoots,
|
|
709
|
+
addonRoots,
|
|
710
|
+
roots
|
|
711
|
+
});
|
|
712
|
+
const data = {
|
|
713
|
+
...loaded,
|
|
714
|
+
config,
|
|
715
|
+
themeMeta
|
|
716
|
+
};
|
|
717
|
+
const resolved = {
|
|
718
|
+
...rootsInfo,
|
|
719
|
+
...entryOptions,
|
|
720
|
+
data,
|
|
721
|
+
mode,
|
|
722
|
+
entry,
|
|
723
|
+
themeRaw,
|
|
724
|
+
theme,
|
|
725
|
+
themeRoots,
|
|
726
|
+
addonRoots,
|
|
727
|
+
roots
|
|
728
|
+
};
|
|
729
|
+
return {
|
|
730
|
+
...resolved,
|
|
731
|
+
utils: await createDataUtils(resolved)
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
async function createDataUtils(resolved) {
|
|
735
|
+
const monacoTypesIgnorePackagesMatches = (resolved.data.config.monacoTypesIgnorePackages || []).map((i) => pm.makeRe(i));
|
|
736
|
+
let _layouts_cache_time = 0;
|
|
737
|
+
let _layouts_cache = {};
|
|
738
|
+
return {
|
|
739
|
+
...await setupShiki(resolved.roots),
|
|
740
|
+
katexOptions: await setupKatex(resolved.roots),
|
|
741
|
+
indexHtml: await setupIndexHtml(resolved),
|
|
742
|
+
define: getDefine(resolved),
|
|
743
|
+
iconsResolvePath: [resolved.clientRoot, ...resolved.roots].reverse(),
|
|
744
|
+
isMonacoTypesIgnored: (pkg) => monacoTypesIgnorePackagesMatches.some((i) => i.test(pkg)),
|
|
745
|
+
getLayouts: () => {
|
|
746
|
+
const now = Date.now();
|
|
747
|
+
if (now - _layouts_cache_time < 2e3) return _layouts_cache;
|
|
748
|
+
const layouts = {};
|
|
749
|
+
for (const root of [resolved.clientRoot, ...resolved.roots]) {
|
|
750
|
+
const layoutPaths = fg.sync("layouts/**/*.{vue,ts}", {
|
|
751
|
+
cwd: root,
|
|
752
|
+
absolute: true,
|
|
753
|
+
suppressErrors: true
|
|
754
|
+
});
|
|
755
|
+
for (const layoutPath of layoutPaths) {
|
|
756
|
+
const layoutName = path.basename(layoutPath).replace(/\.\w+$/, "");
|
|
757
|
+
layouts[layoutName] = layoutPath;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
_layouts_cache_time = now;
|
|
761
|
+
_layouts_cache = layouts;
|
|
762
|
+
return layouts;
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function getDefine(options) {
|
|
767
|
+
const matchMode = (mode) => mode === true || mode === options.mode;
|
|
768
|
+
return objectMap({
|
|
769
|
+
__DEV__: options.mode === "dev",
|
|
770
|
+
__SLIDEV_CLIENT_ROOT__: toAtFS(options.clientRoot),
|
|
771
|
+
__SLIDEV_HASH_ROUTE__: options.data.config.routerMode === "hash",
|
|
772
|
+
__SLIDEV_FEATURE_DRAWINGS__: matchMode(options.data.config.drawings.enabled),
|
|
773
|
+
__SLIDEV_FEATURE_EDITOR__: options.mode === "dev" && options.data.config.editor !== false,
|
|
774
|
+
__SLIDEV_FEATURE_DRAWINGS_PERSIST__: !!options.data.config.drawings.persist,
|
|
775
|
+
__SLIDEV_FEATURE_RECORD__: matchMode(options.data.config.record),
|
|
776
|
+
__SLIDEV_FEATURE_PRESENTER__: matchMode(options.data.config.presenter),
|
|
777
|
+
__SLIDEV_FEATURE_PRINT__: options.mode === "export" || options.mode === "build" && [
|
|
778
|
+
true,
|
|
779
|
+
"true",
|
|
780
|
+
"auto"
|
|
781
|
+
].includes(options.data.config.download),
|
|
782
|
+
__SLIDEV_FEATURE_BROWSER_EXPORTER__: matchMode(options.data.config.browserExporter),
|
|
783
|
+
__SLIDEV_FEATURE_WAKE_LOCK__: matchMode(options.data.config.wakeLock),
|
|
784
|
+
__SLIDEV_HAS_SERVER__: options.mode !== "build"
|
|
785
|
+
}, (v, k) => [v, JSON.stringify(k)]);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
//#endregion
|
|
789
|
+
//#region node/syntax/markdown-it/markdown-it-katex.ts
|
|
790
|
+
function isValidDelim(state, pos) {
|
|
791
|
+
const max = state.posMax;
|
|
792
|
+
let can_open = true;
|
|
793
|
+
let can_close = true;
|
|
794
|
+
const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
|
|
795
|
+
const nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
|
|
796
|
+
if (prevChar === 32 || prevChar === 9 || nextChar >= 48 && nextChar <= 57) can_close = false;
|
|
797
|
+
if (nextChar === 32 || nextChar === 9) can_open = false;
|
|
798
|
+
return {
|
|
799
|
+
can_open,
|
|
800
|
+
can_close
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
function math_inline(state, silent) {
|
|
804
|
+
let match, token, res, pos;
|
|
805
|
+
if (state.src[state.pos] !== "$") return false;
|
|
806
|
+
res = isValidDelim(state, state.pos);
|
|
807
|
+
if (!res.can_open) {
|
|
808
|
+
if (!silent) state.pending += "$";
|
|
809
|
+
state.pos += 1;
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
const start = state.pos + 1;
|
|
813
|
+
match = start;
|
|
814
|
+
while ((match = state.src.indexOf("$", match)) !== -1) {
|
|
815
|
+
pos = match - 1;
|
|
816
|
+
while (state.src[pos] === "\\") pos -= 1;
|
|
817
|
+
if ((match - pos) % 2 === 1) break;
|
|
818
|
+
match += 1;
|
|
819
|
+
}
|
|
820
|
+
if (match === -1) {
|
|
821
|
+
if (!silent) state.pending += "$";
|
|
822
|
+
state.pos = start;
|
|
823
|
+
return true;
|
|
824
|
+
}
|
|
825
|
+
if (match - start === 0) {
|
|
826
|
+
if (!silent) state.pending += "$$";
|
|
827
|
+
state.pos = start + 1;
|
|
828
|
+
return true;
|
|
829
|
+
}
|
|
830
|
+
res = isValidDelim(state, match);
|
|
831
|
+
if (!res.can_close) {
|
|
832
|
+
if (!silent) state.pending += "$";
|
|
833
|
+
state.pos = start;
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
836
|
+
if (!silent) {
|
|
837
|
+
token = state.push("math_inline", "math", 0);
|
|
838
|
+
token.markup = "$";
|
|
839
|
+
token.content = state.src.slice(start, match);
|
|
840
|
+
}
|
|
841
|
+
state.pos = match + 1;
|
|
842
|
+
return true;
|
|
843
|
+
}
|
|
844
|
+
function math_block(state, start, end, silent) {
|
|
845
|
+
let firstLine;
|
|
846
|
+
let lastLine;
|
|
847
|
+
let next;
|
|
848
|
+
let lastPos;
|
|
849
|
+
let found = false;
|
|
850
|
+
let pos = state.bMarks[start] + state.tShift[start];
|
|
851
|
+
let max = state.eMarks[start];
|
|
852
|
+
if (pos + 2 > max) return false;
|
|
853
|
+
if (state.src.slice(pos, pos + 2) !== "$$") return false;
|
|
854
|
+
pos += 2;
|
|
855
|
+
firstLine = state.src.slice(pos, max);
|
|
856
|
+
if (silent) return true;
|
|
857
|
+
if (firstLine.trim().slice(-2) === "$$") {
|
|
858
|
+
firstLine = firstLine.trim().slice(0, -2);
|
|
859
|
+
found = true;
|
|
860
|
+
}
|
|
861
|
+
for (next = start; !found;) {
|
|
862
|
+
next++;
|
|
863
|
+
if (next >= end) break;
|
|
864
|
+
pos = state.bMarks[next] + state.tShift[next];
|
|
865
|
+
max = state.eMarks[next];
|
|
866
|
+
if (pos < max && state.tShift[next] < state.blkIndent) break;
|
|
867
|
+
if (state.src.slice(pos, max).trim().slice(-2) === "$$") {
|
|
868
|
+
lastPos = state.src.slice(0, max).lastIndexOf("$$");
|
|
869
|
+
lastLine = state.src.slice(pos, lastPos);
|
|
870
|
+
found = true;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
state.line = next + 1;
|
|
874
|
+
const token = state.push("math_block", "math", 0);
|
|
875
|
+
token.block = true;
|
|
876
|
+
token.content = (firstLine && firstLine.trim() ? `${firstLine}\n` : "") + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : "");
|
|
877
|
+
token.map = [start, state.line];
|
|
878
|
+
token.markup = "$$";
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
function MarkdownItKatex(md, options) {
|
|
882
|
+
const katexInline = function(latex) {
|
|
883
|
+
options.displayMode = false;
|
|
884
|
+
try {
|
|
885
|
+
return katex.renderToString(latex, options);
|
|
886
|
+
} catch (error) {
|
|
887
|
+
if (options.throwOnError) console.warn(error);
|
|
888
|
+
return latex;
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
const inlineRenderer = function(tokens, idx) {
|
|
892
|
+
return katexInline(tokens[idx].content);
|
|
893
|
+
};
|
|
894
|
+
const katexBlock = function(latex) {
|
|
895
|
+
options.displayMode = true;
|
|
896
|
+
try {
|
|
897
|
+
return `<p>${katex.renderToString(latex, options)}</p>`;
|
|
898
|
+
} catch (error) {
|
|
899
|
+
if (options.throwOnError) console.warn(error);
|
|
900
|
+
return latex;
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
const blockRenderer = function(tokens, idx) {
|
|
904
|
+
return `${katexBlock(tokens[idx].content)}\n`;
|
|
905
|
+
};
|
|
906
|
+
md.inline.ruler.after("escape", "math_inline", math_inline);
|
|
907
|
+
md.block.ruler.after("blockquote", "math_block", math_block, { alt: [
|
|
908
|
+
"paragraph",
|
|
909
|
+
"reference",
|
|
910
|
+
"blockquote",
|
|
911
|
+
"list"
|
|
912
|
+
] });
|
|
913
|
+
md.renderer.rules.math_inline = inlineRenderer;
|
|
914
|
+
md.renderer.rules.math_block = blockRenderer;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
//#endregion
|
|
918
|
+
//#region node/virtual/configs.ts
|
|
919
|
+
const templateConfigs = {
|
|
920
|
+
id: "/@slidev/configs",
|
|
921
|
+
getContent({ data, remote }) {
|
|
922
|
+
const config = {
|
|
923
|
+
...data.config,
|
|
924
|
+
remote,
|
|
925
|
+
slidesTitle: getSlideTitle(data)
|
|
926
|
+
};
|
|
927
|
+
if (isString(config.info)) config.info = sharedMd.render(config.info);
|
|
928
|
+
return `export default ${JSON.stringify(config)}`;
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
//#endregion
|
|
933
|
+
//#region node/virtual/deprecated.ts
|
|
934
|
+
/**
|
|
935
|
+
* Kept for backward compatibility, use #slidev/slides instead
|
|
936
|
+
*
|
|
937
|
+
* @deprecated
|
|
938
|
+
*/
|
|
939
|
+
const templateLegacyRoutes = {
|
|
940
|
+
id: "/@slidev/routes",
|
|
941
|
+
getContent() {
|
|
942
|
+
return [`export { slides } from '#slidev/slides'`, `console.warn('[slidev] #slidev/routes is deprecated, use #slidev/slides instead')`].join("\n");
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
/**
|
|
946
|
+
* Kept for backward compatibility, use #slidev/title-renderer instead
|
|
947
|
+
*
|
|
948
|
+
* @deprecated
|
|
949
|
+
*/
|
|
950
|
+
const templateLegacyTitles = {
|
|
951
|
+
id: "/@slidev/titles.md",
|
|
952
|
+
getContent() {
|
|
953
|
+
return `
|
|
954
|
+
<script setup lang="ts">
|
|
955
|
+
import TitleRenderer from '#slidev/title-renderer'
|
|
956
|
+
defineProps<{ no: number | string }>()
|
|
957
|
+
console.warn('/@slidev/titles.md is deprecated, import from #slidev/title-renderer instead')
|
|
958
|
+
</script>
|
|
959
|
+
|
|
960
|
+
<TitleRenderer :no="no" />
|
|
961
|
+
`;
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
//#endregion
|
|
966
|
+
//#region node/virtual/global-layers.ts
|
|
967
|
+
const templateGlobalLayers = {
|
|
968
|
+
id: `/@slidev/global-layers`,
|
|
969
|
+
getContent({ roots }) {
|
|
970
|
+
const imports = [];
|
|
971
|
+
let n = 0;
|
|
972
|
+
function getComponent(names) {
|
|
973
|
+
const components = roots.flatMap((root) => names.map((name) => join(root, name))).filter((i) => existsSync(i));
|
|
974
|
+
imports.push(components.map((path$1, i) => `import __n${n}_${i} from '${toAtFS(path$1)}'`).join("\n"));
|
|
975
|
+
const render = components.map((_, i) => `h(__n${n}_${i})`).join(",");
|
|
976
|
+
n++;
|
|
977
|
+
return `{ render: () => [${render}] }`;
|
|
978
|
+
}
|
|
979
|
+
const globalTop = getComponent([
|
|
980
|
+
"global.vue",
|
|
981
|
+
"global-top.vue",
|
|
982
|
+
"GlobalTop.vue"
|
|
983
|
+
]);
|
|
984
|
+
const globalBottom = getComponent(["global-bottom.vue", "GlobalBottom.vue"]);
|
|
985
|
+
const slideTop = getComponent(["slide-top.vue", "SlideTop.vue"]);
|
|
986
|
+
const slideBottom = getComponent(["slide-bottom.vue", "SlideBottom.vue"]);
|
|
987
|
+
return [
|
|
988
|
+
imports.join("\n"),
|
|
989
|
+
`import { h } from 'vue'`,
|
|
990
|
+
`export const GlobalTop = ${globalTop}`,
|
|
991
|
+
`export const GlobalBottom = ${globalBottom}`,
|
|
992
|
+
`export const SlideTop = ${slideTop}`,
|
|
993
|
+
`export const SlideBottom = ${slideBottom}`
|
|
994
|
+
].join("\n");
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
//#endregion
|
|
999
|
+
//#region node/virtual/layouts.ts
|
|
1000
|
+
const templateLayouts = {
|
|
1001
|
+
id: "/@slidev/layouts",
|
|
1002
|
+
getContent({ utils }) {
|
|
1003
|
+
const imports = [];
|
|
1004
|
+
const layouts = objectMap(utils.getLayouts(), (k, v) => {
|
|
1005
|
+
imports.push(`import __layout_${k} from "${toAtFS(v)}"`);
|
|
1006
|
+
return [k, `__layout_${k}`];
|
|
1007
|
+
});
|
|
1008
|
+
return [imports.join("\n"), `export default {\n${Object.entries(layouts).map(([k, v]) => `"${k}": ${v}`).join(",\n")}\n}`].join("\n\n");
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
//#endregion
|
|
1013
|
+
//#region node/virtual/monaco-deps.ts
|
|
1014
|
+
const templateMonacoRunDeps = {
|
|
1015
|
+
id: "/@slidev/monaco-run-deps",
|
|
1016
|
+
async getContent({ userRoot, data }) {
|
|
1017
|
+
if (!data.features.monaco) return "";
|
|
1018
|
+
const deps = uniq(data.features.monaco.deps.concat(data.config.monacoTypesAdditionalPackages));
|
|
1019
|
+
const importerPath = resolve(userRoot, "./snippets/__importer__.ts");
|
|
1020
|
+
let result = "";
|
|
1021
|
+
for (let i = 0; i < deps.length; i++) {
|
|
1022
|
+
const specifier = deps[i];
|
|
1023
|
+
const resolved = await this.resolve(specifier, importerPath);
|
|
1024
|
+
if (!resolved) continue;
|
|
1025
|
+
result += `import * as vendored${i} from ${JSON.stringify(resolved.id)}\n`;
|
|
1026
|
+
}
|
|
1027
|
+
result += "export default {\n";
|
|
1028
|
+
for (let i = 0; i < deps.length; i++) result += `${JSON.stringify(deps[i])}: vendored${i},\n`;
|
|
1029
|
+
result += "}\n";
|
|
1030
|
+
return result;
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
//#endregion
|
|
1035
|
+
//#region node/virtual/monaco-types.ts
|
|
1036
|
+
const templateMonacoTypes = {
|
|
1037
|
+
id: "/@slidev/monaco-types",
|
|
1038
|
+
getContent: async ({ userRoot, data, utils }) => {
|
|
1039
|
+
if (!data.features.monaco) return "";
|
|
1040
|
+
const typesRoot = join(userRoot, "snippets");
|
|
1041
|
+
const files = await fg([
|
|
1042
|
+
"**/*.ts",
|
|
1043
|
+
"**/*.mts",
|
|
1044
|
+
"**/*.cts"
|
|
1045
|
+
], { cwd: typesRoot });
|
|
1046
|
+
let result = "import { addFile } from \"@slidev/client/setup/monaco.ts\"\n";
|
|
1047
|
+
for (const file of files) {
|
|
1048
|
+
const url = `${toAtFS(resolve(typesRoot, file))}?monaco-types&raw`;
|
|
1049
|
+
result += `addFile(() => import(${JSON.stringify(url)}), ${JSON.stringify(file)})\n`;
|
|
1050
|
+
}
|
|
1051
|
+
function mapModuleNameToModule(moduleSpecifier) {
|
|
1052
|
+
if (moduleSpecifier.startsWith("node:")) return "node";
|
|
1053
|
+
if (builtinModules.includes(moduleSpecifier)) return "node";
|
|
1054
|
+
const mainPackageName = moduleSpecifier.split("/")[0];
|
|
1055
|
+
if (builtinModules.includes(mainPackageName) && !mainPackageName.startsWith("@")) return "node";
|
|
1056
|
+
const [a = "", b = ""] = moduleSpecifier.split("/");
|
|
1057
|
+
const moduleName = a.startsWith("@") ? `${a}/${b}` : a;
|
|
1058
|
+
return moduleName;
|
|
1059
|
+
}
|
|
1060
|
+
let deps = [...data.config.monacoTypesAdditionalPackages];
|
|
1061
|
+
if (data.config.monacoTypesSource === "local") deps.push(...data.features.monaco.types);
|
|
1062
|
+
deps = uniq(deps.map((specifier) => {
|
|
1063
|
+
if (specifier[0] === ".") return "";
|
|
1064
|
+
return mapModuleNameToModule(specifier);
|
|
1065
|
+
}).filter(Boolean));
|
|
1066
|
+
deps = deps.filter((pkg) => !utils.isMonacoTypesIgnored(pkg));
|
|
1067
|
+
for (const pkg of deps) result += `import(${JSON.stringify(`/@slidev-monaco-types/resolve?${new URLSearchParams({ pkg })}`)})\n`;
|
|
1068
|
+
return result;
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
//#endregion
|
|
1073
|
+
//#region node/virtual/nav-controls.ts
|
|
1074
|
+
const templateNavControls = {
|
|
1075
|
+
id: "/@slidev/custom-nav-controls",
|
|
1076
|
+
getContent({ roots }) {
|
|
1077
|
+
const components = roots.flatMap((root) => {
|
|
1078
|
+
return [join(root, "custom-nav-controls.vue"), join(root, "CustomNavControls.vue")];
|
|
1079
|
+
}).filter((i) => existsSync(i));
|
|
1080
|
+
const imports = components.map((i, idx) => `import __n${idx} from '${toAtFS(i)}'`).join("\n");
|
|
1081
|
+
const render = components.map((i, idx) => `h(__n${idx})`).join(",");
|
|
1082
|
+
return `${imports}
|
|
1083
|
+
import { h } from 'vue'
|
|
1084
|
+
export default {
|
|
1085
|
+
render: () => [${render}],
|
|
1086
|
+
}`;
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
//#endregion
|
|
1091
|
+
//#region node/virtual/setups.ts
|
|
1092
|
+
function createSetupTemplate(name) {
|
|
1093
|
+
return {
|
|
1094
|
+
id: `/@slidev/setups/${name}`,
|
|
1095
|
+
getContent({ roots }) {
|
|
1096
|
+
const setups = roots.flatMap((i) => {
|
|
1097
|
+
const path$1 = join(i, "setup", name);
|
|
1098
|
+
return [
|
|
1099
|
+
".ts",
|
|
1100
|
+
".mts",
|
|
1101
|
+
".js",
|
|
1102
|
+
".mjs"
|
|
1103
|
+
].map((ext) => path$1 + ext);
|
|
1104
|
+
}).filter((i) => existsSync(i));
|
|
1105
|
+
const imports = [];
|
|
1106
|
+
setups.forEach((path$1, idx) => {
|
|
1107
|
+
imports.push(`import __n${idx} from '${toAtFS(path$1)}'`);
|
|
1108
|
+
});
|
|
1109
|
+
imports.push(`export default [${setups.map((_, idx) => `__n${idx}`).join(",")}]`);
|
|
1110
|
+
return imports.join("\n");
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
const setupModules = [
|
|
1115
|
+
"shiki",
|
|
1116
|
+
"code-runners",
|
|
1117
|
+
"monaco",
|
|
1118
|
+
"mermaid",
|
|
1119
|
+
"main",
|
|
1120
|
+
"root",
|
|
1121
|
+
"routes",
|
|
1122
|
+
"shortcuts",
|
|
1123
|
+
"context-menu"
|
|
1124
|
+
];
|
|
1125
|
+
const templateSetups = setupModules.map(createSetupTemplate);
|
|
1126
|
+
|
|
1127
|
+
//#endregion
|
|
1128
|
+
//#region node/virtual/shiki.ts
|
|
1129
|
+
const templateShiki = {
|
|
1130
|
+
id: "/@slidev/shiki",
|
|
1131
|
+
getContent: async ({ utils }) => {
|
|
1132
|
+
const options = utils.shikiOptions;
|
|
1133
|
+
const langs = await resolveLangs(options.langs || [
|
|
1134
|
+
"markdown",
|
|
1135
|
+
"vue",
|
|
1136
|
+
"javascript",
|
|
1137
|
+
"typescript",
|
|
1138
|
+
"html",
|
|
1139
|
+
"css"
|
|
1140
|
+
]);
|
|
1141
|
+
const resolvedThemeOptions = "themes" in options ? { themes: Object.fromEntries(await Promise.all(Object.entries(options.themes).map(async ([name, value]) => [name, await resolveTheme$1(value)]))) } : { theme: await resolveTheme$1(options.theme || "vitesse-dark") };
|
|
1142
|
+
const themes = resolvedThemeOptions.themes ? Object.values(resolvedThemeOptions.themes) : [resolvedThemeOptions.theme];
|
|
1143
|
+
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 };
|
|
1144
|
+
async function normalizeGetter(p) {
|
|
1145
|
+
const r = typeof p === "function" ? p() : p;
|
|
1146
|
+
return r.default || r;
|
|
1147
|
+
}
|
|
1148
|
+
async function resolveLangs(langs$1) {
|
|
1149
|
+
const awaited = await Promise.all(langs$1.map((lang) => normalizeGetter(lang)));
|
|
1150
|
+
return uniq(awaited.flat());
|
|
1151
|
+
}
|
|
1152
|
+
async function resolveTheme$1(theme) {
|
|
1153
|
+
return typeof theme === "string" ? theme : await normalizeGetter(theme);
|
|
1154
|
+
}
|
|
1155
|
+
const langsInit = await Promise.all(langs.map(async (lang) => typeof lang === "string" ? `import('${await resolveImportUrl(`shiki/langs/${lang}.mjs`)}')` : JSON.stringify(lang)));
|
|
1156
|
+
const themesInit = await Promise.all(themes.map(async (theme) => typeof theme === "string" ? `import('${await resolveImportUrl(`shiki/themes/${theme}.mjs`)}')` : JSON.stringify(theme)));
|
|
1157
|
+
const langNames = langs.flatMap((lang) => typeof lang === "string" ? lang : lang.name);
|
|
1158
|
+
const lines = [];
|
|
1159
|
+
lines.push(`import { createHighlighterCore } from "${await resolveImportUrl("shiki/core")}"`, `import { createJavaScriptRegexEngine } from "${await resolveImportUrl("@shikijs/engine-javascript")}"`, `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(",")}],`, ` engine: createJavaScriptRegexEngine(),`, "})", "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", "}");
|
|
1160
|
+
return lines.join("\n");
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
|
|
1164
|
+
//#endregion
|
|
1165
|
+
//#region node/virtual/slides.ts
|
|
1166
|
+
const VIRTUAL_SLIDE_PREFIX = "/@slidev/slides/";
|
|
1167
|
+
const templateSlides = {
|
|
1168
|
+
id: "/@slidev/slides",
|
|
1169
|
+
getContent({ data, utils }) {
|
|
1170
|
+
const layouts = utils.getLayouts();
|
|
1171
|
+
const statements = [
|
|
1172
|
+
`import { defineAsyncComponent, shallowRef } from 'vue'`,
|
|
1173
|
+
`import SlideError from '${layouts.error}'`,
|
|
1174
|
+
`import SlideLoading from '@slidev/client/internals/SlideLoading.vue'`,
|
|
1175
|
+
`const componentsCache = new Array(${data.slides.length})`,
|
|
1176
|
+
`const getAsyncComponent = (idx, loader) => defineAsyncComponent({`,
|
|
1177
|
+
` loader,`,
|
|
1178
|
+
` delay: 300,`,
|
|
1179
|
+
` loadingComponent: SlideLoading,`,
|
|
1180
|
+
` errorComponent: SlideError,`,
|
|
1181
|
+
` onError: e => console.error('Failed to load slide ' + (idx + 1), e) `,
|
|
1182
|
+
`})`
|
|
1183
|
+
];
|
|
1184
|
+
const slides = data.slides.map((_, idx) => {
|
|
1185
|
+
const no = idx + 1;
|
|
1186
|
+
statements.push(
|
|
1187
|
+
`import { meta as f${no} } from '${VIRTUAL_SLIDE_PREFIX}${no}/frontmatter'`,
|
|
1188
|
+
// For some unknown reason, import error won't be caught by the error component. Catch it here.
|
|
1189
|
+
`const load${no} = async () => {`,
|
|
1190
|
+
` try { return componentsCache[${idx}] ??= await import('${VIRTUAL_SLIDE_PREFIX}${no}/md') }`,
|
|
1191
|
+
` catch (e) { console.error('slide failed to load', e); return SlideError }`,
|
|
1192
|
+
`}`
|
|
1193
|
+
);
|
|
1194
|
+
return `{ no: ${no}, meta: f${no}, load: load${no}, component: getAsyncComponent(${idx}, load${no}) }`;
|
|
1195
|
+
});
|
|
1196
|
+
return [
|
|
1197
|
+
...statements,
|
|
1198
|
+
`const data = [\n${slides.join(",\n")}\n]`,
|
|
1199
|
+
`if (import.meta.hot) {`,
|
|
1200
|
+
` import.meta.hot.data.slides ??= shallowRef()`,
|
|
1201
|
+
` import.meta.hot.data.slides.value = data`,
|
|
1202
|
+
` import.meta.hot.dispose(() => componentsCache.length = 0)`,
|
|
1203
|
+
` import.meta.hot.accept()`,
|
|
1204
|
+
`}`,
|
|
1205
|
+
`export const slides = import.meta.hot ? import.meta.hot.data.slides : shallowRef(data)`
|
|
1206
|
+
].join("\n");
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
//#endregion
|
|
1211
|
+
//#region node/virtual/styles.ts
|
|
1212
|
+
const templateStyle = {
|
|
1213
|
+
id: "/@slidev/styles",
|
|
1214
|
+
async getContent({ data, clientRoot, roots }) {
|
|
1215
|
+
function resolveUrlOfClient(name) {
|
|
1216
|
+
return toAtFS(join(clientRoot, name));
|
|
1217
|
+
}
|
|
1218
|
+
const imports = [
|
|
1219
|
+
`import "${resolveUrlOfClient("styles/vars.css")}"`,
|
|
1220
|
+
`import "${resolveUrlOfClient("styles/index.css")}"`,
|
|
1221
|
+
`import "${resolveUrlOfClient("styles/code.css")}"`,
|
|
1222
|
+
`import "${resolveUrlOfClient("styles/katex.css")}"`,
|
|
1223
|
+
`import "${resolveUrlOfClient("styles/transitions.css")}"`
|
|
1224
|
+
];
|
|
1225
|
+
for (const root of roots) {
|
|
1226
|
+
const styles = [
|
|
1227
|
+
join(root, "styles", "index.ts"),
|
|
1228
|
+
join(root, "styles", "index.js"),
|
|
1229
|
+
join(root, "styles", "index.css"),
|
|
1230
|
+
join(root, "styles.css"),
|
|
1231
|
+
join(root, "style.css")
|
|
1232
|
+
];
|
|
1233
|
+
for (const style of styles) if (existsSync(style)) {
|
|
1234
|
+
imports.push(`import "${toAtFS(style)}"`);
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
if (data.features.katex) imports.push(`import "${await resolveImportUrl("katex/dist/katex.min.css")}"`);
|
|
1239
|
+
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")}"`);
|
|
1240
|
+
imports.unshift(`import "${await resolveImportUrl("@unocss/reset/tailwind.css")}"`, "import \"uno:preflights.css\"", "import \"uno:typography.css\"", "import \"uno:shortcuts.css\"");
|
|
1241
|
+
imports.push("import \"uno.css\"");
|
|
1242
|
+
return imports.join("\n");
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
//#endregion
|
|
1247
|
+
//#region node/virtual/titles.ts
|
|
1248
|
+
const templateTitleRendererMd = {
|
|
1249
|
+
id: "/@slidev/title-renderer.md",
|
|
1250
|
+
getContent({ data }) {
|
|
1251
|
+
const lines = data.slides.map(({ title }, i) => `<template ${i === 0 ? "v-if" : "v-else-if"}="no === ${i + 1}">\n\n${title}\n\n</template>`);
|
|
1252
|
+
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>`);
|
|
1253
|
+
return lines.join("\n");
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
const templateTitleRenderer = {
|
|
1257
|
+
id: "/@slidev/title-renderer",
|
|
1258
|
+
async getContent() {
|
|
1259
|
+
return "export { default } from \"/@slidev/title-renderer.md\"";
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
//#endregion
|
|
1264
|
+
//#region node/virtual/index.ts
|
|
1265
|
+
const templates = [
|
|
1266
|
+
templateShiki,
|
|
1267
|
+
templateMonacoTypes,
|
|
1268
|
+
templateMonacoRunDeps,
|
|
1269
|
+
templateConfigs,
|
|
1270
|
+
templateStyle,
|
|
1271
|
+
templateGlobalLayers,
|
|
1272
|
+
templateNavControls,
|
|
1273
|
+
templateSlides,
|
|
1274
|
+
templateLayouts,
|
|
1275
|
+
templateTitleRenderer,
|
|
1276
|
+
templateTitleRendererMd,
|
|
1277
|
+
...templateSetups,
|
|
1278
|
+
templateLegacyRoutes,
|
|
1279
|
+
templateLegacyTitles
|
|
1280
|
+
];
|
|
1281
|
+
|
|
1282
|
+
//#endregion
|
|
1283
|
+
//#region node/vite/loaders.ts
|
|
1284
|
+
function createSlidesLoader(options, serverOptions) {
|
|
1285
|
+
const { data, mode, utils } = options;
|
|
1286
|
+
const notesMd = MarkdownIt({ html: true });
|
|
1287
|
+
notesMd.use(MarkdownItLink);
|
|
1288
|
+
if (data.features.katex) notesMd.use(MarkdownItKatex, utils.katexOptions);
|
|
1289
|
+
const hmrSlidesIndexes = new Set();
|
|
1290
|
+
let server;
|
|
1291
|
+
let skipHmr = null;
|
|
1292
|
+
let sourceIds = resolveSourceIds(data);
|
|
1293
|
+
function resolveSourceIds(data$1) {
|
|
1294
|
+
const ids = {
|
|
1295
|
+
md: [],
|
|
1296
|
+
frontmatter: []
|
|
1297
|
+
};
|
|
1298
|
+
for (const type of ["md", "frontmatter"]) for (let i = 0; i < data$1.slides.length; i++) ids[type].push(`${data$1.slides[i].source.filepath}__slidev_${i + 1}.${type}`);
|
|
1299
|
+
return ids;
|
|
1300
|
+
}
|
|
1301
|
+
function updateServerWatcher() {
|
|
1302
|
+
if (!server) return;
|
|
1303
|
+
server.watcher.add(Object.keys(data.watchFiles));
|
|
1304
|
+
}
|
|
1305
|
+
function getFrontmatter(pageNo) {
|
|
1306
|
+
return {
|
|
1307
|
+
...data.headmatter?.defaults || {},
|
|
1308
|
+
...data.slides[pageNo]?.frontmatter || {}
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
return {
|
|
1312
|
+
name: "slidev:loader",
|
|
1313
|
+
enforce: "pre",
|
|
1314
|
+
configureServer(_server) {
|
|
1315
|
+
server = _server;
|
|
1316
|
+
updateServerWatcher();
|
|
1317
|
+
server.middlewares.use(async (req, res, next) => {
|
|
1318
|
+
const match = req.url?.match(regexSlideReqPath);
|
|
1319
|
+
if (!match) return next();
|
|
1320
|
+
const [, no] = match;
|
|
1321
|
+
const idx = Number.parseInt(no) - 1;
|
|
1322
|
+
if (req.method === "GET") {
|
|
1323
|
+
res.write(JSON.stringify(withRenderedNote(data.slides[idx])));
|
|
1324
|
+
return res.end();
|
|
1325
|
+
} else if (req.method === "POST") {
|
|
1326
|
+
const body = await getBodyJson(req);
|
|
1327
|
+
const slide = data.slides[idx];
|
|
1328
|
+
if (body.content && body.content !== slide.source.content) hmrSlidesIndexes.add(idx);
|
|
1329
|
+
if (body.content) slide.content = slide.source.content = body.content;
|
|
1330
|
+
if (body.frontmatterRaw != null) if (body.frontmatterRaw.trim() === "") slide.source.frontmatterDoc = slide.source.frontmatterStyle = void 0;
|
|
1331
|
+
else {
|
|
1332
|
+
const parsed = YAML.parseDocument(body.frontmatterRaw);
|
|
1333
|
+
if (parsed.errors.length) console.error("ERROR when saving frontmatter", parsed.errors);
|
|
1334
|
+
else slide.source.frontmatterDoc = parsed;
|
|
1335
|
+
}
|
|
1336
|
+
if (body.note) slide.note = slide.source.note = body.note;
|
|
1337
|
+
if (body.frontmatter) {
|
|
1338
|
+
updateFrontmatterPatch(slide.source, body.frontmatter);
|
|
1339
|
+
Object.assign(slide.frontmatter, body.frontmatter);
|
|
1340
|
+
}
|
|
1341
|
+
parser.prettifySlide(slide.source);
|
|
1342
|
+
const fileContent = await parser.save(data.markdownFiles[slide.source.filepath]);
|
|
1343
|
+
if (body.skipHmr) {
|
|
1344
|
+
skipHmr = {
|
|
1345
|
+
filePath: slide.source.filepath,
|
|
1346
|
+
fileContent
|
|
1347
|
+
};
|
|
1348
|
+
server?.moduleGraph.invalidateModule(server.moduleGraph.getModuleById(sourceIds.md[idx]));
|
|
1349
|
+
if (body.frontmatter) server?.moduleGraph.invalidateModule(server.moduleGraph.getModuleById(sourceIds.frontmatter[idx]));
|
|
1350
|
+
}
|
|
1351
|
+
res.statusCode = 200;
|
|
1352
|
+
res.write(JSON.stringify(withRenderedNote(slide)));
|
|
1353
|
+
return res.end();
|
|
1354
|
+
}
|
|
1355
|
+
next();
|
|
1356
|
+
});
|
|
1357
|
+
},
|
|
1358
|
+
async handleHotUpdate(ctx) {
|
|
1359
|
+
const forceChangedSlides = data.watchFiles[ctx.file];
|
|
1360
|
+
if (!forceChangedSlides) return;
|
|
1361
|
+
for (const index of forceChangedSlides) hmrSlidesIndexes.add(index);
|
|
1362
|
+
const newData = await serverOptions.loadData?.({ [ctx.file]: await ctx.read() });
|
|
1363
|
+
if (!newData) return [];
|
|
1364
|
+
if (skipHmr && newData.markdownFiles[skipHmr.filePath]?.raw === skipHmr.fileContent) {
|
|
1365
|
+
skipHmr = null;
|
|
1366
|
+
return [];
|
|
1367
|
+
}
|
|
1368
|
+
const moduleIds = new Set();
|
|
1369
|
+
const newSourceIds = resolveSourceIds(newData);
|
|
1370
|
+
for (const type of ["md", "frontmatter"]) {
|
|
1371
|
+
const old = sourceIds[type];
|
|
1372
|
+
const newIds = newSourceIds[type];
|
|
1373
|
+
for (let i = 0; i < newIds.length; i++) if (old[i] !== newIds[i]) moduleIds.add(`${VIRTUAL_SLIDE_PREFIX}${i + 1}/${type}`);
|
|
1374
|
+
}
|
|
1375
|
+
sourceIds = newSourceIds;
|
|
1376
|
+
if (data.slides.length !== newData.slides.length) moduleIds.add(templateSlides.id);
|
|
1377
|
+
if (!equal(data.headmatter.defaults, newData.headmatter.defaults)) {
|
|
1378
|
+
moduleIds.add(templateSlides.id);
|
|
1379
|
+
range(data.slides.length).map((i) => hmrSlidesIndexes.add(i));
|
|
1380
|
+
}
|
|
1381
|
+
if (!equal(data.config, newData.config)) moduleIds.add(templateConfigs.id);
|
|
1382
|
+
if (!equal(data.features, newData.features)) setTimeout(() => {
|
|
1383
|
+
ctx.server.hot.send({ type: "full-reload" });
|
|
1384
|
+
}, 1);
|
|
1385
|
+
const length = Math.min(data.slides.length, newData.slides.length);
|
|
1386
|
+
for (let i = 0; i < length; i++) {
|
|
1387
|
+
const a = data.slides[i];
|
|
1388
|
+
const b = newData.slides[i];
|
|
1389
|
+
if (!hmrSlidesIndexes.has(i) && a.content.trim() === b.content.trim() && a.title?.trim() === b.title?.trim() && equal(a.frontmatter, b.frontmatter)) {
|
|
1390
|
+
if (a.note !== b.note) ctx.server.hot.send("slidev:update-note", {
|
|
1391
|
+
no: i + 1,
|
|
1392
|
+
note: b.note || "",
|
|
1393
|
+
noteHTML: renderNote(b.note || "")
|
|
1394
|
+
});
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
ctx.server.hot.send("slidev:update-slide", {
|
|
1398
|
+
no: i + 1,
|
|
1399
|
+
data: withRenderedNote(newData.slides[i])
|
|
1400
|
+
});
|
|
1401
|
+
hmrSlidesIndexes.add(i);
|
|
1402
|
+
}
|
|
1403
|
+
Object.assign(data, newData);
|
|
1404
|
+
Object.assign(utils, createDataUtils(options));
|
|
1405
|
+
if (hmrSlidesIndexes.size > 0) moduleIds.add(templateTitleRendererMd.id);
|
|
1406
|
+
const vueModules = Array.from(hmrSlidesIndexes).flatMap((idx) => {
|
|
1407
|
+
const frontmatter = ctx.server.moduleGraph.getModuleById(sourceIds.frontmatter[idx]);
|
|
1408
|
+
const main = ctx.server.moduleGraph.getModuleById(sourceIds.md[idx]);
|
|
1409
|
+
const styles = main ? [...main.clientImportedModules].find((m) => m.id?.includes(`&type=style`)) : void 0;
|
|
1410
|
+
return [
|
|
1411
|
+
frontmatter,
|
|
1412
|
+
main,
|
|
1413
|
+
styles
|
|
1414
|
+
];
|
|
1415
|
+
});
|
|
1416
|
+
hmrSlidesIndexes.clear();
|
|
1417
|
+
const moduleEntries = [
|
|
1418
|
+
...ctx.modules.filter((i) => i.id === templateMonacoRunDeps.id || i.id === templateMonacoTypes.id),
|
|
1419
|
+
...vueModules,
|
|
1420
|
+
...Array.from(moduleIds).map((id) => ctx.server.moduleGraph.getModuleById(id))
|
|
1421
|
+
].filter(notNullish).filter((i) => !i.id?.startsWith("/@id/@vite-icons"));
|
|
1422
|
+
updateServerWatcher();
|
|
1423
|
+
return moduleEntries;
|
|
1424
|
+
},
|
|
1425
|
+
resolveId: {
|
|
1426
|
+
order: "pre",
|
|
1427
|
+
handler(id) {
|
|
1428
|
+
if (id.startsWith("/@slidev/") || id.includes("__slidev_")) return id;
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
async load(id) {
|
|
1433
|
+
const template = templates.find((i) => i.id === id);
|
|
1434
|
+
if (template) return {
|
|
1435
|
+
code: await template.getContent.call(this, options),
|
|
1436
|
+
map: { mappings: "" }
|
|
1437
|
+
};
|
|
1438
|
+
const matchFacade = id.match(regexSlideFacadeId);
|
|
1439
|
+
if (matchFacade) {
|
|
1440
|
+
const [, no, type] = matchFacade;
|
|
1441
|
+
const idx = +no - 1;
|
|
1442
|
+
const sourceId = JSON.stringify(sourceIds[type][idx]);
|
|
1443
|
+
return [`export * from ${sourceId}`, `export { default } from ${sourceId}`].join("\n");
|
|
1444
|
+
}
|
|
1445
|
+
const matchSource = id.match(regexSlideSourceId);
|
|
1446
|
+
if (matchSource) {
|
|
1447
|
+
const [, no, type] = matchSource;
|
|
1448
|
+
const idx = +no - 1;
|
|
1449
|
+
const slide = data.slides[idx];
|
|
1450
|
+
if (!slide) return;
|
|
1451
|
+
if (type === "md") return {
|
|
1452
|
+
code: slide.content,
|
|
1453
|
+
map: { mappings: "" }
|
|
1454
|
+
};
|
|
1455
|
+
else if (type === "frontmatter") {
|
|
1456
|
+
const slideBase = {
|
|
1457
|
+
...withRenderedNote(slide),
|
|
1458
|
+
frontmatter: void 0,
|
|
1459
|
+
source: void 0,
|
|
1460
|
+
importChain: void 0,
|
|
1461
|
+
...mode === "build" ? {
|
|
1462
|
+
raw: "",
|
|
1463
|
+
content: "",
|
|
1464
|
+
note: ""
|
|
1465
|
+
} : {}
|
|
1466
|
+
};
|
|
1467
|
+
const fontmatter = getFrontmatter(idx);
|
|
1468
|
+
return {
|
|
1469
|
+
code: [
|
|
1470
|
+
"// @unocss-include",
|
|
1471
|
+
"import { computed, reactive, shallowReactive } from \"vue\"",
|
|
1472
|
+
`export const frontmatterData = ${JSON.stringify(fontmatter)}`,
|
|
1473
|
+
"if (import.meta.hot) {",
|
|
1474
|
+
" const firstLoad = !import.meta.hot.data.frontmatter",
|
|
1475
|
+
" import.meta.hot.data.frontmatter ??= reactive(frontmatterData)",
|
|
1476
|
+
" import.meta.hot.accept(({ frontmatterData: update }) => {",
|
|
1477
|
+
" if (firstLoad) return",
|
|
1478
|
+
" const frontmatter = import.meta.hot.data.frontmatter",
|
|
1479
|
+
" Object.keys(frontmatter).forEach(key => {",
|
|
1480
|
+
" if (!(key in update)) delete frontmatter[key]",
|
|
1481
|
+
" })",
|
|
1482
|
+
" Object.assign(frontmatter, update)",
|
|
1483
|
+
" })",
|
|
1484
|
+
"}",
|
|
1485
|
+
"export const frontmatter = import.meta.hot ? import.meta.hot.data.frontmatter : reactive(frontmatterData)",
|
|
1486
|
+
"export default frontmatter",
|
|
1487
|
+
"export const meta = shallowReactive({",
|
|
1488
|
+
" get layout(){ return frontmatter.layout },",
|
|
1489
|
+
" get transition(){ return frontmatter.transition },",
|
|
1490
|
+
" get class(){ return frontmatter.class },",
|
|
1491
|
+
" get clicks(){ return frontmatter.clicks },",
|
|
1492
|
+
" get name(){ return frontmatter.name },",
|
|
1493
|
+
" get preload(){ return frontmatter.preload },",
|
|
1494
|
+
" slide: {",
|
|
1495
|
+
` ...(${JSON.stringify(slideBase)}),`,
|
|
1496
|
+
` frontmatter,`,
|
|
1497
|
+
` filepath: ${JSON.stringify(mode === "dev" ? slide.source.filepath : "")},`,
|
|
1498
|
+
` start: ${JSON.stringify(slide.source.start)},`,
|
|
1499
|
+
` id: ${idx},`,
|
|
1500
|
+
` no: ${no},`,
|
|
1501
|
+
" },",
|
|
1502
|
+
" __clicksContext: null,",
|
|
1503
|
+
" __preloaded: false,",
|
|
1504
|
+
"})"
|
|
1505
|
+
].join("\n"),
|
|
1506
|
+
map: { mappings: "" }
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
if (data.markdownFiles[id]) return "";
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1513
|
+
function renderNote(text = "") {
|
|
1514
|
+
let clickCount = 0;
|
|
1515
|
+
const html = notesMd.render(text.replace(/\[click(?::(\d+))?\]/gi, (_, count = 1) => {
|
|
1516
|
+
clickCount += Number(count);
|
|
1517
|
+
return `<span class="slidev-note-click-mark" data-clicks="${clickCount}"></span>`;
|
|
1518
|
+
}));
|
|
1519
|
+
return html;
|
|
1520
|
+
}
|
|
1521
|
+
function withRenderedNote(data$1) {
|
|
1522
|
+
return {
|
|
1523
|
+
...data$1,
|
|
1524
|
+
noteHTML: renderNote(data$1?.note)
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
//#endregion
|
|
1530
|
+
//#region ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=49e14003b6caa0b7d164cbe71da573809d375bab_d6f3113a192503d8f8553bee4b7feffb/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/image-size/specialCharacters.js
|
|
1531
|
+
var SpecialCharacters;
|
|
1532
|
+
(function(SpecialCharacters$1) {
|
|
1533
|
+
SpecialCharacters$1[SpecialCharacters$1["EXCLAMATION_MARK"] = 33] = "EXCLAMATION_MARK";
|
|
1534
|
+
SpecialCharacters$1[SpecialCharacters$1["OPENING_BRACKET"] = 91] = "OPENING_BRACKET";
|
|
1535
|
+
SpecialCharacters$1[SpecialCharacters$1["OPENING_PARENTHESIS"] = 40] = "OPENING_PARENTHESIS";
|
|
1536
|
+
SpecialCharacters$1[SpecialCharacters$1["WHITESPACE"] = 32] = "WHITESPACE";
|
|
1537
|
+
SpecialCharacters$1[SpecialCharacters$1["NEW_LINE"] = 10] = "NEW_LINE";
|
|
1538
|
+
SpecialCharacters$1[SpecialCharacters$1["EQUALS"] = 61] = "EQUALS";
|
|
1539
|
+
SpecialCharacters$1[SpecialCharacters$1["LOWER_CASE_X"] = 120] = "LOWER_CASE_X";
|
|
1540
|
+
SpecialCharacters$1[SpecialCharacters$1["NUMBER_ZERO"] = 48] = "NUMBER_ZERO";
|
|
1541
|
+
SpecialCharacters$1[SpecialCharacters$1["NUMBER_NINE"] = 57] = "NUMBER_NINE";
|
|
1542
|
+
SpecialCharacters$1[SpecialCharacters$1["PERCENTAGE"] = 37] = "PERCENTAGE";
|
|
1543
|
+
SpecialCharacters$1[SpecialCharacters$1["CLOSING_PARENTHESIS"] = 41] = "CLOSING_PARENTHESIS";
|
|
1544
|
+
})(SpecialCharacters || (SpecialCharacters = {}));
|
|
1545
|
+
|
|
1546
|
+
//#endregion
|
|
1547
|
+
//#region ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=49e14003b6caa0b7d164cbe71da573809d375bab_d6f3113a192503d8f8553bee4b7feffb/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/task-lists/index.js
|
|
1548
|
+
const checkboxRegex = /^ *\[([\sx])] /i;
|
|
1549
|
+
function taskLists(md, options = {
|
|
1550
|
+
enabled: false,
|
|
1551
|
+
label: false,
|
|
1552
|
+
lineNumber: false
|
|
1553
|
+
}) {
|
|
1554
|
+
md.core.ruler.after("inline", "task-lists", (state) => processToken(state, options));
|
|
1555
|
+
md.renderer.rules.taskListItemCheckbox = (tokens) => {
|
|
1556
|
+
const token = tokens[0];
|
|
1557
|
+
const checkedAttribute = token.attrGet("checked") ? "checked=\"\" " : "";
|
|
1558
|
+
const disabledAttribute = token.attrGet("disabled") ? "disabled=\"\" " : "";
|
|
1559
|
+
const line = token.attrGet("line");
|
|
1560
|
+
const idAttribute = `id="${token.attrGet("id")}" `;
|
|
1561
|
+
const dataLineAttribute = line && options.lineNumber ? `data-line="${line}" ` : "";
|
|
1562
|
+
return `<input class="task-list-item-checkbox" type="checkbox" ${checkedAttribute}${disabledAttribute}${dataLineAttribute}${idAttribute}/>`;
|
|
1563
|
+
};
|
|
1564
|
+
md.renderer.rules.taskListItemLabel_close = () => {
|
|
1565
|
+
return "</label>";
|
|
1566
|
+
};
|
|
1567
|
+
md.renderer.rules.taskListItemLabel_open = (tokens) => {
|
|
1568
|
+
const token = tokens[0];
|
|
1569
|
+
const id = token.attrGet("id");
|
|
1570
|
+
return `<label for="${id}">`;
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
function processToken(state, options) {
|
|
1574
|
+
const allTokens = state.tokens;
|
|
1575
|
+
for (let i = 2; i < allTokens.length; i++) {
|
|
1576
|
+
if (!isTodoItem(allTokens, i)) continue;
|
|
1577
|
+
todoify(allTokens[i], options);
|
|
1578
|
+
allTokens[i - 2].attrJoin("class", `task-list-item ${options.enabled ? " enabled" : ""}`);
|
|
1579
|
+
const parentToken = findParentToken(allTokens, i - 2);
|
|
1580
|
+
if (parentToken) {
|
|
1581
|
+
const classes = parentToken.attrGet("class") ?? "";
|
|
1582
|
+
if (!classes.match(/(^| )contains-task-list/)) parentToken.attrJoin("class", "contains-task-list");
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
return false;
|
|
1586
|
+
}
|
|
1587
|
+
function findParentToken(tokens, index) {
|
|
1588
|
+
const targetLevel = tokens[index].level - 1;
|
|
1589
|
+
for (let currentTokenIndex = index - 1; currentTokenIndex >= 0; currentTokenIndex--) if (tokens[currentTokenIndex].level === targetLevel) return tokens[currentTokenIndex];
|
|
1590
|
+
return void 0;
|
|
1591
|
+
}
|
|
1592
|
+
function isTodoItem(tokens, index) {
|
|
1593
|
+
return isInline(tokens[index]) && isParagraph(tokens[index - 1]) && isListItem(tokens[index - 2]) && startsWithTodoMarkdown(tokens[index]);
|
|
1594
|
+
}
|
|
1595
|
+
function todoify(token, options) {
|
|
1596
|
+
if (token.children == null) return;
|
|
1597
|
+
const id = generateIdForToken(token);
|
|
1598
|
+
token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id));
|
|
1599
|
+
token.children[1].content = token.children[1].content.replace(checkboxRegex, "");
|
|
1600
|
+
if (options.label) {
|
|
1601
|
+
token.children.splice(1, 0, createLabelBeginToken(id));
|
|
1602
|
+
token.children.push(createLabelEndToken());
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
function generateIdForToken(token) {
|
|
1606
|
+
if (token.map) return `task-item-${token.map[0]}`;
|
|
1607
|
+
else return `task-item-${Math.ceil(Math.random() * (1e4 * 1e3) - 1e3)}`;
|
|
1608
|
+
}
|
|
1609
|
+
function createCheckboxToken(token, enabled, id) {
|
|
1610
|
+
const checkbox = new Token("taskListItemCheckbox", "", 0);
|
|
1611
|
+
if (!enabled) checkbox.attrSet("disabled", "true");
|
|
1612
|
+
if (token.map) checkbox.attrSet("line", token.map[0].toString());
|
|
1613
|
+
checkbox.attrSet("id", id);
|
|
1614
|
+
const checkboxRegexResult = checkboxRegex.exec(token.content);
|
|
1615
|
+
const isChecked = checkboxRegexResult?.[1].toLowerCase() === "x";
|
|
1616
|
+
if (isChecked) checkbox.attrSet("checked", "true");
|
|
1617
|
+
return checkbox;
|
|
1618
|
+
}
|
|
1619
|
+
function createLabelBeginToken(id) {
|
|
1620
|
+
const labelBeginToken = new Token("taskListItemLabel_open", "", 1);
|
|
1621
|
+
labelBeginToken.attrSet("id", id);
|
|
1622
|
+
return labelBeginToken;
|
|
1623
|
+
}
|
|
1624
|
+
function createLabelEndToken() {
|
|
1625
|
+
return new Token("taskListItemLabel_close", "", -1);
|
|
1626
|
+
}
|
|
1627
|
+
function isInline(token) {
|
|
1628
|
+
return token.type === "inline";
|
|
1629
|
+
}
|
|
1630
|
+
function isParagraph(token) {
|
|
1631
|
+
return token.type === "paragraph_open";
|
|
1632
|
+
}
|
|
1633
|
+
function isListItem(token) {
|
|
1634
|
+
return token.type === "list_item_open";
|
|
1635
|
+
}
|
|
1636
|
+
function startsWithTodoMarkdown(token) {
|
|
1637
|
+
return checkboxRegex.test(token.content);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
//#endregion
|
|
1641
|
+
//#region node/syntax/markdown-it/markdown-it-escape-code.ts
|
|
1642
|
+
function MarkdownItEscapeInlineCode(md) {
|
|
1643
|
+
const codeInline = md.renderer.rules.code_inline;
|
|
1644
|
+
md.renderer.rules.code_inline = (tokens, idx, options, env, self) => {
|
|
1645
|
+
const result = codeInline(tokens, idx, options, env, self);
|
|
1646
|
+
return result.replace(/^<code/, "<code v-pre");
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
//#endregion
|
|
1651
|
+
//#region node/syntax/transform/utils.ts
|
|
1652
|
+
function normalizeRangeStr(rangeStr = "") {
|
|
1653
|
+
return !rangeStr.trim() ? [] : rangeStr.trim().split(/\|/g).map((i) => i.trim());
|
|
1654
|
+
}
|
|
1655
|
+
function getCodeBlocks(md) {
|
|
1656
|
+
const codeblocks = Array.from(md.matchAll(/^```[\s\S]*?^```/gm)).map((m) => {
|
|
1657
|
+
const start = m.index;
|
|
1658
|
+
const end = m.index + m[0].length;
|
|
1659
|
+
const startLine = md.slice(0, start).match(/\n/g)?.length || 0;
|
|
1660
|
+
const endLine = md.slice(0, end).match(/\n/g)?.length || 0;
|
|
1661
|
+
return [
|
|
1662
|
+
start,
|
|
1663
|
+
end,
|
|
1664
|
+
startLine,
|
|
1665
|
+
endLine
|
|
1666
|
+
];
|
|
1667
|
+
});
|
|
1668
|
+
return {
|
|
1669
|
+
codeblocks,
|
|
1670
|
+
isInsideCodeblocks(idx) {
|
|
1671
|
+
return codeblocks.some(([s, e]) => s <= idx && idx <= e);
|
|
1672
|
+
},
|
|
1673
|
+
isLineInsideCodeblocks(line) {
|
|
1674
|
+
return codeblocks.some(([, , s, e]) => s <= line && line <= e);
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Escape `{{` in code block to prevent Vue interpret it, #99, #1316
|
|
1680
|
+
*/
|
|
1681
|
+
function escapeVueInCode(md) {
|
|
1682
|
+
return md.replace(/\{\{/g, "{{");
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
//#endregion
|
|
1686
|
+
//#region node/syntax/markdown-it/markdown-it-shiki.ts
|
|
1687
|
+
async function MarkdownItShiki({ data: { config }, mode, utils }) {
|
|
1688
|
+
const transformers = [
|
|
1689
|
+
...utils.shikiOptions.transformers || [],
|
|
1690
|
+
(config.twoslash === true || config.twoslash === mode) && (await import("@shikijs/vitepress-twoslash")).transformerTwoslash({
|
|
1691
|
+
explicitTrigger: true,
|
|
1692
|
+
twoslashOptions: { handbookOptions: { noErrorValidation: true } }
|
|
1693
|
+
}),
|
|
1694
|
+
{
|
|
1695
|
+
pre(pre) {
|
|
1696
|
+
this.addClassToHast(pre, "slidev-code");
|
|
1697
|
+
delete pre.properties.tabindex;
|
|
1698
|
+
},
|
|
1699
|
+
postprocess(code) {
|
|
1700
|
+
return escapeVueInCode(code);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
].filter(isTruthy);
|
|
1704
|
+
return fromHighlighter(utils.shiki, {
|
|
1705
|
+
...utils.shikiOptions,
|
|
1706
|
+
transformers
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
//#endregion
|
|
1711
|
+
//#region node/syntax/markdown-it/markdown-it-v-drag.ts
|
|
1712
|
+
const dragComponentRegex = /<(v-?drag-?\w*)([\s>])/i;
|
|
1713
|
+
const dragDirectiveRegex = /(?<![</\w])v-drag(=".*?")?/i;
|
|
1714
|
+
function MarkdownItVDrag(md, markdownTransformMap) {
|
|
1715
|
+
const visited = new WeakSet();
|
|
1716
|
+
const sourceMapConsumers = new WeakMap();
|
|
1717
|
+
function getSourceMapConsumer(id) {
|
|
1718
|
+
const s = markdownTransformMap.get(id);
|
|
1719
|
+
if (!s) return void 0;
|
|
1720
|
+
let smc = sourceMapConsumers.get(s);
|
|
1721
|
+
if (smc) return smc;
|
|
1722
|
+
const sourceMap = s.generateMap();
|
|
1723
|
+
smc = new SourceMapConsumer({
|
|
1724
|
+
...sourceMap,
|
|
1725
|
+
version: sourceMap.version.toString()
|
|
1726
|
+
});
|
|
1727
|
+
sourceMapConsumers.set(s, smc);
|
|
1728
|
+
return smc;
|
|
1729
|
+
}
|
|
1730
|
+
const _parse = md.parse;
|
|
1731
|
+
md.parse = function(src, env) {
|
|
1732
|
+
const smc = getSourceMapConsumer(env.id);
|
|
1733
|
+
const toOriginalPos = smc ? (line) => smc.originalPositionFor({
|
|
1734
|
+
line: line + 1,
|
|
1735
|
+
column: 0
|
|
1736
|
+
}).line - 1 : (line) => line;
|
|
1737
|
+
function toMarkdownSource(map, idx) {
|
|
1738
|
+
const start = toOriginalPos(map[0]);
|
|
1739
|
+
const end = toOriginalPos(map[1]);
|
|
1740
|
+
return `[${start},${Math.max(start + 1, end)},${idx}]`;
|
|
1741
|
+
}
|
|
1742
|
+
function replaceChildren(token, regex, replacement) {
|
|
1743
|
+
for (const child of token.children ?? []) {
|
|
1744
|
+
if (child.type === "html_block" || child.type === "html_inline") child.content = child.content.replace(regex, replacement);
|
|
1745
|
+
replaceChildren(child, regex, replacement);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return _parse.call(this, src, env).map((token) => {
|
|
1749
|
+
if (![
|
|
1750
|
+
"html_block",
|
|
1751
|
+
"html_inline",
|
|
1752
|
+
"inline"
|
|
1753
|
+
].includes(token.type) || !token.content.includes("drag") || visited.has(token)) return token;
|
|
1754
|
+
token.content = token.content.replace(dragComponentRegex, (_, tag, space, idx) => {
|
|
1755
|
+
const replacement = `<${tag} :markdownSource="${toMarkdownSource(token.map, idx)}"${space}`;
|
|
1756
|
+
replaceChildren(token, dragComponentRegex, replacement);
|
|
1757
|
+
return replacement;
|
|
1758
|
+
}).replace(dragDirectiveRegex, (_, value, idx) => {
|
|
1759
|
+
const replacement = `v-drag${value ?? ""} :markdownSource="${toMarkdownSource(token.map, idx)}"`;
|
|
1760
|
+
replaceChildren(token, dragDirectiveRegex, replacement);
|
|
1761
|
+
return replacement;
|
|
1762
|
+
});
|
|
1763
|
+
visited.add(token);
|
|
1764
|
+
return token;
|
|
1765
|
+
});
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
//#endregion
|
|
1770
|
+
//#region node/syntax/markdown-it/index.ts
|
|
1771
|
+
async function useMarkdownItPlugins(md, options, markdownTransformMap) {
|
|
1772
|
+
const { data: { features, config }, utils: { katexOptions } } = options;
|
|
1773
|
+
if (config.highlighter === "shiki") md.use(await MarkdownItShiki(options));
|
|
1774
|
+
md.use(MarkdownItLink);
|
|
1775
|
+
md.use(MarkdownItEscapeInlineCode);
|
|
1776
|
+
md.use(MarkdownItFootnote);
|
|
1777
|
+
md.use(taskLists, {
|
|
1778
|
+
enabled: true,
|
|
1779
|
+
lineNumber: true,
|
|
1780
|
+
label: true
|
|
1781
|
+
});
|
|
1782
|
+
if (features.katex) md.use(MarkdownItKatex, katexOptions);
|
|
1783
|
+
md.use(MarkdownItVDrag, markdownTransformMap);
|
|
1784
|
+
if (config.mdc) md.use(MarkdownItMdc);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
//#endregion
|
|
1788
|
+
//#region node/setups/transformers.ts
|
|
1789
|
+
async function setupTransformers(roots) {
|
|
1790
|
+
const returns = await loadSetups(roots, "transformers.ts", []);
|
|
1791
|
+
const result = {
|
|
1792
|
+
pre: [],
|
|
1793
|
+
preCodeblock: [],
|
|
1794
|
+
postCodeblock: [],
|
|
1795
|
+
post: []
|
|
1796
|
+
};
|
|
1797
|
+
for (const r of [...returns].reverse()) {
|
|
1798
|
+
if (r.pre) result.pre.push(...r.pre);
|
|
1799
|
+
if (r.preCodeblock) result.preCodeblock.push(...r.preCodeblock);
|
|
1800
|
+
}
|
|
1801
|
+
for (const r of returns) {
|
|
1802
|
+
if (r.postCodeblock) result.postCodeblock.push(...r.postCodeblock);
|
|
1803
|
+
if (r.post) result.post.push(...r.post);
|
|
1804
|
+
}
|
|
1805
|
+
return result;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
//#endregion
|
|
1809
|
+
//#region node/syntax/transform/code-wrapper.ts
|
|
1810
|
+
const reCodeBlock = /^```([\w'-]+)?\s*(?:\{([\w*,|-]+)\}\s*?(\{[^}]*\})?([^\r\n]*))?\r?\n([ \t]*\S[\s\S]*?)^```$/gm;
|
|
1811
|
+
/**
|
|
1812
|
+
* Transform code block with wrapper
|
|
1813
|
+
*/
|
|
1814
|
+
function transformCodeWrapper(ctx) {
|
|
1815
|
+
ctx.s.replace(reCodeBlock, (full, lang = "", rangeStr = "", options = "", attrs = "", code) => {
|
|
1816
|
+
const ranges = normalizeRangeStr(rangeStr);
|
|
1817
|
+
code = code.trimEnd();
|
|
1818
|
+
options = options.trim() || "{}";
|
|
1819
|
+
return `\n<CodeBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>\n\n\`\`\`${lang}${attrs}\n${code}\n\`\`\`\n\n</CodeBlockWrapper>`;
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
//#endregion
|
|
1824
|
+
//#region node/syntax/transform/in-page-css.ts
|
|
1825
|
+
/**
|
|
1826
|
+
* Transform <style> in markdown to scoped style with page selector
|
|
1827
|
+
*/
|
|
1828
|
+
function transformPageCSS(ctx) {
|
|
1829
|
+
const codeBlocks = getCodeBlocks(ctx.s.original);
|
|
1830
|
+
ctx.s.replace(/(\n<style[^>]*>)([\s\S]+?)(<\/style>)/g, (full, start, css, end, index) => {
|
|
1831
|
+
if (codeBlocks.isInsideCodeblocks(index)) return full;
|
|
1832
|
+
if (!start.includes("scoped")) start = start.replace("<style", "<style scoped");
|
|
1833
|
+
return `${start}\n${css}${end}`;
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
//#endregion
|
|
1838
|
+
//#region node/syntax/transform/katex-wrapper.ts
|
|
1839
|
+
/**
|
|
1840
|
+
* Wrapper KaTex syntax `$$...$$` for highlighting
|
|
1841
|
+
*/
|
|
1842
|
+
function transformKaTexWrapper(ctx) {
|
|
1843
|
+
ctx.s.replace(/^\$\$(?:\s*\{([\w*,|-]+)\}\s*?(?:(\{[^}]*\})\s*?)?)?\n(\S[\s\S]*?)^\$\$/gm, (full, rangeStr = "", options = "", code) => {
|
|
1844
|
+
const ranges = !rangeStr.trim() ? [] : rangeStr.trim().split(/\|/g).map((i) => i.trim());
|
|
1845
|
+
code = code.trimEnd();
|
|
1846
|
+
options = options.trim() || "{}";
|
|
1847
|
+
return `<KaTexBlockWrapper v-bind="${options}" :ranges='${JSON.stringify(ranges)}'>\n\n\$\$\n${code}\n\$\$\n</KaTexBlockWrapper>\n`;
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
//#endregion
|
|
1852
|
+
//#region node/syntax/transform/magic-move.ts
|
|
1853
|
+
const reMagicMoveBlock = /^````(?:md|markdown) magic-move *(\{[^}]*\})?([^ \n]*)\n([\s\S]+?)^````$/gm;
|
|
1854
|
+
function parseLineNumbersOption(options) {
|
|
1855
|
+
return /lines: *true/.test(options) ? true : /lines: *false/.test(options) ? false : void 0;
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Transform magic-move code blocks
|
|
1859
|
+
*/
|
|
1860
|
+
function transformMagicMove(ctx) {
|
|
1861
|
+
ctx.s.replace(reMagicMoveBlock, (full, options = "{}", _attrs = "", body) => {
|
|
1862
|
+
const matches = Array.from(body.matchAll(reCodeBlock));
|
|
1863
|
+
if (!matches.length) throw new Error("Magic Move block must contain at least one code block");
|
|
1864
|
+
const defaultLineNumbers = parseLineNumbersOption(options) ?? ctx.options.data.config.lineNumbers;
|
|
1865
|
+
const ranges = matches.map((i) => normalizeRangeStr(i[2]));
|
|
1866
|
+
const steps = matches.map((i) => {
|
|
1867
|
+
const lineNumbers = parseLineNumbersOption(i[3]) ?? defaultLineNumbers;
|
|
1868
|
+
return codeToKeyedTokens(ctx.options.utils.shiki, i[5].trimEnd(), {
|
|
1869
|
+
...ctx.options.utils.shikiOptions,
|
|
1870
|
+
lang: i[1]
|
|
1871
|
+
}, lineNumbers);
|
|
1872
|
+
});
|
|
1873
|
+
const compressed = lz.compressToBase64(JSON.stringify(steps));
|
|
1874
|
+
return `<ShikiMagicMove v-bind="${options}" steps-lz="${compressed}" :step-ranges='${JSON.stringify(ranges)}' />`;
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
//#endregion
|
|
1879
|
+
//#region node/syntax/transform/mermaid.ts
|
|
1880
|
+
/**
|
|
1881
|
+
* Transform Mermaid code blocks (render done on client side)
|
|
1882
|
+
*/
|
|
1883
|
+
function transformMermaid(ctx) {
|
|
1884
|
+
ctx.s.replace(/^```mermaid *(\{[^\n]*\})?\n([\s\S]+?)\n```/gm, (full, options = "", code = "") => {
|
|
1885
|
+
code = code.trim();
|
|
1886
|
+
options = options.trim() || "{}";
|
|
1887
|
+
const encoded = lz.compressToBase64(code);
|
|
1888
|
+
return `<Mermaid code-lz="${encoded}" v-bind="${options}" />`;
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
//#endregion
|
|
1893
|
+
//#region node/syntax/transform/monaco.ts
|
|
1894
|
+
function transformMonaco(ctx) {
|
|
1895
|
+
const enabled = ctx.options.data.config.monaco === true || ctx.options.data.config.monaco === ctx.options.mode;
|
|
1896
|
+
if (!enabled) {
|
|
1897
|
+
ctx.s.replace(/\{monaco([\w:,-]*)\}/g, "");
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
ctx.s.replace(/^```(\w+) *\{monaco-diff\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^~~~ *\n([\s\S]+?)^```/gm, (full, lang = "ts", options = "{}", code, diff) => {
|
|
1901
|
+
lang = lang.trim();
|
|
1902
|
+
options = options.trim() || "{}";
|
|
1903
|
+
const encoded = lz.compressToBase64(code);
|
|
1904
|
+
const encodedDiff = lz.compressToBase64(diff);
|
|
1905
|
+
return `<Monaco code-lz="${encoded}" diff-lz="${encodedDiff}" lang="${lang}" v-bind="${options}" />`;
|
|
1906
|
+
});
|
|
1907
|
+
ctx.s.replace(/^```(\w+) *\{monaco\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^```/gm, (full, lang = "ts", options = "{}", code) => {
|
|
1908
|
+
lang = lang.trim();
|
|
1909
|
+
options = options.trim() || "{}";
|
|
1910
|
+
const encoded = lz.compressToBase64(code);
|
|
1911
|
+
return `<Monaco code-lz="${encoded}" lang="${lang}" v-bind="${options}" />`;
|
|
1912
|
+
});
|
|
1913
|
+
ctx.s.replace(/^```(\w+) *\{monaco-run\} *(?:(\{[^\n]*\}) *)?\n([\s\S]+?)^```/gm, (full, lang = "ts", options = "{}", code) => {
|
|
1914
|
+
lang = lang.trim();
|
|
1915
|
+
options = options.trim() || "{}";
|
|
1916
|
+
const encoded = lz.compressToBase64(code);
|
|
1917
|
+
return `<Monaco runnable code-lz="${encoded}" lang="${lang}" v-bind="${options}" />`;
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
//#endregion
|
|
1922
|
+
//#region node/syntax/transform/plant-uml.ts
|
|
1923
|
+
function transformPlantUml(ctx) {
|
|
1924
|
+
const server = ctx.options.data.config.plantUmlServer;
|
|
1925
|
+
ctx.s.replace(/^```plantuml[^\n{}]*(\{[^}\n]*\})?\n([\s\S]+?)\n```/gm, (full, options = "", content = "") => {
|
|
1926
|
+
const code = encode(content.trim());
|
|
1927
|
+
options = options.trim() || "{}";
|
|
1928
|
+
return `<PlantUml :code="'${code}'" :server="'${server}'" v-bind="${options}" />`;
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
//#endregion
|
|
1933
|
+
//#region node/syntax/transform/slot-sugar.ts
|
|
1934
|
+
function transformSlotSugar(ctx) {
|
|
1935
|
+
const linesWithNewline = ctx.s.original.split(/(\r?\n)/g);
|
|
1936
|
+
const codeBlocks = getCodeBlocks(ctx.s.original);
|
|
1937
|
+
const lines = [];
|
|
1938
|
+
for (let i = 0; i < linesWithNewline.length; i += 2) {
|
|
1939
|
+
const line = linesWithNewline[i];
|
|
1940
|
+
const newline = linesWithNewline[i + 1] || "";
|
|
1941
|
+
lines.push(line + newline);
|
|
1942
|
+
}
|
|
1943
|
+
let prevSlot = false;
|
|
1944
|
+
let offset = 0;
|
|
1945
|
+
lines.forEach((line) => {
|
|
1946
|
+
const start = offset;
|
|
1947
|
+
offset += line.length;
|
|
1948
|
+
if (codeBlocks.isInsideCodeblocks(offset)) return;
|
|
1949
|
+
const match = line.match(/^::\s*([\w.\-:]+)\s*::(\s*)$/);
|
|
1950
|
+
if (match) {
|
|
1951
|
+
ctx.s.overwrite(start, offset - match[2].length, `${prevSlot ? "\n\n</template>\n" : "\n"}<template v-slot:${match[1]}="slotProps">\n`);
|
|
1952
|
+
prevSlot = true;
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
if (prevSlot) ctx.s.append("\n\n</template>");
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
//#endregion
|
|
1959
|
+
//#region node/vite/monacoWrite.ts
|
|
1960
|
+
const monacoWriterWhitelist = new Set();
|
|
1961
|
+
function createMonacoWriterPlugin({ userRoot }) {
|
|
1962
|
+
return {
|
|
1963
|
+
name: "slidev:monaco-write",
|
|
1964
|
+
apply: "serve",
|
|
1965
|
+
configureServer(server) {
|
|
1966
|
+
server.ws.on("connection", (socket) => {
|
|
1967
|
+
socket.on("message", async (data) => {
|
|
1968
|
+
let json;
|
|
1969
|
+
try {
|
|
1970
|
+
json = JSON.parse(data.toString());
|
|
1971
|
+
} catch {
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (json.type === "custom" && json.event === "slidev:monaco-write") {
|
|
1975
|
+
const { file, content } = json.data;
|
|
1976
|
+
if (!monacoWriterWhitelist.has(file)) {
|
|
1977
|
+
console.error(`[Slidev] Unauthorized file write: ${file}`);
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
const filepath = path.join(userRoot, file);
|
|
1981
|
+
console.log("[Slidev] Writing file:", filepath);
|
|
1982
|
+
await fs.writeFile(filepath, content, "utf-8");
|
|
1983
|
+
}
|
|
1984
|
+
});
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
//#endregion
|
|
1991
|
+
//#region node/syntax/transform/snippet.ts
|
|
1992
|
+
function dedent(text) {
|
|
1993
|
+
const lines = text.split("\n");
|
|
1994
|
+
const minIndentLength = lines.reduce((acc, line) => {
|
|
1995
|
+
for (let i = 0; i < line.length; i++) if (line[i] !== " " && line[i] !== " ") return Math.min(i, acc);
|
|
1996
|
+
return acc;
|
|
1997
|
+
}, Number.POSITIVE_INFINITY);
|
|
1998
|
+
if (minIndentLength < Number.POSITIVE_INFINITY) return lines.map((x) => x.slice(minIndentLength)).join("\n");
|
|
1999
|
+
return text;
|
|
2000
|
+
}
|
|
2001
|
+
function findRegion(lines, regionName) {
|
|
2002
|
+
const regionRegexps = [
|
|
2003
|
+
[/^\/\/ ?#?region ([\w*-]+)$/, /^\/\/ ?#?endregion/],
|
|
2004
|
+
[/^\/\* ?#region ([\w*-]+) ?\*\/$/, /^\/\* ?#endregion[\s\w*-]*\*\/$/],
|
|
2005
|
+
[/^#pragma region ([\w*-]+)$/, /^#pragma endregion/],
|
|
2006
|
+
[/^<!-- #?region ([\w*-]+) -->$/, /^<!-- #?region[\s\w*-]*-->$/],
|
|
2007
|
+
[/^#Region ([\w*-]+)$/, /^#End Region/],
|
|
2008
|
+
[/^::#region ([\w*-]+)$/, /^::#endregion/],
|
|
2009
|
+
[/^# ?region ([\w*-]+)$/, /^# ?endregion/]
|
|
2010
|
+
];
|
|
2011
|
+
let endReg = null;
|
|
2012
|
+
let start = -1;
|
|
2013
|
+
for (const [lineId, line] of lines.entries()) if (endReg === null) for (const [startReg, end] of regionRegexps) {
|
|
2014
|
+
const match = line.trim().match(startReg);
|
|
2015
|
+
if (match && match[1] === regionName) {
|
|
2016
|
+
start = lineId + 1;
|
|
2017
|
+
endReg = end;
|
|
2018
|
+
break;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
else if (endReg.test(line.trim())) return {
|
|
2022
|
+
start,
|
|
2023
|
+
end: lineId,
|
|
2024
|
+
regexp: endReg
|
|
2025
|
+
};
|
|
2026
|
+
return null;
|
|
2027
|
+
}
|
|
2028
|
+
const reMonacoWrite = /^\{monaco-write\}/;
|
|
2029
|
+
/**
|
|
2030
|
+
* format: ">>> /path/to/file.extension#region language meta..."
|
|
2031
|
+
* where #region, language and meta are optional
|
|
2032
|
+
* meta should starts with {
|
|
2033
|
+
* lang can contain special characters like C++, C#, F#, etc.
|
|
2034
|
+
* path can be relative to the current file or absolute
|
|
2035
|
+
* file extension is optional
|
|
2036
|
+
* path can contain spaces and dots
|
|
2037
|
+
*
|
|
2038
|
+
* captures: ['/path/to/file.extension', '#region', 'language', '{meta}']
|
|
2039
|
+
*/
|
|
2040
|
+
function transformSnippet({ s, slide, options }) {
|
|
2041
|
+
const watchFiles = options.data.watchFiles;
|
|
2042
|
+
const dir = path.dirname(slide.source?.filepath ?? options.entry ?? options.userRoot);
|
|
2043
|
+
s.replace(
|
|
2044
|
+
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
|
2045
|
+
/^<<<\s*(\S.*?)(#[\w-]+)?\s*(?:\s(\S+?))?\s*(\{.*)?$/gm,
|
|
2046
|
+
(full, filepath = "", regionName = "", lang = "", meta = "") => {
|
|
2047
|
+
const src = slash(/^@\//.test(filepath) ? path.resolve(options.userRoot, filepath.slice(2)) : path.resolve(dir, filepath));
|
|
2048
|
+
meta = meta.trim();
|
|
2049
|
+
lang = lang.trim();
|
|
2050
|
+
lang = lang || path.extname(filepath).slice(1);
|
|
2051
|
+
const isAFile = fs$1.statSync(src).isFile();
|
|
2052
|
+
if (!fs$1.existsSync(src) || !isAFile) throw new Error(isAFile ? `Code snippet path not found: ${src}` : `Invalid code snippet option`);
|
|
2053
|
+
let content = fs$1.readFileSync(src, "utf8");
|
|
2054
|
+
if (regionName) {
|
|
2055
|
+
const lines = content.split(/\r?\n/);
|
|
2056
|
+
const region = findRegion(lines, regionName.slice(1));
|
|
2057
|
+
if (region) content = dedent(lines.slice(region.start, region.end).filter((line) => !region.regexp.test(line.trim())).join("\n"));
|
|
2058
|
+
}
|
|
2059
|
+
if (meta.match(reMonacoWrite)) {
|
|
2060
|
+
monacoWriterWhitelist.add(filepath);
|
|
2061
|
+
lang = lang.trim();
|
|
2062
|
+
meta = meta.replace(reMonacoWrite, "").trim() || "{}";
|
|
2063
|
+
const encoded = lz.compressToBase64(content);
|
|
2064
|
+
return `<Monaco writable=${JSON.stringify(filepath)} code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />`;
|
|
2065
|
+
} else {
|
|
2066
|
+
watchFiles[src] ??= new Set();
|
|
2067
|
+
watchFiles[src].add(slide.index);
|
|
2068
|
+
}
|
|
2069
|
+
return `\`\`\`${lang} ${meta}\n${content}\n\`\`\``;
|
|
2070
|
+
}
|
|
2071
|
+
);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
//#endregion
|
|
2075
|
+
//#region node/syntax/transform/index.ts
|
|
2076
|
+
async function getMarkdownTransformers(options) {
|
|
2077
|
+
const extras = await setupTransformers(options.roots);
|
|
2078
|
+
return [
|
|
2079
|
+
...extras.pre,
|
|
2080
|
+
transformSnippet,
|
|
2081
|
+
options.data.config.highlighter === "shiki" && transformMagicMove,
|
|
2082
|
+
...extras.preCodeblock,
|
|
2083
|
+
transformMermaid,
|
|
2084
|
+
transformPlantUml,
|
|
2085
|
+
options.data.features.monaco && transformMonaco,
|
|
2086
|
+
...extras.postCodeblock,
|
|
2087
|
+
transformCodeWrapper,
|
|
2088
|
+
options.data.features.katex && transformKaTexWrapper,
|
|
2089
|
+
transformPageCSS,
|
|
2090
|
+
transformSlotSugar,
|
|
2091
|
+
...extras.post
|
|
2092
|
+
];
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
//#endregion
|
|
2096
|
+
//#region node/vite/markdown.ts
|
|
2097
|
+
async function createMarkdownPlugin(options, { markdown: mdOptions }) {
|
|
2098
|
+
const markdownTransformMap = new Map();
|
|
2099
|
+
const transformers = await getMarkdownTransformers(options);
|
|
2100
|
+
return Markdown({
|
|
2101
|
+
include: [/\.md$/],
|
|
2102
|
+
wrapperClasses: "",
|
|
2103
|
+
headEnabled: false,
|
|
2104
|
+
frontmatter: false,
|
|
2105
|
+
escapeCodeTagInterpolation: false,
|
|
2106
|
+
markdownItOptions: {
|
|
2107
|
+
quotes: "\"\"''",
|
|
2108
|
+
html: true,
|
|
2109
|
+
xhtmlOut: true,
|
|
2110
|
+
linkify: true,
|
|
2111
|
+
...mdOptions?.markdownItOptions
|
|
2112
|
+
},
|
|
2113
|
+
...mdOptions,
|
|
2114
|
+
async markdownItSetup(md) {
|
|
2115
|
+
await useMarkdownItPlugins(md, options, markdownTransformMap);
|
|
2116
|
+
await mdOptions?.markdownItSetup?.(md);
|
|
2117
|
+
},
|
|
2118
|
+
transforms: {
|
|
2119
|
+
...mdOptions?.transforms,
|
|
2120
|
+
async before(code, id) {
|
|
2121
|
+
if (options.data.markdownFiles[id]) return "";
|
|
2122
|
+
code = await mdOptions?.transforms?.before?.(code, id) ?? code;
|
|
2123
|
+
const match = id.match(regexSlideSourceId);
|
|
2124
|
+
if (!match) return code;
|
|
2125
|
+
const s = new MagicString(code);
|
|
2126
|
+
markdownTransformMap.set(id, s);
|
|
2127
|
+
const ctx = {
|
|
2128
|
+
s,
|
|
2129
|
+
slide: options.data.slides[+match[1] - 1],
|
|
2130
|
+
options
|
|
2131
|
+
};
|
|
2132
|
+
for (const transformer of transformers) {
|
|
2133
|
+
if (!transformer) continue;
|
|
2134
|
+
await transformer(ctx);
|
|
2135
|
+
if (!ctx.s.isEmpty()) ctx.s.commit();
|
|
2136
|
+
}
|
|
2137
|
+
return s.toString();
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
//#endregion
|
|
2144
|
+
//#region node/vite/monacoTypes.ts
|
|
2145
|
+
function createMonacoTypesLoader({ userRoot, utils }) {
|
|
2146
|
+
return {
|
|
2147
|
+
name: "slidev:monaco-types-loader",
|
|
2148
|
+
resolveId(id) {
|
|
2149
|
+
if (id.startsWith("/@slidev-monaco-types/")) return id;
|
|
2150
|
+
return null;
|
|
2151
|
+
},
|
|
2152
|
+
async load(id) {
|
|
2153
|
+
if (!id.startsWith("/@slidev-monaco-types/")) return null;
|
|
2154
|
+
const url = new URL(id, "http://localhost");
|
|
2155
|
+
if (url.pathname === "/@slidev-monaco-types/resolve") {
|
|
2156
|
+
const query = new URLSearchParams(url.search);
|
|
2157
|
+
const pkg = query.get("pkg");
|
|
2158
|
+
const importer = query.get("importer") ?? userRoot;
|
|
2159
|
+
const pkgJsonPath = await findDepPkgJsonPath(pkg, importer);
|
|
2160
|
+
if (!pkgJsonPath) throw new Error(`Package "${pkg}" not found in "${importer}"`);
|
|
2161
|
+
const root = slash(dirname(pkgJsonPath));
|
|
2162
|
+
const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, "utf-8"));
|
|
2163
|
+
let deps = Object.keys(pkgJson.dependencies ?? {});
|
|
2164
|
+
deps = deps.filter((pkg$1) => !utils.isMonacoTypesIgnored(pkg$1));
|
|
2165
|
+
return [`import "/@slidev-monaco-types/load?${new URLSearchParams({
|
|
2166
|
+
root,
|
|
2167
|
+
name: pkgJson.name
|
|
2168
|
+
})}"`, ...deps.map((dep) => `import "/@slidev-monaco-types/resolve?${new URLSearchParams({
|
|
2169
|
+
pkg: dep,
|
|
2170
|
+
importer: root
|
|
2171
|
+
})}"`)].join("\n");
|
|
2172
|
+
}
|
|
2173
|
+
if (url.pathname === "/@slidev-monaco-types/load") {
|
|
2174
|
+
const query = new URLSearchParams(url.search);
|
|
2175
|
+
const root = query.get("root");
|
|
2176
|
+
const name = query.get("name");
|
|
2177
|
+
const files = await fg([
|
|
2178
|
+
"**/*.ts",
|
|
2179
|
+
"**/*.mts",
|
|
2180
|
+
"**/*.cts",
|
|
2181
|
+
"package.json"
|
|
2182
|
+
], {
|
|
2183
|
+
cwd: root,
|
|
2184
|
+
followSymbolicLinks: true,
|
|
2185
|
+
ignore: ["**/node_modules/**"]
|
|
2186
|
+
});
|
|
2187
|
+
if (!files.length) return "/** No files found **/";
|
|
2188
|
+
return ["import { addFile } from \"@slidev/client/setup/monaco.ts\"", ...files.map((file) => `addFile(() => import(${JSON.stringify(`${toAtFS(resolve(root, file))}?monaco-types&raw`)}), ${JSON.stringify(`node_modules/${name}/${file}`)})`)].join("\n");
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
//#endregion
|
|
2195
|
+
//#region node/vite/remoteAssets.ts
|
|
2196
|
+
async function createRemoteAssetsPlugin({ data: { config }, mode }, pluginOptions) {
|
|
2197
|
+
if (!(config.remoteAssets === true || config.remoteAssets === mode)) return;
|
|
2198
|
+
const { VitePluginRemoteAssets, DefaultRules } = await import("vite-plugin-remote-assets");
|
|
2199
|
+
return VitePluginRemoteAssets({
|
|
2200
|
+
resolveMode: (id) => id.endsWith("index.html") ? "relative" : "@fs",
|
|
2201
|
+
awaitDownload: mode === "build",
|
|
2202
|
+
...pluginOptions.remoteAssets,
|
|
2203
|
+
rules: [
|
|
2204
|
+
...DefaultRules,
|
|
2205
|
+
{
|
|
2206
|
+
match: /\b(https?:\/\/image.unsplash\.com.*?)(?=[`'")\]])/gi,
|
|
2207
|
+
ext: ".png"
|
|
2208
|
+
},
|
|
2209
|
+
...pluginOptions.remoteAssets?.rules ?? []
|
|
2210
|
+
]
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
//#endregion
|
|
2215
|
+
//#region node/integrations/drawings.ts
|
|
2216
|
+
function resolveDrawingsDir(options) {
|
|
2217
|
+
return options.data.config.drawings.persist ? resolve(dirname(options.entry), options.data.config.drawings.persist) : void 0;
|
|
2218
|
+
}
|
|
2219
|
+
async function loadDrawings(options) {
|
|
2220
|
+
const dir = resolveDrawingsDir(options);
|
|
2221
|
+
if (!dir || !existsSync(dir)) return {};
|
|
2222
|
+
const files = await fg("*.svg", {
|
|
2223
|
+
onlyFiles: true,
|
|
2224
|
+
cwd: dir,
|
|
2225
|
+
absolute: true,
|
|
2226
|
+
suppressErrors: true
|
|
2227
|
+
});
|
|
2228
|
+
const obj = {};
|
|
2229
|
+
await Promise.all(files.map(async (path$1) => {
|
|
2230
|
+
const num = +basename(path$1, ".svg");
|
|
2231
|
+
if (Number.isNaN(num)) return;
|
|
2232
|
+
const content = await fs.readFile(path$1, "utf8");
|
|
2233
|
+
const lines = content.split(/\n/g);
|
|
2234
|
+
obj[num.toString()] = lines.slice(1, -1).join("\n");
|
|
2235
|
+
}));
|
|
2236
|
+
return obj;
|
|
2237
|
+
}
|
|
2238
|
+
async function writeDrawings(options, drawing) {
|
|
2239
|
+
const dir = resolveDrawingsDir(options);
|
|
2240
|
+
if (!dir) return;
|
|
2241
|
+
const width = options.data.config.canvasWidth;
|
|
2242
|
+
const height = Math.round(width / options.data.config.aspectRatio);
|
|
2243
|
+
const SVG_HEAD = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">`;
|
|
2244
|
+
await fs.mkdir(dir, { recursive: true });
|
|
2245
|
+
return Promise.all(Object.entries(drawing).map(async ([key, value]) => {
|
|
2246
|
+
if (!value) return;
|
|
2247
|
+
const svg = `${SVG_HEAD}\n${value}\n</svg>`;
|
|
2248
|
+
await fs.writeFile(join(dir, `${key}.svg`), svg, "utf-8");
|
|
2249
|
+
}));
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
//#endregion
|
|
2253
|
+
//#region node/integrations/snapshots.ts
|
|
2254
|
+
function resolveSnapshotsDir(options) {
|
|
2255
|
+
return resolve(dirname(options.entry), ".slidev/snapshots");
|
|
2256
|
+
}
|
|
2257
|
+
async function loadSnapshots(options) {
|
|
2258
|
+
const dir = resolveSnapshotsDir(options);
|
|
2259
|
+
const file = join(dir, "snapshots.json");
|
|
2260
|
+
if (!dir || !existsSync(file)) return {};
|
|
2261
|
+
return JSON.parse(await fs.readFile(file, "utf8"));
|
|
2262
|
+
}
|
|
2263
|
+
async function writeSnapshots(options, data) {
|
|
2264
|
+
const dir = resolveSnapshotsDir(options);
|
|
2265
|
+
if (!dir) return;
|
|
2266
|
+
await fs.mkdir(dir, { recursive: true });
|
|
2267
|
+
await fs.writeFile(join(dir, "snapshots.json"), JSON.stringify(data, null, 2), "utf-8");
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
//#endregion
|
|
2271
|
+
//#region node/vite/serverRef.ts
|
|
2272
|
+
async function createServerRefPlugin(options, pluginOptions) {
|
|
2273
|
+
return ServerRef({
|
|
2274
|
+
debug: false,
|
|
2275
|
+
state: {
|
|
2276
|
+
sync: false,
|
|
2277
|
+
nav: {
|
|
2278
|
+
page: 0,
|
|
2279
|
+
clicks: 0
|
|
2280
|
+
},
|
|
2281
|
+
drawings: await loadDrawings(options),
|
|
2282
|
+
snapshots: await loadSnapshots(options),
|
|
2283
|
+
...pluginOptions.serverRef?.state
|
|
2284
|
+
},
|
|
2285
|
+
onChanged(key, data, patch, timestamp) {
|
|
2286
|
+
pluginOptions.serverRef?.onChanged?.(key, data, patch, timestamp);
|
|
2287
|
+
if (options.data.config.drawings.persist && key === "drawings") writeDrawings(options, patch ?? data);
|
|
2288
|
+
if (key === "snapshots") writeSnapshots(options, data);
|
|
2289
|
+
}
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
//#endregion
|
|
2294
|
+
//#region node/vite/staticCopy.ts
|
|
2295
|
+
async function createStaticCopyPlugin({ themeRoots, addonRoots }, pluginOptions) {
|
|
2296
|
+
const publicDirs = [...themeRoots, ...addonRoots].map((i) => join(i, "public")).filter(existsSync);
|
|
2297
|
+
if (!publicDirs.length) return;
|
|
2298
|
+
const { viteStaticCopy } = await import("vite-plugin-static-copy");
|
|
2299
|
+
return viteStaticCopy({
|
|
2300
|
+
silent: true,
|
|
2301
|
+
targets: publicDirs.map((dir) => ({
|
|
2302
|
+
src: `${dir}/*`,
|
|
2303
|
+
dest: "theme"
|
|
2304
|
+
})),
|
|
2305
|
+
...pluginOptions.staticCopy
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
//#endregion
|
|
2310
|
+
//#region node/setups/unocss.ts
|
|
2311
|
+
async function setupUnocss({ clientRoot, roots, data, utils }) {
|
|
2312
|
+
async function loadFileConfigs(root) {
|
|
2313
|
+
return (await Promise.all([resolve(root, "uno.config.ts"), resolve(root, "unocss.config.ts")].map(async (i) => {
|
|
2314
|
+
if (!existsSync(i)) return void 0;
|
|
2315
|
+
const loaded = await loadModule(i);
|
|
2316
|
+
return "default" in loaded ? loaded.default : loaded;
|
|
2317
|
+
}))).filter((x) => !!x);
|
|
2318
|
+
}
|
|
2319
|
+
const configs = [
|
|
2320
|
+
{ presets: [presetIcons({
|
|
2321
|
+
collectionsNodeResolvePath: utils.iconsResolvePath,
|
|
2322
|
+
collections: { slidev: { logo: () => readFileSync(resolve(clientRoot, "assets/logo.svg"), "utf-8") } }
|
|
2323
|
+
})] },
|
|
2324
|
+
...await loadFileConfigs(clientRoot),
|
|
2325
|
+
...await loadSetups(roots, "unocss.ts", [], loadFileConfigs)
|
|
2326
|
+
].filter(Boolean);
|
|
2327
|
+
const config = mergeConfigs(configs);
|
|
2328
|
+
config.theme ||= {};
|
|
2329
|
+
config.theme.fontFamily ||= {};
|
|
2330
|
+
config.theme.fontFamily.sans ||= data.config.fonts.sans.join(",");
|
|
2331
|
+
config.theme.fontFamily.mono ||= data.config.fonts.mono.join(",");
|
|
2332
|
+
config.theme.fontFamily.serif ||= data.config.fonts.serif.join(",");
|
|
2333
|
+
return config;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
//#endregion
|
|
2337
|
+
//#region node/vite/unocss.ts
|
|
2338
|
+
async function createUnocssPlugin(options, pluginOptions) {
|
|
2339
|
+
return UnoCSS({
|
|
2340
|
+
configFile: false,
|
|
2341
|
+
...await setupUnocss(options),
|
|
2342
|
+
...pluginOptions.unocss
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
//#endregion
|
|
2347
|
+
//#region node/vite/userPlugins.ts
|
|
2348
|
+
async function createUserVitePlugins(options) {
|
|
2349
|
+
const createPluginTasks = options.roots.map(async (root) => {
|
|
2350
|
+
const modulePath = path.join(root, "setup", "vite-plugins.ts");
|
|
2351
|
+
if (existsSync(modulePath)) {
|
|
2352
|
+
const module = await loadModule(modulePath);
|
|
2353
|
+
return module.default(options);
|
|
2354
|
+
}
|
|
2355
|
+
return [];
|
|
2356
|
+
});
|
|
2357
|
+
return (await Promise.all(createPluginTasks)).flatMap((p) => p);
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
//#endregion
|
|
2361
|
+
//#region node/vite/vue.ts
|
|
2362
|
+
const customElements = new Set([
|
|
2363
|
+
"annotation",
|
|
2364
|
+
"math",
|
|
2365
|
+
"menclose",
|
|
2366
|
+
"mfrac",
|
|
2367
|
+
"mglyph",
|
|
2368
|
+
"mi",
|
|
2369
|
+
"mlabeledtr",
|
|
2370
|
+
"mn",
|
|
2371
|
+
"mo",
|
|
2372
|
+
"mover",
|
|
2373
|
+
"mpadded",
|
|
2374
|
+
"mphantom",
|
|
2375
|
+
"mroot",
|
|
2376
|
+
"mrow",
|
|
2377
|
+
"mspace",
|
|
2378
|
+
"msqrt",
|
|
2379
|
+
"mstyle",
|
|
2380
|
+
"msub",
|
|
2381
|
+
"msubsup",
|
|
2382
|
+
"msup",
|
|
2383
|
+
"mtable",
|
|
2384
|
+
"mtd",
|
|
2385
|
+
"mtext",
|
|
2386
|
+
"mtr",
|
|
2387
|
+
"munder",
|
|
2388
|
+
"munderover",
|
|
2389
|
+
"semantics"
|
|
2390
|
+
]);
|
|
2391
|
+
async function createVuePlugin(_options, pluginOptions) {
|
|
2392
|
+
const { vue: vueOptions = {}, vuejsx: vuejsxOptions = {} } = pluginOptions;
|
|
2393
|
+
const VuePlugin = Vue({
|
|
2394
|
+
include: [
|
|
2395
|
+
/\.vue$/,
|
|
2396
|
+
/\.vue\?vue/,
|
|
2397
|
+
/\.vue\?v=/,
|
|
2398
|
+
/\.md$/,
|
|
2399
|
+
/\.md\?vue/
|
|
2400
|
+
],
|
|
2401
|
+
exclude: [],
|
|
2402
|
+
...vueOptions,
|
|
2403
|
+
template: {
|
|
2404
|
+
...vueOptions?.template,
|
|
2405
|
+
compilerOptions: {
|
|
2406
|
+
...vueOptions?.template?.compilerOptions,
|
|
2407
|
+
isCustomElement(tag) {
|
|
2408
|
+
return customElements.has(tag) || vueOptions?.template?.compilerOptions?.isCustomElement?.(tag);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
});
|
|
2413
|
+
const VueJsxPlugin = VueJsx(vuejsxOptions);
|
|
2414
|
+
return [VueJsxPlugin, VuePlugin];
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
//#endregion
|
|
2418
|
+
//#region node/vite/index.ts
|
|
2419
|
+
function ViteSlidevPlugin(options, pluginOptions = {}, serverOptions = {}) {
|
|
2420
|
+
return Promise.all([
|
|
2421
|
+
createSlidesLoader(options, serverOptions),
|
|
2422
|
+
createMarkdownPlugin(options, pluginOptions),
|
|
2423
|
+
createLayoutWrapperPlugin(options),
|
|
2424
|
+
createContextInjectionPlugin(),
|
|
2425
|
+
createVuePlugin(options, pluginOptions),
|
|
2426
|
+
createHmrPatchPlugin(),
|
|
2427
|
+
createComponentsPlugin(options, pluginOptions),
|
|
2428
|
+
createIconsPlugin(options, pluginOptions),
|
|
2429
|
+
createRemoteAssetsPlugin(options, pluginOptions),
|
|
2430
|
+
createServerRefPlugin(options, pluginOptions),
|
|
2431
|
+
createConfigPlugin(options),
|
|
2432
|
+
createMonacoTypesLoader(options),
|
|
2433
|
+
createMonacoWriterPlugin(options),
|
|
2434
|
+
createVueCompilerFlagsPlugin(options),
|
|
2435
|
+
createUnocssPlugin(options, pluginOptions),
|
|
2436
|
+
createStaticCopyPlugin(options, pluginOptions),
|
|
2437
|
+
createInspectPlugin(options, pluginOptions),
|
|
2438
|
+
createUserVitePlugins(options)
|
|
2439
|
+
]);
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
//#endregion
|
|
2443
|
+
//#region node/commands/shared.ts
|
|
2444
|
+
const sharedMd = MarkdownIt({ html: true });
|
|
2445
|
+
sharedMd.use(MarkdownItLink);
|
|
2446
|
+
function getSlideTitle(data) {
|
|
2447
|
+
const tokens = sharedMd.parseInline(data.config.title, {});
|
|
2448
|
+
const title = stringifyMarkdownTokens(tokens);
|
|
2449
|
+
const slideTitle = data.config.titleTemplate.replace("%s", title);
|
|
2450
|
+
return slideTitle === "Slidev - Slidev" ? "Slidev" : slideTitle;
|
|
2451
|
+
}
|
|
2452
|
+
async function resolveViteConfigs(options, baseConfig, overrideConfigs, command, serverOptions) {
|
|
2453
|
+
const configEnv = {
|
|
2454
|
+
mode: command === "build" ? "production" : "development",
|
|
2455
|
+
command
|
|
2456
|
+
};
|
|
2457
|
+
const files = options.roots.map((i) => join(i, "vite.config.ts"));
|
|
2458
|
+
for (const file of files) {
|
|
2459
|
+
if (!existsSync(file)) continue;
|
|
2460
|
+
const viteConfig = await loadConfigFromFile(configEnv, file);
|
|
2461
|
+
if (!viteConfig?.config) continue;
|
|
2462
|
+
baseConfig = mergeConfig(baseConfig, viteConfig.config);
|
|
2463
|
+
}
|
|
2464
|
+
baseConfig = mergeConfig(baseConfig, overrideConfigs);
|
|
2465
|
+
baseConfig = mergeConfig(baseConfig, {
|
|
2466
|
+
configFile: false,
|
|
2467
|
+
root: options.userRoot,
|
|
2468
|
+
plugins: await ViteSlidevPlugin(options, baseConfig.slidev, serverOptions),
|
|
2469
|
+
define: { __VUE_PROD_DEVTOOLS__: false }
|
|
2470
|
+
});
|
|
2471
|
+
return baseConfig;
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
//#endregion
|
|
2475
|
+
export { ViteSlidevPlugin, createDataUtils, getThemeMeta, loadSetups, parser$1 as parser, resolveAddons, resolveOptions, resolveTheme, resolveViteConfigs, updateFrontmatterPatch, version };
|