@baseplate-dev/project-builder-cli 0.2.5 → 0.3.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.
@@ -9,7 +9,7 @@ import { logger } from '../services/logger.js';
9
9
  export function addDiffCommand(program) {
10
10
  program
11
11
  .command('diff [directory]')
12
- .description('Shows diff between generated output and current working directory')
12
+ .description('Show diff between generated output and current working directory')
13
13
  .option('--compact', 'Show compact diff format instead of unified diff')
14
14
  .option('--app <apps...>', 'Filter by specific app names')
15
15
  .option('--glob <patterns...>', 'Filter files by glob patterns')
@@ -0,0 +1,6 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Adds snapshot management commands to the program.
4
+ * @param program - The program to add the command to.
5
+ */
6
+ export declare function addSnapshotCommand(program: Command): void;
@@ -0,0 +1,121 @@
1
+ import { confirm } from '@inquirer/prompts';
2
+ import { createSchemaParserContext } from '#src/services/schema-parser-context.js';
3
+ import { getUserConfig } from '#src/services/user-config.js';
4
+ import { expandPathWithTilde } from '#src/utils/path.js';
5
+ import { logger } from '../services/logger.js';
6
+ /**
7
+ * Adds snapshot management commands to the program.
8
+ * @param program - The program to add the command to.
9
+ */
10
+ export function addSnapshotCommand(program) {
11
+ const snapshotCommand = program
12
+ .command('snapshot')
13
+ .description('Manage project snapshots for persistent differences');
14
+ // snapshot add command
15
+ snapshotCommand
16
+ .command('add <project-directory> <app> <files...>')
17
+ .description('Add files to snapshot (use --deleted for intentionally deleted files)')
18
+ .option('--deleted', 'Mark files as intentionally deleted in snapshot')
19
+ .option('--snapshot-dir <directory>', 'Snapshot directory', '.baseplate-snapshot')
20
+ .action(async (projectDirectory, app, files, options) => {
21
+ try {
22
+ const { addFilesToSnapshot } = await import('@baseplate-dev/project-builder-server');
23
+ const resolvedDirectory = expandPathWithTilde(projectDirectory);
24
+ const context = await createSchemaParserContext(resolvedDirectory);
25
+ await addFilesToSnapshot(files, !!options.deleted, {
26
+ projectDirectory: resolvedDirectory,
27
+ snapshotDirectory: options.snapshotDir,
28
+ appName: app,
29
+ context,
30
+ logger,
31
+ });
32
+ }
33
+ catch (error) {
34
+ logger.error('Failed to add files to snapshot:', error);
35
+ throw error;
36
+ }
37
+ });
38
+ // snapshot remove command
39
+ snapshotCommand
40
+ .command('remove <project-directory> <app> <files...>')
41
+ .description('Remove files from snapshot tracking')
42
+ .option('--snapshot-dir <directory>', 'Snapshot directory', '.baseplate-snapshot')
43
+ .action(async (projectDirectory, app, files, options) => {
44
+ try {
45
+ const { removeFilesFromSnapshot } = await import('@baseplate-dev/project-builder-server');
46
+ const resolvedDirectory = expandPathWithTilde(projectDirectory);
47
+ const context = await createSchemaParserContext(resolvedDirectory);
48
+ await removeFilesFromSnapshot(files, {
49
+ projectDirectory: resolvedDirectory,
50
+ snapshotDirectory: options.snapshotDir,
51
+ appName: app,
52
+ context,
53
+ logger,
54
+ });
55
+ }
56
+ catch (error) {
57
+ logger.error('Failed to remove files from snapshot:', error);
58
+ throw error;
59
+ }
60
+ });
61
+ // snapshot save command
62
+ snapshotCommand
63
+ .command('save <project-directory> <app>')
64
+ .description('Save snapshot of current differences (overwrites existing snapshot)')
65
+ .option('--snapshot-dir <directory>', 'Snapshot directory', '.baseplate-snapshot')
66
+ .action(async (projectDirectory, app, options) => {
67
+ try {
68
+ const { createSnapshotForProject } = await import('@baseplate-dev/project-builder-server');
69
+ const resolvedDirectory = expandPathWithTilde(projectDirectory);
70
+ const context = await createSchemaParserContext(resolvedDirectory);
71
+ const userConfig = await getUserConfig();
72
+ // Confirm with user before overwriting existing snapshot
73
+ console.warn('⚠️ This will overwrite any existing snapshot for this app.');
74
+ console.info('Use granular commands (snapshot add/remove) for safer updates.');
75
+ const proceed = await confirm({
76
+ message: 'Are you sure you want to overwrite the existing snapshot?',
77
+ default: false,
78
+ });
79
+ if (!proceed) {
80
+ logger.info('Aborted snapshot save.');
81
+ return;
82
+ }
83
+ await createSnapshotForProject({
84
+ projectDirectory: resolvedDirectory,
85
+ app,
86
+ logger,
87
+ context,
88
+ userConfig,
89
+ snapshotDir: options.snapshotDir,
90
+ });
91
+ logger.info('✅ Snapshot saved successfully');
92
+ }
93
+ catch (error) {
94
+ logger.error('Failed to save snapshot:', error);
95
+ throw error;
96
+ }
97
+ });
98
+ // snapshot show command
99
+ snapshotCommand
100
+ .command('show <project-directory> <app>')
101
+ .description('Show current snapshot contents')
102
+ .option('--snapshot-dir <directory>', 'Snapshot directory', '.baseplate-snapshot')
103
+ .action(async (projectDirectory, app, options) => {
104
+ try {
105
+ const { listSnapshotContents } = await import('@baseplate-dev/project-builder-server');
106
+ const resolvedDirectory = expandPathWithTilde(projectDirectory);
107
+ const context = await createSchemaParserContext(resolvedDirectory);
108
+ await listSnapshotContents({
109
+ projectDirectory: resolvedDirectory,
110
+ appName: app,
111
+ snapshotDirectory: options.snapshotDir,
112
+ context,
113
+ logger,
114
+ });
115
+ }
116
+ catch (error) {
117
+ logger.error('Failed to show snapshot contents:', error);
118
+ throw error;
119
+ }
120
+ });
121
+ }
@@ -10,12 +10,19 @@ export function addSyncCommand(program) {
10
10
  program
11
11
  .command('sync [directory]')
12
12
  .description('Syncs project from project-definition.json in baseplate/ directory')
13
- .option('--force-overwrite', 'Force overwrite existing files without merge conflict detection')
13
+ .option('--overwrite', 'Force overwrite existing files and apply snapshots automatically')
14
+ .option('--snapshot <directory>', 'Apply diffs from snapshot directory (requires --overwrite)')
14
15
  .action(async (directory, options) => {
15
16
  const { syncProject, SyncMetadataController } = await import('@baseplate-dev/project-builder-server');
16
17
  const resolvedDirectory = directory
17
18
  ? expandPathWithTilde(directory)
18
19
  : '.';
20
+ // Validate that --snapshot requires --overwrite
21
+ if (options.snapshot && !options.overwrite) {
22
+ logger.error('Error: --snapshot option requires --overwrite flag');
23
+ logger.error('Snapshots are only applied when overwriting files, not during normal merging.');
24
+ throw new Error('--snapshot option requires --overwrite flag');
25
+ }
19
26
  const context = await createSchemaParserContext(resolvedDirectory);
20
27
  const userConfig = await getUserConfig();
21
28
  const syncMetadataController = new SyncMetadataController(resolvedDirectory, logger);
@@ -27,7 +34,8 @@ export function addSyncCommand(program) {
27
34
  userConfig,
28
35
  cliFilePath: process.argv[1],
29
36
  syncMetadataController,
30
- forceOverwrite: options.forceOverwrite ?? false,
37
+ overwrite: options.overwrite ?? false,
38
+ snapshotDirectory: options.overwrite ? options.snapshot : undefined,
31
39
  });
32
40
  }
33
41
  catch (error) {
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import { program } from 'commander';
2
2
  import { addConfigCommand } from './commands/config.js';
3
3
  import { addDiffCommand } from './commands/diff.js';
4
4
  import { addServeCommand } from './commands/server.js';
5
+ import { addSnapshotCommand } from './commands/snapshot.js';
5
6
  import { addSyncCommand } from './commands/sync.js';
6
7
  import { addTemplatesCommand } from './commands/templates.js';
7
8
  import { getEnabledFeatureFlags } from './services/feature-flags.js';
@@ -16,6 +17,7 @@ export async function runCli() {
16
17
  program.version(version, '-v, --version');
17
18
  if (enabledFlags.includes('TEMPLATE_EXTRACTOR')) {
18
19
  addTemplatesCommand(program);
20
+ addSnapshotCommand(program);
19
21
  }
20
22
  addSyncCommand(program);
21
23
  addDiffCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baseplate-dev/project-builder-cli",
3
- "version": "0.2.5",
3
+ "version": "0.3.0",
4
4
  "description": "Full-stack CLI builder using Baseplate generators",
5
5
  "keywords": [
6
6
  "cli",
@@ -40,28 +40,28 @@
40
40
  "bin/**/*"
41
41
  ],
42
42
  "dependencies": {
43
+ "@inquirer/prompts": "7.2.1",
43
44
  "commander": "^12.1.0",
44
45
  "pino": "9.5.0",
45
46
  "pino-pretty": "13.0.0",
46
47
  "pkg-dir": "^8.0.0",
47
48
  "zod": "3.24.1",
48
- "@baseplate-dev/project-builder-common": "0.2.5",
49
- "@baseplate-dev/project-builder-lib": "0.2.5",
50
- "@baseplate-dev/project-builder-server": "0.2.5",
51
- "@baseplate-dev/project-builder-web": "0.2.5",
52
- "@baseplate-dev/utils": "0.2.5"
49
+ "@baseplate-dev/project-builder-common": "0.3.0",
50
+ "@baseplate-dev/project-builder-lib": "0.3.0",
51
+ "@baseplate-dev/project-builder-server": "0.3.0",
52
+ "@baseplate-dev/project-builder-web": "0.3.0",
53
+ "@baseplate-dev/utils": "0.3.0"
53
54
  },
54
55
  "devDependencies": {
55
56
  "@playwright/test": "1.51.0",
56
57
  "@types/node": "^22.0.0",
57
- "dotenv": "^16.0.3",
58
- "eslint": "9.26.0",
58
+ "eslint": "9.32.0",
59
59
  "fastify": "5.3.2",
60
- "prettier": "3.5.3",
60
+ "prettier": "3.6.2",
61
61
  "tsx": "4.19.3",
62
62
  "typescript": "5.7.3",
63
- "vitest": "3.0.7",
64
- "@baseplate-dev/tools": "0.2.5"
63
+ "vitest": "3.2.4",
64
+ "@baseplate-dev/tools": "0.3.0"
65
65
  },
66
66
  "engines": {
67
67
  "node": "^22.0.0"
@@ -76,13 +76,13 @@
76
76
  "scripts": {
77
77
  "build": "tsc -p tsconfig.build.json",
78
78
  "clean": "rm -rf ./dist",
79
- "dev": "tsx watch --tsconfig ./tsconfig.app.json --exclude /**/node_modules/** -r dotenv/config -C development ./src/cli.ts",
79
+ "dev": "tsx watch --tsconfig ./tsconfig.app.json --exclude /**/node_modules/** --env-file-if-exists=.env -C development ./src/cli.ts",
80
80
  "dev:serve": "pnpm dev serve",
81
81
  "lint": "eslint .",
82
82
  "prettier:check": "prettier --check .",
83
83
  "prettier:write": "prettier -w .",
84
84
  "project:generate": "pnpm start generate",
85
- "start": "tsx --tsconfig ./tsconfig.app.json -r dotenv/config -C development ./src/cli.ts",
85
+ "start": "tsx --tsconfig ./tsconfig.app.json --env-file-if-exists=.env -C development ./src/cli.ts",
86
86
  "templates:extract": "pnpm start templates extract",
87
87
  "templates:generate": "pnpm start templates generate",
88
88
  "test": "vitest",