slidev-workspace 0.5.1 → 0.6.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 CHANGED
@@ -2,12 +2,12 @@
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { existsSync, mkdirSync, readdirSync } from "node:fs";
5
- import { cp } from "node:fs/promises";
5
+ import { cp, rm } from "node:fs/promises";
6
6
  import { execSync } from "node:child_process";
7
7
  import { build, createServer } from "vite";
8
8
  import vue from "@vitejs/plugin-vue";
9
9
  import tailwindcss from "@tailwindcss/vite";
10
- import { existsSync as existsSync$1, readFileSync, readdirSync as readdirSync$1, watch } from "fs";
10
+ import { cpSync, existsSync as existsSync$1, readFileSync, readdirSync as readdirSync$1, watch } from "fs";
11
11
  import { basename, join as join$1, resolve as resolve$1 } from "path";
12
12
  import { parse } from "yaml";
13
13
  import { spawn } from "child_process";
@@ -219,6 +219,34 @@ function slidesPlugin() {
219
219
  let devServers = [];
220
220
  return {
221
221
  name: "vite-plugin-slides",
222
+ async closeBundle() {
223
+ try {
224
+ const config = loadConfig();
225
+ const slidesDirs = resolveSlidesDirs(config);
226
+ for (const slidesDir of slidesDirs) {
227
+ if (!existsSync$1(slidesDir)) continue;
228
+ const slideDirs = readdirSync$1(slidesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).filter((dirent) => !(config.exclude || []).includes(dirent.name)).map((dirent) => dirent.name);
229
+ for (const slideDir of slideDirs) {
230
+ const slideDistPath = join$1(slidesDir, slideDir, "dist");
231
+ const assetsPath = join$1(slideDistPath, "assets");
232
+ if (!existsSync$1(assetsPath)) continue;
233
+ const assetFiles = readdirSync$1(assetsPath);
234
+ const ogImageFile = assetFiles.find((file) => /^og-image-[a-zA-Z0-9]+\.png$/.test(file));
235
+ if (ogImageFile) {
236
+ const sourceFile = join$1(assetsPath, ogImageFile);
237
+ const destFile = join$1(slideDistPath, "og-image.png");
238
+ try {
239
+ cpSync(sourceFile, destFile, { force: true });
240
+ } catch (error) {
241
+ console.warn(`⚠ Failed to copy og-image for ${slideDir}:`, error);
242
+ }
243
+ }
244
+ }
245
+ }
246
+ } catch (error) {
247
+ console.warn("⚠ og-image post-build error:", error);
248
+ }
249
+ },
222
250
  async configureServer(server) {
223
251
  const watchers = [];
224
252
  const config = loadConfig();
@@ -337,6 +365,46 @@ async function buildAllSlides() {
337
365
  }
338
366
  }
339
367
  }
368
+ async function exportOgImages() {
369
+ const workspaceCwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
370
+ const config = loadConfig(workspaceCwd);
371
+ const slidesDirs = resolveSlidesDirs(config, workspaceCwd);
372
+ console.log("🖼️ Exporting OG images for all slides...");
373
+ try {
374
+ execSync("pnpm -r export --format png --range 1", {
375
+ cwd: workspaceCwd,
376
+ stdio: "inherit"
377
+ });
378
+ console.log("📦 Copying exported images to og-image.png...");
379
+ for (const slidesDir of slidesDirs) {
380
+ if (!existsSync(slidesDir)) {
381
+ console.warn(`⚠️ Slides directory not found: ${slidesDir}`);
382
+ continue;
383
+ }
384
+ const slides = readdirSync(slidesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
385
+ for (const slideName of slides) {
386
+ const slideDir = join(slidesDir, slideName);
387
+ const packageJsonPath = join(slideDir, "package.json");
388
+ if (!existsSync(packageJsonPath)) continue;
389
+ const exportedFile = join(slideDir, "slides-export", "1.png");
390
+ const targetFile = join(slideDir, "og-image.png");
391
+ const exportDir = join(slideDir, "slides-export");
392
+ if (existsSync(exportedFile)) {
393
+ await cp(exportedFile, targetFile);
394
+ console.log(`✅ Generated OG image for: ${slideName}`);
395
+ await rm(exportDir, {
396
+ recursive: true,
397
+ force: true
398
+ });
399
+ } else console.warn(`⚠️ Export file not found for ${slideName}: ${exportedFile}`);
400
+ }
401
+ }
402
+ console.log("✅ All OG images exported successfully!");
403
+ } catch (error) {
404
+ console.error("❌ Failed to export OG images:", error);
405
+ process.exit(1);
406
+ }
407
+ }
340
408
  async function copyToGhPages() {
341
409
  const workspaceCwd = process.env.SLIDEV_WORKSPACE_CWD || process.cwd();
342
410
  const config = loadConfig(workspaceCwd);
@@ -396,13 +464,15 @@ Usage:
396
464
  slidev-workspace <command> [options]
397
465
 
398
466
  Commands:
399
- dev Start the development server
400
- build Build the project for production
401
- help Show this help message
467
+ dev Start the development server
468
+ build Build the project for production
469
+ export-og Export OG images for all slides
470
+ help Show this help message
402
471
 
403
472
  Examples:
404
473
  slidev-workspace dev # Start development server
405
474
  slidev-workspace build # Build all slides and preview app
475
+ slidev-workspace export-og # Export OG images for all slides
406
476
 
407
477
  Configuration:
408
478
  Use slidev-workspace.yml to set baseUrl for all builds
@@ -421,6 +491,10 @@ async function main() {
421
491
  process.env.SLIDEV_WORKSPACE_CWD = process.cwd();
422
492
  await runViteBuild();
423
493
  break;
494
+ case "export-og":
495
+ process.env.SLIDEV_WORKSPACE_CWD = process.cwd();
496
+ await exportOgImages();
497
+ break;
424
498
  case "help":
425
499
  case "--help":
426
500
  case "-h":
package/dist/index.js CHANGED
@@ -55,18 +55,21 @@ function isUrl(str) {
55
55
  * returns: "http://localhost:3001/og-image.png"
56
56
  *
57
57
  * Example (production mode with og-image.png):
58
- * returns: "https://my-slides.com/slidev-workspace-starter/og-image.png"
58
+ * returns: "https://my-slides.com/slidev-workspace-starter/og-image.png?v=<hash>"
59
59
  */
60
60
  function resolveImageUrl(slide, domain) {
61
61
  const { hasOgImage, path: slidePath, baseUrl, frontmatter } = slide;
62
62
  const seoOgImage = frontmatter.seoMeta?.ogImage;
63
63
  const background = frontmatter.background;
64
- if (hasOgImage) try {
65
- const path = IS_DEVELOPMENT ? "/og-image.png" : pathJoin(baseUrl, "og-image.png");
66
- return new URL(path, domain).href;
67
- } catch (error) {
68
- console.error("Failed to resolve og-image.png path:", error);
69
- return "https://cover.sli.dev";
64
+ if (hasOgImage) {
65
+ const imagePath = `og-image.png?v=${Date.now()}`;
66
+ try {
67
+ const path = IS_DEVELOPMENT ? pathJoin(slidePath, imagePath) : pathJoin(baseUrl, slidePath, imagePath);
68
+ return new URL(path, domain).href;
69
+ } catch (error) {
70
+ console.error("Failed to resolve og-image.png path:", error);
71
+ return "https://cover.sli.dev";
72
+ }
70
73
  }
71
74
  if (seoOgImage) {
72
75
  if (isUrl(seoOgImage)) return seoOgImage;
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, readdirSync, watch } from "fs";
1
+ 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";
@@ -210,6 +210,34 @@ function slidesPlugin() {
210
210
  let devServers = [];
211
211
  return {
212
212
  name: "vite-plugin-slides",
213
+ async closeBundle() {
214
+ try {
215
+ const config = loadConfig();
216
+ const slidesDirs = resolveSlidesDirs(config);
217
+ for (const slidesDir of slidesDirs) {
218
+ if (!existsSync(slidesDir)) continue;
219
+ const slideDirs = readdirSync(slidesDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).filter((dirent) => !(config.exclude || []).includes(dirent.name)).map((dirent) => dirent.name);
220
+ for (const slideDir of slideDirs) {
221
+ const slideDistPath = join(slidesDir, slideDir, "dist");
222
+ const assetsPath = join(slideDistPath, "assets");
223
+ if (!existsSync(assetsPath)) continue;
224
+ const assetFiles = readdirSync(assetsPath);
225
+ const ogImageFile = assetFiles.find((file) => /^og-image-[a-zA-Z0-9]+\.png$/.test(file));
226
+ if (ogImageFile) {
227
+ const sourceFile = join(assetsPath, ogImageFile);
228
+ const destFile = join(slideDistPath, "og-image.png");
229
+ try {
230
+ cpSync(sourceFile, destFile, { force: true });
231
+ } catch (error) {
232
+ console.warn(`⚠ Failed to copy og-image for ${slideDir}:`, error);
233
+ }
234
+ }
235
+ }
236
+ }
237
+ } catch (error) {
238
+ console.warn("⚠ og-image post-build error:", error);
239
+ }
240
+ },
213
241
  async configureServer(server) {
214
242
  const watchers = [];
215
243
  const config = loadConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slidev-workspace",
3
- "version": "0.5.1",
3
+ "version": "0.6.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",
@@ -28,7 +28,7 @@ function isUrl(str: string | undefined): boolean {
28
28
  * returns: "http://localhost:3001/og-image.png"
29
29
  *
30
30
  * Example (production mode with og-image.png):
31
- * returns: "https://my-slides.com/slidev-workspace-starter/og-image.png"
31
+ * returns: "https://my-slides.com/slidev-workspace-starter/og-image.png?v=<hash>"
32
32
  */
33
33
  export function resolveImageUrl(slide: SlideInfo, domain: string): string {
34
34
  const { hasOgImage, path: slidePath, baseUrl, frontmatter } = slide;
@@ -37,10 +37,12 @@ export function resolveImageUrl(slide: SlideInfo, domain: string): string {
37
37
 
38
38
  // Priority 1: og-image.png (if exists)
39
39
  if (hasOgImage) {
40
+ const imagePath = `og-image.png?v=${Date.now()}`;
40
41
  try {
41
42
  const path = IS_DEVELOPMENT
42
- ? "/og-image.png"
43
- : pathJoin(baseUrl, "og-image.png");
43
+ ? pathJoin(slidePath, imagePath)
44
+ : pathJoin(baseUrl, slidePath, imagePath);
45
+
44
46
  return new URL(path, domain).href;
45
47
  } catch (error) {
46
48
  console.error("Failed to resolve og-image.png path:", error);