Skip to content

feat: Add snapshot management command#619

Merged
kingston merged 12 commits into
mainfrom
kingston/eng-818-implement-structured-directory-snapshots-for-baseplate-diff
Jul 29, 2025
Merged

feat: Add snapshot management command#619
kingston merged 12 commits into
mainfrom
kingston/eng-818-implement-structured-directory-snapshots-for-baseplate-diff

Conversation

@kingston

@kingston kingston commented Jul 29, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Introduced structured directory snapshots and comprehensive snapshot management CLI commands for tracking, saving, and displaying file differences.
    • Enabled application of snapshot diffs during project sync and generation to ensure generated output reflects tracked changes.
    • Enhanced diff functionality to detect added, modified, and deleted files with improved handling for binary and text files.
  • Bug Fixes

    • Improved detection and reporting of deleted files in directory diffs.
    • Optimized overwrite behavior to skip unnecessary file writes when contents are unchanged.
  • Refactor

    • Refined diff result types to clearly distinguish added, modified, and deleted files.
    • Centralized app directory path resolution and improved project definition loading.
    • Updated sync and generate workflows to integrate snapshot application and renamed related options.
  • Chores

    • Updated default ignore patterns to exclude Baseplate config and Prisma migration files.
    • Added dependencies to support new snapshot and CLI features.
  • Tests

    • Added comprehensive unit tests for snapshot utilities, diff logic, and ignore pattern handling.
    • Enhanced test coverage for directory scanning, diff formatting, and snapshot manifest operations.
  • Documentation

    • Added and updated documentation for new snapshot CLI commands and features.

@changeset-bot

changeset-bot Bot commented Jul 29, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8d08df0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
@baseplate-dev/project-builder-server Patch
@baseplate-dev/sync Patch
@baseplate-dev/project-builder-cli Patch
@baseplate-dev/project-builder-common Patch
@baseplate-dev/project-builder-test Patch
@baseplate-dev/project-builder-web Patch
@baseplate-dev/core-generators Patch
@baseplate-dev/fastify-generators Patch
@baseplate-dev/project-builder-lib Patch
@baseplate-dev/react-generators Patch
@baseplate-dev/plugin-auth Patch
@baseplate-dev/plugin-storage Patch
@baseplate-dev/code-morph Patch
@baseplate-dev/create-project Patch
@baseplate-dev/tools Patch
@baseplate-dev/ui-components Patch
@baseplate-dev/utils Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 29, 2025

Copy link
Copy Markdown

Walkthrough

This change introduces a structured directory snapshot system to the project builder, enhancing the baseplate diff command to detect added, modified, and deleted files. It adds a comprehensive snapshot management CLI (baseplate snapshot) for saving, adding, removing, and showing persistent file diffs. The sync command is updated to apply snapshot diffs when overwriting files. Numerous supporting utilities, types, and tests are added to facilitate snapshot creation, application, and manifest management.

Changes

Cohort / File(s) Change Summary
Diff & Directory Snapshot Core
packages/project-builder-server/src/diff/diff-utils.ts, .../diff/diff-utils.unit.test.ts, .../diff/formatters.unit.test.ts, .../diff/types.ts, .../diff/diff-project.ts, .../diff/index.ts
Enhanced diff logic to detect added, modified, and deleted files; introduced directory scanning with globby; improved binary/text diff handling; updated type system for diffs; updated tests and exports.
Snapshot Management & CLI
packages/project-builder-server/src/diff/snapshot/*, packages/project-builder-cli/src/commands/snapshot.ts, packages/project-builder-cli/src/index.ts
Introduced snapshot management modules for creating, saving, applying, and listing snapshots; added CLI commands for snapshot operations; integrated snapshot command registration under a feature flag.
Sync Command & Generator Output
packages/project-builder-cli/src/commands/sync.ts, packages/project-builder-server/src/sync/sync-project.ts, .../sync/generate-for-directory.ts, .../sync/types.ts, .../sync/get-previous-generated-payload.ts, .../sync/generate-for-directory.test.ts
Updated sync command to support snapshots and overwrite logic; refactored generator output handling to apply snapshots; updated types and tests; added previous generated payload retrieval.
Snapshot Utilities & Tests
.../diff/snapshot/snapshot-utils.ts, .../diff/snapshot/snapshot-utils.unit.test.ts, .../diff/snapshot/snapshot-manifest.unit.test.ts
Provided utilities for diff filename management and snapshot directory creation; added comprehensive unit tests for snapshot utilities and manifest logic.
Snapshot Types & Manifest
.../diff/snapshot/snapshot-types.ts, .../diff/snapshot/snapshot-manifest.ts
Defined structured interfaces and schemas for snapshot manifests, directories, diff files, and patching results; implemented manifest load/save and immutable update utilities.
Project Definition Loading
.../project-definition/load-project-definition.ts, .../project-definition/index.ts, .../project-definition/get-single-app-directory-for-project.ts
Added robust project definition loader with schema migration and plugin support; provided utility to resolve app directories; updated exports.
App Directory Utilities
packages/project-builder-lib/src/definition/apps/app-utils.ts, packages/project-builder-server/src/compiler/app-entry-builder.ts
Centralized app directory resolution logic; updated usage to use new helper function.
Diff Command & Changeset Docs
packages/project-builder-cli/src/commands/diff.ts, .changeset/implement-structured-directory-snapshots.md, .changeset/snapshot-management-commands.md, .changeset/sync-snapshot-overwrite.md
Updated diff command description; added documentation for new snapshot and diff functionality.
Ignore Patterns & Tests
packages/sync/src/utils/ignore-patterns.ts, .../ignore-patterns.unit.test.ts
Added new default ignore patterns for Baseplate and Prisma migration files; updated tests to verify new ignore behavior.
Package Metadata
packages/project-builder-cli/package.json, packages/project-builder-server/package.json
Added new dependencies (@inquirer/prompts, globby) to support CLI prompting and efficient directory scanning.
Miscellaneous Utilities
packages/utils/src/fs/remove-empty-directories.ts, packages/utils/src/json/stringify-pretty-stable.ts
Updated directory removal logic to use fs.rm; broadened JSON stringify input type.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI
  participant DiffUtils
  participant SnapshotMgr
  participant FileSystem

  User->>CLI: Run `baseplate diff`
  CLI->>DiffUtils: scanWorkingDirectory()
  DiffUtils->>FileSystem: List files (globby)
  DiffUtils->>FileSystem: Read generated & working files
  DiffUtils->>CLI: Report added, modified, deleted files

  User->>CLI: Run `baseplate snapshot save`
  CLI->>SnapshotMgr: createSnapshotForProject()
  SnapshotMgr->>DiffUtils: compareFiles()
  DiffUtils->>FileSystem: Read files
  SnapshotMgr->>FileSystem: Write snapshot manifest & diffs

  User->>CLI: Run `baseplate sync --overwrite --snapshot`
  CLI->>SnapshotMgr: Load snapshot
  CLI->>DiffUtils: Apply snapshot to generator output
  DiffUtils->>FileSystem: Write files (with snapshot diffs applied)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • halfdomelabs/baseplate#600: Introduced the initial baseplate diff command and basic diff utilities; this PR is directly extended by the current changes, which add snapshot and deleted file support.
  • halfdomelabs/baseplate#614: Added support for .baseplateignore files and a --force-overwrite flag in diff and sync commands; both PRs modify diff logic and ignore pattern handling, showing a direct code-level connection.

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kingston/eng-818-implement-structured-directory-snapshots-for-baseplate-diff

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@vercel

vercel Bot commented Jul 29, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
baseplate-project-builder-web ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 29, 2025 0:59am

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (8)
packages/project-builder-server/package.json (1)

69-69: Consider replacing globby with Node 22’s built-in globbing

globby is great, but Node 22 already exposes fs.glob / fs.globSync, which removes this extra dependency (≈160 KB install, transitive deps, audit surface).

If you don’t rely on advanced globby options (streaming, gitignore, etc.) a native replacement would:

  • reduce cold-start and install time
  • eliminate one supply-chain risk

Otherwise add a comment explaining the feature gap that requires globby.

Let me know if you want a PoC patch.

packages/project-builder-cli/src/commands/diff.ts (1)

17-17: Minor wording nit

If you want a trailing period for consistency with other commands:

-'Show diff between generated output and current working directory'
+'Show diff between generated output and current working directory.'

Totally optional.

packages/project-builder-server/src/project-definition/get-single-app-directory-for-project.ts (1)

1-28: Well-implemented utility function with minor naming suggestion.

The function is well-structured with proper error handling, TypeScript types, and JSDoc documentation. It correctly uses AppUtils.getAppDirectory() for consistency with the broader refactoring.

Consider renaming matchedApps to matchedApp since find() returns a single app or undefined:

-  const matchedApps = projectDefinition.apps.find((a) => a.name === app);
+  const matchedApp = projectDefinition.apps.find((a) => a.name === app);

-  if (!matchedApps) {
+  if (!matchedApp) {

-  return path.join(projectDirectory, AppUtils.getAppDirectory(matchedApps));
+  return path.join(projectDirectory, AppUtils.getAppDirectory(matchedApp));

Also, the JSDoc comment mentions throwing an error for "more than one apps matching the name", but the implementation would return the first match. Consider updating the comment if multiple matches are acceptable.

packages/project-builder-server/src/diff/snapshot/apply-diff-to-generator-output.ts (1)

35-42: Consider immutable update pattern for file contents.

The current implementation directly mutates fileData.contents, which could lead to unexpected side effects since it modifies the original FileData object.

-    fileData.contents = newContents;
+    generatorFiles.set(fileEntry.path, {
+      ...fileData,
+      contents: newContents,
+    });
packages/project-builder-server/src/project-definition/load-project-definition.ts (1)

47-49: Consider safer type handling for parsed JSON.

The type assertion as ProjectDefinition on line 48 could be unsafe if the JSON structure doesn't match expectations. The runSchemaMigrations function should handle validation, but it's worth ensuring this is robust.

Consider adding explicit validation or letting the migration function handle any type mismatches:

-    const { migratedDefinition, appliedMigrations } = runSchemaMigrations(
-      projectJson as ProjectDefinition,
-    );
+    const { migratedDefinition, appliedMigrations } = runSchemaMigrations(
+      projectJson as ProjectDefinition, // Migration function validates structure
+    );
packages/project-builder-server/src/diff/snapshot/snapshot-utils.ts (1)

20-29: Note the limitation with underscore handling.

The implementation is correct for path separators, but original filenames containing underscores will be converted to path separators during round-trip conversion. This limitation is acknowledged in the tests, but consider documenting it in the function comment for clarity.

 /**
  * Converts a safe diff filename back to the original file path
  * Example: "src_components_button.tsx.diff" -> "src/components/button.tsx"
+ * Note: Original underscores in filenames will be converted to path separators
  */
packages/project-builder-server/src/diff/diff-utils.ts (1)

80-104: Consider adding error handling for globby operations.

The function implementation is good, but consider wrapping the globby call in try-catch for better error handling.

 export async function scanWorkingDirectory(
   directory: string,
   globPatterns?: string[],
   ignoreInstance?: ignore.Ignore,
 ): Promise<string[]> {
   // Create glob pattern to match all files
   const patterns =
     globPatterns && globPatterns.length > 0 ? globPatterns : ['**/*'];
 
-  const files = await globby(patterns, {
-    cwd: directory,
-    onlyFiles: true,
-    fs: fsAdapter,
-    gitignore: true,
-    absolute: false, // Return relative paths
-  });
+  try {
+    const files = await globby(patterns, {
+      cwd: directory,
+      onlyFiles: true,
+      fs: fsAdapter,
+      gitignore: true,
+      absolute: false, // Return relative paths
+    });
+    
+    // Filter files using ignore patterns and glob patterns
+    return files.filter((filePath) =>
+      shouldIncludeFile(filePath, globPatterns, ignoreInstance),
+    );
+  } catch (error) {
+    throw new Error(`Failed to scan directory: ${error instanceof Error ? error.message : String(error)}`);
+  }
-
-  // Filter files using ignore patterns and glob patterns
-  return files.filter((filePath) =>
-    shouldIncludeFile(filePath, globPatterns, ignoreInstance),
-  );
 }
packages/project-builder-server/src/diff/snapshot/snapshot-management.ts (1)

53-149: Consider refactoring this long function into smaller helper functions.

This function is 96 lines long with complex nested logic. Consider extracting the logic for handling deleted files (lines 88-97) and modified/added files (lines 98-139) into separate helper functions for better maintainability and testability.

Comment on lines +40 to +85
export async function saveSnapshot(
directory: string,
generatorOutput: GeneratorOutput,
{ ignoreInstance, snapshotDir }: SaveSnapshotOptions = {},
): Promise<SaveSnapshotResult> {
// Get current diff state
const diffSummary = await compareFiles(
directory,
generatorOutput,
undefined,
ignoreInstance,
);

// Create snapshot directory structure
const snapshotDirectory = await createSnapshotDirectory(
directory,
snapshotDir,
);

// Create manifest
const { diffs } = diffSummary;

// For now, we ignore binary modifications
const modifiedTextDiffs = diffs.filter(
(diff) => diff.type === 'modified' && !diff.isBinary,
);
const manifest: SnapshotManifest = {
version: SNAPSHOT_VERSION,
files: {
modified: modifiedTextDiffs.map((diff) => ({
path: diff.path,
diffFile: pathToSafeDiffFilename(diff.path),
})),
added: diffs
.filter((diff) => diff.type === 'added')
.map((diff) => diff.path),
deleted: diffs
.filter((diff) => diff.type === 'deleted')
.map((diff) => diff.path),
},
};

// Clear out existing diffs
await rm(snapshotDirectory.diffsPath, { recursive: true });
await mkdir(snapshotDirectory.diffsPath, { recursive: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for directory operations.

The function clears the diffs directory but doesn't handle potential errors during the recursive operations.

   // Clear out existing diffs
-  await rm(snapshotDirectory.diffsPath, { recursive: true });
+  await rm(snapshotDirectory.diffsPath, { recursive: true, force: true });
   await mkdir(snapshotDirectory.diffsPath, { recursive: true });

Adding force: true ensures the operation succeeds even if the directory doesn't exist.

🤖 Prompt for AI Agents
In packages/project-builder-server/src/diff/snapshot/save-snapshot.ts between
lines 40 and 85, the code clears the diffs directory using rm and mkdir without
error handling. To fix this, add the option force: true to the rm call to avoid
errors if the directory does not exist, and consider wrapping these directory
operations in try-catch blocks or handling promise rejections to properly manage
any errors during recursive directory removal or creation.

Comment on lines +1 to +27
import type { SchemaParserContext } from '@baseplate-dev/project-builder-lib';
import type { Logger } from '@baseplate-dev/sync';

import { enhanceErrorWithContext } from '@baseplate-dev/utils';
import { handleFileNotFoundError } from '@baseplate-dev/utils/node';
import { readFile } from 'node:fs/promises';
import path from 'node:path';

import type { GeneratorOperations } from '#src/sync/types.js';

import { compileApplications } from '#src/compiler/index.js';
import { getSingleAppDirectoryForProject } from '#src/project-definition/get-single-app-directory-for-project.js';
import { loadProjectDefinition } from '#src/project-definition/load-project-definition.js';
import { createTemplateMetadataOptions } from '#src/sync/template-metadata-utils.js';
import { DEFAULT_GENERATOR_OPERATIONS } from '#src/sync/types.js';

import {
removeSnapshotDiffFile,
saveSnapshotDiffFile,
} from './snapshot-diff-utils.js';
import {
initializeSnapshotManifest,
loadSnapshotManifest,
saveSnapshotManifest,
snapshotManifestUtils,
} from './snapshot-manifest.js';
import { resolveSnapshotDirectory } from './snapshot-utils.js';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Sort imports by group: external libraries first, then local imports.

The imports should be reorganized to follow the coding guidelines. External packages should come before local imports.

import type { SchemaParserContext } from '@baseplate-dev/project-builder-lib';
import type { Logger } from '@baseplate-dev/sync';
-
import { enhanceErrorWithContext } from '@baseplate-dev/utils';
import { handleFileNotFoundError } from '@baseplate-dev/utils/node';
import { readFile } from 'node:fs/promises';
import path from 'node:path';

import type { GeneratorOperations } from '#src/sync/types.js';
-
import { compileApplications } from '#src/compiler/index.js';
import { getSingleAppDirectoryForProject } from '#src/project-definition/get-single-app-directory-for-project.js';
import { loadProjectDefinition } from '#src/project-definition/load-project-definition.js';
import { createTemplateMetadataOptions } from '#src/sync/template-metadata-utils.js';
import { DEFAULT_GENERATOR_OPERATIONS } from '#src/sync/types.js';
-
import {
  removeSnapshotDiffFile,
  saveSnapshotDiffFile,
} from './snapshot-diff-utils.js';
import {
  initializeSnapshotManifest,
  loadSnapshotManifest,
  saveSnapshotManifest,
  snapshotManifestUtils,
} from './snapshot-manifest.js';
import { resolveSnapshotDirectory } from './snapshot-utils.js';

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/project-builder-server/src/diff/snapshot/snapshot-management.ts
lines 1 to 27, the imports are not sorted correctly. Reorder the imports so that
all external libraries (like 'node:fs/promises', 'node:path', and packages
starting with '@baseplate-dev') come first, followed by local imports (those
starting with './' or '#src/'). Group and sort them alphabetically within these
categories to follow the coding guidelines.

logger.info(`Snapshot contents (${snapshotDir.path}):`);

if (manifest.files.modified.length > 0) {
logger.info(`\\nModified files (${manifest.files.modified.length}):`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify that the logger correctly interprets escaped newline characters.

The escaped \n characters in the logger output might render literally instead of as newlines, depending on the logger implementation.

If the logger doesn't interpret escape sequences, consider using separate logger calls instead:

-      logger.info(`\\nModified files (${manifest.files.modified.length}):`);
+      logger.info('');
+      logger.info(`Modified files (${manifest.files.modified.length}):`);

Also applies to: 259-259, 266-266


🏁 Script executed:

#!/bin/bash
# Search for the Logger type definition and its usage to understand how it handles escape sequences
ast-grep --pattern 'interface Logger {
  $$$
}'

# Search for logger implementations
rg -A 10 "class.*Logger|function.*createLogger|export.*logger" --type ts

Length of output: 5501


🏁 Script executed:

#!/bin/bash
echo "=== Logger import in snapshot-management.ts ==="
rg -n "import .*logger" packages/project-builder-server/src/diff/snapshot/snapshot-management.ts

echo
echo "=== Logger usage around lines 240-280 ==="
sed -n '240,280p' packages/project-builder-server/src/diff/snapshot/snapshot-management.ts

echo
echo "=== Evented Logger implementation (first 200 lines) ==="
sed -n '1,200p' packages/sync/src/utils/evented-logger.ts

Length of output: 3278


Replace literal “\n” escapes with real newlines or separate logger calls

Our investigation shows that the EventedLogger (and console.info) prints strings as-is—it won’t turn "\n" sequences embedded in the string into line breaks when you escape the backslash ("\\n"). As a result, you’ll see a literal \n in the output instead of an empty line. To restore the intended formatting, update each of these calls in
packages/project-builder-server/src/diff/snapshot/snapshot-management.ts:

• Around line 254 (Modified files)
• Around line 259 (Added files)
• Around line 266 (Deleted files)

Example fix for “Modified files”:

-    logger.info(`\\nModified files (${manifest.files.modified.length}):`);
+    // print a blank line, then the header
+    logger.info('');
+    logger.info(`Modified files (${manifest.files.modified.length}):`);

Apply the same change to the “Added files” and “Deleted files” sections. This ensures you get a real blank line before each header.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info(`\\nModified files (${manifest.files.modified.length}):`);
// print a blank line, then the header
logger.info('');
logger.info(`Modified files (${manifest.files.modified.length}):`);
🤖 Prompt for AI Agents
In packages/project-builder-server/src/diff/snapshot/snapshot-management.ts at
lines 252, 257, and 264, replace the literal string "\\n" in logger.info calls
with actual newline characters or separate the calls to produce real blank lines
before the headers "Modified files", "Added files", and "Deleted files". This
will ensure the output shows proper line breaks instead of the literal "\n"
sequence.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/project-builder-server/src/diff/snapshot/snapshot-management.ts (2)

1-27: Sort imports by group: external libraries first, then local imports.


252-252: Replace literal "\n" escapes with real newlines or separate logger calls.

Also applies to: 259-259, 266-266

🧹 Nitpick comments (2)
packages/project-builder-server/src/diff/snapshot/snapshot-management.ts (2)

209-215: Reduce interface duplication by extending from SnapshotManagementOptions.

The SnapshotListOptions interface duplicates most fields from SnapshotManagementOptions. Consider refactoring to follow the DRY principle.

-export interface SnapshotListOptions {
-  projectDirectory: string;
-  appName: string;
-  snapshotDirectory?: string;
-  context: SchemaParserContext;
-  logger: Logger;
-}
+export interface SnapshotListOptions extends Omit<SnapshotManagementOptions, 'generatorOperations'> {}

201-206: Use consistent error handling pattern across functions.

The error handling here differs from other functions in this module. Consider using enhanceErrorWithContext for consistency.

  } catch (error) {
-    logger.error(
-      `Failed to remove files from snapshot: ${(error as Error).message}`,
-    );
-    throw error;
+    throw enhanceErrorWithContext(error, `Failed to remove files from snapshot`);
  }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 050a6e2 and 8d08df0.

📒 Files selected for processing (4)
  • packages/project-builder-server/src/diff/snapshot/create-snapshot-for-project.ts (1 hunks)
  • packages/project-builder-server/src/diff/snapshot/snapshot-diff-utils.ts (1 hunks)
  • packages/project-builder-server/src/diff/snapshot/snapshot-management.ts (1 hunks)
  • packages/project-builder-server/src/sync/generate-for-directory.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/project-builder-server/src/diff/snapshot/snapshot-diff-utils.ts
  • packages/project-builder-server/src/diff/snapshot/create-snapshot-for-project.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (CLAUDE.md)

**/*.{ts,tsx}: Import UI components from the @baseplate-dev/ui-components package as shown in the provided examples.
Use both standalone and React Hook Form controller variants for form components from @baseplate-dev/ui-components as appropriate.
If a particular interface or type is not exported, change the file so it is exported.

**/*.{ts,tsx}: TypeScript with strict type checking
Always include return types on top-level functions including React components (React.ReactElement)
Include absolute paths in import statements via tsconfig paths (@src/ is the alias for src/)
If a particular interface or type is not exported, change the file so it is exported

Files:

  • packages/project-builder-server/src/diff/snapshot/snapshot-management.ts
  • packages/project-builder-server/src/sync/generate-for-directory.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-style.mdc)

Node 16 module resolution - include file extensions in imports (.js)

Files:

  • packages/project-builder-server/src/diff/snapshot/snapshot-management.ts
  • packages/project-builder-server/src/sync/generate-for-directory.ts
**/*.{ts,tsx,js}

📄 CodeRabbit Inference Engine (.cursor/rules/code-style.mdc)

**/*.{ts,tsx,js}: Sort imports by group: external libs first, then local imports
Use camelCase for variables/functions, PascalCase for types/classes
Order functions such that functions are placed below the variables/functions they use
We use the prefer using nullish coalescing operator (??) ESLint rule instead of a logical or (||), as it is a safer operator
Use console.info/warn/error instead of console.log

Files:

  • packages/project-builder-server/src/diff/snapshot/snapshot-management.ts
  • packages/project-builder-server/src/sync/generate-for-directory.ts
**/*

📄 CodeRabbit Inference Engine (.cursor/rules/code-style.mdc)

Use kebab-case for file names

Files:

  • packages/project-builder-server/src/diff/snapshot/snapshot-management.ts
  • packages/project-builder-server/src/sync/generate-for-directory.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
PR: halfdomelabs/baseplate#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-22T09:09:23.016Z
Learning: Applies to .changeset/*.md : When adding a new feature or changing an existing feature, add a new Changeset in the `.changeset/` directory in the specified markdown format.
Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/ui-rules.mdc:0-0
Timestamp: 2025-07-22T09:11:29.223Z
Learning: Applies to {packages/project-builder-web/**,packages/ui-components/**}/*.tsx : Use ShadCN-based components from `@baseplate-dev/ui-components` and always prefer these components over creating custom ones
packages/project-builder-server/src/diff/snapshot/snapshot-management.ts (19)

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{js,ts,tsx} : Node 16 module resolution - include file extensions in imports (.js)

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-22T09:09:23.016Z
Learning: Applies to **/*.{ts,tsx} : If a particular interface or type is not exported, change the file so it is exported.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{ts,tsx} : If a particular interface or type is not exported, change the file so it is exported

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{ts,tsx} : Include absolute paths in import statements via tsconfig paths (@src/ is the alias for src/)

Learnt from: kingston
PR: #571
File: packages/core-generators/src/renderers/extractor/plugins/typed-templates-file.ts:102-106
Timestamp: 2025-06-11T18:31:22.247Z
Learning: For templateExtractorBarrelExportPlugin.addGeneratedBarrelExport, the generated barrel exports are written into generated/index.ts, therefore the moduleSpecifier must be specified relative to that file (e.g., ./typed-templates.js), not the project root.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-06-30T11:52:28.745Z
Learning: Applies to src/mocks/**/*.{ts,tsx} : Manual mocks are in src/__mocks__/ directory

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-22T09:11:16.930Z
Learning: Applies to src/mocks/**/*.ts : Manual mocks are in src/__mocks__/ directory

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-22T09:11:16.930Z
Learning: Applies to **/*.test.{ts,tsx} : For file system operations in tests, use memfs and mock 'node:fs' and 'node:fs/promises'

Learnt from: kingston
PR: #562
File: plugins/plugin-auth/package.json:32-36
Timestamp: 2025-06-03T09:11:29.651Z
Learning: With TypeScript project references, TypeScript compilation is watched from the root level using "watch:tsc:root" script, so individual packages no longer need to include their "tsc:watch" scripts in their local "watch" commands. The local "tsc:watch" scripts are renamed from "watch:tsc" but are not meant to be run as part of the package's watch command since TypeScript watching is handled centrally at the workspace root.

Learnt from: kingston
PR: #539
File: scripts/check-changesets.ts:10-13
Timestamp: 2025-05-08T12:56:59.222Z
Learning: Node.js 20.12.0 and above include globSync in the core node:fs module, so import { promises as fs, globSync } from 'node:fs'; is valid syntax in projects using these Node.js versions.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{ts,tsx,js} : Sort imports by group: external libs first, then local imports

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/ui-rules.mdc:0-0
Timestamp: 2025-07-22T09:11:29.223Z
Learning: Applies to {packages/project-builder-web/,packages/ui-components/}/*.tsx : Use ShadCN-based components from @baseplate-dev/ui-components and always prefer these components over creating custom ones

Learnt from: kingston
PR: #428
File: packages/project-builder-server/src/sync/index.ts:5-5
Timestamp: 2025-01-23T09:12:46.178Z
Learning: Avoid importing directly from 'dist' directories. Instead, expose functionality through the package's public API and import from the main package entry point.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-22T09:09:23.016Z
Learning: Applies to **/*.{ts,tsx} : Import UI components from the @baseplate-dev/ui-components package as shown in the provided examples.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{ts,tsx,js} : Order functions such that functions are placed below the variables/functions they use

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-22T09:11:16.930Z
Learning: Refactor code to be more easily testable, such as exporting types or functions

Learnt from: kingston
PR: #521
File: packages/react-generators/src/generators/admin/admin-crud-edit/admin-crud-edit.generator.ts:90-94
Timestamp: 2025-05-05T06:36:50.687Z
Learning: In this codebase, import paths can include .ts extensions, and the resolveModuleSpecifier function will handle them appropriately. There's no need to strip file extensions before passing paths to functions like TsCodeUtils.importFragment.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{ts,tsx,js} : Use console.info/warn/error instead of console.log

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-22T09:09:23.016Z
Learning: Run pnpm lint:affected and pnpm typecheck before committing changes.

packages/project-builder-server/src/sync/generate-for-directory.ts (17)

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-22T09:09:23.016Z
Learning: Applies to **/*.{ts,tsx} : If a particular interface or type is not exported, change the file so it is exported.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{ts,tsx} : If a particular interface or type is not exported, change the file so it is exported

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{ts,tsx} : Include absolute paths in import statements via tsconfig paths (@src/ is the alias for src/)

Learnt from: kingston
PR: #562
File: plugins/plugin-auth/package.json:32-36
Timestamp: 2025-06-03T09:11:29.651Z
Learning: With TypeScript project references, TypeScript compilation is watched from the root level using "watch:tsc:root" script, so individual packages no longer need to include their "tsc:watch" scripts in their local "watch" commands. The local "tsc:watch" scripts are renamed from "watch:tsc" but are not meant to be run as part of the package's watch command since TypeScript watching is handled centrally at the workspace root.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/code-style.mdc:0-0
Timestamp: 2025-07-22T09:10:31.413Z
Learning: Applies to **/*.{js,ts,tsx} : Node 16 module resolution - include file extensions in imports (.js)

Learnt from: kingston
PR: #609
File: packages/ui-components/src/components/badge/badge-with-icon.stories.tsx:3-3
Timestamp: 2025-07-14T12:02:36.595Z
Learning: For TypeScript/TSX files: #src/ is the new path alias standard for src/ directory imports, replacing the previous @src/ convention.

Learnt from: kingston
PR: #539
File: scripts/check-changesets.ts:10-13
Timestamp: 2025-05-08T12:56:59.222Z
Learning: Node.js 20.12.0 and above include globSync in the core node:fs module, so import { promises as fs, globSync } from 'node:fs'; is valid syntax in projects using these Node.js versions.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/ui-rules.mdc:0-0
Timestamp: 2025-07-22T09:11:29.223Z
Learning: Applies to {packages/project-builder-web/,packages/ui-components/}/*.tsx : Use ShadCN-based components from @baseplate-dev/ui-components and always prefer these components over creating custom ones

Learnt from: kingston
PR: #606
File: plugins/plugin-auth/src/auth/core/generators/auth-apollo/auth-apollo.generator.ts:24-32
Timestamp: 2025-07-12T19:56:08.559Z
Learning: For generator functions and configuration object methods like those in createGeneratorTask, inferred return types are acceptable when the structure is clear from the implementation. ESLint rules handle enforcement of explicit return types where truly needed, so manual review for this is not necessary.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-22T09:09:23.016Z
Learning: Applies to **/*.{ts,tsx} : Import UI components from the @baseplate-dev/ui-components package as shown in the provided examples.

Learnt from: kingston
PR: #428
File: packages/project-builder-server/src/sync/index.ts:5-5
Timestamp: 2025-01-23T09:12:46.178Z
Learning: Avoid importing directly from 'dist' directories. Instead, expose functionality through the package's public API and import from the main package entry point.

Learnt from: kingston
PR: #539
File: scripts/check-changesets.ts:10-13
Timestamp: 2025-05-08T12:56:59.222Z
Learning: Node.js 22.0.0 and later versions include both glob and globSync functionality in the core node:fs module, making import { promises as fs, globSync } from 'node:fs'; valid syntax.

Learnt from: kingston
PR: #592
File: plugins/plugin-auth/src/auth0/generators/react/auth0-hooks/templates/src/hooks/use-required-user-id.ts:1-2
Timestamp: 2025-07-07T18:24:17.522Z
Learning: Files under templates/** directories can use // @ts-nocheck because they are templates meant for code generation, not direct type checking.

Learnt from: kingston
PR: #571
File: packages/core-generators/src/renderers/extractor/plugins/typed-templates-file.ts:102-106
Timestamp: 2025-06-11T18:31:22.247Z
Learning: For templateExtractorBarrelExportPlugin.addGeneratedBarrelExport, the generated barrel exports are written into generated/index.ts, therefore the moduleSpecifier must be specified relative to that file (e.g., ./typed-templates.js), not the project root.

Learnt from: kingston
PR: #521
File: packages/react-generators/src/generators/admin/admin-components/admin-components.generator.ts:55-61
Timestamp: 2025-05-05T06:35:13.300Z
Learning: In generators, paths should be merged with forward slashes (/) only since JavaScript generated files use POSIX style separators. The system handles conversion to Windows style separators at later stages when writing out files.

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/dev-commands.mdc:0-0
Timestamp: 2025-07-22T09:11:00.767Z
Learning: Build the project from the repository root using pnpm build

Learnt from: CR
PR: halfdomelabs/baseplate#0
File: .cursor/rules/dev-commands.mdc:0-0
Timestamp: 2025-07-22T09:11:00.767Z
Learning: Build a package using pnpm build from within the package directory

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint
  • GitHub Check: Test E2E
🔇 Additional comments (5)
packages/project-builder-server/src/sync/generate-for-directory.ts (5)

8-8: LGTM! Import changes support the snapshot functionality correctly.

The import additions properly follow the coding guidelines with file extensions, absolute paths using the #src/ alias, and appropriate grouping. The refactoring to import GeneratorOperations from types.js and the new snapshot-related imports align well with the PR's snapshot management objectives.

Also applies to: 17-17, 19-24


36-37: Interface changes improve clarity and add snapshot support.

The rename from forceOverwrite to overwrite is more concise and clear. The addition of the optional snapshotDirectory parameter properly supports the new snapshot functionality while maintaining backward compatibility.


47-47: Function parameters correctly implement the interface changes.

The use of DEFAULT_GENERATOR_OPERATIONS as the default value and the parameter renaming from forceOverwrite to overwrite align properly with the interface updates. The new snapshotDirectory parameter is correctly destructured.

Also applies to: 50-51


60-60: Well-implemented snapshot application logic with comprehensive error handling.

The snapshot application logic is properly structured with:

  • Correct conditional checking for the overwrite flag
  • Proper async/await usage for snapshot operations
  • Comprehensive error handling with informative messages directing users to fix conflicts
  • Good logging practices with clear info and error messages
  • Appropriate variable mutation pattern (changing output from const to let)

The flow from resolving the snapshot directory to loading the manifest and applying diffs is logical and robust.

Also applies to: 66-94


130-130: Correct parameter mapping for the renamed variable.

The change properly maps the renamed overwrite parameter to the expected forceOverwrite property, maintaining compatibility with the writeGeneratorOutput API while using the clearer internal parameter name.

@kingston kingston merged commit 687a47e into main Jul 29, 2025
10 checks passed
@kingston kingston deleted the kingston/eng-818-implement-structured-directory-snapshots-for-baseplate-diff branch July 29, 2025 13:04
@github-actions github-actions Bot mentioned this pull request Jul 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant