Skip to content

Conversation

@kingston
Copy link
Collaborator

@kingston kingston commented Dec 26, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced functionality to automatically delete empty folders when files are removed.
    • Added new functions for checking and removing empty directories.
  • Enhancements

    • Updated module name generation to use camelCase format.
    • Integrated in-memory file system for testing purposes.
  • Bug Fixes

    • Improved error handling during directory removal operations.
  • Tests

    • Added unit tests for new directory management functions to ensure reliability.
  • Chores

    • Updated dependencies and added new scripts for testing.

@linear
Copy link

linear bot commented Dec 26, 2024

ENG-516 Delete empty folders when syncing

If we're renaming files or removing folders, we should delete empty folders.

@changeset-bot
Copy link

changeset-bot bot commented Dec 26, 2024

🦋 Changeset detected

Latest commit: c965caa

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

This PR includes changesets to release 12 packages
Name Type
@halfdomelabs/project-builder-server Patch
@halfdomelabs/fastify-generators Patch
@halfdomelabs/sync Patch
@halfdomelabs/project-builder-cli Patch
@halfdomelabs/project-builder-common Patch
@halfdomelabs/project-builder-test Patch
@halfdomelabs/project-builder-web Patch
@halfdomelabs/cli Patch
@halfdomelabs/baseplate-plugin-storage Patch
@halfdomelabs/core-generators Patch
@halfdomelabs/project-builder-lib Patch
@halfdomelabs/react-generators 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
Copy link

coderabbitai bot commented Dec 26, 2024

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The pull request introduces functionality to automatically delete empty folders when files are removed from the codebase. This is achieved by adding new utility functions for directory management, specifically isDirectoryEmpty and removeEmptyAncestorDirectories. The changes span multiple packages, including @halfdomelabs/project-builder-server, @halfdomelabs/fastify-generators, and @halfdomelabs/sync. The implementation involves adding mock file system utilities, updating module name generation to use camelCase, and enhancing file synchronization processes.

Changes

File Change Summary
.changeset/odd-drinks-teach.md Added changeset for empty folder deletion feature
packages/fastify-generators/src/generators/core/app-module/index.ts Modified module name generation to use camelCase instead of direct name usage
packages/project-builder-server/package.json Added test script, memfs dependency, and vitest dev dependency
packages/project-builder-server/src/__mocks__/fs.cts New mock file system export using memfs
packages/project-builder-server/src/__mocks__/fs/promises.cts New mock promises file system export
packages/project-builder-server/src/sync/index.ts Added removeEmptyAncestorDirectories import and usage in file generation
packages/project-builder-server/src/utils/directories.ts Added isDirectoryEmpty and removeEmptyAncestorDirectories utility functions
packages/project-builder-server/src/utils/directories.unit.test.ts Added comprehensive unit tests for directory management functions
packages/sync/package.json Updated memfs dependency version from 4.11.2 to 4.15.1

Sequence Diagram

sequenceDiagram
    participant Sync as File Synchronization
    participant DirUtils as Directory Utilities
    participant FileSystem as File System

    Sync->>DirUtils: Call removeEmptyAncestorDirectories
    DirUtils->>FileSystem: Check directory contents
    FileSystem-->>DirUtils: Return directory status
    alt Directory is Empty
        DirUtils->>FileSystem: Remove empty directory
        DirUtils->>DirUtils: Check parent directory
    else Directory is Not Empty
        DirUtils->>Sync: Stop removal process
    end
Loading

The sequence diagram illustrates the process of removing empty ancestor directories, showing how the synchronization process checks and removes empty directories recursively.


🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

Copy link

@coderabbitai coderabbitai bot left a comment

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 (4)
packages/project-builder-server/src/utils/directories.unit.test.ts (2)

14-16: Consider a top-level reset check.

vol.reset() is a solid approach. Optionally, confirm if you need to reset additional mock states or loggers at the top level in beforeAll or afterAll to handle project-level test side effects.


135-154: Ensure error handling is fully tested.

The test at line 134+ checks for error handling by mocking fs.readdir, but consider additional scenarios such as partial directory permissions or read-only file systems to broaden coverage.

.changeset/odd-drinks-teach.md (1)

Line range hint 1-8: Enhance the changeset description with more details.

While the current description is clear, it would be helpful to add more context about:

  • The motivation behind this change
  • The specific packages affected and their roles
  • Any potential impact on existing behavior

Consider expanding the description like this:

-Delete empty folders when deleting files from codebase
+Delete empty folders when deleting files from codebase
+
+When files are deleted during sync operations, their parent directories will now be automatically removed if they become empty. This change:
+- Keeps the codebase clean by removing unused directories
+- Affects file operations in @halfdomelabs/project-builder-server
+- Updates sync behavior in @halfdomelabs/sync
+- Maintains consistency in @halfdomelabs/fastify-generators
packages/project-builder-server/package.json (1)

30-30: Consider enhancing the test script configuration.

The current test script is minimal. Consider adding commonly used options for better developer experience.

-    "test": "vitest",
+    "test": "vitest run",
+    "test:watch": "vitest",
+    "test:coverage": "vitest run --coverage",
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between 70c6478 and 6774164.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • .changeset/odd-drinks-teach.md (1 hunks)
  • packages/fastify-generators/src/generators/core/app-module/index.ts (2 hunks)
  • packages/project-builder-server/package.json (2 hunks)
  • packages/project-builder-server/src/__mocks__/fs.cts (1 hunks)
  • packages/project-builder-server/src/__mocks__/fs/promises.cts (1 hunks)
  • packages/project-builder-server/src/sync/index.ts (2 hunks)
  • packages/project-builder-server/src/utils/directories.ts (1 hunks)
  • packages/project-builder-server/src/utils/directories.unit.test.ts (1 hunks)
  • packages/sync/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/project-builder-server/src/mocks/fs.cts
  • packages/project-builder-server/src/mocks/fs/promises.cts
🔇 Additional comments (6)
packages/project-builder-server/src/utils/directories.ts (1)

4-11: Implement thorough path validation.

Consider validating that dirPath actually points to a directory rather than relying solely on error catching. Suppose the path points to a file or an invalid symlink. The current approach returns false on error, which might mask deeper issues.

packages/fastify-generators/src/generators/core/app-module/index.ts (2)

11-11: Remove unused imports when possible.

If no other code references kebabCase, you might consider removing the import to keep the imports consistent with usage.


35-35: Clarify naming convention.

Using camelCase for the descriptor name and then appending "Module" is logical, but ensure it aligns with any existing naming conventions in the rest of the project for consistency.

packages/project-builder-server/src/utils/directories.unit.test.ts (1)

1-3: Confirm test environment isolation.

In-memory filesystem usage is great for isolation. Ensure any global mocks (like node:fs) are restored properly after all tests to avoid potential side effects in parallel test runs.

packages/project-builder-server/src/sync/index.ts (1)

11-12: Confirm consistent import paths.

Ensure @src/utils/directories.js is consistently referenced across the codebase and no duplicates are introduced (e.g., relative vs. alias paths).

packages/sync/package.json (1)

58-58: Verify memfs version compatibility.

The memfs version has been updated from 4.11.2 to 4.15.1. This is a significant version bump that could include breaking changes.

Let's verify the changes between these versions:

✅ Verification successful

memfs version update is safe to proceed

The version bump from 4.11.2 to 4.15.1 includes several improvements and bug fixes:

  • Bug fix for relative symlinks resolution
  • New features for FileHandle streams
  • Improved permissions support
  • No breaking changes reported in the changelog

The verification also shows that memfs is consistently used at version 4.15.1 across packages, maintaining version alignment in the monorepo.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check memfs changelog and verify version consistency across packages

# Check if memfs is used consistently across packages
echo "Checking memfs version consistency across packages..."
fd -t f "package.json" -x grep -l "memfs" {} \; | xargs cat | grep "memfs"

# Fetch memfs releases between versions
gh api repos/streamich/memfs/releases --jq '.[] | select(.tag_name | contains("v4.")) | select(.tag_name >= "v4.11.2" and .tag_name <= "v4.15.1") | {version: .tag_name, body: .body}'

Length of output: 3114

Comment on lines +23 to +46
// Process each directory
for (const dir of uniqueParentDirs) {
let currentDir = dir;

// Continue until we hit the stop directory or root
while (
currentDir !== stopAt &&
currentDir !== path.parse(currentDir).root &&
!parsedDirs.has(currentDir)
) {
parsedDirs.add(currentDir);
try {
if (await isDirectoryEmpty(currentDir)) {
await fs.rmdir(currentDir);
currentDir = path.dirname(currentDir);
} else {
break; // Stop if directory is not empty
}
} catch {
break; // Stop on any error (e.g., permissions, non-existent directory)
}
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure no partial directory removals on transient failures.

When catching errors at line 41, any partially removed directories remain unreverted. Consider adding a rollback mechanism or differentiating which errors can be retried (e.g., temporary permission issues). This avoids leaving half-removed directory states.

Comment on lines +178 to +184
if (deletedCleanFiles.length > 0) {
// clean up empty directories
await removeEmptyAncestorDirectories(
deletedCleanFiles.map((f) => path.join(projectDirectory, f.filePath)),
projectDirectory,
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid repeated directory removal calls in loops.

Whenever multiple files in the same directory are deleted, repeated calls to removeEmptyAncestorDirectories might be unnecessary if the function is not idempotent or if it triggers duplicative checks. Consider batching the directory removal in the calling function (which you are partly doing by passing the entire array).

"inflection": "3.0.0",
"json-stringify-pretty-compact": "^4.0.0",
"lodash": "^4.17.21",
"memfs": "4.15.1",
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Move memfs to devDependencies as it's only used in tests

Based on the search results, memfs is exclusively used in test files (.test.ts) and mock files (__mocks__ directory). Specifically:

  • Used in test files for volume manipulation: loader.unit.test.ts, copy-directory-action.unit.test.ts, generator-output-writer.unit.test.ts, and directories.unit.test.ts
  • Used in mock files to mock the filesystem: fs.ts, fs.cts, promises.ts, and promises.cts

There are no usages in production code, so this package should be moved to devDependencies.

🔗 Analysis chain

Verify if memfs should be a runtime dependency.

memfs is typically used for testing purposes, but it's added as a runtime dependency. This might indicate:

  1. It's being used in production code for in-memory operations, or
  2. It should be moved to devDependencies if it's only used for testing.

Let's verify its usage:


Let me try a different approach to verify the usage of memfs.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how memfs is being used in the codebase

# Look for memfs imports in source files
echo "Checking memfs usage in source code..."
rg -l "memfs" --type ts --type tsx --glob '!**/*.test.{ts,tsx}' --glob '!**/__mocks__/**'

# Look for memfs imports in test files
echo "Checking memfs usage in test files..."
rg -l "memfs" --type ts --type tsx --glob '**/*.test.{ts,tsx}' --glob '**/__mocks__/**'

Length of output: 405


Script:

#!/bin/bash
# Search for memfs usage in all files, excluding node_modules
echo "Checking memfs usage in source code..."
rg -l "memfs" --type-add 'ts:*.{ts,tsx}' --type ts --glob '!**/*.test.{ts,tsx}' --glob '!**/__mocks__/**' --glob '!**/node_modules/**'

echo -e "\nChecking memfs usage in test files..."
rg -l "memfs" --type-add 'ts:*.{ts,tsx}' --type ts --glob '**/*.test.{ts,tsx}' --glob '**/__mocks__/**' --glob '!**/node_modules/**'

# Let's also check the actual usage context
echo -e "\nChecking the context of memfs usage..."
rg "memfs" --type-add 'ts:*.{ts,tsx}' --type ts --glob '!**/node_modules/**' -C 2

Length of output: 2416

@kingston kingston merged commit 17fc44f into main Dec 26, 2024
4 checks passed
@kingston kingston deleted the kingston/eng-516-delete-empty-folders-when-syncing branch December 26, 2024 15:13
@github-actions github-actions bot mentioned this pull request Jan 17, 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.

2 participants