create-supaslidev 0.1.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/index.js ADDED
@@ -0,0 +1,1675 @@
1
+ import { Command } from "commander";
2
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
3
+ import { basename, dirname, join, relative } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { spawn } from "node:child_process";
6
+ import * as p from "@clack/prompts";
7
+ import ejs from "ejs";
8
+ import pc from "picocolors";
9
+ import { tmpdir } from "node:os";
10
+ import { parse, parseDocument, stringify } from "yaml";
11
+ import { IndentationText, Node, Project, SyntaxKind } from "ts-morph";
12
+
13
+ //#region src/create.ts
14
+ const CLI_VERSION$2 = "0.1.0";
15
+ function createSafeSpinner() {
16
+ if (process.stdout.isTTY && process.stdin.isTTY) {
17
+ const spinner = p.spinner();
18
+ return {
19
+ start: (msg) => spinner.start(msg),
20
+ stop: (msg) => spinner.stop(msg),
21
+ message: (msg) => spinner.message(msg)
22
+ };
23
+ }
24
+ return {
25
+ start: () => {},
26
+ stop: () => {},
27
+ message: () => {}
28
+ };
29
+ }
30
+ const templatesDir = join(dirname(fileURLToPath(import.meta.url)), "..", "templates");
31
+ const createdPaths = [];
32
+ function trackPath(path) {
33
+ createdPaths.push(path);
34
+ }
35
+ function cleanup() {
36
+ for (const path of createdPaths.reverse()) try {
37
+ if (existsSync(path)) rmSync(path, {
38
+ recursive: true,
39
+ force: true
40
+ });
41
+ } catch {}
42
+ }
43
+ async function renderTemplate(templatePath, data) {
44
+ return ejs.renderFile(templatePath, data);
45
+ }
46
+ function getOutputFileName(templateFileName) {
47
+ const name = templateFileName.replace(".ejs", "");
48
+ if (name === "gitignore") return ".gitignore";
49
+ if (name === "npmrc") return ".npmrc";
50
+ return name;
51
+ }
52
+ function runCommand(command, args, cwd) {
53
+ return new Promise((resolve, reject) => {
54
+ const child = spawn(command, args, {
55
+ cwd,
56
+ stdio: "inherit",
57
+ shell: true
58
+ });
59
+ child.on("error", reject);
60
+ child.on("close", (code) => {
61
+ if (code === 0) resolve();
62
+ else reject(/* @__PURE__ */ new Error(`Command "${command} ${args.join(" ")}" exited with code ${code}`));
63
+ });
64
+ });
65
+ }
66
+ async function renderWorkspaceTemplates(targetDir, templateName, data) {
67
+ const templateDir = join(templatesDir, templateName);
68
+ if (!existsSync(templateDir)) throw new Error(`Template "${templateName}" not found`);
69
+ await renderTemplatesRecursively(templateDir, targetDir, data);
70
+ }
71
+ async function renderTemplatesRecursively(sourceDir, targetDir, data) {
72
+ const entries = readdirSync(sourceDir);
73
+ for (const entry of entries) {
74
+ const sourcePath = join(sourceDir, entry);
75
+ if (statSync(sourcePath).isDirectory()) {
76
+ const subTargetDir = join(targetDir, entry);
77
+ mkdirSync(subTargetDir, { recursive: true });
78
+ await renderTemplatesRecursively(sourcePath, subTargetDir, data);
79
+ } else if (entry.endsWith(".ejs")) writeFileSync(join(targetDir, getOutputFileName(entry)), await renderTemplate(sourcePath, data), "utf-8");
80
+ }
81
+ }
82
+ function createDirectoryStructure(targetDir) {
83
+ for (const dir of [
84
+ "presentations",
85
+ "packages",
86
+ "scripts"
87
+ ]) {
88
+ const fullPath = join(targetDir, dir);
89
+ mkdirSync(fullPath, { recursive: true });
90
+ trackPath(fullPath);
91
+ }
92
+ }
93
+ async function createPresentation(targetDir, presentationName) {
94
+ const presentationDir = join(targetDir, "presentations", presentationName);
95
+ mkdirSync(presentationDir, { recursive: true });
96
+ trackPath(presentationDir);
97
+ const packageJson = {
98
+ name: `@supaslidev/${presentationName}`,
99
+ private: true,
100
+ type: "module",
101
+ scripts: {
102
+ build: "slidev build",
103
+ dev: "slidev --open",
104
+ export: "slidev export"
105
+ },
106
+ dependencies: {
107
+ "@slidev/cli": "catalog:",
108
+ "@slidev/theme-default": "catalog:",
109
+ "@slidev/theme-seriph": "catalog:",
110
+ "@slidev/theme-apple-basic": "catalog:",
111
+ "@supaslidev/shared": "workspace:*",
112
+ vue: "catalog:"
113
+ },
114
+ devDependencies: {}
115
+ };
116
+ writeFileSync(join(presentationDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
117
+ const slidesContent = `---
118
+ theme: default
119
+ title: ${presentationName}
120
+ addons:
121
+ - '@supaslidev/shared'
122
+ info: |
123
+ A new Slidev presentation
124
+ class: text-center
125
+ transition: slide-left
126
+ mdc: true
127
+ ---
128
+
129
+ # ${presentationName}
130
+
131
+ Welcome to your new presentation
132
+
133
+ ---
134
+
135
+ # Slide 2
136
+
137
+ Add your content here
138
+
139
+ ---
140
+
141
+ # Learn More
142
+
143
+ [Slidev Documentation](https://sli.dev/)
144
+ `;
145
+ 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");
230
+ }
231
+ function createSharedPackage(targetDir) {
232
+ const sharedDir = join(targetDir, "packages", "shared");
233
+ mkdirSync(sharedDir, { recursive: true });
234
+ trackPath(sharedDir);
235
+ for (const subdir of [
236
+ "components",
237
+ "layouts",
238
+ "styles"
239
+ ]) {
240
+ const fullPath = join(sharedDir, subdir);
241
+ mkdirSync(fullPath, { recursive: true });
242
+ trackPath(fullPath);
243
+ }
244
+ writeFileSync(join(sharedDir, "package.json"), JSON.stringify({
245
+ name: "@supaslidev/shared",
246
+ private: true,
247
+ type: "module",
248
+ keywords: ["slidev-addon", "slidev"],
249
+ dependencies: { vue: "catalog:" }
250
+ }, null, 2) + "\n", "utf-8");
251
+ writeFileSync(join(sharedDir, "components", "SharedBadge.vue"), `<template>
252
+ <span class="shared-badge">
253
+ <slot />
254
+ </span>
255
+ </template>
256
+
257
+ <style scoped>
258
+ .shared-badge {
259
+ 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;
266
+ }
267
+ </style>
268
+ `, "utf-8");
269
+ writeFileSync(join(sharedDir, "README.md"), `# @supaslidev/shared
270
+
271
+ Shared components, layouts, and styles for your Slidev presentations.
272
+
273
+ ## Usage
274
+
275
+ This package is configured as a Slidev addon. Components in the \`components\` directory are automatically available in all presentations that include this addon.
276
+
277
+ ## Structure
278
+
279
+ - \`components/\` - Shared Vue components
280
+ - \`layouts/\` - Custom slide layouts
281
+ - \`styles/\` - Global styles
282
+ `, "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
+ }
295
+ async function create(options = {}) {
296
+ const spinner = createSafeSpinner();
297
+ try {
298
+ let projectName;
299
+ let presentationName;
300
+ let initGit;
301
+ let runInstall;
302
+ if (options.name !== void 0 || options.presentation !== void 0) {
303
+ projectName = options.name ?? "my-presentations";
304
+ presentationName = options.presentation ?? "my-first-deck";
305
+ initGit = options.git ?? true;
306
+ runInstall = options.install ?? true;
307
+ if (!/^[a-z0-9-]+$/.test(projectName)) {
308
+ p.log.error("Project name must be lowercase alphanumeric with hyphens only");
309
+ process.exit(1);
310
+ }
311
+ if (projectName.startsWith("-") || projectName.endsWith("-")) {
312
+ p.log.error("Project name cannot start or end with a hyphen");
313
+ process.exit(1);
314
+ }
315
+ if (!/^[a-z0-9-]+$/.test(presentationName)) {
316
+ p.log.error("Presentation name must be lowercase alphanumeric with hyphens only");
317
+ process.exit(1);
318
+ }
319
+ if (presentationName.startsWith("-") || presentationName.endsWith("-")) {
320
+ p.log.error("Presentation name cannot start or end with a hyphen");
321
+ process.exit(1);
322
+ }
323
+ } else {
324
+ p.intro(pc.cyan("Create a new Supaslidev workspace"));
325
+ const projectNameResult = await p.text({
326
+ message: "What is your project name?",
327
+ placeholder: "my-presentations",
328
+ validate: (value) => {
329
+ if (!value.trim()) return "Project name is required";
330
+ if (!/^[a-z0-9-]+$/.test(value)) return "Project name must be lowercase alphanumeric with hyphens only";
331
+ if (value.startsWith("-") || value.endsWith("-")) return "Project name cannot start or end with a hyphen";
332
+ }
333
+ });
334
+ if (p.isCancel(projectNameResult)) {
335
+ p.cancel("Operation cancelled");
336
+ process.exit(0);
337
+ }
338
+ projectName = projectNameResult;
339
+ const presentationNameResult = await p.text({
340
+ message: "What is the name of your first presentation?",
341
+ placeholder: "my-first-deck",
342
+ initialValue: "my-first-deck",
343
+ validate: (value) => {
344
+ if (!value.trim()) return "Presentation name is required";
345
+ if (!/^[a-z0-9-]+$/.test(value)) return "Presentation name must be lowercase alphanumeric with hyphens only";
346
+ if (value.startsWith("-") || value.endsWith("-")) return "Presentation name cannot start or end with a hyphen";
347
+ }
348
+ });
349
+ if (p.isCancel(presentationNameResult)) {
350
+ p.cancel("Operation cancelled");
351
+ process.exit(0);
352
+ }
353
+ presentationName = presentationNameResult;
354
+ const initGitResult = await p.confirm({
355
+ message: "Initialize a git repository?",
356
+ initialValue: true
357
+ });
358
+ if (p.isCancel(initGitResult)) {
359
+ p.cancel("Operation cancelled");
360
+ process.exit(0);
361
+ }
362
+ initGit = initGitResult;
363
+ const runInstallResult = await p.confirm({
364
+ message: "Run pnpm install after scaffolding?",
365
+ initialValue: true
366
+ });
367
+ if (p.isCancel(runInstallResult)) {
368
+ p.cancel("Operation cancelled");
369
+ process.exit(0);
370
+ }
371
+ runInstall = runInstallResult;
372
+ }
373
+ const targetDir = join(process.cwd(), projectName);
374
+ if (existsSync(targetDir)) {
375
+ p.log.error(`Directory "${projectName}" already exists`);
376
+ process.exit(1);
377
+ }
378
+ mkdirSync(targetDir, { recursive: true });
379
+ trackPath(targetDir);
380
+ spinner.start("Creating workspace structure...");
381
+ createDirectoryStructure(targetDir);
382
+ const templateData = {
383
+ projectName,
384
+ presentationName,
385
+ description: `${projectName} - Slidev presentations monorepo`,
386
+ cliVersion: CLI_VERSION$2,
387
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
388
+ };
389
+ await renderWorkspaceTemplates(targetDir, options.template ?? "default", templateData);
390
+ spinner.message("Creating presentation...");
391
+ await createPresentation(targetDir, presentationName);
392
+ spinner.message("Creating shared package...");
393
+ createSharedPackage(targetDir);
394
+ spinner.message("Creating scripts...");
395
+ createScripts(targetDir);
396
+ spinner.stop("Workspace structure created");
397
+ if (initGit) {
398
+ spinner.start("Initializing git repository...");
399
+ try {
400
+ await runCommand("git", ["init"], targetDir);
401
+ spinner.stop("Git repository initialized");
402
+ } catch {
403
+ spinner.stop("Failed to initialize git repository");
404
+ p.log.warn("Git initialization failed. You can run \"git init\" manually.");
405
+ }
406
+ }
407
+ if (runInstall) {
408
+ spinner.start("Installing dependencies (this may take a while)...");
409
+ try {
410
+ await runCommand("pnpm", ["install"], targetDir);
411
+ spinner.stop("Dependencies installed");
412
+ } catch {
413
+ spinner.stop("Failed to install dependencies");
414
+ p.log.warn("Dependency installation failed. You can run \"pnpm install\" manually.");
415
+ }
416
+ }
417
+ createdPaths.length = 0;
418
+ p.outro(pc.green("Workspace created successfully!"));
419
+ console.log("");
420
+ console.log(pc.cyan("Next steps:"));
421
+ console.log(` ${pc.dim("$")} cd ${projectName}`);
422
+ if (!runInstall) console.log(` ${pc.dim("$")} pnpm install`);
423
+ console.log(` ${pc.dim("$")} pnpm dev ${presentationName}`);
424
+ console.log("");
425
+ } catch (error) {
426
+ spinner.stop("Failed");
427
+ if (createdPaths.length > 0) {
428
+ p.log.warn("Cleaning up partial files...");
429
+ cleanup();
430
+ p.log.info("Cleanup complete");
431
+ }
432
+ const message = error instanceof Error ? error.message : "Unknown error occurred";
433
+ p.log.error(message);
434
+ process.exit(1);
435
+ }
436
+ }
437
+
438
+ //#endregion
439
+ //#region src/state.ts
440
+ const CLI_VERSION$1 = "0.1.0";
441
+ const STATE_DIR = ".supaslidev";
442
+ const STATE_FILE = "state.json";
443
+ function getStatePath(workspaceDir) {
444
+ return join(workspaceDir, STATE_DIR, STATE_FILE);
445
+ }
446
+ function getStateDir(workspaceDir) {
447
+ return join(workspaceDir, STATE_DIR);
448
+ }
449
+ function createInitialState() {
450
+ const now = (/* @__PURE__ */ new Date()).toISOString();
451
+ return {
452
+ cliVersion: CLI_VERSION$1,
453
+ createdAt: now,
454
+ lastUpdatedAt: now,
455
+ appliedMigrations: []
456
+ };
457
+ }
458
+ function readState(workspaceDir) {
459
+ const statePath = getStatePath(workspaceDir);
460
+ if (!existsSync(statePath)) return null;
461
+ try {
462
+ const content = readFileSync(statePath, "utf-8");
463
+ return JSON.parse(content);
464
+ } catch {
465
+ return null;
466
+ }
467
+ }
468
+ function writeState(workspaceDir, state) {
469
+ const stateDir = getStateDir(workspaceDir);
470
+ const statePath = getStatePath(workspaceDir);
471
+ if (!existsSync(stateDir)) mkdirSync(stateDir, { recursive: true });
472
+ const updatedState = {
473
+ ...state,
474
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
475
+ };
476
+ writeFileSync(statePath, JSON.stringify(updatedState, null, 2) + "\n", "utf-8");
477
+ }
478
+ function initializeState(workspaceDir) {
479
+ const state = createInitialState();
480
+ writeState(workspaceDir, state);
481
+ return state;
482
+ }
483
+ function stateExists(workspaceDir) {
484
+ return existsSync(getStatePath(workspaceDir));
485
+ }
486
+ function addMigration(workspaceDir, migrationId) {
487
+ const state = readState(workspaceDir);
488
+ if (!state) throw new Error("State file not found. Is this a Supaslidev workspace?");
489
+ if (state.appliedMigrations.some((m) => m.id === migrationId)) return;
490
+ state.appliedMigrations.push({
491
+ id: migrationId,
492
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString()
493
+ });
494
+ writeState(workspaceDir, state);
495
+ }
496
+ function hasMigration(workspaceDir, migrationId) {
497
+ const state = readState(workspaceDir);
498
+ if (!state) return false;
499
+ return state.appliedMigrations.some((m) => m.id === migrationId);
500
+ }
501
+ function updateCliVersion(workspaceDir) {
502
+ const state = readState(workspaceDir);
503
+ if (!state) throw new Error("State file not found. Is this a Supaslidev workspace?");
504
+ state.cliVersion = CLI_VERSION$1;
505
+ writeState(workspaceDir, state);
506
+ }
507
+ function findWorkspaceRoot(startDir = process.cwd()) {
508
+ let currentDir = startDir;
509
+ while (currentDir !== dirname(currentDir)) {
510
+ if (stateExists(currentDir)) return currentDir;
511
+ currentDir = dirname(currentDir);
512
+ }
513
+ return null;
514
+ }
515
+ function addImportedPresentation(workspaceDir, presentation) {
516
+ const state = readState(workspaceDir);
517
+ if (!state) throw new Error("State file not found. Is this a Supaslidev workspace?");
518
+ if (!state.importedPresentations) state.importedPresentations = [];
519
+ const existingIndex = state.importedPresentations.findIndex((p) => p.name === presentation.name);
520
+ if (existingIndex >= 0) state.importedPresentations[existingIndex] = presentation;
521
+ else state.importedPresentations.push(presentation);
522
+ writeState(workspaceDir, state);
523
+ }
524
+ function getImportedPresentations(workspaceDir) {
525
+ return readState(workspaceDir)?.importedPresentations ?? [];
526
+ }
527
+ function removeImportedPresentation(workspaceDir, name) {
528
+ const state = readState(workspaceDir);
529
+ if (!state) throw new Error("State file not found. Is this a Supaslidev workspace?");
530
+ if (!state.importedPresentations) return;
531
+ state.importedPresentations = state.importedPresentations.filter((p) => p.name !== name);
532
+ writeState(workspaceDir, state);
533
+ }
534
+
535
+ //#endregion
536
+ //#region src/migrations/manifest.ts
537
+ const MIGRATIONS_MANIFEST_FILE = "migrations.json";
538
+ function readManifest(migrationsDir) {
539
+ const manifestPath = join(migrationsDir, MIGRATIONS_MANIFEST_FILE);
540
+ if (!existsSync(manifestPath)) return null;
541
+ const content = readFileSync(manifestPath, "utf-8");
542
+ return JSON.parse(content);
543
+ }
544
+ function writeManifest(migrationsDir, manifest) {
545
+ writeFileSync(join(migrationsDir, MIGRATIONS_MANIFEST_FILE), JSON.stringify(manifest, null, 2) + "\n", "utf-8");
546
+ }
547
+ function createEmptyManifest() {
548
+ return {
549
+ version: "1.0.0",
550
+ migrations: []
551
+ };
552
+ }
553
+ function validateManifest(manifest) {
554
+ const errors = [];
555
+ if (!manifest.version) errors.push("Manifest missing version field");
556
+ if (!Array.isArray(manifest.migrations)) {
557
+ errors.push("Manifest migrations field must be an array");
558
+ return errors;
559
+ }
560
+ const ids = /* @__PURE__ */ new Set();
561
+ for (const migration of manifest.migrations) {
562
+ if (!migration.id) {
563
+ errors.push("Migration entry missing id field");
564
+ continue;
565
+ }
566
+ if (ids.has(migration.id)) errors.push(`Duplicate migration id: ${migration.id}`);
567
+ ids.add(migration.id);
568
+ if (!migration.description) errors.push(`Migration ${migration.id} missing description field`);
569
+ if (!migration.version) errors.push(`Migration ${migration.id} missing version field`);
570
+ if (migration.dependencies) {
571
+ for (const dep of migration.dependencies) if (!ids.has(dep)) {
572
+ if (!manifest.migrations.some((m) => m.id === dep)) errors.push(`Migration ${migration.id} has unknown dependency: ${dep}`);
573
+ }
574
+ }
575
+ }
576
+ return errors;
577
+ }
578
+ function getMigrationOrder(manifest) {
579
+ const visited = /* @__PURE__ */ new Set();
580
+ const order = [];
581
+ const migrationMap = new Map(manifest.migrations.map((m) => [m.id, m]));
582
+ function visit(id, path) {
583
+ if (visited.has(id)) return;
584
+ if (path.has(id)) throw new Error(`Circular dependency detected involving migration: ${id}`);
585
+ const migration = migrationMap.get(id);
586
+ if (!migration) return;
587
+ path.add(id);
588
+ for (const dep of migration.dependencies ?? []) visit(dep, path);
589
+ path.delete(id);
590
+ visited.add(id);
591
+ order.push(id);
592
+ }
593
+ for (const migration of manifest.migrations) visit(migration.id, /* @__PURE__ */ new Set());
594
+ return order;
595
+ }
596
+
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
+ //#endregion
671
+ //#region src/commands/status.ts
672
+ function getPendingMigrationsCount(workspaceDir) {
673
+ const manifest = readManifest(join(workspaceDir, ".supaslidev", "migrations"));
674
+ if (!manifest) return 0;
675
+ return manifest.migrations.filter((m) => !hasMigration(workspaceDir, m.id)).length;
676
+ }
677
+ function getAllPresentations(workspaceDir) {
678
+ const presentationsDir = join(workspaceDir, "presentations");
679
+ if (!existsSync(presentationsDir)) return [];
680
+ return readdirSync(presentationsDir).filter((name) => {
681
+ const fullPath = join(presentationsDir, name);
682
+ return statSync(fullPath).isDirectory() && existsSync(join(fullPath, "slides.md"));
683
+ }).sort();
684
+ }
685
+ async function getStatus(workspaceDir) {
686
+ const resolvedDir = workspaceDir ?? findWorkspaceRoot() ?? process.cwd();
687
+ const state = readState(resolvedDir);
688
+ const latestVersion = await fetchLatestVersion();
689
+ const updateAvailable = latestVersion ? compareVersions(CLI_VERSION, latestVersion) : false;
690
+ const importedPresentations = getImportedPresentations(resolvedDir);
691
+ const importedNames = new Set(importedPresentations.map((p) => p.name));
692
+ const nativePresentations = getAllPresentations(resolvedDir).filter((name) => !importedNames.has(name));
693
+ return {
694
+ cliVersion: CLI_VERSION,
695
+ stateVersion: state?.cliVersion ?? null,
696
+ createdAt: state?.createdAt ?? null,
697
+ lastUpdatedAt: state?.lastUpdatedAt ?? null,
698
+ pendingMigrations: state ? getPendingMigrationsCount(resolvedDir) : 0,
699
+ latestVersion,
700
+ updateAvailable,
701
+ nativePresentations,
702
+ importedPresentations
703
+ };
704
+ }
705
+ function formatDate(isoDate) {
706
+ return new Date(isoDate).toLocaleDateString("en-US", {
707
+ year: "numeric",
708
+ month: "short",
709
+ day: "numeric",
710
+ hour: "2-digit",
711
+ minute: "2-digit"
712
+ });
713
+ }
714
+ function formatStatus(status) {
715
+ const lines = [];
716
+ lines.push(pc.bold("Supaslidev Status"));
717
+ lines.push("─".repeat(40));
718
+ lines.push("");
719
+ lines.push(`${pc.dim("CLI Version:")} ${status.cliVersion}`);
720
+ if (status.stateVersion) lines.push(`${pc.dim("State Version:")} ${status.stateVersion}`);
721
+ else lines.push(`${pc.dim("State Version:")} ${pc.yellow("Not initialized")}`);
722
+ lines.push("");
723
+ if (status.createdAt) lines.push(`${pc.dim("Created:")} ${formatDate(status.createdAt)}`);
724
+ if (status.lastUpdatedAt) lines.push(`${pc.dim("Last Updated:")} ${formatDate(status.lastUpdatedAt)}`);
725
+ lines.push("");
726
+ if (status.pendingMigrations > 0) lines.push(`${pc.dim("Pending Migrations:")} ${pc.yellow(String(status.pendingMigrations))}`);
727
+ else lines.push(`${pc.dim("Pending Migrations:")} ${pc.green("0")}`);
728
+ lines.push("");
729
+ lines.push(pc.bold("Presentations"));
730
+ lines.push("─".repeat(40));
731
+ lines.push("");
732
+ lines.push(pc.dim("Native:"));
733
+ if (status.nativePresentations.length === 0) lines.push(" No native presentations");
734
+ else for (const name of status.nativePresentations) lines.push(` ${name}`);
735
+ lines.push("");
736
+ lines.push(pc.dim("Imported:"));
737
+ if (status.importedPresentations.length === 0) lines.push(" No imported presentations");
738
+ else for (const presentation of status.importedPresentations) {
739
+ lines.push(` ${pc.bold(presentation.name)}`);
740
+ lines.push(` ${pc.dim("Source:")} ${presentation.sourcePath}`);
741
+ lines.push(` ${pc.dim("Imported:")} ${formatDate(presentation.importedAt)}`);
742
+ }
743
+ lines.push("");
744
+ if (status.updateAvailable && status.latestVersion) {
745
+ lines.push(pc.yellow(`Update available: ${status.cliVersion} → ${status.latestVersion}`));
746
+ lines.push(pc.dim(" Run `pnpm add -g @supaslidev/cli` to update"));
747
+ } else if (status.latestVersion) lines.push(pc.green("✓ CLI is up to date"));
748
+ else lines.push(pc.dim("Could not check for updates"));
749
+ return lines.join("\n");
750
+ }
751
+ async function status(workspaceDir) {
752
+ const result = await getStatus(workspaceDir);
753
+ console.log(formatStatus(result));
754
+ }
755
+
756
+ //#endregion
757
+ //#region src/migrations/backup.ts
758
+ const BACKUP_DIR = ".supaslidev/backups";
759
+ const BACKUP_METADATA_FILE = "backup.json";
760
+ const EXCLUDED_PATTERNS = [
761
+ "node_modules",
762
+ ".git",
763
+ ".supaslidev/backups",
764
+ "dist",
765
+ ".turbo",
766
+ ".cache"
767
+ ];
768
+ function getBackupDir(workspaceDir) {
769
+ return join(workspaceDir, BACKUP_DIR);
770
+ }
771
+ function getBackupPath(workspaceDir, backupId) {
772
+ return join(getBackupDir(workspaceDir), backupId);
773
+ }
774
+ function generateBackupId() {
775
+ return `backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("Z", "")}`;
776
+ }
777
+ function shouldExclude(relativePath) {
778
+ return EXCLUDED_PATTERNS.some((pattern) => relativePath === pattern || relativePath.startsWith(`${pattern}/`));
779
+ }
780
+ function collectFiles(dir, baseDir) {
781
+ const files = [];
782
+ const entries = readdirSync(dir, { withFileTypes: true });
783
+ for (const entry of entries) {
784
+ const fullPath = join(dir, entry.name);
785
+ const relativePath = relative(baseDir, fullPath);
786
+ if (shouldExclude(relativePath)) continue;
787
+ if (entry.isDirectory()) files.push(...collectFiles(fullPath, baseDir));
788
+ else if (entry.isFile()) files.push(relativePath);
789
+ }
790
+ return files;
791
+ }
792
+ function createBackup(workspaceDir) {
793
+ const state = readState(workspaceDir);
794
+ if (!state) throw new Error("State file not found. Is this a Supaslidev workspace?");
795
+ const backupId = generateBackupId();
796
+ const backupPath = getBackupPath(workspaceDir, backupId);
797
+ mkdirSync(backupPath, { recursive: true });
798
+ const files = collectFiles(workspaceDir, workspaceDir);
799
+ for (const file of files) {
800
+ const sourcePath = join(workspaceDir, file);
801
+ const destPath = join(backupPath, "files", file);
802
+ const destDir = join(destPath, "..");
803
+ if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
804
+ cpSync(sourcePath, destPath);
805
+ }
806
+ const metadata = {
807
+ backupId,
808
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
809
+ workspaceDir,
810
+ cliVersion: state.cliVersion,
811
+ files
812
+ };
813
+ writeFileSync(join(backupPath, BACKUP_METADATA_FILE), JSON.stringify(metadata, null, 2) + "\n", "utf-8");
814
+ return backupId;
815
+ }
816
+ function restoreBackup(workspaceDir, backupId) {
817
+ const backupPath = getBackupPath(workspaceDir, backupId);
818
+ const metadataPath = join(backupPath, BACKUP_METADATA_FILE);
819
+ if (!existsSync(metadataPath)) throw new Error(`Backup not found: ${backupId}`);
820
+ const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"));
821
+ for (const file of metadata.files) {
822
+ const sourcePath = join(backupPath, "files", file);
823
+ const destPath = join(workspaceDir, file);
824
+ if (existsSync(sourcePath)) {
825
+ const destDir = join(destPath, "..");
826
+ if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
827
+ cpSync(sourcePath, destPath);
828
+ }
829
+ }
830
+ }
831
+ function deleteBackup(workspaceDir, backupId) {
832
+ const backupPath = getBackupPath(workspaceDir, backupId);
833
+ if (existsSync(backupPath)) rmSync(backupPath, {
834
+ recursive: true,
835
+ force: true
836
+ });
837
+ }
838
+ function listBackups(workspaceDir) {
839
+ const backupDir = getBackupDir(workspaceDir);
840
+ if (!existsSync(backupDir)) return [];
841
+ const entries = readdirSync(backupDir, { withFileTypes: true });
842
+ const backups = [];
843
+ for (const entry of entries) {
844
+ if (!entry.isDirectory()) continue;
845
+ const metadataPath = join(backupDir, entry.name, BACKUP_METADATA_FILE);
846
+ if (existsSync(metadataPath)) {
847
+ const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"));
848
+ backups.push(metadata);
849
+ }
850
+ }
851
+ return backups.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
852
+ }
853
+ function getBackupMetadata(workspaceDir, backupId) {
854
+ const metadataPath = join(getBackupPath(workspaceDir, backupId), BACKUP_METADATA_FILE);
855
+ if (!existsSync(metadataPath)) return null;
856
+ return JSON.parse(readFileSync(metadataPath, "utf-8"));
857
+ }
858
+ function backupExists(workspaceDir, backupId) {
859
+ return existsSync(join(getBackupPath(workspaceDir, backupId), BACKUP_METADATA_FILE));
860
+ }
861
+
862
+ //#endregion
863
+ //#region src/migrations/journal.ts
864
+ const JOURNAL_DIR = ".supaslidev";
865
+ const JOURNAL_FILE = "migration-journal.json";
866
+ function getJournalPath(workspaceDir) {
867
+ return join(workspaceDir, JOURNAL_DIR, JOURNAL_FILE);
868
+ }
869
+ function getJournalDir(workspaceDir) {
870
+ return join(workspaceDir, JOURNAL_DIR);
871
+ }
872
+ function ensureJournalDir(workspaceDir) {
873
+ const journalDir = getJournalDir(workspaceDir);
874
+ if (!existsSync(journalDir)) mkdirSync(journalDir, { recursive: true });
875
+ }
876
+ function readJournal(workspaceDir) {
877
+ const journalPath = getJournalPath(workspaceDir);
878
+ if (!existsSync(journalPath)) return { entries: [] };
879
+ const content = readFileSync(journalPath, "utf-8");
880
+ return JSON.parse(content);
881
+ }
882
+ function writeJournal(workspaceDir, journal) {
883
+ ensureJournalDir(workspaceDir);
884
+ writeFileSync(getJournalPath(workspaceDir), JSON.stringify(journal, null, 2) + "\n", "utf-8");
885
+ }
886
+ function addJournalEntry(workspaceDir, entry) {
887
+ const journal = readJournal(workspaceDir);
888
+ journal.entries.push(entry);
889
+ writeJournal(workspaceDir, journal);
890
+ }
891
+ function createJournalEntry(migrationId, backupId, success, rolledBack = false, error) {
892
+ return {
893
+ migrationId,
894
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
895
+ backupId,
896
+ success,
897
+ rolledBack,
898
+ error: error?.message
899
+ };
900
+ }
901
+ function getJournalEntries(workspaceDir) {
902
+ return readJournal(workspaceDir).entries;
903
+ }
904
+ function getLastSuccessfulEntry(workspaceDir) {
905
+ const successful = getJournalEntries(workspaceDir).filter((e) => e.success && !e.rolledBack);
906
+ return successful.length > 0 ? successful[successful.length - 1] : null;
907
+ }
908
+ function getMigrationHistory(workspaceDir, migrationId) {
909
+ return getJournalEntries(workspaceDir).filter((e) => e.migrationId === migrationId);
910
+ }
911
+ function wasMigrationSuccessful(workspaceDir, migrationId) {
912
+ const history = getMigrationHistory(workspaceDir, migrationId);
913
+ const lastEntry = history[history.length - 1];
914
+ return lastEntry?.success === true && !lastEntry.rolledBack;
915
+ }
916
+ function getFailedMigrations(workspaceDir) {
917
+ return getJournalEntries(workspaceDir).filter((e) => !e.success || e.rolledBack);
918
+ }
919
+ function clearJournal(workspaceDir) {
920
+ writeJournal(workspaceDir, { entries: [] });
921
+ }
922
+
923
+ //#endregion
924
+ //#region src/migrations/runner.ts
925
+ function loadMigration(migrations, id) {
926
+ return migrations.find((m) => m.id === id) ?? null;
927
+ }
928
+ function dryRun(options) {
929
+ const { workspaceDir, migrationsDir } = options;
930
+ const results = [];
931
+ const manifest = readManifest(migrationsDir);
932
+ if (!manifest) throw new Error(`No migrations.json found in ${migrationsDir}`);
933
+ const errors = validateManifest(manifest);
934
+ if (errors.length > 0) throw new Error(`Invalid migrations.json:\n${errors.join("\n")}`);
935
+ const order = getMigrationOrder(manifest);
936
+ const manifestMap = new Map(manifest.migrations.map((m) => [m.id, m]));
937
+ for (const id of order) {
938
+ const entry = manifestMap.get(id);
939
+ if (!entry) continue;
940
+ const alreadyApplied = hasMigration(workspaceDir, id);
941
+ results.push({
942
+ migrationId: id,
943
+ description: entry.description,
944
+ wouldApply: !alreadyApplied,
945
+ alreadyApplied,
946
+ breaking: entry.breaking
947
+ });
948
+ }
949
+ return results;
950
+ }
951
+ function formatDryRunOutput(results) {
952
+ const lines = [];
953
+ lines.push("Migration Preview (Dry Run)");
954
+ lines.push("=".repeat(50));
955
+ lines.push("");
956
+ const toApply = results.filter((r) => r.wouldApply);
957
+ const alreadyApplied = results.filter((r) => r.alreadyApplied);
958
+ if (toApply.length === 0) lines.push("No migrations to apply. Workspace is up to date.");
959
+ else {
960
+ lines.push(`Migrations to apply: ${toApply.length}`);
961
+ lines.push("");
962
+ for (const result of toApply) {
963
+ const breakingTag = result.breaking ? " [BREAKING]" : "";
964
+ lines.push(` → ${result.migrationId}${breakingTag}`);
965
+ lines.push(` ${result.description}`);
966
+ }
967
+ }
968
+ if (alreadyApplied.length > 0) {
969
+ lines.push("");
970
+ lines.push(`Already applied: ${alreadyApplied.length}`);
971
+ for (const result of alreadyApplied) lines.push(` ✓ ${result.migrationId}`);
972
+ }
973
+ lines.push("");
974
+ lines.push("Run with --apply to execute migrations.");
975
+ return lines.join("\n");
976
+ }
977
+ async function executeMigration(migration, context) {
978
+ try {
979
+ await migration.up(context);
980
+ return {
981
+ migrationId: migration.id,
982
+ success: true,
983
+ rolledBack: false
984
+ };
985
+ } catch (error) {
986
+ return {
987
+ migrationId: migration.id,
988
+ success: false,
989
+ error: error instanceof Error ? error : new Error(String(error)),
990
+ rolledBack: false
991
+ };
992
+ }
993
+ }
994
+ async function run(options) {
995
+ const { workspaceDir, migrationsDir, apply = false, migrations = [], migrationOptions = {} } = options;
996
+ if (!apply) return {
997
+ success: true,
998
+ applied: [],
999
+ skipped: dryRun(options).filter((r) => r.alreadyApplied).map((r) => r.migrationId),
1000
+ failed: null,
1001
+ backupId: null,
1002
+ rolledBack: false
1003
+ };
1004
+ const manifest = readManifest(migrationsDir);
1005
+ if (!manifest) throw new Error(`No migrations.json found in ${migrationsDir}`);
1006
+ const errors = validateManifest(manifest);
1007
+ if (errors.length > 0) throw new Error(`Invalid migrations.json:\n${errors.join("\n")}`);
1008
+ const order = getMigrationOrder(manifest);
1009
+ const toApply = order.filter((id) => !hasMigration(workspaceDir, id));
1010
+ if (toApply.length === 0) return {
1011
+ success: true,
1012
+ applied: [],
1013
+ skipped: order,
1014
+ failed: null,
1015
+ backupId: null,
1016
+ rolledBack: false
1017
+ };
1018
+ const backupId = createBackup(workspaceDir);
1019
+ const state = readState(workspaceDir);
1020
+ if (!state) throw new Error("State file not found. Is this a Supaslidev workspace?");
1021
+ const baseContext = {
1022
+ workspaceDir,
1023
+ state,
1024
+ backupPath: backupId ? join(workspaceDir, ".supaslidev", "backups", backupId) : null
1025
+ };
1026
+ const applied = [];
1027
+ let failed = null;
1028
+ let rolledBack = false;
1029
+ for (const id of toApply) {
1030
+ const migration = loadMigration(migrations, id);
1031
+ if (!migration) {
1032
+ failed = {
1033
+ migrationId: id,
1034
+ success: false,
1035
+ error: /* @__PURE__ */ new Error(`Migration implementation not found: ${id}`),
1036
+ rolledBack: false
1037
+ };
1038
+ break;
1039
+ }
1040
+ const result = await executeMigration(migration, {
1041
+ ...baseContext,
1042
+ options: migrationOptions[id]
1043
+ });
1044
+ if (result.success) {
1045
+ applied.push(result);
1046
+ addMigration(workspaceDir, id);
1047
+ addJournalEntry(workspaceDir, createJournalEntry(id, backupId, true, false));
1048
+ } else {
1049
+ failed = result;
1050
+ addJournalEntry(workspaceDir, createJournalEntry(id, backupId, false, false, result.error));
1051
+ break;
1052
+ }
1053
+ }
1054
+ if (failed) {
1055
+ restoreBackup(workspaceDir, backupId);
1056
+ rolledBack = true;
1057
+ addJournalEntry(workspaceDir, createJournalEntry(failed.migrationId, backupId, false, true, failed.error));
1058
+ failed.rolledBack = true;
1059
+ } else deleteBackup(workspaceDir, backupId);
1060
+ const skipped = order.filter((id) => !toApply.includes(id) || !applied.some((a) => a.migrationId === id) && failed?.migrationId !== id);
1061
+ return {
1062
+ success: !failed,
1063
+ applied,
1064
+ skipped,
1065
+ failed,
1066
+ backupId: failed ? backupId : null,
1067
+ rolledBack
1068
+ };
1069
+ }
1070
+ function formatRunOutput(result) {
1071
+ const lines = [];
1072
+ if (result.success) {
1073
+ lines.push("Migration Complete");
1074
+ lines.push("=".repeat(50));
1075
+ lines.push("");
1076
+ if (result.applied.length === 0) lines.push("No migrations were applied. Workspace is up to date.");
1077
+ else {
1078
+ lines.push(`Applied ${result.applied.length} migration(s):`);
1079
+ for (const migration of result.applied) lines.push(` ✓ ${migration.migrationId}`);
1080
+ }
1081
+ if (result.skipped.length > 0) {
1082
+ lines.push("");
1083
+ lines.push(`Skipped ${result.skipped.length} (already applied):`);
1084
+ for (const id of result.skipped) lines.push(` - ${id}`);
1085
+ }
1086
+ } else {
1087
+ lines.push("Migration Failed");
1088
+ lines.push("=".repeat(50));
1089
+ lines.push("");
1090
+ if (result.failed) {
1091
+ lines.push(`Failed migration: ${result.failed.migrationId}`);
1092
+ if (result.failed.error) lines.push(`Error: ${result.failed.error.message}`);
1093
+ }
1094
+ if (result.rolledBack) {
1095
+ lines.push("");
1096
+ lines.push("Workspace has been restored from backup.");
1097
+ if (result.backupId) lines.push(`Backup preserved: ${result.backupId}`);
1098
+ }
1099
+ if (result.applied.length > 0) {
1100
+ lines.push("");
1101
+ lines.push(`Rolled back ${result.applied.length} migration(s):`);
1102
+ for (const migration of result.applied) lines.push(` ↩ ${migration.migrationId}`);
1103
+ }
1104
+ }
1105
+ return lines.join("\n");
1106
+ }
1107
+
1108
+ //#endregion
1109
+ //#region src/migrations/loader.ts
1110
+ const MIGRATIONS = [];
1111
+ async function loadMigrations() {
1112
+ return {
1113
+ migrations: MIGRATIONS.map((entry) => ({
1114
+ id: entry.id,
1115
+ description: entry.description,
1116
+ up: entry.module.up,
1117
+ down: entry.module.down
1118
+ })),
1119
+ order: MIGRATIONS.map((entry) => entry.id)
1120
+ };
1121
+ }
1122
+ async function getMigrationById(id) {
1123
+ const { migrations } = await loadMigrations();
1124
+ return migrations.find((m) => m.id === id) ?? null;
1125
+ }
1126
+ async function loadInteractiveMigration(id) {
1127
+ const entry = MIGRATIONS.find((m) => m.id === id);
1128
+ if (!entry) return null;
1129
+ return {
1130
+ migration: {
1131
+ id: entry.id,
1132
+ description: entry.description,
1133
+ up: entry.module.up,
1134
+ down: entry.module.down
1135
+ },
1136
+ getAffectedPresentations: entry.module.getAffectedPresentations
1137
+ };
1138
+ }
1139
+
1140
+ //#endregion
1141
+ //#region src/prompts.ts
1142
+ async function promptForCatalogSelection(presentations) {
1143
+ p.intro(pc.cyan("Catalog Conversion Selection"));
1144
+ p.note(`${pc.bold("Select presentations to convert to catalog: references")}\n\nSelected presentations will use ${pc.green("catalog:")} (managed versions)\nUnselected presentations will use ${pc.yellow("^52.11.3")} (pinned version)\n\n${pc.dim("Controls:")} ${pc.bold("Space")} ${pc.dim("toggle |")} ${pc.bold("a")} ${pc.dim("toggle all |")} ${pc.bold("Enter")} ${pc.dim("confirm")}`, "Migration Options");
1145
+ const options = presentations.map((pres) => ({
1146
+ value: pres.name,
1147
+ label: pres.name,
1148
+ hint: `currently ${pres.currentVersion}`
1149
+ }));
1150
+ const selected = await p.multiselect({
1151
+ message: "Which presentations should use catalog: references?",
1152
+ options,
1153
+ initialValues: presentations.map((p) => p.name),
1154
+ required: false
1155
+ });
1156
+ if (p.isCancel(selected)) {
1157
+ p.cancel("Migration cancelled");
1158
+ return {
1159
+ selectedForCatalog: [],
1160
+ cancelled: true
1161
+ };
1162
+ }
1163
+ const selectedCount = selected.length;
1164
+ const pinnedCount = presentations.length - selectedCount;
1165
+ if (selectedCount > 0) p.log.success(`${selectedCount} presentation(s) will use catalog: references`);
1166
+ if (pinnedCount > 0) p.log.info(`${pinnedCount} presentation(s) will use pinned ^52.11.3`);
1167
+ return {
1168
+ selectedForCatalog: selected,
1169
+ cancelled: false
1170
+ };
1171
+ }
1172
+
1173
+ //#endregion
1174
+ //#region src/commands/migrate.ts
1175
+ async function getMigrateResult(options = {}) {
1176
+ const workspaceDir = findWorkspaceRoot();
1177
+ if (!workspaceDir) return {
1178
+ success: false,
1179
+ dryRun: !options.apply,
1180
+ migrationsToApply: 0,
1181
+ migrationsApplied: 0,
1182
+ output: "Not a Supaslidev workspace. No .supaslidev/state.json found."
1183
+ };
1184
+ const migrationsDir = join(workspaceDir, ".supaslidev", "migrations");
1185
+ if (options.apply) {
1186
+ const { migrations } = await loadMigrations();
1187
+ const migrationOptions = {};
1188
+ const slidev51to52 = await loadInteractiveMigration("slidev-51-to-52");
1189
+ if (slidev51to52?.getAffectedPresentations) {
1190
+ const affected = slidev51to52.getAffectedPresentations(workspaceDir);
1191
+ if (affected.length > 0) {
1192
+ const selectionResult = await promptForCatalogSelection(affected);
1193
+ if (selectionResult.cancelled) return {
1194
+ success: false,
1195
+ dryRun: false,
1196
+ migrationsToApply: 0,
1197
+ migrationsApplied: 0,
1198
+ output: "Migration cancelled by user."
1199
+ };
1200
+ migrationOptions["slidev-51-to-52"] = {
1201
+ interactive: true,
1202
+ selectedForCatalog: selectionResult.selectedForCatalog
1203
+ };
1204
+ }
1205
+ }
1206
+ const result = await run({
1207
+ workspaceDir,
1208
+ migrationsDir,
1209
+ apply: true,
1210
+ migrations,
1211
+ migrationOptions
1212
+ });
1213
+ return {
1214
+ success: result.success,
1215
+ dryRun: false,
1216
+ migrationsToApply: result.applied.length + (result.failed ? 1 : 0),
1217
+ migrationsApplied: result.applied.length,
1218
+ output: formatRunOutput(result)
1219
+ };
1220
+ }
1221
+ try {
1222
+ const results = dryRun({
1223
+ workspaceDir,
1224
+ migrationsDir
1225
+ });
1226
+ return {
1227
+ success: true,
1228
+ dryRun: true,
1229
+ migrationsToApply: results.filter((r) => r.wouldApply).length,
1230
+ migrationsApplied: 0,
1231
+ output: formatDryRunOutput(results)
1232
+ };
1233
+ } catch (error) {
1234
+ const message = error instanceof Error ? error.message : String(error);
1235
+ if (message.includes("No migrations.json found")) return {
1236
+ success: true,
1237
+ dryRun: true,
1238
+ migrationsToApply: 0,
1239
+ migrationsApplied: 0,
1240
+ output: "No migrations available. Workspace is up to date."
1241
+ };
1242
+ return {
1243
+ success: false,
1244
+ dryRun: true,
1245
+ migrationsToApply: 0,
1246
+ migrationsApplied: 0,
1247
+ output: `Error: ${message}`
1248
+ };
1249
+ }
1250
+ }
1251
+ function formatMigrateOutput(result) {
1252
+ const lines = [];
1253
+ lines.push(pc.bold("Supaslidev Migrate"));
1254
+ lines.push("─".repeat(40));
1255
+ lines.push("");
1256
+ if (result.dryRun) {
1257
+ lines.push(pc.cyan("Mode: Dry Run (no changes will be made)"));
1258
+ lines.push("");
1259
+ }
1260
+ lines.push(result.output);
1261
+ if (!result.success) {
1262
+ lines.push("");
1263
+ lines.push(pc.red("✗ Migration failed"));
1264
+ } else if (result.dryRun && result.migrationsToApply > 0) {
1265
+ lines.push("");
1266
+ lines.push(pc.yellow(`Run ${pc.bold("supaslidev migrate --apply")} to execute migrations.`));
1267
+ } else if (!result.dryRun && result.migrationsApplied > 0) {
1268
+ lines.push("");
1269
+ lines.push(pc.green("✓ All migrations applied successfully"));
1270
+ }
1271
+ return lines.join("\n");
1272
+ }
1273
+ async function migrate(options = {}) {
1274
+ const result = await getMigrateResult(options);
1275
+ console.log(formatMigrateOutput(result));
1276
+ if (!result.success) process.exit(1);
1277
+ }
1278
+
1279
+ //#endregion
1280
+ //#region src/commands/update.ts
1281
+ async function getUpdateResult() {
1282
+ const check = await checkForUpdates();
1283
+ return {
1284
+ currentVersion: check.currentVersion,
1285
+ latestVersion: check.latestVersion,
1286
+ updateAvailable: check.updateAvailable,
1287
+ error: check.latestVersion === null ? "Could not reach npm registry" : null
1288
+ };
1289
+ }
1290
+ function formatUpdateResult(result) {
1291
+ const lines = [];
1292
+ lines.push(pc.bold("Supaslidev Update Check"));
1293
+ lines.push("─".repeat(40));
1294
+ lines.push("");
1295
+ lines.push(`${pc.dim("Current Version:")} ${result.currentVersion}`);
1296
+ if (result.error) {
1297
+ lines.push("");
1298
+ lines.push(pc.yellow(`⚠ ${result.error}`));
1299
+ return lines.join("\n");
1300
+ }
1301
+ lines.push(`${pc.dim("Latest Version:")} ${result.latestVersion}`);
1302
+ lines.push("");
1303
+ if (result.updateAvailable) {
1304
+ lines.push(pc.yellow(`Update available: ${result.currentVersion} → ${result.latestVersion}`));
1305
+ lines.push("");
1306
+ lines.push(pc.bold("To update, run:"));
1307
+ lines.push(` ${pc.cyan(`npm install -g ${PACKAGE_NAME}`)}`);
1308
+ lines.push("");
1309
+ lines.push(pc.dim("or with pnpm:"));
1310
+ lines.push(` ${pc.cyan(`pnpm add -g ${PACKAGE_NAME}`)}`);
1311
+ } else lines.push(pc.green("✓ You are using the latest version"));
1312
+ return lines.join("\n");
1313
+ }
1314
+ async function update() {
1315
+ const result = await getUpdateResult();
1316
+ console.log(formatUpdateResult(result));
1317
+ if (result.error) process.exit(1);
1318
+ }
1319
+
1320
+ //#endregion
1321
+ //#region src/background-update.ts
1322
+ function startBackgroundUpdateCheck() {
1323
+ const cached = checkForUpdatesCached();
1324
+ if (cached.updateAvailable && cached.latestVersion) {
1325
+ scheduleNotification(cached.latestVersion);
1326
+ return;
1327
+ }
1328
+ fetchLatestVersion().then((latestVersion) => {
1329
+ if (latestVersion && compareVersions(CLI_VERSION, latestVersion)) scheduleNotification(latestVersion);
1330
+ }).catch(() => {});
1331
+ }
1332
+ function scheduleNotification(latestVersion) {
1333
+ process.on("beforeExit", () => {
1334
+ printUpdateNotification(latestVersion);
1335
+ });
1336
+ }
1337
+ function printUpdateNotification(latestVersion) {
1338
+ const border = "─".repeat(50);
1339
+ console.log("");
1340
+ console.log(pc.yellow(border));
1341
+ console.log(pc.yellow(` Update available: ${pc.dim(CLI_VERSION)} → ${pc.green(latestVersion)}`));
1342
+ console.log(pc.yellow(` Run ${pc.cyan(`npm install -g ${PACKAGE_NAME}`)} to update`));
1343
+ console.log(pc.yellow(border));
1344
+ }
1345
+
1346
+ //#endregion
1347
+ //#region src/cli.ts
1348
+ const program = new Command();
1349
+ program.name("create-supaslidev").description("CLI tool for scaffolding Supaslidev presentations").version("0.1.0");
1350
+ 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
+ await create(options);
1352
+ });
1353
+ program.command("status").description("Show project status and check for updates").action(async () => {
1354
+ await status();
1355
+ });
1356
+ program.command("migrate").description("Run migrations to update the workspace").option("--apply", "Execute migrations (default is dry-run mode)").action(async (options) => {
1357
+ await migrate(options);
1358
+ });
1359
+ program.command("update").description("Check for CLI updates").action(async () => {
1360
+ await update();
1361
+ });
1362
+ async function run$1() {
1363
+ startBackgroundUpdateCheck();
1364
+ await program.parseAsync();
1365
+ }
1366
+ const scriptName = process.argv[1] ? basename(process.argv[1]) : "";
1367
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href || scriptName === "cli.js") run$1().catch((err) => {
1368
+ console.error(err);
1369
+ process.exit(1);
1370
+ });
1371
+
1372
+ //#endregion
1373
+ //#region src/transformers/json.ts
1374
+ function readJsonFile(filePath) {
1375
+ const content = readFileSync(filePath, "utf-8");
1376
+ return JSON.parse(content);
1377
+ }
1378
+ function writeJsonFile(filePath, data, indent = 2) {
1379
+ writeFileSync(filePath, JSON.stringify(data, null, indent) + "\n", "utf-8");
1380
+ }
1381
+ function transformJson(filePath, transformer) {
1382
+ const original = readJsonFile(filePath);
1383
+ const transformed = transformer(structuredClone(original));
1384
+ const changed = JSON.stringify(original) !== JSON.stringify(transformed);
1385
+ if (changed) writeJsonFile(filePath, transformed);
1386
+ return {
1387
+ original,
1388
+ transformed,
1389
+ changed
1390
+ };
1391
+ }
1392
+ function getJsonValue(data, path) {
1393
+ if (path === "") return data;
1394
+ const keys = path.split(".");
1395
+ let current = data;
1396
+ for (const key of keys) {
1397
+ if (current === null || typeof current !== "object") return;
1398
+ current = current[key];
1399
+ }
1400
+ return current;
1401
+ }
1402
+ function setJsonValue(data, path, value) {
1403
+ const keys = path.split(".");
1404
+ const result = structuredClone(data);
1405
+ let current = result;
1406
+ for (let i = 0; i < keys.length - 1; i++) {
1407
+ const key = keys[i];
1408
+ const nextKey = keys[i + 1];
1409
+ const isNextKeyNumeric = /^\d+$/.test(nextKey);
1410
+ if (Array.isArray(current)) {
1411
+ const index = parseInt(key, 10);
1412
+ if (isNaN(index)) throw new Error(`Cannot use non-numeric key "${key}" on array at path "${keys.slice(0, i).join(".")}"`);
1413
+ if (current[index] === void 0 || current[index] === null || typeof current[index] !== "object") current[index] = isNextKeyNumeric ? [] : {};
1414
+ current = current[index];
1415
+ } else {
1416
+ if (!(key in current) || current[key] === null || typeof current[key] !== "object") current[key] = isNextKeyNumeric ? [] : {};
1417
+ else if (Array.isArray(current[key]) && !isNextKeyNumeric) throw new Error(`Cannot use non-numeric key "${nextKey}" on array at path "${keys.slice(0, i + 1).join(".")}"`);
1418
+ current = current[key];
1419
+ }
1420
+ }
1421
+ const finalKey = keys[keys.length - 1];
1422
+ if (Array.isArray(current)) {
1423
+ const index = parseInt(finalKey, 10);
1424
+ if (isNaN(index)) throw new Error(`Cannot use non-numeric key "${finalKey}" on array`);
1425
+ current[index] = value;
1426
+ } else current[finalKey] = value;
1427
+ return result;
1428
+ }
1429
+ function deleteJsonValue(data, path) {
1430
+ const keys = path.split(".");
1431
+ const result = structuredClone(data);
1432
+ let current = result;
1433
+ for (let i = 0; i < keys.length - 1; i++) {
1434
+ const key = keys[i];
1435
+ if (Array.isArray(current)) {
1436
+ const index = parseInt(key, 10);
1437
+ if (isNaN(index) || current[index] === void 0 || current[index] === null || typeof current[index] !== "object") return result;
1438
+ current = current[index];
1439
+ } else {
1440
+ if (!(key in current) || current[key] === null || typeof current[key] !== "object") return result;
1441
+ current = current[key];
1442
+ }
1443
+ }
1444
+ const finalKey = keys[keys.length - 1];
1445
+ if (Array.isArray(current)) {
1446
+ const index = parseInt(finalKey, 10);
1447
+ if (!isNaN(index) && index < current.length) current.splice(index, 1);
1448
+ } else delete current[finalKey];
1449
+ return result;
1450
+ }
1451
+ function mergeJson(target, source) {
1452
+ const result = structuredClone(target);
1453
+ for (const key of Object.keys(source)) {
1454
+ const sourceValue = source[key];
1455
+ const targetValue = result[key];
1456
+ if (sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== null && typeof targetValue === "object" && !Array.isArray(targetValue)) result[key] = mergeJson(targetValue, sourceValue);
1457
+ else result[key] = structuredClone(sourceValue);
1458
+ }
1459
+ return result;
1460
+ }
1461
+
1462
+ //#endregion
1463
+ //#region src/transformers/yaml.ts
1464
+ const defaultParseOptions = { keepSourceTokens: true };
1465
+ function readYamlFile(filePath) {
1466
+ return parse(readFileSync(filePath, "utf-8"));
1467
+ }
1468
+ function writeYamlFile(filePath, data) {
1469
+ writeFileSync(filePath, stringify(data), "utf-8");
1470
+ }
1471
+ function readYamlDocument(filePath) {
1472
+ return parseDocument(readFileSync(filePath, "utf-8"), defaultParseOptions);
1473
+ }
1474
+ function writeYamlDocument(filePath, doc) {
1475
+ writeFileSync(filePath, doc.toString(), "utf-8");
1476
+ }
1477
+ function transformYaml(filePath, transformer) {
1478
+ const original = readYamlFile(filePath);
1479
+ const transformed = transformer(structuredClone(original));
1480
+ const changed = stringify(original) !== stringify(transformed);
1481
+ if (changed) writeYamlFile(filePath, transformed);
1482
+ return {
1483
+ original,
1484
+ transformed,
1485
+ changed
1486
+ };
1487
+ }
1488
+ function transformYamlDocument(filePath, transformer) {
1489
+ const doc = readYamlDocument(filePath);
1490
+ const originalContent = doc.toString();
1491
+ transformer(doc);
1492
+ const newContent = doc.toString();
1493
+ const changed = originalContent !== newContent;
1494
+ if (changed) writeFileSync(filePath, newContent, "utf-8");
1495
+ return { changed };
1496
+ }
1497
+ function getYamlValue(data, path) {
1498
+ const keys = path.split(".");
1499
+ let current = data;
1500
+ for (const key of keys) {
1501
+ if (current === null || typeof current !== "object") return;
1502
+ current = current[key];
1503
+ }
1504
+ return current;
1505
+ }
1506
+ function setYamlValue(data, path, value) {
1507
+ const keys = path.split(".");
1508
+ const result = structuredClone(data);
1509
+ let current = result;
1510
+ for (let i = 0; i < keys.length - 1; i++) {
1511
+ const key = keys[i];
1512
+ if (!(key in current) || typeof current[key] !== "object" || current[key] === null) current[key] = {};
1513
+ current = current[key];
1514
+ }
1515
+ current[keys[keys.length - 1]] = value;
1516
+ return result;
1517
+ }
1518
+ function deleteYamlValue(data, path) {
1519
+ const keys = path.split(".");
1520
+ const result = structuredClone(data);
1521
+ let current = result;
1522
+ for (let i = 0; i < keys.length - 1; i++) {
1523
+ const key = keys[i];
1524
+ if (!(key in current) || typeof current[key] !== "object" || current[key] === null) return result;
1525
+ current = current[key];
1526
+ }
1527
+ delete current[keys[keys.length - 1]];
1528
+ return result;
1529
+ }
1530
+ function mergeYaml(target, source) {
1531
+ const result = structuredClone(target);
1532
+ for (const key of Object.keys(source)) {
1533
+ const sourceValue = source[key];
1534
+ const targetValue = result[key];
1535
+ if (sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== null && typeof targetValue === "object" && !Array.isArray(targetValue)) result[key] = mergeYaml(targetValue, sourceValue);
1536
+ else result[key] = structuredClone(sourceValue);
1537
+ }
1538
+ return result;
1539
+ }
1540
+
1541
+ //#endregion
1542
+ //#region src/transformers/typescript.ts
1543
+ function createTransformer(filePath) {
1544
+ const project = new Project({
1545
+ useInMemoryFileSystem: false,
1546
+ manipulationSettings: {
1547
+ indentationText: IndentationText.TwoSpaces,
1548
+ useTrailingCommas: true
1549
+ }
1550
+ });
1551
+ const sourceFile = project.addSourceFileAtPath(filePath);
1552
+ const originalContent = sourceFile.getFullText();
1553
+ return {
1554
+ project,
1555
+ sourceFile,
1556
+ save: () => {
1557
+ const newContent = sourceFile.getFullText();
1558
+ const changed = originalContent !== newContent;
1559
+ if (changed) sourceFile.saveSync();
1560
+ return {
1561
+ changed,
1562
+ originalContent,
1563
+ newContent
1564
+ };
1565
+ }
1566
+ };
1567
+ }
1568
+ function transformTypeScript(filePath, transformer) {
1569
+ const { sourceFile, save } = createTransformer(filePath);
1570
+ transformer(sourceFile);
1571
+ return save();
1572
+ }
1573
+ function addImport(sourceFile, moduleSpecifier, namedImports) {
1574
+ const existingImport = sourceFile.getImportDeclaration(moduleSpecifier);
1575
+ if (existingImport) {
1576
+ const existingNamedImports = existingImport.getNamedImports().map((n) => n.getName());
1577
+ const newImports = namedImports.filter((n) => !existingNamedImports.includes(n));
1578
+ if (newImports.length > 0) existingImport.addNamedImports(newImports);
1579
+ return existingImport;
1580
+ }
1581
+ return sourceFile.addImportDeclaration({
1582
+ moduleSpecifier,
1583
+ namedImports
1584
+ });
1585
+ }
1586
+ function removeImport(sourceFile, moduleSpecifier, namedImports) {
1587
+ const importDecl = sourceFile.getImportDeclaration(moduleSpecifier);
1588
+ if (!importDecl) return false;
1589
+ if (!namedImports) {
1590
+ importDecl.remove();
1591
+ return true;
1592
+ }
1593
+ const existingNamedImports = importDecl.getNamedImports();
1594
+ for (const namedImport of existingNamedImports) if (namedImports.includes(namedImport.getName())) namedImport.remove();
1595
+ const hasNamedImports = importDecl.getNamedImports().length > 0;
1596
+ const hasDefaultImport = !!importDecl.getDefaultImport();
1597
+ const hasNamespaceImport = !!importDecl.getNamespaceImport();
1598
+ if (!hasNamedImports && !hasDefaultImport && !hasNamespaceImport) importDecl.remove();
1599
+ return true;
1600
+ }
1601
+ function addExport(sourceFile, moduleSpecifier, namedExports) {
1602
+ const existingExport = sourceFile.getExportDeclarations().find((e) => e.getModuleSpecifierValue() === moduleSpecifier);
1603
+ if (existingExport) {
1604
+ const existingNamedExports = existingExport.getNamedExports().map((e) => e.getName());
1605
+ const newExports = namedExports.filter((e) => !existingNamedExports.includes(e));
1606
+ if (newExports.length > 0) existingExport.addNamedExports(newExports);
1607
+ return existingExport;
1608
+ }
1609
+ return sourceFile.addExportDeclaration({
1610
+ moduleSpecifier,
1611
+ namedExports
1612
+ });
1613
+ }
1614
+ function findObjectLiteral(sourceFile, variableName) {
1615
+ const variable = sourceFile.getVariableDeclaration(variableName);
1616
+ if (!variable) return;
1617
+ const initializer = variable.getInitializer();
1618
+ if (initializer?.getKind() === SyntaxKind.ObjectLiteralExpression) return initializer;
1619
+ }
1620
+ function findArrayLiteral(sourceFile, variableName) {
1621
+ const variable = sourceFile.getVariableDeclaration(variableName);
1622
+ if (!variable) return;
1623
+ const initializer = variable.getInitializer();
1624
+ if (initializer?.getKind() === SyntaxKind.ArrayLiteralExpression) return initializer;
1625
+ }
1626
+ function getObjectProperty(obj, propertyName) {
1627
+ const property = obj.getProperty(propertyName);
1628
+ if (property && Node.isPropertyAssignment(property)) return property;
1629
+ }
1630
+ function setObjectProperty(obj, propertyName, value) {
1631
+ const existing = getObjectProperty(obj, propertyName);
1632
+ if (existing) existing.setInitializer(value);
1633
+ else obj.addPropertyAssignment({
1634
+ name: propertyName,
1635
+ initializer: value
1636
+ });
1637
+ }
1638
+ function removeObjectProperty(obj, propertyName) {
1639
+ const property = obj.getProperty(propertyName);
1640
+ if (property) {
1641
+ property.remove();
1642
+ return true;
1643
+ }
1644
+ return false;
1645
+ }
1646
+ function addArrayElement(arr, element) {
1647
+ arr.addElement(element);
1648
+ }
1649
+ function removeArrayElement(arr, element) {
1650
+ const elements = arr.getElements();
1651
+ for (const el of elements) if (el.getText() === element) {
1652
+ arr.removeElement(el);
1653
+ return true;
1654
+ }
1655
+ return false;
1656
+ }
1657
+ function findDefaultExportObject(sourceFile) {
1658
+ const defaultExport = sourceFile.getDefaultExportSymbol();
1659
+ if (!defaultExport) return;
1660
+ const declarations = defaultExport.getDeclarations();
1661
+ for (const decl of declarations) if (Node.isExportAssignment(decl)) {
1662
+ const expression = decl.getExpression();
1663
+ if (expression.getKind() === SyntaxKind.ObjectLiteralExpression) return expression;
1664
+ }
1665
+ }
1666
+ function findCallExpression(sourceFile, functionName) {
1667
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
1668
+ for (const call of callExpressions) if (call.getExpression().getText() === functionName) {
1669
+ const args = call.getArguments();
1670
+ if (args.length > 0 && args[0].getKind() === SyntaxKind.ObjectLiteralExpression) return args[0];
1671
+ }
1672
+ }
1673
+
1674
+ //#endregion
1675
+ export { addArrayElement, addExport, addImport, addImportedPresentation, addJournalEntry, addMigration, backupExists, clearJournal, createBackup, createEmptyManifest, createInitialState, createJournalEntry, createTransformer, deleteBackup, deleteJsonValue, deleteYamlValue, dryRun, findArrayLiteral, findCallExpression, findDefaultExportObject, findObjectLiteral, findWorkspaceRoot, formatDryRunOutput, formatRunOutput, formatStatus, getBackupMetadata, getFailedMigrations, getImportedPresentations, getJournalEntries, getJsonValue, getLastSuccessfulEntry, getMigrationById, getMigrationHistory, getMigrationOrder, getObjectProperty, getStatus, getYamlValue, hasMigration, initializeState, listBackups, loadInteractiveMigration, loadMigrations, mergeJson, mergeYaml, readJournal, readJsonFile, readManifest, readState, readYamlDocument, readYamlFile, removeArrayElement, removeImport, removeImportedPresentation, removeObjectProperty, restoreBackup, run, run$1 as runCli, setJsonValue, setObjectProperty, setYamlValue, stateExists, status, transformJson, transformTypeScript, transformYaml, transformYamlDocument, updateCliVersion, validateManifest, wasMigrationSuccessful, writeJournal, writeJsonFile, writeManifest, writeState, writeYamlDocument, writeYamlFile };