slidev-workspace 0.7.2 → 0.8.0
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/cli.js +217 -180
- package/dist/index.js +2 -1
- package/dist/plugin-slides.d.ts +4 -1
- package/dist/plugin-slides.js +119 -85
- package/package.json +2 -1
- package/src/preview/composables/useSlides.ts +6 -2
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
4
4
|
import { existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
5
5
|
import { cp, rm } from "node:fs/promises";
|
|
6
6
|
import { execSync } from "node:child_process";
|
|
7
|
+
import { Command } from "commander";
|
|
7
8
|
import { build, createServer } from "vite";
|
|
8
9
|
import vue from "@vitejs/plugin-vue";
|
|
9
10
|
import tailwindcss from "@tailwindcss/vite";
|
|
@@ -124,83 +125,114 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
124
125
|
console.log(JSON.stringify(slides, null, 2));
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/scripts/collectSlides.ts
|
|
130
|
+
function collectSlides({ slidesDirs, names = [], exclude = [] }) {
|
|
131
|
+
const entries = [];
|
|
132
|
+
for (const slidesDir of slidesDirs) {
|
|
133
|
+
if (!existsSync(slidesDir)) {
|
|
134
|
+
console.warn(`⚠️ Slides directory not found: ${slidesDir}`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (names.length > 0) {
|
|
138
|
+
for (const slideName of names) {
|
|
139
|
+
if (exclude.includes(slideName)) continue;
|
|
140
|
+
const slideDir = join(slidesDir, slideName);
|
|
141
|
+
if (!existsSync(slideDir)) continue;
|
|
142
|
+
entries.push({
|
|
143
|
+
slidesDir,
|
|
144
|
+
slideName,
|
|
145
|
+
slideDir
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const slideNames = readdirSync(slidesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).filter((dirent) => !exclude.includes(dirent.name)).map((dirent) => dirent.name);
|
|
151
|
+
for (const slideName of slideNames) {
|
|
152
|
+
const slideDir = join(slidesDir, slideName);
|
|
153
|
+
entries.push({
|
|
154
|
+
slidesDir,
|
|
155
|
+
slideName,
|
|
156
|
+
slideDir
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return entries;
|
|
161
|
+
}
|
|
162
|
+
|
|
127
163
|
//#endregion
|
|
128
164
|
//#region src/scripts/devServer.ts
|
|
129
165
|
const runningServers = new Map();
|
|
130
|
-
async function startAllSlidesDevServer(
|
|
131
|
-
const cwd =
|
|
166
|
+
async function startAllSlidesDevServer({ basePort = 3001 } = {}) {
|
|
167
|
+
const cwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
|
|
132
168
|
const config = loadConfig(cwd);
|
|
133
169
|
const slidesDirs = resolveSlidesDirs(config, cwd);
|
|
134
|
-
let currentPort =
|
|
170
|
+
let currentPort = basePort;
|
|
135
171
|
const devServers = [];
|
|
136
172
|
console.log("🚀 Starting Slidev dev servers for all slides...");
|
|
137
173
|
console.log("📁 Working directory:", cwd);
|
|
138
174
|
console.log("📂 Slides directories found:", slidesDirs);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
175
|
+
const slides = collectSlides({
|
|
176
|
+
slidesDirs,
|
|
177
|
+
exclude: config.exclude
|
|
178
|
+
});
|
|
179
|
+
for (const { slideDir, slideName } of slides) {
|
|
180
|
+
const packageJsonPath = join$1(slideDir, "package.json");
|
|
181
|
+
const slideKey = slideDir;
|
|
182
|
+
if (runningServers.has(slideKey)) {
|
|
183
|
+
console.log(`⏭️ ${slideName} dev server already running, skipping...`);
|
|
184
|
+
devServers.push(runningServers.get(slideKey));
|
|
142
185
|
continue;
|
|
143
186
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
devProcess
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const serverInfo = {
|
|
194
|
-
name: slideName,
|
|
195
|
-
port: currentPort,
|
|
196
|
-
process: devProcess
|
|
197
|
-
};
|
|
198
|
-
devServers.push(serverInfo);
|
|
199
|
-
runningServers.set(slideKey, serverInfo);
|
|
200
|
-
currentPort++;
|
|
201
|
-
} catch (error) {
|
|
202
|
-
console.error(`❌ Failed to start dev server for ${slideName}:`, error);
|
|
203
|
-
}
|
|
187
|
+
if (!existsSync$1(packageJsonPath)) {
|
|
188
|
+
console.warn(`⚠️ Skipping ${slideName}: no package.json found`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const nodeModulesPath = join$1(slideDir, "node_modules");
|
|
192
|
+
if (!existsSync$1(nodeModulesPath)) {
|
|
193
|
+
console.warn(`⚠️ Skipping ${slideName}: dependencies not installed (run pnpm install)`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
console.log(`📦 Starting Slidev dev server for ${slideName} on port ${currentPort}...`);
|
|
197
|
+
try {
|
|
198
|
+
const devProcess = spawn("pnpm", [
|
|
199
|
+
"run",
|
|
200
|
+
"dev",
|
|
201
|
+
"--port",
|
|
202
|
+
currentPort.toString(),
|
|
203
|
+
"--open",
|
|
204
|
+
"false"
|
|
205
|
+
], {
|
|
206
|
+
cwd: slideDir,
|
|
207
|
+
stdio: [
|
|
208
|
+
"pipe",
|
|
209
|
+
"pipe",
|
|
210
|
+
"pipe"
|
|
211
|
+
],
|
|
212
|
+
detached: false,
|
|
213
|
+
env: {
|
|
214
|
+
...process.env,
|
|
215
|
+
PATH: process.env.PATH
|
|
216
|
+
},
|
|
217
|
+
shell: true
|
|
218
|
+
});
|
|
219
|
+
devProcess.stdout?.on("data", (data) => {
|
|
220
|
+
const output = data.toString();
|
|
221
|
+
if (output.includes("Local:") || output.includes("ready")) console.log(`✅ ${slideName} dev server ready on port ${currentPort}`);
|
|
222
|
+
});
|
|
223
|
+
devProcess.stderr?.on("data", (data) => {
|
|
224
|
+
console.error(`❌ ${slideName} dev server error:`, data.toString());
|
|
225
|
+
});
|
|
226
|
+
const serverInfo = {
|
|
227
|
+
name: slideName,
|
|
228
|
+
port: currentPort,
|
|
229
|
+
process: devProcess
|
|
230
|
+
};
|
|
231
|
+
devServers.push(serverInfo);
|
|
232
|
+
runningServers.set(slideKey, serverInfo);
|
|
233
|
+
currentPort++;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error(`❌ Failed to start dev server for ${slideName}:`, error);
|
|
204
236
|
}
|
|
205
237
|
}
|
|
206
238
|
return devServers;
|
|
@@ -242,8 +274,9 @@ async function transformIndexHtml(html) {
|
|
|
242
274
|
|
|
243
275
|
//#endregion
|
|
244
276
|
//#region src/vite/plugin-slides.ts
|
|
245
|
-
function slidesPlugin() {
|
|
277
|
+
function slidesPlugin(options = {}) {
|
|
246
278
|
let devServers = [];
|
|
279
|
+
const devServerBasePort = options.devServerBasePort ?? 3001;
|
|
247
280
|
return {
|
|
248
281
|
name: "vite-plugin-slides",
|
|
249
282
|
async transformIndexHtml(html) {
|
|
@@ -253,23 +286,23 @@ function slidesPlugin() {
|
|
|
253
286
|
try {
|
|
254
287
|
const config = loadConfig();
|
|
255
288
|
const slidesDirs = resolveSlidesDirs(config);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
289
|
+
const slides = collectSlides({
|
|
290
|
+
slidesDirs,
|
|
291
|
+
exclude: config.exclude
|
|
292
|
+
});
|
|
293
|
+
for (const { slideDir } of slides) {
|
|
294
|
+
const slideDistPath = join$1(slideDir, "dist");
|
|
295
|
+
const assetsPath = join$1(slideDistPath, "assets");
|
|
296
|
+
if (!existsSync$1(assetsPath)) continue;
|
|
297
|
+
const assetFiles = readdirSync$1(assetsPath);
|
|
298
|
+
const ogImageFile = assetFiles.find((file) => /^og-image-[a-zA-Z0-9]+\.png$/.test(file));
|
|
299
|
+
if (ogImageFile) {
|
|
300
|
+
const sourceFile = join$1(assetsPath, ogImageFile);
|
|
301
|
+
const destFile = join$1(slideDistPath, "og-image.png");
|
|
302
|
+
try {
|
|
303
|
+
cpSync(sourceFile, destFile, { force: true });
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.warn(`⚠ Failed to copy og-image for ${slideDir}:`, error);
|
|
273
306
|
}
|
|
274
307
|
}
|
|
275
308
|
}
|
|
@@ -282,7 +315,7 @@ function slidesPlugin() {
|
|
|
282
315
|
const config = loadConfig();
|
|
283
316
|
const slidesDirs = resolveSlidesDirs(config);
|
|
284
317
|
try {
|
|
285
|
-
devServers = await startAllSlidesDevServer();
|
|
318
|
+
devServers = await startAllSlidesDevServer({ basePort: devServerBasePort });
|
|
286
319
|
} catch (error) {
|
|
287
320
|
console.error("❌ Failed to start slides dev servers:", error);
|
|
288
321
|
}
|
|
@@ -337,25 +370,30 @@ export default configData;`;
|
|
|
337
370
|
//#region src/cli.ts
|
|
338
371
|
const __filename = fileURLToPath(import.meta.url);
|
|
339
372
|
const __dirname = dirname(__filename);
|
|
340
|
-
const args = process.argv.slice(2);
|
|
341
|
-
const command = args[0];
|
|
342
|
-
const dirsArg = args[1];
|
|
343
373
|
const packageRoot = join(__dirname, "..");
|
|
344
|
-
|
|
374
|
+
const DEFAULT_PREVIEW_PORT = 3e3;
|
|
375
|
+
function parsePortOption(value) {
|
|
376
|
+
const port = Number(value);
|
|
377
|
+
if (!Number.isInteger(port) || port <= 0) throw new Error(`Invalid port: ${value}`);
|
|
378
|
+
return port;
|
|
379
|
+
}
|
|
380
|
+
function createViteConfig(previewPort = DEFAULT_PREVIEW_PORT) {
|
|
345
381
|
const workspaceCwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
|
|
346
382
|
const config = loadConfig(workspaceCwd);
|
|
383
|
+
const devServerBasePort = previewPort + 1;
|
|
347
384
|
return {
|
|
348
385
|
root: resolve(packageRoot, "src/preview"),
|
|
349
386
|
base: config.baseUrl,
|
|
350
387
|
plugins: [
|
|
351
388
|
vue(),
|
|
352
389
|
tailwindcss(),
|
|
353
|
-
slidesPlugin()
|
|
390
|
+
slidesPlugin({ devServerBasePort })
|
|
354
391
|
],
|
|
355
392
|
resolve: { alias: { "@": resolve(packageRoot, "src/preview") } },
|
|
393
|
+
define: { __SLIDEV_WORKSPACE_DEV_PORT_BASE__: devServerBasePort },
|
|
356
394
|
build: { outDir: resolve(workspaceCwd, config.outputDir) },
|
|
357
395
|
server: {
|
|
358
|
-
port:
|
|
396
|
+
port: previewPort,
|
|
359
397
|
open: true
|
|
360
398
|
}
|
|
361
399
|
};
|
|
@@ -365,34 +403,31 @@ async function buildSlides(names) {
|
|
|
365
403
|
const config = loadConfig(workspaceCwd);
|
|
366
404
|
const slidesDirs = resolveSlidesDirs(config, workspaceCwd);
|
|
367
405
|
console.log(names ? `🔨 Building slides: ${names.join(", ")}...` : "🔨 Building all slides...");
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
406
|
+
const slides = collectSlides({
|
|
407
|
+
slidesDirs,
|
|
408
|
+
names,
|
|
409
|
+
exclude: config.exclude
|
|
410
|
+
});
|
|
411
|
+
for (const { slideDir, slideName } of slides) {
|
|
412
|
+
const packageJsonPath = join(slideDir, "package.json");
|
|
413
|
+
if (!existsSync(packageJsonPath)) {
|
|
414
|
+
console.warn(`⚠️ Skipping ${slideName}: no package.json found`);
|
|
371
415
|
continue;
|
|
372
416
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
execSync(buildCmd, {
|
|
388
|
-
cwd: workspaceCwd,
|
|
389
|
-
stdio: "inherit"
|
|
390
|
-
});
|
|
391
|
-
console.log(`✅ Built slide: ${slideName}`);
|
|
392
|
-
} catch (error) {
|
|
393
|
-
console.error(`❌ Failed to build slide ${slideName}:`, error);
|
|
394
|
-
process.exit(1);
|
|
395
|
-
}
|
|
417
|
+
console.log(`📦 Building slide: ${slideName}`);
|
|
418
|
+
try {
|
|
419
|
+
const baseUrl = config.baseUrl.endsWith("/") ? config.baseUrl : config.baseUrl + "/";
|
|
420
|
+
const subDir = slideDir.startsWith(workspaceCwd) ? slideDir.replace(workspaceCwd, "").replace(/^\//, "") : slideDir;
|
|
421
|
+
const buildCmd = `pnpm --filter "./${subDir}" run build --base ${baseUrl}${slideName}/`;
|
|
422
|
+
console.log(buildCmd);
|
|
423
|
+
execSync(buildCmd, {
|
|
424
|
+
cwd: workspaceCwd,
|
|
425
|
+
stdio: "inherit"
|
|
426
|
+
});
|
|
427
|
+
console.log(`✅ Built slide: ${slideName}`);
|
|
428
|
+
} catch (error) {
|
|
429
|
+
console.error(`❌ Failed to build slide ${slideName}:`, error);
|
|
430
|
+
process.exit(1);
|
|
396
431
|
}
|
|
397
432
|
}
|
|
398
433
|
}
|
|
@@ -407,28 +442,24 @@ async function exportOgImages() {
|
|
|
407
442
|
stdio: "inherit"
|
|
408
443
|
});
|
|
409
444
|
console.log("📦 Copying exported images to og-image.png...");
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
force: true
|
|
429
|
-
});
|
|
430
|
-
} else console.warn(`⚠️ Export file not found for ${slideName}: ${exportedFile}`);
|
|
431
|
-
}
|
|
445
|
+
const slides = collectSlides({
|
|
446
|
+
slidesDirs,
|
|
447
|
+
exclude: config.exclude
|
|
448
|
+
});
|
|
449
|
+
for (const { slideDir, slideName } of slides) {
|
|
450
|
+
const packageJsonPath = join(slideDir, "package.json");
|
|
451
|
+
if (!existsSync(packageJsonPath)) continue;
|
|
452
|
+
const exportedFile = join(slideDir, "slides-export", "1.png");
|
|
453
|
+
const targetFile = join(slideDir, "og-image.png");
|
|
454
|
+
const exportDir = join(slideDir, "slides-export");
|
|
455
|
+
if (existsSync(exportedFile)) {
|
|
456
|
+
await cp(exportedFile, targetFile);
|
|
457
|
+
console.log(`✅ Generated OG image for: ${slideName}`);
|
|
458
|
+
await rm(exportDir, {
|
|
459
|
+
recursive: true,
|
|
460
|
+
force: true
|
|
461
|
+
});
|
|
462
|
+
} else console.warn(`⚠️ Export file not found for ${slideName}: ${exportedFile}`);
|
|
432
463
|
}
|
|
433
464
|
console.log("✅ All OG images exported successfully!");
|
|
434
465
|
} catch (error) {
|
|
@@ -443,16 +474,17 @@ async function copySlidesToOutputDir(names) {
|
|
|
443
474
|
const deployDir = resolve(workspaceCwd, config.outputDir);
|
|
444
475
|
console.log(`📁 Copying slide builds into ${deployDir}...`);
|
|
445
476
|
if (!existsSync(deployDir)) mkdirSync(deployDir, { recursive: true });
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
477
|
+
const slides = collectSlides({
|
|
478
|
+
slidesDirs,
|
|
479
|
+
names,
|
|
480
|
+
exclude: config.exclude
|
|
481
|
+
});
|
|
482
|
+
for (const { slideDir, slideName } of slides) {
|
|
483
|
+
const slideDistDir = join(slideDir, "dist");
|
|
484
|
+
const targetDir = join(deployDir, slideName);
|
|
485
|
+
if (existsSync(slideDistDir)) {
|
|
486
|
+
console.log(`📋 Copying ${slideName}...`);
|
|
487
|
+
await cp(slideDistDir, targetDir, { recursive: true });
|
|
456
488
|
}
|
|
457
489
|
}
|
|
458
490
|
console.log(`✅ All slide assets copied into ${deployDir}!`);
|
|
@@ -470,10 +502,10 @@ async function runViteBuild(names) {
|
|
|
470
502
|
process.exit(1);
|
|
471
503
|
}
|
|
472
504
|
}
|
|
473
|
-
async function runVitePreview() {
|
|
505
|
+
async function runVitePreview(previewPort) {
|
|
474
506
|
try {
|
|
475
507
|
console.log("🚀 Starting Slidev Workspace development server...");
|
|
476
|
-
const config = createViteConfig();
|
|
508
|
+
const config = createViteConfig(previewPort);
|
|
477
509
|
const server = await createServer(config);
|
|
478
510
|
await server.listen();
|
|
479
511
|
server.printUrls();
|
|
@@ -491,13 +523,16 @@ Usage:
|
|
|
491
523
|
|
|
492
524
|
Commands:
|
|
493
525
|
dev Start the development server
|
|
494
|
-
build [names] Build the
|
|
495
|
-
[names]: Optional
|
|
526
|
+
build [names] Build the preview app and selected slides (or all if omitted)
|
|
527
|
+
[names]: Optional slide folder names (comma-separated or space-separated)
|
|
496
528
|
export-og Export OG images for all slides
|
|
497
529
|
help Show this help message
|
|
530
|
+
Options:
|
|
531
|
+
--port, -p <number> Set the preview server port (dev/preview only)
|
|
498
532
|
|
|
499
533
|
Examples:
|
|
500
534
|
slidev-workspace dev # Start development server
|
|
535
|
+
slidev-workspace dev --port 3030 # Start dev server on custom port
|
|
501
536
|
slidev-workspace build # Build all slides and preview app
|
|
502
537
|
slidev-workspace build slide1,slide2 # Build only specific slides by name
|
|
503
538
|
slidev-workspace export-og # Export OG images for all slides
|
|
@@ -508,35 +543,37 @@ Configuration:
|
|
|
508
543
|
For more information, visit: https://github.com/author/slidev-workspace
|
|
509
544
|
`);
|
|
510
545
|
}
|
|
546
|
+
function setWorkspaceCwd() {
|
|
547
|
+
process.env.SLIDEV_WORKSPACE_CWD = process.cwd();
|
|
548
|
+
}
|
|
549
|
+
function parseNames(names) {
|
|
550
|
+
if (!names || names.length === 0) return void 0;
|
|
551
|
+
const parsed = names.flatMap((name) => name.split(",")).map((name) => name.trim()).filter(Boolean);
|
|
552
|
+
return parsed.length > 0 ? parsed : void 0;
|
|
553
|
+
}
|
|
511
554
|
async function main() {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
break;
|
|
533
|
-
default: if (!command) showHelp();
|
|
534
|
-
else {
|
|
535
|
-
console.error(`Unknown command: ${command}`);
|
|
536
|
-
console.error("Run \"slidev-workspace help\" for available commands.");
|
|
537
|
-
process.exit(1);
|
|
538
|
-
}
|
|
555
|
+
const program = new Command();
|
|
556
|
+
program.name("slidev-workspace").description("A tool for managing multiple Slidev presentations with a workspace preview app").showHelpAfterError();
|
|
557
|
+
program.command("dev").alias("preview").description("Start the development server").option("-p, --port <number>", "Set the preview server port", (value) => parsePortOption(value)).action(async (options) => {
|
|
558
|
+
setWorkspaceCwd();
|
|
559
|
+
await runVitePreview(options.port);
|
|
560
|
+
});
|
|
561
|
+
program.command("build").description("Build the preview app and selected slides (or all if omitted)").argument("[names...]", "Optional slide folder names (comma-separated or space-separated)").action(async (names) => {
|
|
562
|
+
setWorkspaceCwd();
|
|
563
|
+
await runViteBuild(parseNames(names));
|
|
564
|
+
});
|
|
565
|
+
program.command("export-og").description("Export OG images for all slides").action(async () => {
|
|
566
|
+
setWorkspaceCwd();
|
|
567
|
+
await exportOgImages();
|
|
568
|
+
});
|
|
569
|
+
program.command("help").description("Show this help message").action(() => {
|
|
570
|
+
showHelp();
|
|
571
|
+
});
|
|
572
|
+
if (process.argv.length <= 2) {
|
|
573
|
+
program.outputHelp();
|
|
574
|
+
return;
|
|
539
575
|
}
|
|
576
|
+
await program.parseAsync(process.argv);
|
|
540
577
|
}
|
|
541
578
|
main().catch((error) => {
|
|
542
579
|
console.error("❌ An error occurred:", error);
|
package/dist/index.js
CHANGED
|
@@ -94,6 +94,7 @@ function resolveImageUrl(slide, domain) {
|
|
|
94
94
|
function useSlides() {
|
|
95
95
|
const slidesData = ref([]);
|
|
96
96
|
const isLoading = ref(true);
|
|
97
|
+
const devServerBasePort = typeof __SLIDEV_WORKSPACE_DEV_PORT_BASE__ === "number" ? __SLIDEV_WORKSPACE_DEV_PORT_BASE__ : 3001;
|
|
97
98
|
const loadSlidesData = async () => {
|
|
98
99
|
try {
|
|
99
100
|
const module = await import("slidev:content");
|
|
@@ -109,7 +110,7 @@ function useSlides() {
|
|
|
109
110
|
const slides = computed(() => {
|
|
110
111
|
if (!slidesData.value || slidesData.value.length === 0) return [];
|
|
111
112
|
return slidesData.value.map((slide, index) => {
|
|
112
|
-
const port =
|
|
113
|
+
const port = devServerBasePort + index;
|
|
113
114
|
const devServerUrl = `http://localhost:${port}`;
|
|
114
115
|
const domain = IS_DEVELOPMENT ? devServerUrl : window.location.origin;
|
|
115
116
|
const imageUrl = resolveImageUrl(slide, domain);
|
package/dist/plugin-slides.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
|
|
3
3
|
//#region src/vite/plugin-slides.d.ts
|
|
4
|
-
|
|
4
|
+
interface SlidesPluginOptions {
|
|
5
|
+
devServerBasePort?: number;
|
|
6
|
+
}
|
|
7
|
+
declare function slidesPlugin(options?: SlidesPluginOptions): Plugin;
|
|
5
8
|
//#endregion
|
|
6
9
|
export { slidesPlugin };
|
package/dist/plugin-slides.js
CHANGED
|
@@ -2,6 +2,8 @@ import { cpSync, existsSync, readFileSync, readdirSync, watch } from "fs";
|
|
|
2
2
|
import { basename, join, resolve } from "path";
|
|
3
3
|
import { parse } from "yaml";
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
|
+
import { existsSync as existsSync$1, readdirSync as readdirSync$1 } from "node:fs";
|
|
6
|
+
import { join as join$1 } from "node:path";
|
|
5
7
|
import { createHead, transformHtmlTemplate } from "unhead/server";
|
|
6
8
|
|
|
7
9
|
//#region src/scripts/config.ts
|
|
@@ -115,83 +117,114 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
115
117
|
console.log(JSON.stringify(slides, null, 2));
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/scripts/collectSlides.ts
|
|
122
|
+
function collectSlides({ slidesDirs, names = [], exclude = [] }) {
|
|
123
|
+
const entries = [];
|
|
124
|
+
for (const slidesDir of slidesDirs) {
|
|
125
|
+
if (!existsSync$1(slidesDir)) {
|
|
126
|
+
console.warn(`⚠️ Slides directory not found: ${slidesDir}`);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (names.length > 0) {
|
|
130
|
+
for (const slideName of names) {
|
|
131
|
+
if (exclude.includes(slideName)) continue;
|
|
132
|
+
const slideDir = join$1(slidesDir, slideName);
|
|
133
|
+
if (!existsSync$1(slideDir)) continue;
|
|
134
|
+
entries.push({
|
|
135
|
+
slidesDir,
|
|
136
|
+
slideName,
|
|
137
|
+
slideDir
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const slideNames = readdirSync$1(slidesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).filter((dirent) => !exclude.includes(dirent.name)).map((dirent) => dirent.name);
|
|
143
|
+
for (const slideName of slideNames) {
|
|
144
|
+
const slideDir = join$1(slidesDir, slideName);
|
|
145
|
+
entries.push({
|
|
146
|
+
slidesDir,
|
|
147
|
+
slideName,
|
|
148
|
+
slideDir
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return entries;
|
|
153
|
+
}
|
|
154
|
+
|
|
118
155
|
//#endregion
|
|
119
156
|
//#region src/scripts/devServer.ts
|
|
120
157
|
const runningServers = new Map();
|
|
121
|
-
async function startAllSlidesDevServer(
|
|
122
|
-
const cwd =
|
|
158
|
+
async function startAllSlidesDevServer({ basePort = 3001 } = {}) {
|
|
159
|
+
const cwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
|
|
123
160
|
const config = loadConfig(cwd);
|
|
124
161
|
const slidesDirs = resolveSlidesDirs(config, cwd);
|
|
125
|
-
let currentPort =
|
|
162
|
+
let currentPort = basePort;
|
|
126
163
|
const devServers = [];
|
|
127
164
|
console.log("🚀 Starting Slidev dev servers for all slides...");
|
|
128
165
|
console.log("📁 Working directory:", cwd);
|
|
129
166
|
console.log("📂 Slides directories found:", slidesDirs);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
167
|
+
const slides = collectSlides({
|
|
168
|
+
slidesDirs,
|
|
169
|
+
exclude: config.exclude
|
|
170
|
+
});
|
|
171
|
+
for (const { slideDir, slideName } of slides) {
|
|
172
|
+
const packageJsonPath = join(slideDir, "package.json");
|
|
173
|
+
const slideKey = slideDir;
|
|
174
|
+
if (runningServers.has(slideKey)) {
|
|
175
|
+
console.log(`⏭️ ${slideName} dev server already running, skipping...`);
|
|
176
|
+
devServers.push(runningServers.get(slideKey));
|
|
133
177
|
continue;
|
|
134
178
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
devProcess
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const serverInfo = {
|
|
185
|
-
name: slideName,
|
|
186
|
-
port: currentPort,
|
|
187
|
-
process: devProcess
|
|
188
|
-
};
|
|
189
|
-
devServers.push(serverInfo);
|
|
190
|
-
runningServers.set(slideKey, serverInfo);
|
|
191
|
-
currentPort++;
|
|
192
|
-
} catch (error) {
|
|
193
|
-
console.error(`❌ Failed to start dev server for ${slideName}:`, error);
|
|
194
|
-
}
|
|
179
|
+
if (!existsSync(packageJsonPath)) {
|
|
180
|
+
console.warn(`⚠️ Skipping ${slideName}: no package.json found`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const nodeModulesPath = join(slideDir, "node_modules");
|
|
184
|
+
if (!existsSync(nodeModulesPath)) {
|
|
185
|
+
console.warn(`⚠️ Skipping ${slideName}: dependencies not installed (run pnpm install)`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
console.log(`📦 Starting Slidev dev server for ${slideName} on port ${currentPort}...`);
|
|
189
|
+
try {
|
|
190
|
+
const devProcess = spawn("pnpm", [
|
|
191
|
+
"run",
|
|
192
|
+
"dev",
|
|
193
|
+
"--port",
|
|
194
|
+
currentPort.toString(),
|
|
195
|
+
"--open",
|
|
196
|
+
"false"
|
|
197
|
+
], {
|
|
198
|
+
cwd: slideDir,
|
|
199
|
+
stdio: [
|
|
200
|
+
"pipe",
|
|
201
|
+
"pipe",
|
|
202
|
+
"pipe"
|
|
203
|
+
],
|
|
204
|
+
detached: false,
|
|
205
|
+
env: {
|
|
206
|
+
...process.env,
|
|
207
|
+
PATH: process.env.PATH
|
|
208
|
+
},
|
|
209
|
+
shell: true
|
|
210
|
+
});
|
|
211
|
+
devProcess.stdout?.on("data", (data) => {
|
|
212
|
+
const output = data.toString();
|
|
213
|
+
if (output.includes("Local:") || output.includes("ready")) console.log(`✅ ${slideName} dev server ready on port ${currentPort}`);
|
|
214
|
+
});
|
|
215
|
+
devProcess.stderr?.on("data", (data) => {
|
|
216
|
+
console.error(`❌ ${slideName} dev server error:`, data.toString());
|
|
217
|
+
});
|
|
218
|
+
const serverInfo = {
|
|
219
|
+
name: slideName,
|
|
220
|
+
port: currentPort,
|
|
221
|
+
process: devProcess
|
|
222
|
+
};
|
|
223
|
+
devServers.push(serverInfo);
|
|
224
|
+
runningServers.set(slideKey, serverInfo);
|
|
225
|
+
currentPort++;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error(`❌ Failed to start dev server for ${slideName}:`, error);
|
|
195
228
|
}
|
|
196
229
|
}
|
|
197
230
|
return devServers;
|
|
@@ -233,8 +266,9 @@ async function transformIndexHtml(html) {
|
|
|
233
266
|
|
|
234
267
|
//#endregion
|
|
235
268
|
//#region src/vite/plugin-slides.ts
|
|
236
|
-
function slidesPlugin() {
|
|
269
|
+
function slidesPlugin(options = {}) {
|
|
237
270
|
let devServers = [];
|
|
271
|
+
const devServerBasePort = options.devServerBasePort ?? 3001;
|
|
238
272
|
return {
|
|
239
273
|
name: "vite-plugin-slides",
|
|
240
274
|
async transformIndexHtml(html) {
|
|
@@ -244,23 +278,23 @@ function slidesPlugin() {
|
|
|
244
278
|
try {
|
|
245
279
|
const config = loadConfig();
|
|
246
280
|
const slidesDirs = resolveSlidesDirs(config);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
281
|
+
const slides = collectSlides({
|
|
282
|
+
slidesDirs,
|
|
283
|
+
exclude: config.exclude
|
|
284
|
+
});
|
|
285
|
+
for (const { slideDir } of slides) {
|
|
286
|
+
const slideDistPath = join(slideDir, "dist");
|
|
287
|
+
const assetsPath = join(slideDistPath, "assets");
|
|
288
|
+
if (!existsSync(assetsPath)) continue;
|
|
289
|
+
const assetFiles = readdirSync(assetsPath);
|
|
290
|
+
const ogImageFile = assetFiles.find((file) => /^og-image-[a-zA-Z0-9]+\.png$/.test(file));
|
|
291
|
+
if (ogImageFile) {
|
|
292
|
+
const sourceFile = join(assetsPath, ogImageFile);
|
|
293
|
+
const destFile = join(slideDistPath, "og-image.png");
|
|
294
|
+
try {
|
|
295
|
+
cpSync(sourceFile, destFile, { force: true });
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.warn(`⚠ Failed to copy og-image for ${slideDir}:`, error);
|
|
264
298
|
}
|
|
265
299
|
}
|
|
266
300
|
}
|
|
@@ -273,7 +307,7 @@ function slidesPlugin() {
|
|
|
273
307
|
const config = loadConfig();
|
|
274
308
|
const slidesDirs = resolveSlidesDirs(config);
|
|
275
309
|
try {
|
|
276
|
-
devServers = await startAllSlidesDevServer();
|
|
310
|
+
devServers = await startAllSlidesDevServer({ basePort: devServerBasePort });
|
|
277
311
|
} catch (error) {
|
|
278
312
|
console.error("❌ Failed to start slides dev servers:", error);
|
|
279
313
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slidev-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A workspace tool for managing multiple Slidev presentations with API-based content management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"@vueuse/core": "^13.5.0",
|
|
41
41
|
"class-variance-authority": "^0.7.1",
|
|
42
42
|
"clsx": "^2.1.1",
|
|
43
|
+
"commander": "^14.0.0",
|
|
43
44
|
"lucide-vue-next": "^0.525.0",
|
|
44
45
|
"tailwind-merge": "^3.3.1",
|
|
45
46
|
"tw-animate-css": "^1.3.5",
|
|
@@ -89,6 +89,10 @@ export function resolveImageUrl(slide: SlideInfo, domain: string): string {
|
|
|
89
89
|
export function useSlides() {
|
|
90
90
|
const slidesData = ref<SlideInfo[]>([]);
|
|
91
91
|
const isLoading = ref(true);
|
|
92
|
+
const devServerBasePort =
|
|
93
|
+
typeof __SLIDEV_WORKSPACE_DEV_PORT_BASE__ === "number"
|
|
94
|
+
? __SLIDEV_WORKSPACE_DEV_PORT_BASE__
|
|
95
|
+
: 3001;
|
|
92
96
|
|
|
93
97
|
// Dynamically import slidev:content to avoid build-time issues
|
|
94
98
|
const loadSlidesData = async () => {
|
|
@@ -110,8 +114,8 @@ export function useSlides() {
|
|
|
110
114
|
if (!slidesData.value || slidesData.value.length === 0) return [];
|
|
111
115
|
|
|
112
116
|
return slidesData.value.map((slide, index) => {
|
|
113
|
-
// Generate port based on slide index:
|
|
114
|
-
const port =
|
|
117
|
+
// Generate port based on slide index: base, base + 1, base + 2...
|
|
118
|
+
const port = devServerBasePort + index;
|
|
115
119
|
// Create dev server URL
|
|
116
120
|
const devServerUrl = `http://localhost:${port}`;
|
|
117
121
|
const domain = IS_DEVELOPMENT ? devServerUrl : window.location.origin;
|