@slidev/cli 52.17.0 → 52.17.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-Cszi5hDI.mjs → build-C6LBmXd-.mjs} +4 -4
- package/dist/cli.mjs +10 -10
- package/dist/{export-CU2GL9dm.mjs → export-CtL3Az7M.mjs} +35 -29
- package/dist/index.mjs +1 -1
- package/dist/{resolver-BBT539iz.mjs → resolver-BV2a2pHC.mjs} +1 -1
- package/dist/{serve-CgOXLPUI.mjs → serve-CDMTHA3C.mjs} +211 -169
- package/dist/{stdio-LsewwOTO.mjs → stdio-Cq1YeFUk.mjs} +2 -2
- package/package.json +5 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveViteConfigs } from "./serve-
|
|
2
|
-
import { join, resolve } from "
|
|
1
|
+
import { n as resolveViteConfigs } from "./serve-CDMTHA3C.mjs";
|
|
2
|
+
import { join, resolve } from "pathe";
|
|
3
3
|
import { build as build$1 } from "vite";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import fs$1 from "node:fs/promises";
|
|
@@ -51,7 +51,7 @@ async function build(options, viteConfig = {}, args) {
|
|
|
51
51
|
dev: true
|
|
52
52
|
}));
|
|
53
53
|
server.listen(port);
|
|
54
|
-
const { exportSlides } = await import("./export-
|
|
54
|
+
const { exportSlides } = await import("./export-CtL3Az7M.mjs");
|
|
55
55
|
const tempDir = resolve(outDir, "temp");
|
|
56
56
|
await fs$1.mkdir(tempDir, { recursive: true });
|
|
57
57
|
await exportSlides({
|
|
@@ -92,7 +92,7 @@ async function build(options, viteConfig = {}, args) {
|
|
|
92
92
|
"true",
|
|
93
93
|
"auto"
|
|
94
94
|
].includes(options.data.config.download)) {
|
|
95
|
-
const { exportSlides, getExportOptions } = await import("./export-
|
|
95
|
+
const { exportSlides, getExportOptions } = await import("./export-CtL3Az7M.mjs");
|
|
96
96
|
const port = 12445;
|
|
97
97
|
const app = connect();
|
|
98
98
|
const server = http.createServer(app);
|
package/dist/cli.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { i as resolveEntry, r as isInstalledGlobally } from "./resolver-
|
|
2
|
-
import { c as getThemeMeta, d as loadSetups, f as updateFrontmatterPatch, l as resolveTheme, o as resolveOptions, s as parser, t as createServer, u as version } from "./serve-
|
|
3
|
-
import path from "node:path";
|
|
1
|
+
import { i as resolveEntry, r as isInstalledGlobally } from "./resolver-BV2a2pHC.mjs";
|
|
2
|
+
import { c as getThemeMeta, d as loadSetups, f as updateFrontmatterPatch, l as resolveTheme, o as resolveOptions, s as parser, t as createServer, u as version } from "./serve-CDMTHA3C.mjs";
|
|
4
3
|
import process from "node:process";
|
|
4
|
+
import path from "pathe";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import { blue, bold, cyan, cyanBright, dim, gray, green, underline, yellow } from "ansis";
|
|
7
7
|
import { injectPreparserExtensionLoader } from "@slidev/parser/fs";
|
|
8
8
|
import equal from "fast-deep-equal";
|
|
9
|
-
import {
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import * as readline from "node:readline";
|
|
12
12
|
import { verifyConfig } from "@slidev/parser";
|
|
@@ -202,7 +202,7 @@ cli.command("* [entry]", "Start a local server for Slidev", (args) => commonOpti
|
|
|
202
202
|
name: "e",
|
|
203
203
|
fullname: "edit",
|
|
204
204
|
action() {
|
|
205
|
-
|
|
205
|
+
execFile(process.env.EDITOR || "code", [entry]);
|
|
206
206
|
}
|
|
207
207
|
},
|
|
208
208
|
{
|
|
@@ -300,7 +300,7 @@ cli.command("build [entry..]", "Build hostable SPA", (args) => exportOptions(com
|
|
|
300
300
|
describe: "enable the inspect plugin for debugging"
|
|
301
301
|
}).strict().help(), async (args) => {
|
|
302
302
|
const { entry, theme, base, download, out, inspect, "without-notes": withoutNotes, "router-mode": routerMode } = args;
|
|
303
|
-
const { build } = await import("./build-
|
|
303
|
+
const { build } = await import("./build-C6LBmXd-.mjs");
|
|
304
304
|
for (const entryFile of entry) {
|
|
305
305
|
const options = await resolveOptions({
|
|
306
306
|
entry: entryFile,
|
|
@@ -329,7 +329,7 @@ cli.command("format [entry..]", "Format the markdown file", (args) => commonOpti
|
|
|
329
329
|
}
|
|
330
330
|
});
|
|
331
331
|
cli.command("mcp [entry]", "Start an MCP (Model Context Protocol) server over stdio for AI agents to inspect and edit the slides", (args) => commonOptions(args).strict().help(), async ({ entry }) => {
|
|
332
|
-
const { startMcpStdioServer } = await import("./stdio-
|
|
332
|
+
const { startMcpStdioServer } = await import("./stdio-Cq1YeFUk.mjs");
|
|
333
333
|
await startMcpStdioServer(await resolveEntry(entry));
|
|
334
334
|
});
|
|
335
335
|
cli.command("theme [subcommand]", "Theme related operations", (command) => {
|
|
@@ -369,7 +369,7 @@ cli.command("theme [subcommand]", "Theme related operations", (command) => {
|
|
|
369
369
|
});
|
|
370
370
|
cli.command("export [entry..]", "Export slides to PDF", (args) => exportOptions(commonOptions(args)).strict().help(), async (args) => {
|
|
371
371
|
const { entry, theme } = args;
|
|
372
|
-
const { exportSlides, getExportOptions } = await import("./export-
|
|
372
|
+
const { exportSlides, getExportOptions } = await import("./export-CtL3Az7M.mjs");
|
|
373
373
|
const candidatePort = await getPort(12445);
|
|
374
374
|
let warned = false;
|
|
375
375
|
for (const entryFile of entry) {
|
|
@@ -421,7 +421,7 @@ cli.command("export-notes [entry..]", "Export slide notes to PDF", (args) => arg
|
|
|
421
421
|
type: "number",
|
|
422
422
|
describe: "wait for the specified ms before exporting"
|
|
423
423
|
}).strict().help(), async ({ entry, output, timeout, wait }) => {
|
|
424
|
-
const { exportNotes } = await import("./export-
|
|
424
|
+
const { exportNotes } = await import("./export-CtL3Az7M.mjs");
|
|
425
425
|
const candidatePort = await getPort(12445);
|
|
426
426
|
for (const entryFile of entry) {
|
|
427
427
|
const options = await resolveOptions({ entry: entryFile }, "export");
|
|
@@ -547,7 +547,7 @@ function printInfo(options, port, base, remote, tunnelUrl, publicIp) {
|
|
|
547
547
|
if (options.utils.define.__SLIDEV_FEATURE_PRESENTER__) console.log(`${dim(" presenter mode ")} > ${blue(`${baseUrl}${presenterPath}`)}`);
|
|
548
548
|
console.log(`${dim(" slides overview ")} > ${blue(`${baseUrl}${overviewPath}`)}`);
|
|
549
549
|
if (options.utils.define.__SLIDEV_FEATURE_BROWSER_EXPORTER__) console.log(`${dim(" export slides")} > ${blue(`${baseUrl}/export/`)}`);
|
|
550
|
-
if (options.mode === "dev" && options.data.config.mcp !== false) console.log(`${dim(" mcp server
|
|
550
|
+
if (options.mode === "dev" && options.data.config.mcp !== false) console.log(`${dim(" mcp server ")} > ${blue(`http://localhost:${bold(port)}/__mcp`)}`);
|
|
551
551
|
if (options.inspect) console.log(`${dim(" vite inspector")} > ${yellow(`${baseUrl}/__inspect/`)}`);
|
|
552
552
|
let lastRemoteUrl = "";
|
|
553
553
|
if (remote !== void 0) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as getRoots } from "./resolver-
|
|
2
|
-
import path, { dirname, relative } from "node:path";
|
|
1
|
+
import { n as getRoots } from "./resolver-BV2a2pHC.mjs";
|
|
3
2
|
import process from "node:process";
|
|
3
|
+
import path, { dirname, relative } from "pathe";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import { clearUndefined, ensureSuffix, slash } from "@antfu/utils";
|
|
6
6
|
import { blue, cyan, dim, green, yellow } from "ansis";
|
|
@@ -78,26 +78,29 @@ async function exportNotes({ port = 18724, base = "/", output = "notes", timeout
|
|
|
78
78
|
const progress = createSlidevProgress(true);
|
|
79
79
|
progress.start(1);
|
|
80
80
|
if (!output.endsWith(".pdf")) output = `${output}.pdf`;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
try {
|
|
82
|
+
await page.goto(`http://localhost:${port}${base}presenter/print`, {
|
|
83
|
+
waitUntil: "networkidle",
|
|
84
|
+
timeout
|
|
85
|
+
});
|
|
86
|
+
await page.waitForLoadState("networkidle");
|
|
87
|
+
await page.emulateMedia({ media: "screen" });
|
|
88
|
+
if (wait) await page.waitForTimeout(wait);
|
|
89
|
+
await page.pdf({
|
|
90
|
+
path: output,
|
|
91
|
+
margin: {
|
|
92
|
+
left: 0,
|
|
93
|
+
top: 0,
|
|
94
|
+
right: 0,
|
|
95
|
+
bottom: 0
|
|
96
|
+
},
|
|
97
|
+
printBackground: true,
|
|
98
|
+
preferCSSPageSize: true
|
|
99
|
+
});
|
|
100
|
+
} finally {
|
|
101
|
+
progress.stop();
|
|
102
|
+
await browser.close();
|
|
103
|
+
}
|
|
101
104
|
return output;
|
|
102
105
|
}
|
|
103
106
|
async function exportSlides({ port = 18724, total = 0, range, format = "pdf", output = "slides", slides, base = "/", timeout = 3e4, wait = 0, dark = false, routerMode = "history", width = 1920, height = 1080, withClicks = false, executablePath = void 0, withToc = false, perSlide = false, scale = 1, waitUntil, omitBackground = false }) {
|
|
@@ -113,13 +116,16 @@ async function exportSlides({ port = 18724, total = 0, range, format = "pdf", ou
|
|
|
113
116
|
})).newPage();
|
|
114
117
|
const progress = createSlidevProgress(!perSlide);
|
|
115
118
|
progress.start(pages.length);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
try {
|
|
120
|
+
if (format === "pdf") await genPagePdf();
|
|
121
|
+
else if (format === "png") await genPagePng(output);
|
|
122
|
+
else if (format === "md") await genPageMd();
|
|
123
|
+
else if (format === "pptx") await genPagePptx(await genPagePng(false));
|
|
124
|
+
else throw new Error(`[slidev] Unsupported exporting format "${format}"`);
|
|
125
|
+
} finally {
|
|
126
|
+
progress.stop();
|
|
127
|
+
await browser.close();
|
|
128
|
+
}
|
|
123
129
|
const relativeOutput = slash(relative(".", output));
|
|
124
130
|
return relativeOutput.startsWith(".") ? relativeOutput : `./${relativeOutput}`;
|
|
125
131
|
async function go(no, clicks) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createDataUtils, o as resolveOptions, r as ViteSlidevPlugin, s as parser, t as createServer } from "./serve-
|
|
1
|
+
import { a as createDataUtils, o as resolveOptions, r as ViteSlidevPlugin, s as parser, t as createServer } from "./serve-CDMTHA3C.mjs";
|
|
2
2
|
export { ViteSlidevPlugin, createDataUtils, createServer, parser, resolveOptions };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
2
1
|
import process from "node:process";
|
|
2
|
+
import { dirname, join, relative, resolve, sep } from "pathe";
|
|
3
3
|
import { existsSync, lstatSync, readdirSync, readlinkSync } from "node:fs";
|
|
4
4
|
import { copyFile, readFile } from "node:fs/promises";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as resolveImportPath, c as toAtFS, i as resolveEntry, n as getRoots, o as resolveImportUrl, r as isInstalledGlobally, s as resolveSourceFiles, t as createResolver } from "./resolver-
|
|
1
|
+
import { a as resolveImportPath, c as toAtFS, i as resolveEntry, n as getRoots, o as resolveImportUrl, r as isInstalledGlobally, s as resolveSourceFiles, t as createResolver } from "./resolver-BV2a2pHC.mjs";
|
|
2
2
|
import { builtinModules } from "node:module";
|
|
3
|
-
import path, { basename, dirname, join, relative, resolve, win32 } from "node:path";
|
|
4
3
|
import process from "node:process";
|
|
4
|
+
import path, { basename, dirname, isAbsolute, join, relative, resolve, win32 } from "pathe";
|
|
5
5
|
import { createServer, loadConfigFromFile, mergeConfig, parseSync } from "vite";
|
|
6
6
|
import MarkdownExit from "markdown-exit";
|
|
7
7
|
import fs, { existsSync, mkdirSync, realpathSync, writeFileSync } from "node:fs";
|
|
@@ -70,7 +70,172 @@ function MarkdownItLink(md) {
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|
|
73
|
+
//#region node/virtual/deprecated.ts
|
|
74
|
+
/**
|
|
75
|
+
* Kept for backward compatibility, use #slidev/slides instead
|
|
76
|
+
*
|
|
77
|
+
* @deprecated
|
|
78
|
+
*/
|
|
79
|
+
const templateLegacyRoutes = {
|
|
80
|
+
id: "/@slidev/routes",
|
|
81
|
+
getContent() {
|
|
82
|
+
return [`export { slides } from '#slidev/slides'`, `console.warn('[slidev] #slidev/routes is deprecated, use #slidev/slides instead')`].join("\n");
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Kept for backward compatibility, use #slidev/title-renderer instead
|
|
87
|
+
*
|
|
88
|
+
* @deprecated
|
|
89
|
+
*/
|
|
90
|
+
const templateLegacyTitles = {
|
|
91
|
+
id: "/@slidev/titles.md",
|
|
92
|
+
getContent() {
|
|
93
|
+
return `
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
import TitleRenderer from '#slidev/title-renderer'
|
|
96
|
+
defineProps<{ no: number | string }>()
|
|
97
|
+
console.warn('/@slidev/titles.md is deprecated, import from #slidev/title-renderer instead')
|
|
98
|
+
<\/script>
|
|
99
|
+
|
|
100
|
+
<TitleRenderer :no="no" />
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region node/virtual/titles.ts
|
|
106
|
+
const templateTitleRendererMd = {
|
|
107
|
+
id: "/@slidev/title-renderer.md",
|
|
108
|
+
getContent({ data }) {
|
|
109
|
+
const lines = data.slides.map(({ title }, i) => `<template ${i === 0 ? "v-if" : "v-else-if"}="no === ${i + 1}">\n\n${title}\n\n</template>`);
|
|
110
|
+
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>`);
|
|
111
|
+
return lines.join("\n");
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const templateTitleRenderer = {
|
|
115
|
+
id: "/@slidev/title-renderer",
|
|
116
|
+
async getContent() {
|
|
117
|
+
return "export { default } from \"/@slidev/title-renderer.md\"";
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region node/vite/common.ts
|
|
122
|
+
const regexSlideReqPath = /^\/__slidev\/slides\/(\d+)\.json$/;
|
|
123
|
+
const regexSlideFacadeId = /^\/@slidev\/slides\/(\d+)\/(md|frontmatter)($|\?)/;
|
|
124
|
+
const regexSlideSourceId = /__slidev_(\d+)\.(md|frontmatter)$/;
|
|
125
|
+
const templateInjectionMarker = "/* @slidev-injection */";
|
|
126
|
+
const templateImportContextUtils = `import { useSlideContext as _useSlideContext, frontmatterToProps as _frontmatterToProps } from "@slidev/client/context.ts"`;
|
|
127
|
+
const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = _useSlideContext()`;
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region node/vite/importGuard.ts
|
|
130
|
+
const virtualSlideMarkdownIds = /* @__PURE__ */ new Set([templateTitleRendererMd.id, templateLegacyTitles.id]);
|
|
131
|
+
function createSlideImportGuardPlugin() {
|
|
132
|
+
let config;
|
|
133
|
+
let allowRoots = [];
|
|
134
|
+
return {
|
|
135
|
+
name: "slidev:slide-import-guard",
|
|
136
|
+
configResolved(resolved) {
|
|
137
|
+
config = resolved;
|
|
138
|
+
allowRoots = config.server.fs.allow.map(normalizeFsPath);
|
|
139
|
+
},
|
|
140
|
+
async transform(code, id) {
|
|
141
|
+
if (!isSlideMarkdownId(id) || !config?.server.fs.strict) return null;
|
|
142
|
+
const importer = filePathFromId(id) ?? id;
|
|
143
|
+
await Promise.all(extractImportSources(code, id).map(async ({ value, start }) => {
|
|
144
|
+
const resolved = await this.resolve(value, importer, { skipSelf: true });
|
|
145
|
+
if (!resolved || resolved.external) return;
|
|
146
|
+
const filePath = filePathFromId(resolved.id);
|
|
147
|
+
if (!filePath) return;
|
|
148
|
+
const normalized = normalizeFsPath(filePath);
|
|
149
|
+
if (isAllowedFile(normalized, allowRoots)) return;
|
|
150
|
+
if (isBareImport(value) && isDependencyFile(normalized)) return;
|
|
151
|
+
this.error(`[slidev] Import "${value}" from slide Markdown resolves outside of Vite server.fs.allow: ${normalized}`, start);
|
|
152
|
+
}));
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function isSlideMarkdownId(id) {
|
|
158
|
+
const clean = cleanUrl$1(id);
|
|
159
|
+
return regexSlideSourceId.test(clean) || virtualSlideMarkdownIds.has(clean);
|
|
160
|
+
}
|
|
161
|
+
function filePathFromId(id) {
|
|
162
|
+
const clean = cleanUrl$1(id);
|
|
163
|
+
if (clean.startsWith("file://")) return fileURLToPath(clean);
|
|
164
|
+
if (clean.startsWith("/@fs/")) return clean.slice(4);
|
|
165
|
+
if (clean.startsWith("/@")) return null;
|
|
166
|
+
if (path.isAbsolute(clean)) return clean;
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function isAllowedFile(filePath, allowRoots) {
|
|
170
|
+
return allowRoots.some((root) => isFileInRoot(root, filePath));
|
|
171
|
+
}
|
|
172
|
+
function extractImportSources(code, id) {
|
|
173
|
+
const result = parseSync(id, code);
|
|
174
|
+
const sources = [];
|
|
175
|
+
for (const item of result.module.staticImports) sources.push({
|
|
176
|
+
value: item.moduleRequest.value,
|
|
177
|
+
start: item.moduleRequest.start
|
|
178
|
+
});
|
|
179
|
+
for (const item of result.module.staticExports) for (const entry of item.entries) if (entry.moduleRequest) sources.push({
|
|
180
|
+
value: entry.moduleRequest.value,
|
|
181
|
+
start: entry.moduleRequest.start
|
|
182
|
+
});
|
|
183
|
+
for (const item of result.module.dynamicImports) {
|
|
184
|
+
const source = parseStringLiteral(code.slice(item.moduleRequest.start, item.moduleRequest.end));
|
|
185
|
+
if (source) sources.push({
|
|
186
|
+
value: source,
|
|
187
|
+
start: item.moduleRequest.start
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return sources;
|
|
191
|
+
}
|
|
192
|
+
function cleanUrl$1(id) {
|
|
193
|
+
return id.replace(/[?#].*$/, "");
|
|
194
|
+
}
|
|
195
|
+
function normalizeFsPath(filePath) {
|
|
196
|
+
const absolute = path.resolve(filePath);
|
|
197
|
+
if (!existsSync(absolute)) return normalizeMissingFsPath(absolute);
|
|
198
|
+
return realpathSync.native(absolute);
|
|
199
|
+
}
|
|
200
|
+
function normalizeMissingFsPath(filePath) {
|
|
201
|
+
const dir = path.dirname(filePath);
|
|
202
|
+
if (dir === filePath) return filePath;
|
|
203
|
+
if (existsSync(dir)) return path.join(realpathSync.native(dir), path.basename(filePath));
|
|
204
|
+
return path.join(normalizeMissingFsPath(dir), path.basename(filePath));
|
|
205
|
+
}
|
|
206
|
+
function isFileInRoot(root, filePath) {
|
|
207
|
+
const relative = path.relative(root, filePath);
|
|
208
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
209
|
+
}
|
|
210
|
+
function isDependencyFile(filePath) {
|
|
211
|
+
return filePath.split(/[\\/]/).includes("node_modules");
|
|
212
|
+
}
|
|
213
|
+
function isBareImport(source) {
|
|
214
|
+
return /^(?![a-z]:)[\w@](?!.*:\/\/)/i.test(source);
|
|
215
|
+
}
|
|
216
|
+
function parseStringLiteral(raw) {
|
|
217
|
+
const trimmed = raw.trim();
|
|
218
|
+
const quote = trimmed[0];
|
|
219
|
+
if (quote !== "\"" && quote !== "'" && quote !== "`") return null;
|
|
220
|
+
if (trimmed.at(-1) !== quote) return null;
|
|
221
|
+
if (quote === "`" && trimmed.includes("${")) return null;
|
|
222
|
+
try {
|
|
223
|
+
if (quote === "\"") return JSON.parse(trimmed);
|
|
224
|
+
return JSON.parse(`"${trimmed.slice(1, -1).replace(/"/g, "\\\"")}"`);
|
|
225
|
+
} catch {
|
|
226
|
+
return trimmed.slice(1, -1);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
73
230
|
//#region node/utils.ts
|
|
231
|
+
/**
|
|
232
|
+
* Whether `filePath` resolves inside any of `roots` (no `..` escape). Shared
|
|
233
|
+
* containment predicate reused by the snippet (`<<<`) and `src:` deck-file
|
|
234
|
+
* reads, and by the Vite slide-import guard (`isAllowedFile`).
|
|
235
|
+
*/
|
|
236
|
+
function isPathInsideRoots(filePath, roots) {
|
|
237
|
+
return isAllowedFile(filePath, roots);
|
|
238
|
+
}
|
|
74
239
|
const RE_WHITESPACE_ONLY = /^\s*$/;
|
|
75
240
|
const RE_QUOTED_STRING = /^(['"])(.*)\1$/;
|
|
76
241
|
const RE_WHITESPACE = /\s+/g;
|
|
@@ -243,14 +408,6 @@ function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
|
|
|
243
408
|
});
|
|
244
409
|
}
|
|
245
410
|
//#endregion
|
|
246
|
-
//#region node/vite/common.ts
|
|
247
|
-
const regexSlideReqPath = /^\/__slidev\/slides\/(\d+)\.json$/;
|
|
248
|
-
const regexSlideFacadeId = /^\/@slidev\/slides\/(\d+)\/(md|frontmatter)($|\?)/;
|
|
249
|
-
const regexSlideSourceId = /__slidev_(\d+)\.(md|frontmatter)$/;
|
|
250
|
-
const templateInjectionMarker = "/* @slidev-injection */";
|
|
251
|
-
const templateImportContextUtils = `import { useSlideContext as _useSlideContext, frontmatterToProps as _frontmatterToProps } from "@slidev/client/context.ts"`;
|
|
252
|
-
const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = _useSlideContext()`;
|
|
253
|
-
//#endregion
|
|
254
411
|
//#region node/vite/contextInjection.ts
|
|
255
412
|
const RE_EXPORT_DEFAULT_OBJECT = /export\s+default\s+\{/;
|
|
256
413
|
const RE_INJECT_OPTION = /.*inject\s*:\s*([[{])/;
|
|
@@ -484,155 +641,6 @@ function createIconsPlugin(options, pluginOptions) {
|
|
|
484
641
|
});
|
|
485
642
|
}
|
|
486
643
|
//#endregion
|
|
487
|
-
//#region node/virtual/deprecated.ts
|
|
488
|
-
/**
|
|
489
|
-
* Kept for backward compatibility, use #slidev/slides instead
|
|
490
|
-
*
|
|
491
|
-
* @deprecated
|
|
492
|
-
*/
|
|
493
|
-
const templateLegacyRoutes = {
|
|
494
|
-
id: "/@slidev/routes",
|
|
495
|
-
getContent() {
|
|
496
|
-
return [`export { slides } from '#slidev/slides'`, `console.warn('[slidev] #slidev/routes is deprecated, use #slidev/slides instead')`].join("\n");
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
/**
|
|
500
|
-
* Kept for backward compatibility, use #slidev/title-renderer instead
|
|
501
|
-
*
|
|
502
|
-
* @deprecated
|
|
503
|
-
*/
|
|
504
|
-
const templateLegacyTitles = {
|
|
505
|
-
id: "/@slidev/titles.md",
|
|
506
|
-
getContent() {
|
|
507
|
-
return `
|
|
508
|
-
<script setup lang="ts">
|
|
509
|
-
import TitleRenderer from '#slidev/title-renderer'
|
|
510
|
-
defineProps<{ no: number | string }>()
|
|
511
|
-
console.warn('/@slidev/titles.md is deprecated, import from #slidev/title-renderer instead')
|
|
512
|
-
<\/script>
|
|
513
|
-
|
|
514
|
-
<TitleRenderer :no="no" />
|
|
515
|
-
`;
|
|
516
|
-
}
|
|
517
|
-
};
|
|
518
|
-
//#endregion
|
|
519
|
-
//#region node/virtual/titles.ts
|
|
520
|
-
const templateTitleRendererMd = {
|
|
521
|
-
id: "/@slidev/title-renderer.md",
|
|
522
|
-
getContent({ data }) {
|
|
523
|
-
const lines = data.slides.map(({ title }, i) => `<template ${i === 0 ? "v-if" : "v-else-if"}="no === ${i + 1}">\n\n${title}\n\n</template>`);
|
|
524
|
-
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>`);
|
|
525
|
-
return lines.join("\n");
|
|
526
|
-
}
|
|
527
|
-
};
|
|
528
|
-
const templateTitleRenderer = {
|
|
529
|
-
id: "/@slidev/title-renderer",
|
|
530
|
-
async getContent() {
|
|
531
|
-
return "export { default } from \"/@slidev/title-renderer.md\"";
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
|
-
//#endregion
|
|
535
|
-
//#region node/vite/importGuard.ts
|
|
536
|
-
const virtualSlideMarkdownIds = /* @__PURE__ */ new Set([templateTitleRendererMd.id, templateLegacyTitles.id]);
|
|
537
|
-
function createSlideImportGuardPlugin() {
|
|
538
|
-
let config;
|
|
539
|
-
let allowRoots = [];
|
|
540
|
-
return {
|
|
541
|
-
name: "slidev:slide-import-guard",
|
|
542
|
-
configResolved(resolved) {
|
|
543
|
-
config = resolved;
|
|
544
|
-
allowRoots = config.server.fs.allow.map(normalizeFsPath);
|
|
545
|
-
},
|
|
546
|
-
async transform(code, id) {
|
|
547
|
-
if (!isSlideMarkdownId(id) || !config?.server.fs.strict) return null;
|
|
548
|
-
const importer = filePathFromId(id) ?? id;
|
|
549
|
-
await Promise.all(extractImportSources(code, id).map(async ({ value, start }) => {
|
|
550
|
-
const resolved = await this.resolve(value, importer, { skipSelf: true });
|
|
551
|
-
if (!resolved || resolved.external) return;
|
|
552
|
-
const filePath = filePathFromId(resolved.id);
|
|
553
|
-
if (!filePath) return;
|
|
554
|
-
const normalized = normalizeFsPath(filePath);
|
|
555
|
-
if (isAllowedFile(normalized, allowRoots)) return;
|
|
556
|
-
if (isBareImport(value) && isDependencyFile(normalized)) return;
|
|
557
|
-
this.error(`[slidev] Import "${value}" from slide Markdown resolves outside of Vite server.fs.allow: ${normalized}`, start);
|
|
558
|
-
}));
|
|
559
|
-
return null;
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
function isSlideMarkdownId(id) {
|
|
564
|
-
const clean = cleanUrl$1(id);
|
|
565
|
-
return regexSlideSourceId.test(clean) || virtualSlideMarkdownIds.has(clean);
|
|
566
|
-
}
|
|
567
|
-
function filePathFromId(id) {
|
|
568
|
-
const clean = cleanUrl$1(id);
|
|
569
|
-
if (clean.startsWith("file://")) return fileURLToPath(clean);
|
|
570
|
-
if (clean.startsWith("/@fs/")) return clean.slice(4);
|
|
571
|
-
if (clean.startsWith("/@")) return null;
|
|
572
|
-
if (path.isAbsolute(clean)) return clean;
|
|
573
|
-
return null;
|
|
574
|
-
}
|
|
575
|
-
function isAllowedFile(filePath, allowRoots) {
|
|
576
|
-
return allowRoots.some((root) => isFileInRoot(root, filePath));
|
|
577
|
-
}
|
|
578
|
-
function extractImportSources(code, id) {
|
|
579
|
-
const result = parseSync(id, code);
|
|
580
|
-
const sources = [];
|
|
581
|
-
for (const item of result.module.staticImports) sources.push({
|
|
582
|
-
value: item.moduleRequest.value,
|
|
583
|
-
start: item.moduleRequest.start
|
|
584
|
-
});
|
|
585
|
-
for (const item of result.module.staticExports) for (const entry of item.entries) if (entry.moduleRequest) sources.push({
|
|
586
|
-
value: entry.moduleRequest.value,
|
|
587
|
-
start: entry.moduleRequest.start
|
|
588
|
-
});
|
|
589
|
-
for (const item of result.module.dynamicImports) {
|
|
590
|
-
const source = parseStringLiteral(code.slice(item.moduleRequest.start, item.moduleRequest.end));
|
|
591
|
-
if (source) sources.push({
|
|
592
|
-
value: source,
|
|
593
|
-
start: item.moduleRequest.start
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
return sources;
|
|
597
|
-
}
|
|
598
|
-
function cleanUrl$1(id) {
|
|
599
|
-
return id.replace(/[?#].*$/, "");
|
|
600
|
-
}
|
|
601
|
-
function normalizeFsPath(filePath) {
|
|
602
|
-
const absolute = path.resolve(filePath);
|
|
603
|
-
if (!existsSync(absolute)) return normalizeMissingFsPath(absolute);
|
|
604
|
-
return realpathSync.native(absolute);
|
|
605
|
-
}
|
|
606
|
-
function normalizeMissingFsPath(filePath) {
|
|
607
|
-
const dir = path.dirname(filePath);
|
|
608
|
-
if (dir === filePath) return filePath;
|
|
609
|
-
if (existsSync(dir)) return path.join(realpathSync.native(dir), path.basename(filePath));
|
|
610
|
-
return path.join(normalizeMissingFsPath(dir), path.basename(filePath));
|
|
611
|
-
}
|
|
612
|
-
function isFileInRoot(root, filePath) {
|
|
613
|
-
const relative = path.relative(root, filePath);
|
|
614
|
-
return relative === "" || !!relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
615
|
-
}
|
|
616
|
-
function isDependencyFile(filePath) {
|
|
617
|
-
return filePath.split(/[\\/]/).includes("node_modules");
|
|
618
|
-
}
|
|
619
|
-
function isBareImport(source) {
|
|
620
|
-
return /^(?![a-z]:)[\w@](?!.*:\/\/)/i.test(source);
|
|
621
|
-
}
|
|
622
|
-
function parseStringLiteral(raw) {
|
|
623
|
-
const trimmed = raw.trim();
|
|
624
|
-
const quote = trimmed[0];
|
|
625
|
-
if (quote !== "\"" && quote !== "'" && quote !== "`") return null;
|
|
626
|
-
if (trimmed.at(-1) !== quote) return null;
|
|
627
|
-
if (quote === "`" && trimmed.includes("${")) return null;
|
|
628
|
-
try {
|
|
629
|
-
if (quote === "\"") return JSON.parse(trimmed);
|
|
630
|
-
return JSON.parse(`"${trimmed.slice(1, -1).replace(/"/g, "\\\"")}"`);
|
|
631
|
-
} catch {
|
|
632
|
-
return trimmed.slice(1, -1);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
//#endregion
|
|
636
644
|
//#region node/vite/inspect.ts
|
|
637
645
|
async function createInspectPlugin(options, pluginOptions) {
|
|
638
646
|
if (!options.inspect) return;
|
|
@@ -690,7 +698,7 @@ function createLayoutWrapperPlugin({ data, utils }) {
|
|
|
690
698
|
}
|
|
691
699
|
//#endregion
|
|
692
700
|
//#region package.json
|
|
693
|
-
var version = "52.17.
|
|
701
|
+
var version = "52.17.2";
|
|
694
702
|
//#endregion
|
|
695
703
|
//#region node/integrations/addons.ts
|
|
696
704
|
async function resolveAddons(addonsInConfig) {
|
|
@@ -986,22 +994,31 @@ const debug = createDebug("slidev:options");
|
|
|
986
994
|
async function resolveOptions(entryOptions, mode) {
|
|
987
995
|
const entry = await resolveEntry(entryOptions.entry);
|
|
988
996
|
const rootsInfo = await getRoots(entry);
|
|
989
|
-
const
|
|
997
|
+
const initialLoaded = await parser.load({
|
|
990
998
|
userRoot: rootsInfo.userRoot,
|
|
991
|
-
roots: [rootsInfo.userRoot]
|
|
999
|
+
roots: [rootsInfo.userRoot],
|
|
1000
|
+
allowedRoots: uniq([rootsInfo.userWorkspaceRoot, rootsInfo.userRoot])
|
|
992
1001
|
}, entry, void 0, mode);
|
|
993
|
-
let themeRaw = entryOptions.theme ||
|
|
1002
|
+
let themeRaw = entryOptions.theme || initialLoaded.headmatter.theme;
|
|
994
1003
|
themeRaw = themeRaw === null ? "none" : themeRaw || "default";
|
|
995
1004
|
const [theme, themeRoot] = await resolveTheme(themeRaw, entry);
|
|
996
1005
|
const themeRoots = themeRoot ? [themeRoot] : [];
|
|
997
1006
|
const themeMeta = themeRoot ? await getThemeMeta(theme, themeRoot) : void 0;
|
|
998
|
-
const
|
|
999
|
-
const addonRoots = await resolveAddons(
|
|
1007
|
+
const initialConfig = parser.resolveConfig(initialLoaded.headmatter, themeMeta, entryOptions.entry);
|
|
1008
|
+
const addonRoots = await resolveAddons(initialConfig.addons);
|
|
1000
1009
|
const roots = uniq([
|
|
1001
1010
|
...themeRoots,
|
|
1002
1011
|
...addonRoots,
|
|
1003
1012
|
rootsInfo.userRoot
|
|
1004
1013
|
]);
|
|
1014
|
+
const loaded = await parser.load({
|
|
1015
|
+
userRoot: rootsInfo.userRoot,
|
|
1016
|
+
roots,
|
|
1017
|
+
allowedRoots: uniq([rootsInfo.userWorkspaceRoot, ...roots])
|
|
1018
|
+
}, entry, void 0, mode);
|
|
1019
|
+
const config = parser.resolveConfig(loaded.headmatter, themeMeta, entryOptions.entry);
|
|
1020
|
+
config.theme = themeRaw;
|
|
1021
|
+
config.addons = initialConfig.addons;
|
|
1005
1022
|
if (entryOptions.download) config.download ||= entryOptions.download;
|
|
1006
1023
|
if (entryOptions.routerMode) config.routerMode = entryOptions.routerMode;
|
|
1007
1024
|
debug({
|
|
@@ -2043,7 +2060,12 @@ function createMonacoWriterPlugin({ userRoot }) {
|
|
|
2043
2060
|
console.error(`[Slidev] Unauthorized file write: ${file}`);
|
|
2044
2061
|
return;
|
|
2045
2062
|
}
|
|
2046
|
-
const filepath = path.
|
|
2063
|
+
const filepath = path.resolve(userRoot, file);
|
|
2064
|
+
const rel = path.relative(userRoot, filepath);
|
|
2065
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
2066
|
+
console.error(`[slidev] Refusing monaco write outside project root: ${file}`);
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2047
2069
|
console.log("[Slidev] Writing file:", filepath);
|
|
2048
2070
|
await fs$1.writeFile(filepath, content, "utf-8");
|
|
2049
2071
|
}
|
|
@@ -2128,7 +2150,7 @@ function findRegion(lines, regionName) {
|
|
|
2128
2150
|
return null;
|
|
2129
2151
|
}
|
|
2130
2152
|
const RE_SNIPPET_IMPORT = /^<<<[ \t]*(\S.*?)(#[\w-]+)?[ \t]*(?:[ \t](\S+?))?[ \t]*(\{.*)?$/;
|
|
2131
|
-
function resolveSnippetImport(lineText, userRoot, slide) {
|
|
2153
|
+
function resolveSnippetImport(lineText, userRoot, slide, allowedRoots = [userRoot]) {
|
|
2132
2154
|
const match = lineText.trimStart().match(RE_SNIPPET_IMPORT);
|
|
2133
2155
|
if (!match) return null;
|
|
2134
2156
|
let [, filepath = "", regionName = "", lang = "", meta = ""] = match;
|
|
@@ -2136,6 +2158,7 @@ function resolveSnippetImport(lineText, userRoot, slide) {
|
|
|
2136
2158
|
const src = slash(filepath.startsWith("@/") ? path.resolve(userRoot, filepath.slice(2)) : path.resolve(dir, filepath));
|
|
2137
2159
|
lang = lang.trim() || path.extname(filepath).slice(1);
|
|
2138
2160
|
meta = meta.trim();
|
|
2161
|
+
if (!isPathInsideRoots(src, allowedRoots)) throw new Error(`Code snippet path escapes the project root: ${src}`);
|
|
2139
2162
|
if (!(fs.existsSync(src) && fs.statSync(src).isFile())) throw new Error(`Code snippet path not found: ${src}`);
|
|
2140
2163
|
let content = fs.readFileSync(src, "utf8");
|
|
2141
2164
|
if (regionName) {
|
|
@@ -2151,7 +2174,12 @@ function resolveSnippetImport(lineText, userRoot, slide) {
|
|
|
2151
2174
|
src
|
|
2152
2175
|
};
|
|
2153
2176
|
}
|
|
2154
|
-
function MarkdownItSnippet(md, { userRoot, data: { watchFiles, slides } }) {
|
|
2177
|
+
function MarkdownItSnippet(md, { userRoot, userWorkspaceRoot, roots, data: { watchFiles, slides } }) {
|
|
2178
|
+
const allowedRoots = [...new Set([
|
|
2179
|
+
userWorkspaceRoot,
|
|
2180
|
+
userRoot,
|
|
2181
|
+
...roots ?? []
|
|
2182
|
+
].filter(Boolean))];
|
|
2155
2183
|
md.block.ruler.before("fence", "snippet_import", (state, startLine, _endLine, silent) => {
|
|
2156
2184
|
const pos = state.bMarks[startLine] + state.tShift[startLine];
|
|
2157
2185
|
const max = state.eMarks[startLine];
|
|
@@ -2164,7 +2192,7 @@ function MarkdownItSnippet(md, { userRoot, data: { watchFiles, slides } }) {
|
|
|
2164
2192
|
console.warn(yellow(`[markdown-it-snippet] Snippet syntax is not supported in ${state.env.id || "unknown source"}. Skipped.`));
|
|
2165
2193
|
return false;
|
|
2166
2194
|
}
|
|
2167
|
-
const snippet = resolveSnippetImport(lineText, userRoot, slide);
|
|
2195
|
+
const snippet = resolveSnippetImport(lineText, userRoot, slide, allowedRoots);
|
|
2168
2196
|
if (!snippet) return false;
|
|
2169
2197
|
const { content, filepath, src } = snippet;
|
|
2170
2198
|
let { lang, meta } = snippet;
|
|
@@ -2870,6 +2898,16 @@ function createSlidevMcpServer(ctx) {
|
|
|
2870
2898
|
}
|
|
2871
2899
|
//#endregion
|
|
2872
2900
|
//#region node/integrations/drawings.ts
|
|
2901
|
+
/**
|
|
2902
|
+
* Whether `key` is a safe bare slide id that can be joined onto `dir` without
|
|
2903
|
+
* escaping it. `loadDrawings` only ever produces numeric keys, so writes
|
|
2904
|
+
* should only ever accept numeric keys too.
|
|
2905
|
+
*/
|
|
2906
|
+
function isSafeDrawingKey(dir, key) {
|
|
2907
|
+
if (!/^\d+$/.test(key)) return false;
|
|
2908
|
+
const rel = relative(dir, join(dir, `${key}.svg`));
|
|
2909
|
+
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
2910
|
+
}
|
|
2873
2911
|
function resolveDrawingsDir(options) {
|
|
2874
2912
|
return options.data.config.drawings.persist ? resolve(dirname(options.entry), options.data.config.drawings.persist) : void 0;
|
|
2875
2913
|
}
|
|
@@ -2899,6 +2937,10 @@ async function writeDrawings(options, drawing) {
|
|
|
2899
2937
|
await fs$1.mkdir(dir, { recursive: true });
|
|
2900
2938
|
return Promise.all(Object.entries(drawing).map(async ([key, value]) => {
|
|
2901
2939
|
if (!value) return;
|
|
2940
|
+
if (!isSafeDrawingKey(dir, key)) {
|
|
2941
|
+
console.warn(`[slidev] Ignoring drawing with unsafe key: ${key}`);
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2902
2944
|
const svg = `${SVG_HEAD}\n${value}\n</svg>`;
|
|
2903
2945
|
await fs$1.writeFile(join(dir, `${key}.svg`), svg, "utf-8");
|
|
2904
2946
|
}));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as getRoots } from "./resolver-
|
|
2
|
-
import { i as createSlidevMcpServer, s as parser, u as version } from "./serve-
|
|
1
|
+
import { n as getRoots } from "./resolver-BV2a2pHC.mjs";
|
|
2
|
+
import { i as createSlidevMcpServer, s as parser, u as version } from "./serve-CDMTHA3C.mjs";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
//#region node/mcp/stdio.ts
|
|
5
5
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "52.17.
|
|
4
|
+
"version": "52.17.2",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"monaco-editor": "^0.55.1",
|
|
83
83
|
"obug": "^2.1.3",
|
|
84
84
|
"open": "^11.0.0",
|
|
85
|
+
"pathe": "^2.0.3",
|
|
85
86
|
"pdf-lib": "^1.17.1",
|
|
86
87
|
"picomatch": "^4.0.5",
|
|
87
88
|
"plantuml-encoder": "^1.4.0",
|
|
@@ -114,9 +115,9 @@
|
|
|
114
115
|
"yaml": "^2.9.0",
|
|
115
116
|
"yargs": "^18.0.0",
|
|
116
117
|
"zod": "^3.25.76",
|
|
117
|
-
"@slidev/
|
|
118
|
-
"@slidev/
|
|
119
|
-
"@slidev/types": "52.17.
|
|
118
|
+
"@slidev/client": "52.17.2",
|
|
119
|
+
"@slidev/parser": "52.17.2",
|
|
120
|
+
"@slidev/types": "52.17.2"
|
|
120
121
|
},
|
|
121
122
|
"devDependencies": {
|
|
122
123
|
"@hedgedoc/markdown-it-plugins": "^2.1.4",
|