Skip to content

feat: implement Phase 1 of generator abstraction layer (RFC #45)#46

Closed
michaelbe812 wants to merge 2 commits into
mainfrom
feat/generator-abstraction-layer
Closed

feat: implement Phase 1 of generator abstraction layer (RFC #45)#46
michaelbe812 wants to merge 2 commits into
mainfrom
feat/generator-abstraction-layer

Conversation

@michaelbe812

@michaelbe812 michaelbe812 commented Sep 5, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR implements Phase 1 of RFC #45: Generator Abstraction Layer for Multi-Generator Support. It introduces a pluggable architecture that removes tight coupling with @openapitools/openapi-generator-cli while maintaining 100% backward compatibility.

✅ Status: Ready for Review

All issues from the initial code review have been addressed:

  • All tests pass (119/119 tests passing)
  • Build successful for both packages
  • Linting passes for main package
  • TypeScript compilation error-free
  • Dependencies properly declared

Key Changes

Core Abstraction Layer

  • GeneratorPlugin interface and supporting types
  • BaseGenerator abstract class for plugin development
  • GeneratorRegistry singleton for plugin management
  • PluginLoader for dynamic plugin discovery and loading
  • PluginAutoInstaller for automatic missing plugin installation
  • Comprehensive error handling with custom error classes

Bundled OpenAPI Tools Generator

  • Maintains backward compatibility as default generator
  • Implements the new GeneratorPlugin interface
  • Includes command builder for CLI argument generation
  • All existing configurations continue to work

External Plugin Package

  • New @lambda-solutions/nx-openapi-plugin-openapi-tools package
  • Demonstrates the plugin architecture pattern
  • Can be published separately to npm
  • Full TypeScript support with proper build configuration

Schema Enhancements

  • Added generator field for selecting generator plugin
  • Added generatorType field for generator-specific types
  • Added autoInstall field for automatic plugin installation
  • Full backward compatibility maintained

Architecture Benefits

  • Extensible: Easy to add new generators
  • Maintainable: Clean separation of concerns
  • Type-safe: Full TypeScript support throughout
  • Backward compatible: Zero breaking changes
  • Auto-installable: Plugins can be installed on-demand

Testing

  • Updated all executor tests to work with new architecture
  • Tests cover plugin discovery, loading, and execution
  • Auto-installation behavior properly tested
  • Error handling scenarios covered

Files Changed

  • Core abstraction implementation in packages/nx-plugin-openapi/src/lib/core/
  • Updated executor to use registry pattern
  • New external plugin package
  • Comprehensive test suite updates

Next Steps (Phase 2)

  • Create Orval generator plugin
  • Create OpenAPI TypeScript generator plugin
  • Enhance plugin discovery mechanisms
  • Add integration tests for multi-generator scenarios

Related

🤖 Generated with Claude Code

- Created core interfaces and base classes for plugin system
- Implemented generator registry and plugin loader
- Added bundled OpenAPI Tools plugin for backward compatibility
- Created external plugin package structure
- Implemented auto-installation mechanism for missing plugins
- Updated executor to use new abstraction layer
- Added generator and autoInstall fields to schema
- Maintained 100% backward compatibility

Breaking changes: None

This is Phase 1 of the multi-package architecture implementation
as described in GitHub issue #45. Existing configurations will
continue to work without any changes.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
@michaelbe812

Copy link
Copy Markdown
Contributor Author

Code Review - Phase 1 Generator Abstraction Layer

Overview

This is a well-structured implementation of Phase 1 of the generator abstraction layer as outlined in RFC #45. The implementation provides a solid foundation for pluggable generators with auto-installation and registry support while maintaining backward compatibility.

✅ Strengths

Architecture Design:

  • Clean separation of concerns with well-defined interfaces
  • Singleton registry pattern ensures consistency
  • Proper abstraction layers that allow for easy extension
  • Good error handling with custom exception types

Implementation Quality:

  • Comprehensive TypeScript interfaces with good documentation
  • BaseGenerator class provides excellent template method pattern
  • Plugin loader handles multiple discovery strategies
  • Auto-installer with package manager detection

Backward Compatibility:

  • Maintains existing executor interface
  • Provides seamless migration path
  • Bundled OpenAPI Tools generator for immediate functionality

🔧 Issues Requiring Attention

Critical - Linting Errors:
Several linting issues need to be addressed before merge:

  1. Unused variables in executor.ts:184 - Clean up destructured variables that aren't used

  2. TypeScript ESLint violations:

    • Replace any types with proper typing in command-builder.ts and plugin-loader.ts
    • Remove unused imports like logger in bundled generator
    • Fix no-require-imports violations by using proper ES6 imports where possible
  3. Dependency checks: Missing @openapitools/openapi-generator-cli in dependencies

Medium Priority:

  • Plugin discovery by keyword has placeholder implementation - consider completing or documenting limitation
  • Version comparison in BaseGenerator is basic - consider using semver library for production
  • Auto-installer prompting is stubbed - implement proper user interaction

📋 Specific Recommendations

Fix Linting Issues:

// In executor.ts line 184, remove unused destructured variables:
const { inputSpec, outputPath } = options; // Keep only what's used

// In command-builder.ts, replace 'any' with proper types:
buildArgs(options: Record<string, unknown>): string[]

Improve Type Safety:

  • Define proper interface for module loading instead of any
  • Add generic constraints where appropriate
  • Consider using branded types for package names

Dependencies:
Add missing peer dependency to package.json or document installation requirements clearly.

🧪 Testing Status

  • ❌ Tests are failing - need investigation
  • ✅ Build passes for most projects
  • ❌ Linting fails - requires fixes before merge

📊 Summary

This is a solid Phase 1 implementation that establishes the core architecture well. The code demonstrates good software engineering practices and provides a clean foundation for future generator plugins.

Recommendation: Request changes to fix linting issues and investigate test failures before approval.

Priority: Fix the linting errors first, then address test failures. The architectural decisions are sound.

Comment on lines +274 to +275
const available = gen.isAvailable() ? '✓' : '✗';
lines.push(` - ${gen.name} (${gen.displayName}) [${available}]`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

isAvailable() returns a Promise but is being used synchronously here. This will store the Promise object itself rather than its resolved value. Consider either:

  1. Making getSummary() async and using await gen.isAvailable(), or
  2. Using a synchronous check like gen.packageName ? '?' : '✓' as a fallback indicator, or
  3. Pre-computing availability status for all generators before building the summary

This will prevent displaying misleading availability indicators in the registry summary.

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

- Fixed TypeScript compilation errors in abstraction layer
- Updated tests to work with new generator architecture
- Fixed linting violations across all files
- Added missing project.json for nx-openapi-plugin-openapi-tools package
- Fixed async/sync type mismatches
- Added proper dependency declarations
- All tests (119) now pass successfully
- Both packages build without errors
Comment on lines +65 to +68
if (key === 'globalProperties' && typeof value === 'object' && value !== null) {
this.addGlobalProperties(args, value as Record<string, string>);
continue;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The buildArgs method handles globalProperties but is missing similar handling for additionalProperties. This creates an inconsistency where additional properties defined in the options won't be passed to the generator. Consider adding the following case after the globalProperties handling:

if (key === 'additionalProperties' && typeof value === 'object' && value !== null) {
  this.addAdditionalProperties(args, value as Record<string, string>);
  continue;
}

The addAdditionalProperties method is already defined but not being used, which suggests this was likely an oversight.

Suggested change
if (key === 'globalProperties' && typeof value === 'object' && value !== null) {
this.addGlobalProperties(args, value as Record<string, string>);
continue;
}
if (key === 'globalProperties' && typeof value === 'object' && value !== null) {
this.addGlobalProperties(args, value as Record<string, string>);
continue;
}
if (key === 'additionalProperties' && typeof value === 'object' && value !== null) {
this.addAdditionalProperties(args, value as Record<string, string>);
continue;
}

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@michaelbe812 michaelbe812 deleted the feat/generator-abstraction-layer branch September 6, 2025 03:13
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.

RFC: Introduce Generator Abstraction Layer for Multi-Generator Support

1 participant