Skip to content

Conversation

@kingston
Copy link
Collaborator

@kingston kingston commented Jan 12, 2025

Summary by CodeRabbit

Release Notes for Sync Package

  • New Features

    • Introduced new utility functions for file existence checks and directory creation.
    • Added new snippets for TypeScript code generation related to providers and generators.
    • Enhanced functionality for managing generator configurations and outputs.
  • Refactoring

    • Restructured module imports and file organization.
    • Updated type definitions and schemas for generator tasks and outputs.
    • Simplified generator and provider configurations.
  • Dependency Updates

    • Added globby library for file path matching.
    • Removed multimatch dependency.
    • Updated versioning for core dependencies.
  • Performance Improvements

    • Streamlined file reading and generator loading processes.
    • Enhanced type safety for JSON parsing and validation.
  • Breaking Changes

    • Significant reorganization of module structure.
    • Changes to import paths and type definitions.
    • Removal of some existing utility functions and interfaces.

@linear
Copy link

linear bot commented Jan 12, 2025

ENG-542 Refactor sync engine

We have historical code organization that is hard to follow so we should refactor the sync engine to better reflect better code organization.

@changeset-bot
Copy link

changeset-bot bot commented Jan 12, 2025

🦋 Changeset detected

Latest commit: b3539b7

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

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

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

This pull request introduces a comprehensive refactoring of the sync package, focusing on improving code organization and modularity. The changes involve restructuring the project's directory structure, updating import paths, and consolidating type definitions across multiple modules. Key modifications include moving core functionalities into more specialized directories like generators, output, and providers, and updating the way generators, outputs, and providers are managed and imported. Additionally, several files have been removed, and new utility functions have been added to enhance functionality.

Changes

File/Directory Change Summary
.changeset/blue-wolves-nail.md Updated dependency versions for @halfdomelabs/sync and @halfdomelabs/core-generators
.vscode/generator.json.code-snippets Added new TypeScript code snippets for generators and providers
packages/sync/eslint.config.js Updated ESLint ignore pattern for test directories
packages/sync/package.json Added globby dependency, removed multimatch
packages/sync/src/actions/* Updated import paths for builder action creator and replaced readDirectoryRecursive with globby
packages/sync/src/core/* Removed multiple core files, moved types to new locations
packages/sync/src/generators/* Added new modules for generator management and defined new types/interfaces
packages/sync/src/output/* Created new modules for output handling and merge algorithms
packages/sync/src/providers/* Restructured provider-related types and utilities, removed ProviderExportScope
packages/sync/src/runner/* Updated import paths and type references
packages/sync/src/utils/* Added new utility functions, removed deprecated ones
packages/sync/tsconfig.json Updated TypeScript exclusion pattern
packages/sync/.prettierignore Updated ignored test files path

Sequence Diagram

sequenceDiagram
    participant Generator
    participant GeneratorLoader
    participant ProviderManager
    participant OutputBuilder
    
    Generator->>GeneratorLoader: Load generator configuration
    GeneratorLoader-->>Generator: Return generator config
    Generator->>ProviderManager: Resolve dependencies
    ProviderManager-->>Generator: Provide dependencies
    Generator->>OutputBuilder: Create output builder
    OutputBuilder->>Generator: Prepare output
    Generator->>OutputBuilder: Write files
    OutputBuilder-->>Generator: Confirm output generation
Loading

The sequence diagram illustrates the high-level flow of the generator system after the refactoring, showing how generators are loaded, dependencies are resolved, and output is generated through a more modular approach.

Finishing Touches

  • 📝 Generate Docstrings (Beta)

🪧 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: 4

🔭 Outside diff range comments (2)
packages/sync/src/providers/providers.ts (1)

Line range hint 134-188: Ensure correct binding of 'this' in returned methods

The methods dependency and export, along with their nested methods like optional, reference, and optionalReference, use the this keyword. Since these methods are returned as part of an object and may be called independently, this might not refer to the expected context, leading to potential errors. To ensure that this refers to the correct object, consider converting these methods to arrow functions, which lexically bind this.

Apply this diff to fix the this binding issue:

   return {
     type: 'type',
     name,
     isReadOnly: options?.isReadOnly,
-    dependency() {
+    dependency: () => {
       return {
         ...this,
         type: 'dependency',
         options: options?.isReadOnly ? { isReadOnly: true } : {},
-        optional() {
+        optional: () => {
           return toMerged(this, { options: { optional: true } });
         },
-        reference(exportName) {
+        reference: (exportName) => {
           if (this.options.exportName !== undefined) {
             throw new Error('Cannot overwrite export name on provider type');
           }
           return toMerged(this, {
             options: { exportName },
           });
         },
-        optionalReference(exportName) {
+        optionalReference: (exportName) => {
           if (this.options.exportName !== undefined) {
             throw new Error('Cannot overwrite export name on provider type');
           }
           return toMerged(this, {
             options: { exportName: exportName ?? '', optional: true },
           });
         },
       };
     },
-    export(scope, exportName) {
+    export: (scope, exportName) => {
       return {
         ...this,
         type: 'export',
         exports: [{ scope, exportName }],
-        andExport(scope, exportName) {
+        andExport: (scope, exportName) => {
           return toMerged(this, {
             exports: [...this.exports, { scope, exportName }],
           });
         },
       };
     },
   };
packages/sync/src/actions/write-template-action.ts (1)

Line range hint 13-31: Add error handling and input validation.

The template action needs better error handling:

  1. No validation of template existence
  2. Template path construction could be unsafe
  3. No error handling for file operations
  4. Template rendering errors not caught

Consider this improved implementation:

 export const writeTemplateAction = createBuilderActionCreator<[Options]>(
   (options: Options) => async (builder) => {
     const { destination, template, data, noFormat } = options;
 
+    if (!template || !destination) {
+      throw new Error('Template and destination are required');
+    }
+
     const templatePath = path.join(
       builder.generatorBaseDirectory,
       'templates',
       template,
     );
 
-    const templateContents = await fs.readFile(templatePath, 'utf8');
+    try {
+      // Verify template exists
+      await fs.access(templatePath);
+      
+      const templateContents = await fs.readFile(templatePath, 'utf8');
 
-    const contents = ejs.render(templateContents, data);
+      // Render template with error handling
+      const contents = await new Promise<string>((resolve, reject) => {
+        try {
+          const result = ejs.render(templateContents, data);
+          resolve(result);
+        } catch (error) {
+          reject(new Error(`Template rendering failed: ${error.message}`));
+        }
+      });
 
-    builder.writeFile(destination, contents, { shouldFormat: !noFormat });
+      builder.writeFile(destination, contents, { shouldFormat: !noFormat });
+    } catch (error) {
+      throw new Error(`Failed to process template ${template}: ${error.message}`);
+    }
   },
 );
🧹 Nitpick comments (11)
packages/sync/src/utils/fs.ts (1)

9-15: Refactor pathExists to use async/await syntax for consistency

The function pathExists uses .then() and .catch() for promise handling, whereas the rest of the code uses async/await. Refactoring to use async/await will improve code consistency and readability.

Apply this diff to refactor the function:

 export async function pathExists(filePath: string): Promise<boolean> {
-  return fs
-      .access(filePath)
-      .then(() => true)
-      .catch(() => false);
+  try {
+    await fs.access(filePath);
+    return true;
+  } catch {
+    return false;
+  }
 }
packages/sync/src/engine/engine.ts (1)

49-52: Consider strict schema validation instead of .passthrough()

When reading the root descriptor, using baseDescriptorSchema.passthrough() allows extra properties not defined in the schema. If you want to enforce strict schema validation and prevent unexpected properties, consider using .strict() instead of .passthrough().

packages/sync/src/generators/loader.ts (1)

98-98: Ensure replaceAll compatibility with supported Node.js versions

The use of String.prototype.replaceAll may not be supported in Node.js versions prior to 15.0.0. If you need to support earlier versions, consider using replace with a global regular expression or split and join as alternatives.

packages/sync/src/utils/load-default-export.ts (1)

4-9: Enhance JSDoc with security considerations.

The function documentation should include security considerations for dynamic imports.

Add security notes to the JSDoc:

 /**
  * Simple function to load the default export of a module dynamically
  *
  * @param modulePath - Path to the module to load
  * @returns The default export of the module
+ * @throws {Error} If the module cannot be loaded
+ * @security This function performs dynamic imports. Ensure that modulePath comes from a trusted source.
  */
packages/sync/src/output/builder-action.ts (1)

23-29: Consider adding error handling.

While the implementation is clean, consider adding error handling for potential failures during execution.

 export function createBuilderActionCreator<T extends unknown[]>(
   creator: (...args: T) => BuilderAction['execute'],
 ): BuilderActionCreator<T> {
   return (...args) => ({
-    execute: (builder) => creator(...args)(builder),
+    execute: async (builder) => {
+      try {
+        return await creator(...args)(builder);
+      } catch (error) {
+        throw new Error(`Builder action execution failed: ${error.message}`);
+      }
+    },
   });
 }
packages/sync/src/output/formatter.ts (1)

20-33: Consider adding format options parameter.

The formatter interface is well-designed, but consider adding an optional options parameter to the format function for future extensibility.

 export interface GeneratorOutputFormatter {
   name: string;
   format: FormatFunction;
   fileExtensions?: string[];
+  options?: Record<string, unknown>;
 }
packages/sync/src/actions/copy-directory-action.ts (1)

Line range hint 31-46: Consider adding error handling for file operations.

While the implementation works, it would be beneficial to add proper error handling for file operations.

     await Promise.all(
       files.map(async (file) => {
+        try {
           const relativePath = path.relative(templatePath, file);
           const destinationPath = path.join(destination, relativePath);
 
           if (shouldFormat) {
             const fileContents = await fs.readFile(file, 'utf8');
             builder.writeFile(destinationPath, fileContents, {
               shouldFormat: true,
             });
           } else {
             const fileContents = await fs.readFile(file);
             builder.writeFile(destinationPath, fileContents);
           }
+        } catch (error) {
+          throw new Error(`Failed to copy file ${file}: ${error.message}`);
+        }
       }),
     );
packages/sync/src/actions/copy-directory-action.unit.test.ts (1)

19-24: Consider using object destructuring for action parameters

While the functionality is correct, consider using object destructuring for better readability:

-    await copyDirectoryAction({
-      source: '/',
-      destination: '/',
-    }).execute(builder);
+    const { execute } = copyDirectoryAction({
+      source: '/',
+      destination: '/',
+    });
+    await execute(builder);
packages/sync/src/generators/generators.ts (1)

132-160: Consider adding validation examples in documentation.

The GeneratorConfig interface is well-structured, but it would be helpful to add examples of descriptor validation in the documentation.

packages/sync/src/output/generator-task-output.ts (1)

260-273: Consider adding formatter priority mechanism.

The current implementation doesn't handle formatter priority, which could be important when multiple formatters could apply to the same file extension.

Consider adding a priority field to the formatter interface and sorting formatters by priority when applying them.

packages/sync/src/utils/create-generator-with-tasks.ts (1)

Line range hint 25-31: Consider adding JSDoc for type parameters.

While the interface documentation is good, consider adding JSDoc for the type parameters to improve code maintainability.

Apply this diff to add type parameter documentation:

 /**
  * The output of a simple generator task.
+ * @template TaskOutput - The type of the task's output
  */
 export interface SimpleGeneratorTaskOutput<TaskOutput = void> {
   name: string;
   getOutput: () => TaskOutput;
 }

Also applies to: 38-40

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9cae149 and 10dfcc3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (53)
  • .changeset/blue-wolves-nail.md (1 hunks)
  • .vscode/generator.json.code-snippets (0 hunks)
  • packages/core-generators/src/generators/node/prettier/index.ts (1 hunks)
  • packages/sync/eslint.config.js (1 hunks)
  • packages/sync/package.json (1 hunks)
  • packages/sync/src/actions/copy-directory-action.ts (2 hunks)
  • packages/sync/src/actions/copy-directory-action.unit.test.ts (4 hunks)
  • packages/sync/src/actions/copy-file-action.ts (1 hunks)
  • packages/sync/src/actions/write-formatted-action.ts (1 hunks)
  • packages/sync/src/actions/write-json-action.ts (1 hunks)
  • packages/sync/src/actions/write-template-action.ts (1 hunks)
  • packages/sync/src/core/descriptor.ts (0 hunks)
  • packages/sync/src/core/engine/descriptor-loader.ts (0 hunks)
  • packages/sync/src/core/engine/descriptor-loader.unit.test.ts (0 hunks)
  • packages/sync/src/core/generator-output.ts (0 hunks)
  • packages/sync/src/core/generator.ts (0 hunks)
  • packages/sync/src/core/index.ts (0 hunks)
  • packages/sync/src/core/loader.ts (0 hunks)
  • packages/sync/src/engine/engine.ts (3 hunks)
  • packages/sync/src/generators/entry-builder.ts (4 hunks)
  • packages/sync/src/generators/entry-builder.unit.test.ts (4 hunks)
  • packages/sync/src/generators/generators.ts (1 hunks)
  • packages/sync/src/generators/index.ts (1 hunks)
  • packages/sync/src/generators/loader.ts (1 hunks)
  • packages/sync/src/generators/loader.unit.test.ts (3 hunks)
  • packages/sync/src/index.ts (1 hunks)
  • packages/sync/src/output/builder-action.ts (1 hunks)
  • packages/sync/src/output/formatter.ts (1 hunks)
  • packages/sync/src/output/generator-task-output.ts (1 hunks)
  • packages/sync/src/output/index.ts (1 hunks)
  • packages/sync/src/output/merge-algorithms/index.ts (1 hunks)
  • packages/sync/src/output/write-generator-output.ts (2 hunks)
  • packages/sync/src/output/write-generator-output.unit.test.ts (11 hunks)
  • packages/sync/src/providers/export-scopes.ts (1 hunks)
  • packages/sync/src/providers/index.ts (1 hunks)
  • packages/sync/src/providers/provider.unit.test.ts (1 hunks)
  • packages/sync/src/providers/providers.ts (5 hunks)
  • packages/sync/src/runner/dependency-map.ts (1 hunks)
  • packages/sync/src/runner/dependency-map.unit.test.ts (1 hunks)
  • packages/sync/src/runner/dependency-sort.ts (1 hunks)
  • packages/sync/src/runner/dependency-sort.unit.test.ts (1 hunks)
  • packages/sync/src/runner/generator-runner.ts (5 hunks)
  • packages/sync/src/runner/generator-runner.unit.test.ts (2 hunks)
  • packages/sync/src/runner/index.ts (1 hunks)
  • packages/sync/src/runner/tests/factories.test-helper.ts (2 hunks)
  • packages/sync/src/runner/utils.ts (1 hunks)
  • packages/sync/src/utils/create-generator-types.ts (1 hunks)
  • packages/sync/src/utils/create-generator-with-tasks.ts (2 hunks)
  • packages/sync/src/utils/fs.ts (1 hunks)
  • packages/sync/src/utils/load-default-export.ts (1 hunks)
  • packages/sync/src/utils/require.ts (0 hunks)
  • packages/sync/src/utils/validation.ts (1 hunks)
  • packages/sync/tsconfig.json (1 hunks)
💤 Files with no reviewable changes (9)
  • packages/sync/src/core/engine/descriptor-loader.unit.test.ts
  • packages/sync/src/core/engine/descriptor-loader.ts
  • packages/sync/src/core/descriptor.ts
  • .vscode/generator.json.code-snippets
  • packages/sync/src/core/index.ts
  • packages/sync/src/utils/require.ts
  • packages/sync/src/core/loader.ts
  • packages/sync/src/core/generator-output.ts
  • packages/sync/src/core/generator.ts
✅ Files skipped from review due to trivial changes (14)
  • packages/sync/src/providers/index.ts
  • packages/sync/eslint.config.js
  • packages/sync/src/actions/write-json-action.ts
  • packages/sync/src/utils/create-generator-types.ts
  • packages/sync/src/runner/dependency-sort.unit.test.ts
  • packages/sync/src/runner/dependency-map.unit.test.ts
  • .changeset/blue-wolves-nail.md
  • packages/sync/src/runner/index.ts
  • packages/sync/src/generators/index.ts
  • packages/sync/src/output/index.ts
  • packages/sync/src/actions/copy-file-action.ts
  • packages/sync/src/utils/validation.ts
  • packages/sync/src/providers/provider.unit.test.ts
  • packages/sync/src/actions/write-formatted-action.ts
🔇 Additional comments (39)
packages/sync/tsconfig.json (1)

12-12: LGTM! Verify test file migration.

The updated exclusion pattern aligns with the refactoring objectives to improve code organization. The change reflects the new directory structure where test files have been moved from src/merge/tests to src/output/merge-algorithms/tests.

Let's verify that all test files have been properly migrated:

✅ Verification successful

Test file migration verified successfully

The test files have been completely migrated to the new location src/output/merge-algorithms/tests with proper organization into categories (diff3, json, simple-diff). No test files remain in the old location.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify test file migration is complete
# Expected: No test files in old location, all test files in new location

echo "Checking for any remaining test files in the old location..."
fd . "packages/sync/src/merge/tests"

echo "Verifying test files exist in the new location..."
fd . "packages/sync/src/output/merge-algorithms/tests"

echo "Checking for any test files that might have been missed in the migration..."
rg -g "*.test.*" -g "*.spec.*" "packages/sync/src/merge"

Length of output: 3155

packages/sync/src/output/write-generator-output.unit.test.ts (4)

6-9: LGTM! Clean import restructuring.

The import path changes and mock setup follow best practices. The use of explicit node: imports and proper mocking of filesystem modules is a good practice.


102-102: LGTM! Comprehensive test coverage for various scenarios.

The test suite thoroughly covers different use cases including:

  • Empty outputs
  • File merging strategies
  • Conflict handling
  • Binary files
  • Post-write command execution

Also applies to: 135-135, 171-171, 200-200, 214-214, 240-240, 286-286, 318-318


23-23: Address the TODO comment for clean folder tests.

The test suite is missing coverage for folder cleaning functionality. This is important for ensuring proper cleanup during file operations.

Would you like me to help implement the missing test cases for clean folder functionality? This would help ensure comprehensive test coverage.


26-26: Verify formatter functionality with the new globalFormatters parameter.

The test correctly validates the formatting functionality with the new parameter name. However, let's ensure we have test coverage for edge cases with multiple formatters.

Run this script to check for other formatter-related tests:

Also applies to: 46-51

✅ Verification successful

The formatter functionality is properly tested with the globalFormatters parameter

The test suite includes comprehensive coverage for the formatter functionality:

  • Mock formatter verification with correct parameters
  • File extension filtering
  • Integration with other features (merging, binary files)
  • Edge cases with empty formatters array
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other formatter-related tests to ensure comprehensive coverage
rg -l "globalFormatters.*format.*function" --type ts

Length of output: 54


Script:

#!/bin/bash
# Search with broader patterns to find formatter-related code
echo "=== Searching for formatter-related tests ==="
rg -l "globalFormatters" --type ts

echo -e "\n=== Searching for formatter implementations ==="
ast-grep --pattern 'function $_($$_): $_ {
  $$$
  format($$$)
  $$$
}'

echo -e "\n=== Checking the specific test file content ==="
cat packages/sync/src/output/write-generator-output.unit.test.ts

Length of output: 9131

packages/sync/src/providers/export-scopes.ts (1)

1-34: Well-structured implementation of ProviderExportScope

The implementation of ProviderExportScope and createProviderExportScope is clear and follows best practices. Input validation and error handling are appropriately handled.

packages/sync/src/output/merge-algorithms/index.ts (1)

Line range hint 1-5: LGTM! Well-organized exports.

The new export follows the established pattern and maintains consistency with other exports in the file.

packages/sync/src/index.ts (1)

2-5: Verify impact of restructured exports on package consumers.

The reorganization from core/merge to engine/generators/output/providers improves code organization. However, since this is the main entry point, we should verify that this change doesn't break existing consumers.

Let's check for potential usage patterns that might be affected:

✅ Verification successful

Export reorganization appears safe

The codebase scan shows that all consumers import from the main package entry point @halfdomelabs/sync rather than from specific subdirectories. The restructuring of internal exports from core/merge to engine/generators/output/providers won't impact external consumers since they're using the public API through the main entry point.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for import patterns from @coderabbit/sync across the codebase
rg "from ['\"]@coderabbit/sync['\"]|from ['\"]@coderabbit/sync/(core|merge)['\"]" -g '!{node_modules,dist}/**'

Length of output: 120


Script:

#!/bin/bash
# First check package name in package.json
cat packages/sync/package.json | jq -r '.name'

# Then search for relative imports of core/merge
rg "from ['\"]\.\.?/(?:core|merge)['\"]" packages/sync/

# Also search for any imports from this package in tests
rg "from ['\"]\.\.?/" packages/sync/src/__tests__/

Length of output: 251


Script:

#!/bin/bash
# Search for package imports with correct name
rg "from ['\"]@halfdomelabs/sync['\"]|from ['\"]@halfdomelabs/sync/(core|merge)['\"]" -g '!{node_modules,dist}/**'

# Find and check test files
fd "__tests__" packages/sync/ -t f -x rg "from ['\"]\.\.?/" {}

Length of output: 23244

packages/sync/src/actions/write-template-action.ts (1)

5-5: LGTM! Import path update aligns with refactoring.

The change from relative to absolute import using @src alias improves code organization and maintainability.

packages/sync/src/output/builder-action.ts (1)

6-8: LGTM! Well-designed interface with async support.

The BuilderAction interface is cleanly designed with proper TypeScript types and async support.

packages/sync/src/output/formatter.ts (1)

11-15: LGTM! Well-typed format function definition.

The FormatFunction type is well-defined with proper parameter types and async support.

packages/sync/src/runner/utils.ts (1)

3-11: LGTM! Clean import path updates.

The import path updates align with the package reorganization effort.

packages/sync/src/actions/copy-directory-action.ts (1)

25-29: LGTM! Improved file discovery with globby.

The switch to globby provides more robust file discovery and potential performance improvements.

packages/sync/src/runner/tests/factories.test-helper.ts (2)

6-6: LGTM! Import path update aligns with package reorganization

The import path change reflects the move of generator-related types to a dedicated generators directory, improving code organization.


54-54: LGTM! Property addition matches type changes

The addition of generatorBaseDirectory property aligns with the updated GeneratorEntry interface.

packages/sync/src/runner/dependency-sort.ts (1)

5-5: LGTM! Import path update maintains consistency

The import path update aligns with the package's new organizational structure, maintaining consistency with other files.

packages/sync/src/actions/copy-directory-action.unit.test.ts (1)

4-5: LGTM! Output builder standardization

The switch to GeneratorTaskOutputBuilder standardizes the output handling across the codebase.

packages/sync/src/runner/generator-runner.unit.test.ts (2)

3-4: LGTM! Comprehensive import reorganization

The import updates properly organize types and utilities into their respective specialized directories (generators, providers, output).

Also applies to: 7-14


28-28: LGTM! Type update maintains consistency

The builder parameter type update to GeneratorTaskOutputBuilder aligns with the standardized output handling.

packages/sync/src/generators/loader.unit.test.ts (2)

4-4: LGTM! Import and mock updates align with the refactored structure.

The changes correctly reflect the reorganization of the sync package, with updated import paths and mock implementations.

Also applies to: 6-7, 12-12, 14-14


46-51: LGTM! Mock implementations are consistent.

The mock implementations correctly use loadDefaultExport consistently across all test cases, maintaining the test coverage while adapting to the refactored structure.

Also applies to: 102-107

packages/sync/src/runner/generator-runner.ts (3)

69-73: Improved error message clarity for provider validation.

The error message now more clearly indicates when a task has exports but no providers.


94-96: LGTM! Updated output builder instantiation.

The code correctly uses the new GeneratorTaskOutputBuilder class with the appropriate constructor parameter.


120-122: Verify the impact of renaming 'formatters' to 'globalFormatters'.

The property has been renamed from 'formatters' to 'globalFormatters'. Ensure all consumers of this output are updated accordingly.

✅ Verification successful

Property rename verification completed successfully

The rename from 'formatters' to 'globalFormatters' has been properly implemented across the codebase. The only occurrence of '.formatters' found is unrelated and belongs to Pino logger configuration in the logger service.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old 'formatters' property
rg -l "\.formatters\b" --type ts

Length of output: 106


Script:

#!/bin/bash
# Check the content of the file with formatters reference
cat packages/fastify-generators/src/generators/core/logger-service/index.ts

# Search for type definitions related to generator outputs
ast-grep --pattern 'interface $_Output {
  $$$
  formatters$_
  $$$
}'

# Also search for type definitions with globalFormatters
ast-grep --pattern 'interface $_Output {
  $$$
  globalFormatters$_
  $$$
}'

Length of output: 4591


Script:

#!/bin/bash
# Search for generator output related types
rg -A 5 "type\s+GeneratorOutput|interface\s+GeneratorOutput" --type ts

Length of output: 1331


Script:

#!/bin/bash
# Get the full content of the generator task output file
cat packages/sync/src/output/generator-task-output.ts

Length of output: 6901

packages/sync/src/generators/generators.ts (2)

16-25: LGTM! Well-defined base schema with clear documentation.

The base descriptor schema is well-documented and properly defines the core fields required for a generator descriptor.


89-123: Excellent type safety in GeneratorTask interface.

The GeneratorTask interface provides strong type safety through:

  • Generic type parameters for export and dependency maps
  • Conditional type for the run method return value
  • Comprehensive documentation
packages/sync/src/generators/entry-builder.ts (2)

92-95: LGTM! Improved JSON validation with schema.

The switch to readJsonWithSchema with passthrough() provides better validation while maintaining flexibility for additional fields.


167-169: LGTM! Consistent generator config structure.

The return object correctly includes both the generator config and base directory, maintaining consistency with the new structure.

packages/sync/src/generators/entry-builder.unit.test.ts (3)

3-3: LGTM! Import changes align with the refactoring.

The imports have been properly updated to reflect the new package structure, with clear separation of concerns between utilities, providers, and generators.

Also applies to: 8-14


184-184: LGTM! Mock expectations properly updated.

The mock expectations for readJsonWithSchema are correctly updated to include the base descriptor schema.

Also applies to: 202-205


64-116: Verify test coverage for all generator configurations.

The generator configurations have been restructured to use a config property, which is a good improvement for organization. However, ensure that all possible generator configurations are tested, including edge cases.

Run this script to analyze test coverage:

✅ Verification successful

Test coverage for generator configurations is comprehensive and matches production patterns.

The test configurations in entry-builder.unit.test.ts effectively cover:

  • Basic generator functionality with dependencies and exports
  • Nested generator configurations
  • Reference handling and duplicate references
  • Descriptor validation

These patterns align well with real-world generator implementations found across the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test coverage for generator configurations
# Look for generator configurations in the codebase and compare with test cases

# Find all generator configurations
rg -l "createGeneratorWithTasks|createGenerator" 

# Find corresponding test files
rg -l "describe.*generator" --glob "**/*.test.ts"

Length of output: 10354


Script:

#!/bin/bash
# Search for test files with better patterns
echo "=== Test Files ==="
rg -l "describe.*[\"'].*generator" --type ts
echo -e "\n=== Generator Implementations ==="
# Look for actual generator implementations
ast-grep --pattern 'createGenerator = () => $$$'
ast-grep --pattern 'createGeneratorWithTasks($$$)'

Length of output: 66750

packages/sync/src/runner/dependency-map.ts (1)

8-9: LGTM! Import paths updated correctly.

The import paths have been properly updated to reflect the new package structure, maintaining clear boundaries between generators and providers.

packages/sync/src/output/generator-task-output.ts (2)

11-132: Well-structured type definitions with clear documentation.

The interfaces are well-organized and thoroughly documented, providing a robust type system for generator outputs.


185-201: LGTM! Proper error handling in writeFile method.

The method includes appropriate validation:

  • Prevents file overwriting
  • Validates formatting constraints for Buffer contents
packages/core-generators/src/generators/node/prettier/index.ts (1)

Line range hint 237-241: LGTM! Method updated to match new API.

The change from addFormatter to addGlobalFormatter correctly aligns with the new GeneratorTaskOutputBuilder API.

packages/sync/src/output/write-generator-output.ts (2)

9-12: LGTM!

The import paths have been reorganized to improve modularity by moving merge-related types to a dedicated module.


263-263: Verify the impact of the parameter rename.

The parameter has been renamed from formatters to output.globalFormatters. This change suggests a broader refactoring of the formatter management system.

Run this script to verify the parameter usage across the codebase:

packages/sync/src/utils/create-generator-with-tasks.ts (1)

12-16: LGTM!

The import paths have been reorganized to improve code organization, with types now imported from their respective modules.

packages/sync/package.json (1)

40-40: LGTM! Good choice replacing multimatch with globby.

The switch to globby is a good decision as it's more actively maintained and provides better performance for glob pattern matching.

Run this script to verify the removal of multimatch usage:

✅ Verification successful

✓ Verified: multimatch successfully replaced with globby

All instances of multimatch have been completely removed and replaced with proper globby implementations across the codebase. The migration is thorough and well-executed, with appropriate glob patterns and options being used in each context.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that multimatch has been completely replaced

# Search for any remaining multimatch imports or requires
echo "Checking for remaining multimatch usage..."
rg "(?:require|import).*['\"]multimatch['\"]"

# Search for potential glob pattern matching to ensure they're compatible with globby
echo "Checking glob pattern usage..."
rg "(?:glob|pattern).*['\"]\\*"

Length of output: 601


Script:

#!/bin/bash
# Check implementation details of files using globby
echo "Checking copy-directory-action.ts implementation..."
ast-grep --pattern 'const $_ = await globby($$$)' -f packages/sync/src/actions/copy-directory-action.ts

echo -e "\nChecking error handling..."
rg -B2 -A2 "globby\(" 

Length of output: 3770

Comment on lines +155 to +163
if (!generatorPackagePaths[plugin.name]) {
const pluginIndex = require.resolve(plugin.name);
const pluginPath = await packageUp({
cwd: path.dirname(pluginIndex),
});
if (pluginPath) {
generatorPackagePaths[plugin.name] = path.dirname(pluginPath);
}
}
Copy link

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 when resolving plugin paths

In the loadGeneratorsForProject function, if require.resolve(plugin.name) fails because the plugin cannot be found, it will throw an error and halt execution. To handle missing or misconfigured plugins gracefully, consider wrapping the require.resolve and packageUp calls in a try/catch block to handle the errors appropriately without crashing the application.

Apply this diff to add error handling:

         if (!generatorPackagePaths[plugin.name]) {
-          const pluginIndex = require.resolve(plugin.name);
-          const pluginPath = await packageUp({
-            cwd: path.dirname(pluginIndex),
-          });
-          if (pluginPath) {
-            generatorPackagePaths[plugin.name] = path.dirname(pluginPath);
-          }
+          try {
+            const pluginIndex = require.resolve(plugin.name);
+            const pluginPath = await packageUp({
+              cwd: path.dirname(pluginIndex),
+            });
+            if (pluginPath) {
+              generatorPackagePaths[plugin.name] = path.dirname(pluginPath);
+            }
+          } catch (error) {
+            logger.warn(`Failed to resolve plugin "${plugin.name}": ${error.message}`);
+          }
         }
📝 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
if (!generatorPackagePaths[plugin.name]) {
const pluginIndex = require.resolve(plugin.name);
const pluginPath = await packageUp({
cwd: path.dirname(pluginIndex),
});
if (pluginPath) {
generatorPackagePaths[plugin.name] = path.dirname(pluginPath);
}
}
if (!generatorPackagePaths[plugin.name]) {
try {
const pluginIndex = require.resolve(plugin.name);
const pluginPath = await packageUp({
cwd: path.dirname(pluginIndex),
});
if (pluginPath) {
generatorPackagePaths[plugin.name] = path.dirname(pluginPath);
}
} catch (error) {
logger.warn(`Failed to resolve plugin "${plugin.name}": ${error.message}`);
}
}

Comment on lines +10 to +23
export async function loadDefaultExport(modulePath: string): Promise<unknown> {
const fileUrl = modulePath.startsWith('file://')
? modulePath
: pathToFileURL(modulePath).href;
const fullUrl =
path.extname(fileUrl) === '.js' ? fileUrl : `${fileUrl}/index.js`;
const importedModule = (await import(fullUrl)) as
| { default?: unknown }
| undefined;
if (!importedModule?.default) {
return undefined;
}
return importedModule.default;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling and input validation.

The function needs better error handling and input validation:

  1. Missing try-catch for import failures
  2. No validation of modulePath
  3. Path manipulation could be more robust

Consider this improved implementation:

 export async function loadDefaultExport(modulePath: string): Promise<unknown> {
+  if (!modulePath) {
+    throw new Error('modulePath is required');
+  }
+
   const fileUrl = modulePath.startsWith('file://')
     ? modulePath
     : pathToFileURL(modulePath).href;
+
   const fullUrl =
-    path.extname(fileUrl) === '.js' ? fileUrl : `${fileUrl}/index.js`;
+    path.extname(fileUrl) === '.js' ? fileUrl : path.join(fileUrl, 'index.js');
+
+  try {
     const importedModule = (await import(fullUrl)) as
       | { default?: unknown }
       | undefined;
     if (!importedModule?.default) {
       return undefined;
     }
     return importedModule.default;
+  } catch (error) {
+    throw new Error(`Failed to load module at ${modulePath}: ${error.message}`);
+  }
 }

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

Comment on lines +103 to +114
const generatorConfigEntry = context.generatorMap[descriptor.generator];
if (!generatorConfigEntry) {
throw new Error(
`Generator ${descriptor.generator} not found in generator map`,
);
}
const { config } = generatorConfigEntry;

const { children = {}, validatedDescriptor } =
generatorConfig.parseDescriptor(descriptor, {
generatorMap: context.generatorMap,
id,
logger: context.logger,
});
const { children = {}, validatedDescriptor } = config.parseDescriptor(
descriptor,
{ id, logger: context.logger },
);
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Missing test coverage for generator not found error

The error handling for missing generators is implemented but not verified in tests. A test case should be added to entry-builder.unit.test.ts to verify that attempting to use a non-existent generator throws the expected error message.

Example test to add:

it('should throw when generator is not found in generator map', async () => {
  await expect(buildGeneratorEntry(
    { generator: 'non-existent' },
    'project',
    generatorContext,
  )).rejects.toThrow('Generator non-existent not found in generator map');
});
🔗 Analysis chain

Verify error handling for missing generators.

The error handling for missing generators has been improved. However, we should verify that the error message is propagated correctly to the user.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for error handling patterns in tests
rg -A 5 "Generator.*not found in generator map" --type ts

Length of output: 533


Script:

#!/bin/bash
# Find test files related to entry-builder
fd "entry-builder.test|entry-builder.spec" --type f

# Find imports of entry-builder
rg -l "from.*entry-builder" --type ts

# Look for error handling patterns in test files
rg -B 2 -A 2 "expect.*toThrow|expect.*rejects" --type ts packages/sync/src/

Length of output: 3874


Script:

#!/bin/bash
# Check entry-builder test file content
cat packages/sync/src/generators/entry-builder.unit.test.ts

# Look for any test cases involving generatorMap
rg -B 3 -A 3 "generatorMap" packages/sync/src/generators/entry-builder.unit.test.ts

Length of output: 7193

Comment on lines +209 to +219
resolvePath(relativePath: string): string {
return (
(
this.baseDirectory
? path.join(this.baseDirectory, relativePath)
: relativePath
)
// normalize all paths to POSIX style / paths
.replaceAll(path.sep, path.posix.sep)
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider handling path traversal security risks.

The path resolution logic should be enhanced to prevent path traversal attacks.

Apply this diff to add path validation:

 resolvePath(relativePath: string): string {
+  const normalizedPath = path.normalize(relativePath);
+  if (normalizedPath.startsWith('..')) {
+    throw new Error('Path traversal is not allowed');
+  }
   return (
     (
       this.baseDirectory
-        ? path.join(this.baseDirectory, relativePath)
+        ? path.join(this.baseDirectory, normalizedPath)
-        : relativePath
+        : normalizedPath
     )
       // normalize all paths to POSIX style / paths
       .replaceAll(path.sep, path.posix.sep)
   );
 }
📝 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
resolvePath(relativePath: string): string {
return (
(
this.baseDirectory
? path.join(this.baseDirectory, relativePath)
: relativePath
)
// normalize all paths to POSIX style / paths
.replaceAll(path.sep, path.posix.sep)
);
}
resolvePath(relativePath: string): string {
const normalizedPath = path.normalize(relativePath);
if (normalizedPath.startsWith('..')) {
throw new Error('Path traversal is not allowed');
}
return (
(
this.baseDirectory
? path.join(this.baseDirectory, normalizedPath)
: normalizedPath
)
// normalize all paths to POSIX style / paths
.replaceAll(path.sep, path.posix.sep)
);
}

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: 1

🧹 Nitpick comments (2)
packages/sync/src/generators/entry-builder.unit.test.ts (2)

202-205: Enhance mock validation for readJsonWithSchema.

The test should explicitly verify that the correct schema is being passed to readJsonWithSchema.

Replace the generic expect.anything() with a more specific expectation:

   expect(mockedReadJsonWithSchema).toHaveBeenCalledWith(
     '/root/child-descriptor.json',
-    expect.anything() as unknown,
+    baseDescriptorSchema,
   );

Line range hint 1-205: Consider adding error case tests.

While the happy path tests are comprehensive, consider adding test cases for:

  • Invalid descriptor schema validation
  • File not found scenarios for reference children
  • Malformed generator configurations

Would you like me to provide example test cases for these scenarios?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 10dfcc3 and 2722251.

📒 Files selected for processing (2)
  • packages/sync/.prettierignore (1 hunks)
  • packages/sync/src/generators/entry-builder.unit.test.ts (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/sync/.prettierignore
🔇 Additional comments (2)
packages/sync/src/generators/entry-builder.unit.test.ts (2)

3-3: LGTM! Well-organized imports.

The import statements are properly organized, with a clear separation between function imports and type imports. The use of absolute imports for utilities is a good practice.

Also applies to: 6-6, 8-14


64-76: LGTM! Well-structured generator configurations.

The generator configurations are well-organized with a clear separation of concerns between configuration and directory information.

Also applies to: 79-88, 91-95, 98-107, 110-116

} from './generators.js';

vi.mock('./descriptor-loader');
vi.mock('../utils/fs.js');
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix mock path inconsistency.

The mock path ../utils/fs.js doesn't match the import path @src/utils/fs.js. This could lead to issues with the mock not being properly applied.

Apply this diff to fix the mock path:

-vi.mock('../utils/fs.js');
+vi.mock('@src/utils/fs.js');

Also applies to: 18-18

@kingston
Copy link
Collaborator Author

@coderabbitai pause

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: 0

♻️ Duplicate comments (1)
packages/sync/src/generators/entry-builder.unit.test.ts (1)

15-17: ⚠️ Potential issue

Fix mock path inconsistency.

The mock path ../utils/fs.js doesn't match the import path @src/utils/fs.js. This could lead to issues with the mock not being properly applied.

Apply this diff to fix the mock path:

-vi.mock('../utils/fs.js');
+vi.mock('@src/utils/fs.js');
🧹 Nitpick comments (1)
packages/sync/src/generators/entry-builder.unit.test.ts (1)

183-204: Add test cases for error scenarios.

While the happy path is well tested, consider adding test cases for:

  • Invalid JSON file content
  • Schema validation failures
  • File not found scenarios

Example test case:

it('should handle invalid JSON file', async () => {
  mockedReadJsonWithSchema.mockRejectedValueOnce(new Error('Invalid JSON'));
  
  await expect(
    buildGeneratorEntry(
      { generator: 'reference' },
      'project',
      generatorContext,
    )
  ).rejects.toThrow('Invalid JSON');
});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2722251 and b3539b7.

📒 Files selected for processing (1)
  • packages/sync/src/generators/entry-builder.unit.test.ts (4 hunks)
🔇 Additional comments (1)
packages/sync/src/generators/entry-builder.unit.test.ts (1)

62-115: Well-structured generator configurations!

The new configuration structure with separate config and directory properties provides better organization and type safety. The consistent pattern across all generator types improves maintainability.

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2025

✅ Actions performed

Reviews paused.

@kingston kingston merged commit 71e3257 into main Jan 12, 2025
5 checks passed
@kingston kingston deleted the kingston/eng-542-refactor-sync-engine branch January 12, 2025 22:02
@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