create-supaslidev 0.1.3 → 0.2.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/README.md CHANGED
@@ -43,6 +43,7 @@ pnpm create supaslidev --name my-slides --presentation intro-deck
43
43
  | `--install` / `--no-install` | Run pnpm install | `true` |
44
44
 
45
45
  The wizard creates a pnpm workspace with:
46
+
46
47
  - A `presentations/` directory for your decks
47
48
  - A `packages/shared/` directory with reusable components, layouts, and styles (configured as a Slidev addon)
48
49
  - Shared dependency management via pnpm catalog
package/dist/cli.js CHANGED
@@ -10,8 +10,86 @@ import ejs from "ejs";
10
10
  import pc from "picocolors";
11
11
  import { tmpdir } from "node:os";
12
12
 
13
+ //#region src/version.ts
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
16
+ const CLI_VERSION = pkg.version;
17
+ const PACKAGE_NAME = "@supaslidev/cli";
18
+ const CACHE_DIR = join(tmpdir(), "supaslidev-cli");
19
+ const CACHE_FILE = join(CACHE_DIR, "version-cache.json");
20
+ const CACHE_TTL_MS = 1440 * 60 * 1e3;
21
+ function compareVersions(current, latest) {
22
+ const parseVersion = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
23
+ const currentParts = parseVersion(current);
24
+ const latestParts = parseVersion(latest);
25
+ for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
26
+ const curr = currentParts[i] ?? 0;
27
+ const lat = latestParts[i] ?? 0;
28
+ if (lat > curr) return true;
29
+ if (lat < curr) return false;
30
+ }
31
+ return false;
32
+ }
33
+ function readCache() {
34
+ try {
35
+ if (!existsSync(CACHE_FILE)) return null;
36
+ const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
37
+ if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
38
+ return data;
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ function writeCache(latestVersion) {
44
+ try {
45
+ if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
46
+ const cache = {
47
+ latestVersion,
48
+ checkedAt: Date.now()
49
+ };
50
+ writeFileSync(CACHE_FILE, JSON.stringify(cache));
51
+ } catch {}
52
+ }
53
+ async function fetchLatestPackageVersion(packageName) {
54
+ try {
55
+ const controller = new AbortController();
56
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
57
+ const response = await fetch(`https://registry.npmjs.org/${packageName}`, { signal: controller.signal });
58
+ clearTimeout(timeoutId);
59
+ if (!response.ok) return null;
60
+ return (await response.json())["dist-tags"].latest;
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ async function fetchLatestVersion() {
66
+ const version = await fetchLatestPackageVersion(PACKAGE_NAME);
67
+ if (version) writeCache(version);
68
+ return version;
69
+ }
70
+ function getCachedLatestVersion() {
71
+ return readCache()?.latestVersion ?? null;
72
+ }
73
+ async function checkForUpdates() {
74
+ const latestVersion = await fetchLatestVersion();
75
+ return {
76
+ currentVersion: CLI_VERSION,
77
+ latestVersion,
78
+ updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
79
+ };
80
+ }
81
+ function checkForUpdatesCached() {
82
+ const latestVersion = getCachedLatestVersion();
83
+ return {
84
+ currentVersion: CLI_VERSION,
85
+ latestVersion,
86
+ updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
87
+ };
88
+ }
89
+
90
+ //#endregion
13
91
  //#region src/create.ts
14
- const CLI_VERSION$1 = "0.1.0";
92
+ const SUPASLIDEV_FALLBACK_VERSION = "0.1.4";
15
93
  function createSafeSpinner() {
16
94
  if (process.stdout.isTTY && process.stdin.isTTY) {
17
95
  const spinner = p.spinner();
@@ -80,11 +158,7 @@ async function renderTemplatesRecursively(sourceDir, targetDir, data) {
80
158
  }
81
159
  }
82
160
  function createDirectoryStructure(targetDir) {
83
- for (const dir of [
84
- "presentations",
85
- "packages",
86
- "scripts"
87
- ]) {
161
+ for (const dir of ["presentations", "packages"]) {
88
162
  const fullPath = join(targetDir, dir);
89
163
  mkdirSync(fullPath, { recursive: true });
90
164
  trackPath(fullPath);
@@ -143,90 +217,7 @@ Add your content here
143
217
  [Slidev Documentation](https://sli.dev/)
144
218
  `;
145
219
  writeFileSync(join(presentationDir, "slides.md"), slidesContent, "utf-8");
146
- writeFileSync(join(presentationDir, ".gitignore"), "node_modules\ndist\n.slidev\n", "utf-8");
147
- writeFileSync(join(presentationDir, ".npmrc"), "shamefully-hoist=true\n", "utf-8");
148
- }
149
- function createScripts(targetDir) {
150
- writeFileSync(join(join(targetDir, "scripts"), "dev-presentation.mjs"), `#!/usr/bin/env node
151
-
152
- import { existsSync, readdirSync, statSync } from 'node:fs';
153
- import { join, dirname } from 'node:path';
154
- import { fileURLToPath } from 'node:url';
155
- import { spawn } from 'node:child_process';
156
-
157
- const __dirname = dirname(fileURLToPath(import.meta.url));
158
- const rootDir = join(__dirname, '..');
159
- const presentationsDir = join(rootDir, 'presentations');
160
-
161
- function getPresentations() {
162
- if (!existsSync(presentationsDir)) {
163
- return [];
164
- }
165
-
166
- return readdirSync(presentationsDir)
167
- .filter((name) => {
168
- const fullPath = join(presentationsDir, name);
169
- return statSync(fullPath).isDirectory() && existsSync(join(fullPath, 'slides.md'));
170
- })
171
- .sort();
172
- }
173
-
174
- function printUsage(presentations) {
175
- console.error('Usage: pnpm dev <presentation-name>');
176
- console.error('\\nAvailable presentations:');
177
-
178
- if (presentations.length === 0) {
179
- console.error(' No presentations found');
180
- } else {
181
- presentations.forEach((name) => {
182
- console.error(\` \${name}\`);
183
- });
184
- }
185
- }
186
-
187
- function runDev(name) {
188
- const packageName = \`@supaslidev/\${name}\`;
189
-
190
- console.log(\`\\nStarting dev server for \${name}...\\n\`);
191
-
192
- const pnpm = spawn('pnpm', ['--filter', packageName, 'dev'], {
193
- cwd: rootDir,
194
- stdio: 'inherit',
195
- shell: true,
196
- });
197
-
198
- pnpm.on('error', (err) => {
199
- console.error(\`Failed to start dev server: \${err.message}\`);
200
- process.exit(1);
201
- });
202
-
203
- pnpm.on('close', (code) => {
204
- process.exit(code ?? 0);
205
- });
206
- }
207
-
208
- function main() {
209
- const args = process.argv.slice(2);
210
- const name = args[0];
211
- const presentations = getPresentations();
212
-
213
- if (!name) {
214
- console.error('Error: Presentation name is required');
215
- printUsage(presentations);
216
- process.exit(1);
217
- }
218
-
219
- if (!presentations.includes(name)) {
220
- console.error(\`Error: Presentation "\${name}" not found\`);
221
- printUsage(presentations);
222
- process.exit(1);
223
- }
224
-
225
- runDev(name);
226
- }
227
-
228
- main();
229
- `, "utf-8");
220
+ writeFileSync(join(presentationDir, ".gitignore"), "node_modules\n.DS_Store\ndist\n*.local\n.vite-inspect\n.remote-assets\ncomponents.d.ts\n", "utf-8");
230
221
  }
231
222
  function createSharedPackage(targetDir) {
232
223
  const sharedDir = join(targetDir, "packages", "shared");
@@ -248,49 +239,99 @@ function createSharedPackage(targetDir) {
248
239
  keywords: ["slidev-addon", "slidev"],
249
240
  dependencies: { vue: "catalog:" }
250
241
  }, null, 2) + "\n", "utf-8");
251
- writeFileSync(join(sharedDir, "components", "SharedBadge.vue"), `<template>
252
- <span class="shared-badge">
253
- <slot />
254
- </span>
242
+ writeFileSync(join(sharedDir, "components", "SharedBadge.vue"), `<script setup lang="ts">
243
+ defineProps<{
244
+ text?: string;
245
+ }>();
246
+ <\/script>
247
+
248
+ <template>
249
+ <span class="shared-badge">{{ text ?? 'Shared' }}</span>
255
250
  </template>
256
251
 
257
252
  <style scoped>
258
253
  .shared-badge {
259
254
  display: inline-block;
260
- padding: 0.25rem 0.5rem;
261
- border-radius: 0.25rem;
262
- background-color: var(--slidev-theme-primary, #3b82f6);
263
- color: white;
264
- font-size: 0.875rem;
265
- font-weight: 500;
255
+ padding: 0.25rem 0.75rem;
256
+ font-size: 0.75rem;
257
+ font-weight: 600;
258
+ line-height: 1;
259
+ text-transform: uppercase;
260
+ letter-spacing: 0.05em;
261
+ color: #fff;
262
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
263
+ border-radius: 9999px;
266
264
  }
267
265
  </style>
268
266
  `, "utf-8");
269
267
  writeFileSync(join(sharedDir, "README.md"), `# @supaslidev/shared
270
268
 
271
- Shared components, layouts, and styles for your Slidev presentations.
269
+ A local Slidev addon for sharing components, layouts, and styles across all presentations in your workspace.
270
+
271
+ ## How It Works
272
+
273
+ This package follows the [Slidev addon pattern](https://sli.dev/guide/write-addon). Slidev automatically discovers and imports resources from the following directories:
274
+
275
+ - **components/** - Vue components available in all slides
276
+ - **layouts/** - Custom slide layouts
277
+ - **styles/** - Shared CSS/SCSS styles
278
+
279
+ ## Using This Addon
280
+
281
+ Add the addon to your presentation's frontmatter:
282
+
283
+ \`\`\`yaml
284
+ ---
285
+ addons:
286
+ - '@supaslidev/shared'
287
+ ---
288
+ \`\`\`
289
+
290
+ ## Example: Using SharedBadge
272
291
 
273
- ## Usage
292
+ The \`SharedBadge\` component is available globally once the addon is configured:
274
293
 
275
- This package is configured as a Slidev addon. Components in the \`components\` directory are automatically available in all presentations that include this addon.
294
+ \`\`\`md
295
+ ---
296
+ addons:
297
+ - '@supaslidev/shared'
298
+ ---
299
+
300
+ # My Slide
301
+
302
+ <SharedBadge text="New" />
303
+ \`\`\`
276
304
 
277
- ## Structure
305
+ ## Directory Structure
306
+
307
+ \`\`\`
308
+ shared/
309
+ ├── components/ # Vue components (auto-imported)
310
+ │ └── SharedBadge.vue
311
+ ├── layouts/ # Custom layouts
312
+ ├── styles/ # Shared styles
313
+ ├── package.json
314
+ └── README.md
315
+ \`\`\`
316
+
317
+ ## Adding New Components
318
+
319
+ Create a \`.vue\` file in \`components/\`:
320
+
321
+ \`\`\`vue
322
+ <script setup lang="ts">
323
+ defineProps<{
324
+ label: string;
325
+ }>();
326
+ <\/script>
327
+
328
+ <template>
329
+ <div class="my-component">{{ label }}</div>
330
+ </template>
331
+ \`\`\`
278
332
 
279
- - \`components/\` - Shared Vue components
280
- - \`layouts/\` - Custom slide layouts
281
- - \`styles/\` - Global styles
333
+ The component is immediately available in all presentations using this addon.
282
334
  `, "utf-8");
283
- writeFileSync(join(sharedDir, "tsconfig.json"), JSON.stringify({
284
- compilerOptions: {
285
- target: "ESNext",
286
- module: "ESNext",
287
- moduleResolution: "bundler",
288
- strict: true,
289
- jsx: "preserve",
290
- skipLibCheck: true
291
- },
292
- include: ["**/*.ts", "**/*.vue"]
293
- }, null, 2) + "\n", "utf-8");
294
335
  }
295
336
  async function create(options = {}) {
296
337
  const spinner = createSafeSpinner();
@@ -379,20 +420,20 @@ async function create(options = {}) {
379
420
  trackPath(targetDir);
380
421
  spinner.start("Creating workspace structure...");
381
422
  createDirectoryStructure(targetDir);
423
+ const supaslidevVersion = `^${await fetchLatestPackageVersion("supaslidev") ?? SUPASLIDEV_FALLBACK_VERSION}`;
382
424
  const templateData = {
383
425
  projectName,
384
426
  presentationName,
385
427
  description: `${projectName} - Slidev presentations monorepo`,
386
- cliVersion: CLI_VERSION$1,
387
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
428
+ cliVersion: CLI_VERSION,
429
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
430
+ supaslidevVersion
388
431
  };
389
432
  await renderWorkspaceTemplates(targetDir, options.template ?? "default", templateData);
390
433
  spinner.message("Creating presentation...");
391
434
  await createPresentation(targetDir, presentationName);
392
435
  spinner.message("Creating shared package...");
393
436
  createSharedPackage(targetDir);
394
- spinner.message("Creating scripts...");
395
- createScripts(targetDir);
396
437
  spinner.stop("Workspace structure created");
397
438
  if (initGit) {
398
439
  spinner.start("Initializing git repository...");
@@ -548,79 +589,6 @@ function getMigrationOrder(manifest) {
548
589
  return order;
549
590
  }
550
591
 
551
- //#endregion
552
- //#region src/version.ts
553
- const CLI_VERSION = "0.1.0";
554
- const PACKAGE_NAME = "@supaslidev/cli";
555
- const CACHE_DIR = join(tmpdir(), "supaslidev-cli");
556
- const CACHE_FILE = join(CACHE_DIR, "version-cache.json");
557
- const CACHE_TTL_MS = 1440 * 60 * 1e3;
558
- function compareVersions(current, latest) {
559
- const parseVersion = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
560
- const currentParts = parseVersion(current);
561
- const latestParts = parseVersion(latest);
562
- for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
563
- const curr = currentParts[i] ?? 0;
564
- const lat = latestParts[i] ?? 0;
565
- if (lat > curr) return true;
566
- if (lat < curr) return false;
567
- }
568
- return false;
569
- }
570
- function readCache() {
571
- try {
572
- if (!existsSync(CACHE_FILE)) return null;
573
- const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
574
- if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
575
- return data;
576
- } catch {
577
- return null;
578
- }
579
- }
580
- function writeCache(latestVersion) {
581
- try {
582
- if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
583
- const cache = {
584
- latestVersion,
585
- checkedAt: Date.now()
586
- };
587
- writeFileSync(CACHE_FILE, JSON.stringify(cache));
588
- } catch {}
589
- }
590
- async function fetchLatestVersion() {
591
- try {
592
- const controller = new AbortController();
593
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
594
- const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`, { signal: controller.signal });
595
- clearTimeout(timeoutId);
596
- if (!response.ok) return null;
597
- const version = (await response.json())["dist-tags"].latest;
598
- writeCache(version);
599
- return version;
600
- } catch {
601
- return null;
602
- }
603
- }
604
- function getCachedLatestVersion() {
605
- return readCache()?.latestVersion ?? null;
606
- }
607
- async function checkForUpdates() {
608
- const latestVersion = await fetchLatestVersion();
609
- return {
610
- currentVersion: CLI_VERSION,
611
- latestVersion,
612
- updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
613
- };
614
- }
615
- function checkForUpdatesCached() {
616
- const latestVersion = getCachedLatestVersion();
617
- return {
618
- currentVersion: CLI_VERSION,
619
- latestVersion,
620
- updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
621
- };
622
- }
623
-
624
592
  //#endregion
625
593
  //#region src/commands/status.ts
626
594
  function getPendingMigrationsCount(workspaceDir) {
@@ -1252,7 +1220,7 @@ function printUpdateNotification(latestVersion) {
1252
1220
  //#endregion
1253
1221
  //#region src/cli.ts
1254
1222
  const program = new Command();
1255
- program.name("create-supaslidev").description("CLI tool for scaffolding Supaslidev presentations").version("0.1.0");
1223
+ program.name("create-supaslidev").description("CLI tool for scaffolding Supaslidev presentations").version(CLI_VERSION);
1256
1224
  program.command("create", { isDefault: true }).description("Create a new Supaslidev workspace").option("-n, --name <name>", "Name of the workspace").option("-p, --presentation <name>", "Name of the first presentation").option("-t, --template <template>", "Template to use", "default").option("--git", "Initialize a git repository").option("--no-git", "Skip git initialization").option("--install", "Run pnpm install after scaffolding").option("--no-install", "Skip pnpm install").action(async (options) => {
1257
1225
  await create(options);
1258
1226
  });
package/dist/index.js CHANGED
@@ -10,8 +10,86 @@ import { tmpdir } from "node:os";
10
10
  import { parse, parseDocument, stringify } from "yaml";
11
11
  import { IndentationText, Node, Project, SyntaxKind } from "ts-morph";
12
12
 
13
+ //#region src/version.ts
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
16
+ const CLI_VERSION = pkg.version;
17
+ const PACKAGE_NAME = "@supaslidev/cli";
18
+ const CACHE_DIR = join(tmpdir(), "supaslidev-cli");
19
+ const CACHE_FILE = join(CACHE_DIR, "version-cache.json");
20
+ const CACHE_TTL_MS = 1440 * 60 * 1e3;
21
+ function compareVersions(current, latest) {
22
+ const parseVersion = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
23
+ const currentParts = parseVersion(current);
24
+ const latestParts = parseVersion(latest);
25
+ for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
26
+ const curr = currentParts[i] ?? 0;
27
+ const lat = latestParts[i] ?? 0;
28
+ if (lat > curr) return true;
29
+ if (lat < curr) return false;
30
+ }
31
+ return false;
32
+ }
33
+ function readCache() {
34
+ try {
35
+ if (!existsSync(CACHE_FILE)) return null;
36
+ const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
37
+ if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
38
+ return data;
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ function writeCache(latestVersion) {
44
+ try {
45
+ if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
46
+ const cache = {
47
+ latestVersion,
48
+ checkedAt: Date.now()
49
+ };
50
+ writeFileSync(CACHE_FILE, JSON.stringify(cache));
51
+ } catch {}
52
+ }
53
+ async function fetchLatestPackageVersion(packageName) {
54
+ try {
55
+ const controller = new AbortController();
56
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
57
+ const response = await fetch(`https://registry.npmjs.org/${packageName}`, { signal: controller.signal });
58
+ clearTimeout(timeoutId);
59
+ if (!response.ok) return null;
60
+ return (await response.json())["dist-tags"].latest;
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ async function fetchLatestVersion() {
66
+ const version = await fetchLatestPackageVersion(PACKAGE_NAME);
67
+ if (version) writeCache(version);
68
+ return version;
69
+ }
70
+ function getCachedLatestVersion() {
71
+ return readCache()?.latestVersion ?? null;
72
+ }
73
+ async function checkForUpdates() {
74
+ const latestVersion = await fetchLatestVersion();
75
+ return {
76
+ currentVersion: CLI_VERSION,
77
+ latestVersion,
78
+ updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
79
+ };
80
+ }
81
+ function checkForUpdatesCached() {
82
+ const latestVersion = getCachedLatestVersion();
83
+ return {
84
+ currentVersion: CLI_VERSION,
85
+ latestVersion,
86
+ updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
87
+ };
88
+ }
89
+
90
+ //#endregion
13
91
  //#region src/create.ts
14
- const CLI_VERSION$2 = "0.1.0";
92
+ const SUPASLIDEV_FALLBACK_VERSION = "0.1.4";
15
93
  function createSafeSpinner() {
16
94
  if (process.stdout.isTTY && process.stdin.isTTY) {
17
95
  const spinner = p.spinner();
@@ -80,11 +158,7 @@ async function renderTemplatesRecursively(sourceDir, targetDir, data) {
80
158
  }
81
159
  }
82
160
  function createDirectoryStructure(targetDir) {
83
- for (const dir of [
84
- "presentations",
85
- "packages",
86
- "scripts"
87
- ]) {
161
+ for (const dir of ["presentations", "packages"]) {
88
162
  const fullPath = join(targetDir, dir);
89
163
  mkdirSync(fullPath, { recursive: true });
90
164
  trackPath(fullPath);
@@ -143,90 +217,7 @@ Add your content here
143
217
  [Slidev Documentation](https://sli.dev/)
144
218
  `;
145
219
  writeFileSync(join(presentationDir, "slides.md"), slidesContent, "utf-8");
146
- writeFileSync(join(presentationDir, ".gitignore"), "node_modules\ndist\n.slidev\n", "utf-8");
147
- writeFileSync(join(presentationDir, ".npmrc"), "shamefully-hoist=true\n", "utf-8");
148
- }
149
- function createScripts(targetDir) {
150
- writeFileSync(join(join(targetDir, "scripts"), "dev-presentation.mjs"), `#!/usr/bin/env node
151
-
152
- import { existsSync, readdirSync, statSync } from 'node:fs';
153
- import { join, dirname } from 'node:path';
154
- import { fileURLToPath } from 'node:url';
155
- import { spawn } from 'node:child_process';
156
-
157
- const __dirname = dirname(fileURLToPath(import.meta.url));
158
- const rootDir = join(__dirname, '..');
159
- const presentationsDir = join(rootDir, 'presentations');
160
-
161
- function getPresentations() {
162
- if (!existsSync(presentationsDir)) {
163
- return [];
164
- }
165
-
166
- return readdirSync(presentationsDir)
167
- .filter((name) => {
168
- const fullPath = join(presentationsDir, name);
169
- return statSync(fullPath).isDirectory() && existsSync(join(fullPath, 'slides.md'));
170
- })
171
- .sort();
172
- }
173
-
174
- function printUsage(presentations) {
175
- console.error('Usage: pnpm dev <presentation-name>');
176
- console.error('\\nAvailable presentations:');
177
-
178
- if (presentations.length === 0) {
179
- console.error(' No presentations found');
180
- } else {
181
- presentations.forEach((name) => {
182
- console.error(\` \${name}\`);
183
- });
184
- }
185
- }
186
-
187
- function runDev(name) {
188
- const packageName = \`@supaslidev/\${name}\`;
189
-
190
- console.log(\`\\nStarting dev server for \${name}...\\n\`);
191
-
192
- const pnpm = spawn('pnpm', ['--filter', packageName, 'dev'], {
193
- cwd: rootDir,
194
- stdio: 'inherit',
195
- shell: true,
196
- });
197
-
198
- pnpm.on('error', (err) => {
199
- console.error(\`Failed to start dev server: \${err.message}\`);
200
- process.exit(1);
201
- });
202
-
203
- pnpm.on('close', (code) => {
204
- process.exit(code ?? 0);
205
- });
206
- }
207
-
208
- function main() {
209
- const args = process.argv.slice(2);
210
- const name = args[0];
211
- const presentations = getPresentations();
212
-
213
- if (!name) {
214
- console.error('Error: Presentation name is required');
215
- printUsage(presentations);
216
- process.exit(1);
217
- }
218
-
219
- if (!presentations.includes(name)) {
220
- console.error(\`Error: Presentation "\${name}" not found\`);
221
- printUsage(presentations);
222
- process.exit(1);
223
- }
224
-
225
- runDev(name);
226
- }
227
-
228
- main();
229
- `, "utf-8");
220
+ writeFileSync(join(presentationDir, ".gitignore"), "node_modules\n.DS_Store\ndist\n*.local\n.vite-inspect\n.remote-assets\ncomponents.d.ts\n", "utf-8");
230
221
  }
231
222
  function createSharedPackage(targetDir) {
232
223
  const sharedDir = join(targetDir, "packages", "shared");
@@ -248,49 +239,99 @@ function createSharedPackage(targetDir) {
248
239
  keywords: ["slidev-addon", "slidev"],
249
240
  dependencies: { vue: "catalog:" }
250
241
  }, null, 2) + "\n", "utf-8");
251
- writeFileSync(join(sharedDir, "components", "SharedBadge.vue"), `<template>
252
- <span class="shared-badge">
253
- <slot />
254
- </span>
242
+ writeFileSync(join(sharedDir, "components", "SharedBadge.vue"), `<script setup lang="ts">
243
+ defineProps<{
244
+ text?: string;
245
+ }>();
246
+ <\/script>
247
+
248
+ <template>
249
+ <span class="shared-badge">{{ text ?? 'Shared' }}</span>
255
250
  </template>
256
251
 
257
252
  <style scoped>
258
253
  .shared-badge {
259
254
  display: inline-block;
260
- padding: 0.25rem 0.5rem;
261
- border-radius: 0.25rem;
262
- background-color: var(--slidev-theme-primary, #3b82f6);
263
- color: white;
264
- font-size: 0.875rem;
265
- font-weight: 500;
255
+ padding: 0.25rem 0.75rem;
256
+ font-size: 0.75rem;
257
+ font-weight: 600;
258
+ line-height: 1;
259
+ text-transform: uppercase;
260
+ letter-spacing: 0.05em;
261
+ color: #fff;
262
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
263
+ border-radius: 9999px;
266
264
  }
267
265
  </style>
268
266
  `, "utf-8");
269
267
  writeFileSync(join(sharedDir, "README.md"), `# @supaslidev/shared
270
268
 
271
- Shared components, layouts, and styles for your Slidev presentations.
269
+ A local Slidev addon for sharing components, layouts, and styles across all presentations in your workspace.
272
270
 
273
- ## Usage
271
+ ## How It Works
274
272
 
275
- This package is configured as a Slidev addon. Components in the \`components\` directory are automatically available in all presentations that include this addon.
273
+ This package follows the [Slidev addon pattern](https://sli.dev/guide/write-addon). Slidev automatically discovers and imports resources from the following directories:
276
274
 
277
- ## Structure
275
+ - **components/** - Vue components available in all slides
276
+ - **layouts/** - Custom slide layouts
277
+ - **styles/** - Shared CSS/SCSS styles
278
278
 
279
- - \`components/\` - Shared Vue components
280
- - \`layouts/\` - Custom slide layouts
281
- - \`styles/\` - Global styles
279
+ ## Using This Addon
280
+
281
+ Add the addon to your presentation's frontmatter:
282
+
283
+ \`\`\`yaml
284
+ ---
285
+ addons:
286
+ - '@supaslidev/shared'
287
+ ---
288
+ \`\`\`
289
+
290
+ ## Example: Using SharedBadge
291
+
292
+ The \`SharedBadge\` component is available globally once the addon is configured:
293
+
294
+ \`\`\`md
295
+ ---
296
+ addons:
297
+ - '@supaslidev/shared'
298
+ ---
299
+
300
+ # My Slide
301
+
302
+ <SharedBadge text="New" />
303
+ \`\`\`
304
+
305
+ ## Directory Structure
306
+
307
+ \`\`\`
308
+ shared/
309
+ ├── components/ # Vue components (auto-imported)
310
+ │ └── SharedBadge.vue
311
+ ├── layouts/ # Custom layouts
312
+ ├── styles/ # Shared styles
313
+ ├── package.json
314
+ └── README.md
315
+ \`\`\`
316
+
317
+ ## Adding New Components
318
+
319
+ Create a \`.vue\` file in \`components/\`:
320
+
321
+ \`\`\`vue
322
+ <script setup lang="ts">
323
+ defineProps<{
324
+ label: string;
325
+ }>();
326
+ <\/script>
327
+
328
+ <template>
329
+ <div class="my-component">{{ label }}</div>
330
+ </template>
331
+ \`\`\`
332
+
333
+ The component is immediately available in all presentations using this addon.
282
334
  `, "utf-8");
283
- writeFileSync(join(sharedDir, "tsconfig.json"), JSON.stringify({
284
- compilerOptions: {
285
- target: "ESNext",
286
- module: "ESNext",
287
- moduleResolution: "bundler",
288
- strict: true,
289
- jsx: "preserve",
290
- skipLibCheck: true
291
- },
292
- include: ["**/*.ts", "**/*.vue"]
293
- }, null, 2) + "\n", "utf-8");
294
335
  }
295
336
  async function create(options = {}) {
296
337
  const spinner = createSafeSpinner();
@@ -379,20 +420,20 @@ async function create(options = {}) {
379
420
  trackPath(targetDir);
380
421
  spinner.start("Creating workspace structure...");
381
422
  createDirectoryStructure(targetDir);
423
+ const supaslidevVersion = `^${await fetchLatestPackageVersion("supaslidev") ?? SUPASLIDEV_FALLBACK_VERSION}`;
382
424
  const templateData = {
383
425
  projectName,
384
426
  presentationName,
385
427
  description: `${projectName} - Slidev presentations monorepo`,
386
- cliVersion: CLI_VERSION$2,
387
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
428
+ cliVersion: CLI_VERSION,
429
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
430
+ supaslidevVersion
388
431
  };
389
432
  await renderWorkspaceTemplates(targetDir, options.template ?? "default", templateData);
390
433
  spinner.message("Creating presentation...");
391
434
  await createPresentation(targetDir, presentationName);
392
435
  spinner.message("Creating shared package...");
393
436
  createSharedPackage(targetDir);
394
- spinner.message("Creating scripts...");
395
- createScripts(targetDir);
396
437
  spinner.stop("Workspace structure created");
397
438
  if (initGit) {
398
439
  spinner.start("Initializing git repository...");
@@ -437,7 +478,6 @@ async function create(options = {}) {
437
478
 
438
479
  //#endregion
439
480
  //#region src/state.ts
440
- const CLI_VERSION$1 = "0.1.0";
441
481
  const STATE_DIR = ".supaslidev";
442
482
  const STATE_FILE = "state.json";
443
483
  function getStatePath(workspaceDir) {
@@ -449,7 +489,7 @@ function getStateDir(workspaceDir) {
449
489
  function createInitialState() {
450
490
  const now = (/* @__PURE__ */ new Date()).toISOString();
451
491
  return {
452
- cliVersion: CLI_VERSION$1,
492
+ cliVersion: CLI_VERSION,
453
493
  createdAt: now,
454
494
  lastUpdatedAt: now,
455
495
  appliedMigrations: []
@@ -501,7 +541,7 @@ function hasMigration(workspaceDir, migrationId) {
501
541
  function updateCliVersion(workspaceDir) {
502
542
  const state = readState(workspaceDir);
503
543
  if (!state) throw new Error("State file not found. Is this a Supaslidev workspace?");
504
- state.cliVersion = CLI_VERSION$1;
544
+ state.cliVersion = CLI_VERSION;
505
545
  writeState(workspaceDir, state);
506
546
  }
507
547
  function findWorkspaceRoot(startDir = process.cwd()) {
@@ -594,79 +634,6 @@ function getMigrationOrder(manifest) {
594
634
  return order;
595
635
  }
596
636
 
597
- //#endregion
598
- //#region src/version.ts
599
- const CLI_VERSION = "0.1.0";
600
- const PACKAGE_NAME = "@supaslidev/cli";
601
- const CACHE_DIR = join(tmpdir(), "supaslidev-cli");
602
- const CACHE_FILE = join(CACHE_DIR, "version-cache.json");
603
- const CACHE_TTL_MS = 1440 * 60 * 1e3;
604
- function compareVersions(current, latest) {
605
- const parseVersion = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
606
- const currentParts = parseVersion(current);
607
- const latestParts = parseVersion(latest);
608
- for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
609
- const curr = currentParts[i] ?? 0;
610
- const lat = latestParts[i] ?? 0;
611
- if (lat > curr) return true;
612
- if (lat < curr) return false;
613
- }
614
- return false;
615
- }
616
- function readCache() {
617
- try {
618
- if (!existsSync(CACHE_FILE)) return null;
619
- const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
620
- if (Date.now() - data.checkedAt > CACHE_TTL_MS) return null;
621
- return data;
622
- } catch {
623
- return null;
624
- }
625
- }
626
- function writeCache(latestVersion) {
627
- try {
628
- if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
629
- const cache = {
630
- latestVersion,
631
- checkedAt: Date.now()
632
- };
633
- writeFileSync(CACHE_FILE, JSON.stringify(cache));
634
- } catch {}
635
- }
636
- async function fetchLatestVersion() {
637
- try {
638
- const controller = new AbortController();
639
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
640
- const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`, { signal: controller.signal });
641
- clearTimeout(timeoutId);
642
- if (!response.ok) return null;
643
- const version = (await response.json())["dist-tags"].latest;
644
- writeCache(version);
645
- return version;
646
- } catch {
647
- return null;
648
- }
649
- }
650
- function getCachedLatestVersion() {
651
- return readCache()?.latestVersion ?? null;
652
- }
653
- async function checkForUpdates() {
654
- const latestVersion = await fetchLatestVersion();
655
- return {
656
- currentVersion: CLI_VERSION,
657
- latestVersion,
658
- updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
659
- };
660
- }
661
- function checkForUpdatesCached() {
662
- const latestVersion = getCachedLatestVersion();
663
- return {
664
- currentVersion: CLI_VERSION,
665
- latestVersion,
666
- updateAvailable: latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false
667
- };
668
- }
669
-
670
637
  //#endregion
671
638
  //#region src/commands/status.ts
672
639
  function getPendingMigrationsCount(workspaceDir) {
@@ -1346,7 +1313,7 @@ function printUpdateNotification(latestVersion) {
1346
1313
  //#endregion
1347
1314
  //#region src/cli.ts
1348
1315
  const program = new Command();
1349
- program.name("create-supaslidev").description("CLI tool for scaffolding Supaslidev presentations").version("0.1.0");
1316
+ program.name("create-supaslidev").description("CLI tool for scaffolding Supaslidev presentations").version(CLI_VERSION);
1350
1317
  program.command("create", { isDefault: true }).description("Create a new Supaslidev workspace").option("-n, --name <name>", "Name of the workspace").option("-p, --presentation <name>", "Name of the first presentation").option("-t, --template <template>", "Template to use", "default").option("--git", "Initialize a git repository").option("--no-git", "Skip git initialization").option("--install", "Run pnpm install after scaffolding").option("--no-install", "Skip pnpm install").action(async (options) => {
1351
1318
  await create(options);
1352
1319
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-supaslidev",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "CLI tool for scaffolding Supaslidev presentations",
5
5
  "keywords": [
6
6
  "slidev",
@@ -50,7 +50,7 @@
50
50
  "tsdown": "^0.12.5",
51
51
  "tsx": "^4.19.0",
52
52
  "typescript": "^5.3.3",
53
- "vitest": "^3.0.0"
53
+ "vitest": "^4.0.0"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsdown",
@@ -4,4 +4,3 @@ deploy
4
4
  .DS_Store
5
5
  *.log
6
6
  .slidev
7
- .turbo
@@ -0,0 +1,3 @@
1
+ import { defineSupaslidevConfig } from 'supaslidev';
2
+
3
+ export default defineNuxtConfig(defineSupaslidevConfig({}));
@@ -9,10 +9,13 @@
9
9
  "new": "supaslidev new",
10
10
  "present": "supaslidev present",
11
11
  "export": "supaslidev export",
12
- "deploy": "supaslidev deploy"
12
+ "deploy": "supaslidev deploy",
13
+ "build": "pnpm --filter @supaslidev/* run build"
13
14
  },
14
15
  "devDependencies": {
15
- "supaslidev": "^0.1.4"
16
+ "nuxt": "catalog:core",
17
+ "playwright-chromium": "catalog:",
18
+ "supaslidev": "<%= supaslidevVersion %>"
16
19
  },
17
20
  "engines": {
18
21
  "node": ">=18.0.0"
@@ -8,6 +8,27 @@ catalog:
8
8
  '@slidev/theme-seriph': latest
9
9
  '@slidev/theme-apple-basic': latest
10
10
  vue: ^3.5.26
11
- '@vue/compiler-sfc': ^3.5.27
12
- typescript: ^5.3.3
13
- vue-tsc: ^2.0.0
11
+ playwright-chromium: ^1.58.2
12
+
13
+ catalogs:
14
+ core:
15
+ nuxt: ^4.4.2
16
+ '@vue/compiler-sfc': ^3.5.27
17
+ typescript: ^5.3.3
18
+ vue-tsc: ^2.0.0
19
+
20
+ catalogMode: prefer
21
+
22
+ linkWorkspacePackages: true
23
+
24
+ shellEmulator: true
25
+
26
+ trustPolicy: no-downgrade
27
+
28
+ trustPolicyExclude:
29
+ - axios
30
+ - chokidar
31
+ - semver
32
+ - undici
33
+ - undici-types
34
+ - vite
@@ -1 +0,0 @@
1
- shamefully-hoist=true
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "jsx": "preserve",
8
- "resolveJsonModule": true,
9
- "isolatedModules": true,
10
- "esModuleInterop": true,
11
- "lib": ["ESNext", "DOM"],
12
- "skipLibCheck": true,
13
- "noEmit": true
14
- },
15
- "include": ["packages/**/*.ts", "packages/**/*.vue"],
16
- "exclude": ["node_modules", "**/dist"]
17
- }
@@ -1,24 +0,0 @@
1
- {
2
- "$schema": "https://turbo.build/schema.json",
3
- "tasks": {
4
- "typecheck": {
5
- "dependsOn": ["^typecheck"],
6
- "inputs": ["**/*.ts", "**/*.tsx", "**/*.vue", "tsconfig.json"],
7
- "outputs": []
8
- },
9
- "lint": {
10
- "dependsOn": ["^lint"],
11
- "inputs": ["**/*.ts", "**/*.tsx", "**/*.vue", "**/*.js", "**/*.mjs"],
12
- "outputs": []
13
- },
14
- "build": {
15
- "dependsOn": ["^build"],
16
- "inputs": ["src/**", "**/*.ts", "**/*.vue", "package.json"],
17
- "outputs": ["dist/**"]
18
- },
19
- "dev": {
20
- "cache": false,
21
- "persistent": true
22
- }
23
- }
24
- }