slidev-workspace 0.7.1 → 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 +227 -185
- 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,61 +370,64 @@ 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
373
|
const packageRoot = join(__dirname, "..");
|
|
343
|
-
|
|
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) {
|
|
344
381
|
const workspaceCwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
|
|
345
382
|
const config = loadConfig(workspaceCwd);
|
|
383
|
+
const devServerBasePort = previewPort + 1;
|
|
346
384
|
return {
|
|
347
385
|
root: resolve(packageRoot, "src/preview"),
|
|
348
386
|
base: config.baseUrl,
|
|
349
387
|
plugins: [
|
|
350
388
|
vue(),
|
|
351
389
|
tailwindcss(),
|
|
352
|
-
slidesPlugin()
|
|
390
|
+
slidesPlugin({ devServerBasePort })
|
|
353
391
|
],
|
|
354
392
|
resolve: { alias: { "@": resolve(packageRoot, "src/preview") } },
|
|
393
|
+
define: { __SLIDEV_WORKSPACE_DEV_PORT_BASE__: devServerBasePort },
|
|
355
394
|
build: { outDir: resolve(workspaceCwd, config.outputDir) },
|
|
356
395
|
server: {
|
|
357
|
-
port:
|
|
396
|
+
port: previewPort,
|
|
358
397
|
open: true
|
|
359
398
|
}
|
|
360
399
|
};
|
|
361
400
|
}
|
|
362
|
-
async function
|
|
401
|
+
async function buildSlides(names) {
|
|
363
402
|
const workspaceCwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
|
|
364
403
|
const config = loadConfig(workspaceCwd);
|
|
365
404
|
const slidesDirs = resolveSlidesDirs(config, workspaceCwd);
|
|
366
|
-
console.log("🔨 Building all slides...");
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
405
|
+
console.log(names ? `🔨 Building slides: ${names.join(", ")}...` : "🔨 Building all slides...");
|
|
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`);
|
|
370
415
|
continue;
|
|
371
416
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
execSync(buildCmd, {
|
|
387
|
-
cwd: workspaceCwd,
|
|
388
|
-
stdio: "inherit"
|
|
389
|
-
});
|
|
390
|
-
console.log(`✅ Built slide: ${slideName}`);
|
|
391
|
-
} catch (error) {
|
|
392
|
-
console.error(`❌ Failed to build slide ${slideName}:`, error);
|
|
393
|
-
process.exit(1);
|
|
394
|
-
}
|
|
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);
|
|
395
431
|
}
|
|
396
432
|
}
|
|
397
433
|
}
|
|
@@ -406,28 +442,24 @@ async function exportOgImages() {
|
|
|
406
442
|
stdio: "inherit"
|
|
407
443
|
});
|
|
408
444
|
console.log("📦 Copying exported images to og-image.png...");
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
force: true
|
|
428
|
-
});
|
|
429
|
-
} else console.warn(`⚠️ Export file not found for ${slideName}: ${exportedFile}`);
|
|
430
|
-
}
|
|
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}`);
|
|
431
463
|
}
|
|
432
464
|
console.log("✅ All OG images exported successfully!");
|
|
433
465
|
} catch (error) {
|
|
@@ -435,44 +467,45 @@ async function exportOgImages() {
|
|
|
435
467
|
process.exit(1);
|
|
436
468
|
}
|
|
437
469
|
}
|
|
438
|
-
async function copySlidesToOutputDir() {
|
|
470
|
+
async function copySlidesToOutputDir(names) {
|
|
439
471
|
const workspaceCwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
|
|
440
472
|
const config = loadConfig(workspaceCwd);
|
|
441
473
|
const slidesDirs = resolveSlidesDirs(config, workspaceCwd);
|
|
442
474
|
const deployDir = resolve(workspaceCwd, config.outputDir);
|
|
443
475
|
console.log(`📁 Copying slide builds into ${deployDir}...`);
|
|
444
476
|
if (!existsSync(deployDir)) mkdirSync(deployDir, { recursive: true });
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
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 });
|
|
455
488
|
}
|
|
456
489
|
}
|
|
457
490
|
console.log(`✅ All slide assets copied into ${deployDir}!`);
|
|
458
491
|
}
|
|
459
|
-
async function runViteBuild() {
|
|
492
|
+
async function runViteBuild(names) {
|
|
460
493
|
try {
|
|
461
|
-
await
|
|
494
|
+
await buildSlides(names);
|
|
462
495
|
console.log("📦 Building Slidev Workspace for production...");
|
|
463
496
|
const config = createViteConfig();
|
|
464
497
|
await build(config);
|
|
465
|
-
await copySlidesToOutputDir();
|
|
498
|
+
await copySlidesToOutputDir(names);
|
|
466
499
|
console.log("✅ Build completed successfully!");
|
|
467
500
|
} catch (error) {
|
|
468
501
|
console.error("❌ Build failed:", error);
|
|
469
502
|
process.exit(1);
|
|
470
503
|
}
|
|
471
504
|
}
|
|
472
|
-
async function runVitePreview() {
|
|
505
|
+
async function runVitePreview(previewPort) {
|
|
473
506
|
try {
|
|
474
507
|
console.log("🚀 Starting Slidev Workspace development server...");
|
|
475
|
-
const config = createViteConfig();
|
|
508
|
+
const config = createViteConfig(previewPort);
|
|
476
509
|
const server = await createServer(config);
|
|
477
510
|
await server.listen();
|
|
478
511
|
server.printUrls();
|
|
@@ -489,14 +522,19 @@ Usage:
|
|
|
489
522
|
slidev-workspace <command> [options]
|
|
490
523
|
|
|
491
524
|
Commands:
|
|
492
|
-
dev
|
|
493
|
-
build
|
|
494
|
-
|
|
495
|
-
|
|
525
|
+
dev Start the development server
|
|
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)
|
|
528
|
+
export-og Export OG images for all slides
|
|
529
|
+
help Show this help message
|
|
530
|
+
Options:
|
|
531
|
+
--port, -p <number> Set the preview server port (dev/preview only)
|
|
496
532
|
|
|
497
533
|
Examples:
|
|
498
534
|
slidev-workspace dev # Start development server
|
|
535
|
+
slidev-workspace dev --port 3030 # Start dev server on custom port
|
|
499
536
|
slidev-workspace build # Build all slides and preview app
|
|
537
|
+
slidev-workspace build slide1,slide2 # Build only specific slides by name
|
|
500
538
|
slidev-workspace export-og # Export OG images for all slides
|
|
501
539
|
|
|
502
540
|
Configuration:
|
|
@@ -505,33 +543,37 @@ Configuration:
|
|
|
505
543
|
For more information, visit: https://github.com/author/slidev-workspace
|
|
506
544
|
`);
|
|
507
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
|
+
}
|
|
508
554
|
async function main() {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
else {
|
|
530
|
-
console.error(`Unknown command: ${command}`);
|
|
531
|
-
console.error("Run \"slidev-workspace help\" for available commands.");
|
|
532
|
-
process.exit(1);
|
|
533
|
-
}
|
|
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;
|
|
534
575
|
}
|
|
576
|
+
await program.parseAsync(process.argv);
|
|
535
577
|
}
|
|
536
578
|
main().catch((error) => {
|
|
537
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;
|