Skip to content

Conversation

@deepracticexs
Copy link
Member

Summary

Implements #16 - Refactors generators to use NodeSpec's own files as templates instead of hardcoded configuration strings, following the "eat your own dog food" principle.

Architecture

File-Level Building Blocks

  • BaseGenerator: Abstract base class with file processor chain
  • FileProcessor Interface: Atomic file-level processing with canProcess() and process()
  • Explicit File Mappings: Generators declare which files they need (whitelist approach)

Processors Implemented

  • PackageJsonProcessor: Handles package.json with field merging strategy
  • AppPackageJsonProcessor: Handles app package.json with bin field configuration
  • TsConfigProcessor: Handles JSONC (JSON with Comments) format
  • TypeScriptProcessor: Direct copy of .ts files
  • MarkdownProcessor: Direct copy of .md files

Key Design Decisions

  1. No Priority System: Each processor uses atomic matching on file names
  2. No Directory Scanning: Explicit file mappings, no need for exclude lists
  3. Preserves Comments: TsConfigProcessor uses direct file copy to preserve JSONC comments
  4. Extensible: Easy to add new file types by creating new processors

Benefits

Single Source of Truth: Uses NodeSpec's actual files as templates
No Hardcoded Configs: Eliminated 100+ lines of hardcoded configuration strings
Automatic Sync: Template changes automatically apply to generated projects
File-Level Control: Precise control over which files are used
Extensible: Simple to add support for new file types

Code Reduction

  • PackageGenerator: 120 lines → 46 lines (-61%)
  • AppGenerator: 153 lines → 67 lines (-56%)

Testing

✅ Package generation with template source
✅ App generation with CLI source
✅ JSONC support (tsconfig.json with comments)
✅ Configuration inheritance (scripts, dependencies, exports)
✅ All hooks passed: format, lint, typecheck, commitlint

Template Source Mapping

Generator Template Source Files Used
PackageGenerator packages/template/ package.json, tsconfig.json, tsup.config.ts, src/index.ts
AppGenerator apps/cli/ package.json, tsconfig.json, tsup.config.ts, src/index.ts, src/cli.ts

Future Work

  • Handle template embedding for CLI distribution (getNodeSpecRoot runtime detection)
  • Add support for more file types as needed
  • Refactor ServiceGenerator and ProjectGenerator to use same pattern

Closes #16

deepracticexs and others added 23 commits October 10, 2025 16:58
Implements #16 - use NodeSpec's own files as templates instead of hardcoded configs.

## Architecture Changes

- BaseGenerator: Abstract base class with file processor chain
- FileProcessor Interface: Atomic file-level processing
- File Processors:
  - PackageJsonProcessor: Handles package.json with field merging
  - AppPackageJsonProcessor: Handles app package.json with bin field
  - TsConfigProcessor: Handles JSONC (JSON with Comments) format
  - TypeScriptProcessor: Direct copy of .ts files
  - MarkdownProcessor: Direct copy of .md files

## Benefits

- Single Source of Truth: Uses NodeSpec's actual files as templates
- No Hardcoded Configs: Eliminated 100+ lines of hardcoded strings
- File-Level Composition: Explicit file mappings, no directory scanning
- Automatic Sync: Template changes automatically apply to generated projects
- Extensible: Easy to add new file types via new processors

## Code Reduction

- PackageGenerator: 120 lines → 46 lines (-61%)
- AppGenerator: 153 lines → 67 lines (-56%)

## Testing

- Package generation with template source
- App generation with CLI source
- JSONC support (tsconfig.json with comments)
- Configuration inheritance (scripts, dependencies, exports)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

Major refactoring of generator architecture to use NodeSpec itself as template source (dogfooding).

## Changes

### Template Architecture
- Renamed `packages/template` → `packages/example`
- Created `apps/app-example` - CLI app development example
- Created `services/service-example` - Express service development example
- All examples use unified TypeScript and build configurations

### Generator Refactoring
- **BaseGenerator**: Abstract base class with file processor chain pattern
- **MonorepoGenerator**: Uses NodeSpec root files as templates (214 → 85 lines, -60%)
- **PackageGenerator**: Uses `packages/example` (120 → 46 lines, -61%)
- **AppGenerator**: Uses `apps/app-example` (153 → 67 lines, -56%)
- **ServiceGenerator**: Uses `services/service-example` (214 → 85 lines, -60%)

### File Processor Chain
- `PackageJsonProcessor` - Handles package.json with dependency resolution
- `AppPackageJsonProcessor` - Handles app-specific bin configuration
- `MonorepoPackageJsonProcessor` - Handles monorepo root package.json
- `ServicePackageJsonProcessor` - Handles service package.json
- `TsConfigProcessor` - Preserves JSONC format (comments)
- `TypeScriptProcessor` - Direct copy of .ts files
- `MarkdownProcessor` - Direct copy of .md files
- `DependencyResolver` - Converts workspace:* to actual versions

### Mirror System
- Created `@deepracticex/nodespec-mirror` package
- Automatically mirrors entire NodeSpec project (respects .gitignore)
- Version format: `YYYY.MM.DD` (auto-updated on build)
- Mirror size: ~587KB, 228 files
- CLI bundles mirror in `dist/mirror/` for production use

### Key Benefits
- **Single Source of Truth**: NodeSpec project IS the template
- **Dogfooding**: Generated projects use same structure as NodeSpec
- **No Hardcoded Config**: All configuration from actual files
- **Workspace Resolution**: Automatic dependency version resolution
- **Dual Mode Support**:
  - Development: Uses NodeSpec repo directly
  - Production: Uses bundled mirror

### Files Changed
- Deleted: `src/core/template/ProjectGenerator.ts`, `src/core/template/versions.ts`
- Added: Mirror package, example packages, processor chain
- Updated: All generators now use BaseGenerator pattern

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

## Changes

### 1. Created @deepracticex/vitest-cucumber package
- Fully integrated Cucumber runner for Vitest
- Auto-detection of special character escaping issues
- Clean architecture: api/core/types

### 2. Removed old Cucumber config generator
- Deleted src/api/cucumber.ts
- Deleted features/cucumber-config.feature
- Removed all related references
- All tests now run through Vitest

### 3. Unified config naming to 'base'
- eslint: default → base
- prettier: default → base
- commitlint: default → base
- typescript, vitest, tsup: already using base

### 4. Removed strict/node config variants
- Removed eslint.strict
- Removed typescript.strict
- Removed typescript.node
- Updated feature files and step definitions

### 5. Fixed commitlint test
- Changed subject-max-length to header-max-length

## Test Results
✅ eslint-config: all pass
✅ commitlint-config: all pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Consolidate all configuration packages into a single unified system:
- Remove 8 separate config packages (commitlint, eslint, prettier, tsup, vitest, typescript, monorepo, cucumber)
- Migrate all configs to @deepracticex/configurer with consistent structure
- Generate TypeScript base config as JSON at build time for proper extends support
- Follow @tsconfig/bases best practice: only compiler options in base config
- Each project defines its own paths, include, exclude to avoid relative path issues

Benefits:
- Single source of truth for all project configurations
- Consistent configuration across all packages
- Stricter TypeScript settings (noUncheckedIndexedAccess) caught 10 type safety issues
- Simpler maintenance and updates

Changes:
- Delete entire configs/ directory (8 packages removed)
- Update @deepracticex/configurer to provide all config types
- Add build script to generate typescript-base.json from TS source
- Update all package tsconfig.json to extend from @deepracticex/configurer
- Fix type errors in CLI tests exposed by stricter TypeScript config
- Update all package.json to use @deepracticex/configurer

Net result: -1,044 lines of code, 10/10 packages typecheck passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add *.bundled_*.mjs pattern to .gitignore to ignore tsup's temporary
bundled configuration files that are generated during build process.

These files should not be committed to the repository.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Remove standalone cucumber configuration files from all packages.
All cucumber tests now run through @deepracticex/vitest-cucumber
integration, making these config files obsolete.

Deleted files:
- apps/cli/cucumber.cjs
- packages/error-handling/cucumber.cjs
- packages/example/cucumber.cjs
- packages/logger/cucumber.cjs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add --passWithNoTests flag to vitest run command for @deepracticex/vitest-cucumber.
This package is a testing integration layer and doesn't have its own tests yet,
so we allow the test command to pass without failing the build.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implemented vitest-cucumber as a native Vite plugin following Deepractice architecture standards (api/core/types).

## Architecture
- **api/**: Public API (Given/When/Then/And/But, vitestCucumber plugin)
- **types/**: Public TypeScript types (plugin options, step definitions, features)
- **core/**: Internal implementation (parser, transformer, runtime, discovery)

## Features Implemented
- Vite plugin with transform hook for .feature files
- Gherkin parser using @cucumber/gherkin
- Code generator for Vitest test output
- Step registry and executor with parameter extraction
- Context manager for step state sharing
- Public API for step definitions (Given/When/Then/And/But)
- Hooks API stubs (Before/After/BeforeAll/AfterAll)

## Testing
- 7 E2E tests covering basic plugin functionality
- Test fixtures for features and step definitions
- All tests passing (7/7)

## Design Documentation
- PLUGIN_DESIGN.md: Complete technical design
- FEATURE_SPEC.md: Feature writing guidelines
- 4 feature files defining plugin requirements:
  - plugin-basic.feature
  - plugin-configuration.feature
  - plugin-step-definitions.feature
  - plugin-advanced.feature

## Pending Implementation
- Scenario Outline support
- Background support
- Rule support
- Tag filtering
- Complete hooks implementation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implemented core Gherkin features: Scenario Outline, Background, and Rule.

## Features Added

### Scenario Outline
- Parameterized tests with Examples tables
- Each example row generates independent test
- Placeholder replacement (<a>, <b>, etc.)
- Test names include example values

### Background
- Shared setup steps before each scenario
- Converted to beforeEach() blocks
- Works with both feature-level and rule-level backgrounds
- Proper state isolation per scenario

### Rule
- Organize scenarios by business rules
- Nested describe() blocks
- Rule-specific backgrounds
- Clear test hierarchy

## Implementation

- **Types**: Added Background, Examples, Rule interfaces
- **Parser**: Extract Background, Rules, and Scenario Outline from Gherkin AST
- **CodeGenerator**: Generate beforeEach() for Background, nested describe() for Rules, multiple tests for Scenario Outline
- **Helpers**: replacePlaceholders() for example substitution

## Testing

- 25 tests passing (7 basic + 14 advanced + 4 verification)
- Test fixtures for each advanced feature
- Verification tests confirm actual Cucumber execution
- Complete step definitions for all scenarios

## Test Coverage

✅ Scenario Outline: 4 examples × 3 steps = 12 steps executed
✅ Background: 3 scenarios × 4 steps each = 12 steps
✅ Rules: 2 rules × 2 scenarios × 3 steps = 12 steps

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement complete native Cucumber experience with DataTable, Hooks,
parameter types, and DocString support.

## Features Implemented

### 1. DataTable Support
- Created DataTable class with Cucumber-compatible methods
- rowsHash(): Convert two-column table to key-value object
- hashes(): Treat first row as headers, return array of objects
- raw()/rows(): Return raw string[][] array
- Updated parser to instantiate DataTable class
- Updated CodeGenerator to properly serialize DataTable

### 2. Hooks System
- Created HookRegistry singleton for hook management
- Before/After: Run per scenario (via beforeEach/afterEach)
- BeforeAll/AfterAll: Run per feature (via Vitest's lifecycle)
- Hooks share same context as steps
- CodeGenerator injects hook execution automatically

### 3. Parameter Type Conversion
- Created ParameterTypeConverter utility class
- {int} → parseInt() with type checking
- {float} → parseFloat() with decimal support
- {string} → remove quotes and unescape
- {word} → keep as-is
- Cucumber expressions auto-convert to RegExp
- StepRegistry tracks parameter types
- StepExecutor applies type conversion

### 4. DocString Support
- Already working via parser and executor
- Multi-line text preserved with newlines
- Optional contentType handling
- Passed as last parameter after DataTable

## Architecture

New files:
- src/core/runtime/DataTable.ts - DataTable class implementation
- src/core/runtime/HookRegistry.ts - Hook registration and management
- src/core/runtime/ParameterTypeConverter.ts - Type conversion utilities
- tests/e2e/fixtures/steps/test-helpers.ts - Vitest-compatible expect for Cucumber

Modified files:
- src/core/runtime/StepExecutor.ts - Integrated type conversion
- src/core/runtime/StepRegistry.ts - Track parameter types
- src/core/transformer/CodeGenerator.ts - Inject hooks, serialize DataTable
- src/api/hooks.ts - Implemented actual hook registration
- All step definition files - Use test-helpers instead of vitest

## Tests

Added comprehensive E2E tests:
- tests/e2e/plugin/data-table.test.ts (11 scenarios)
- tests/e2e/plugin/hooks.test.ts (18 scenarios)
- tests/e2e/plugin/doc-string.test.ts (16 scenarios)
- tests/e2e/plugin/parameter-types.test.ts (21 scenarios)

Fixed critical issue: Step definitions now use test-helpers.ts instead
of importing from vitest, which caused "Vitest failed to access internal
state" errors when Cucumber CLI ran in separate process.

Old tests: 25/25 passing ✅
Total coverage: 101 test scenarios

## Design Decisions

- OOP-first approach following Deepractice standards
- Singleton pattern for registries (consistent with StepRegistry)
- DataTable exported as class (not interface) for method availability
- Type conversions happen at execution time, not registration
- Backwards compatible - all existing tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ency issues

## Bug Fixes

### GherkinParser State Pollution (Issue #18)
- Fixed state pollution when parsing multiple .feature files
- Root cause: Singleton parser/builder instances reused across parses
- Solution: Create fresh parser, builder, and matcher instances per parse
- Added missing GherkinClassicTokenMatcher parameter to Parser constructor

### Dependency Resolution
- Added @deepracticex/vitest-cucumber to root devDependencies for workspace-wide access
- Configured vitest-cucumber as external in configurer's tsup.config.ts
- Moved vitest-cucumber from devDependencies to dependencies in configurer

## Integration
- Integrated vitest-cucumber plugin into @deepracticex/configurer's vitest configuration
- Added **/*.feature to include patterns for transparent Cucumber support
- Consumers only need to use configurer, no direct vitest-cucumber dependency required

## Package Rename
- Renamed packages/example to packages/example-package for clarity
- Added example Cucumber features to demonstrate plugin capabilities

Related: #18

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The vitest-cucumber package has been successfully migrated to its own
dedicated repository (EnhancedVitest) and published to npm.

Changes:
- Remove packages/vitest-cucumber directory (migrated to EnhancedVitest)
- Update @deepracticex/configurer to use npm package ^0.1.0
- Remove workspace dependency from root package.json
- Published package: @deepracticex/vitest-cucumber@0.1.0

Migration benefits:
- Dedicated repository for focused development
- Independent versioning and release cycle
- Better separation of concerns
- Available on npm for all projects

Related: EnhancedVitest release-20251011-145448

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
## Major Changes

### 1. Separate Cucumber exports from config exports
- Create dedicated `src/api/cucumber.ts` module
- Remove Cucumber functions from `vitest.ts`
- Add `/cucumber` subpath export in package.json
- Update tsup config to build cucumber module

**Before:**
```ts
import { vitest, Given, When, Then } from "@deepracticex/configurer";
```

**After:**
```ts
import { vitest } from "@deepracticex/configurer";
import { Given, When, Then } from "@deepracticex/configurer/cucumber";
```

**Benefits:**
- Clear separation: configs vs BDD utilities
- Tree-shaking: Cucumber not loaded unless needed
- Better semantics: `/cucumber` path is self-documenting

### 2. Fix Vite version conflicts
- Remove `vitest@3.2.4` from configurer devDependencies
- Use workspace root `vitest@2.1.9` instead
- Resolves Vite 5.x vs 7.x type conflicts
- All packages now use consistent Vite/Vitest versions

### 3. Directory structure cleanup
- Delete duplicate `features/steps/` directory
- Remove obsolete `tests/e2e/cucumber.test.ts`
- Keep `tests/e2e/steps/` as the single source

### 4. Add comprehensive documentation
- Create detailed README.md with usage examples
- Document all configuration presets
- Include best practices and troubleshooting
- Add migration guide

### 5. Fix TypeScript errors
- Add type guard for undefined module names
- Rename unused parameters with underscore prefix
- All typecheck errors resolved

## Test Results
- ✅ All 23 tests passing
- ✅ TypeScript compilation successful
- ✅ No version conflicts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update imports from @deepracticex/configurer/vitest to @deepracticex/configurer/cucumber
to match the new Cucumber exports structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update imports from @deepracticex/vitest-cucumber to @deepracticex/configurer/cucumber
to match the new Cucumber exports structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Remove unused ErrorHandlingWorld type import and fix Before/After hooks
to not use explicit this typing, as Cucumber provides context automatically.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Major architectural refactoring to improve package clarity and separation of concerns.

## Breaking Changes

- Package renamed: `@deepracticex/configurer` → `@deepracticex/config-preset`
- New package: `@deepracticex/testing-utils` for testing utilities
- Import path changed: `/bdd` → `/cucumber` (more specific naming)
- Removed: Cucumber functions from config-preset (moved to testing-utils)

## Package Architecture

### @deepracticex/config-preset
Pure configuration presets for build tools and linters:
- ESLint, Prettier, TypeScript configurations
- Vitest, tsup, commitlint configurations
- Pre-configured vitest-cucumber plugin with runtimeModule option
- All configuration is declarative (what things look like)

### @deepracticex/testing-utils
Testing utility functions and BDD tools:
- Cucumber/Gherkin step definitions (Given, When, Then)
- Hooks (Before, After, BeforeAll, AfterAll)
- World constructor setup
- Re-exports from @deepracticex/vitest-cucumber
- Imperative tools (what things can do)

## Dependency Strategy

- config-preset: vitest-cucumber-plugin as peerDependency (compile-time)
- testing-utils: vitest-cucumber as dependency (runtime)
- User packages: only need testing-utils (transitive dependencies work)

## Upgrades

- vitest-cucumber: 1.0.0 → 1.0.1 (fixes {int} type conversion, Context sharing)
- vitest-cucumber-plugin: 1.0.0 → 1.1.0 (adds runtimeModule config)

## Migration Guide

### For configuration imports
```typescript
// Before
import { vitest } from '@deepracticex/configurer/vitest';

// After
import { vitest } from '@deepracticex/config-preset/vitest';
```

### For testing utilities
```typescript
// Before
import { Given, When, Then } from '@deepracticex/configurer/bdd';

// After
import { Given, When, Then } from '@deepracticex/testing-utils/cucumber';
```

### For assertions
```typescript
// Before (chai)
expect(value).to.equal(expected);

// After (vitest)
expect(value).toBe(expected);
```

## Fixes

- Removed {int} workaround (fixed in vitest-cucumber 1.0.1)
- Context now shared between Background and Scenario steps (fixed in 1.0.1)
- DocString now passes as {contentType, content} object
- Updated all test files to use new imports and assertions

## Tests

- error-handling: 28/28 tests passing ✅
- example-package: 21/21 tests passing ✅

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
## Migration to vitest-cucumber

### Packages migrated
- **@deepracticex/logger** - 8/8 tests passing
- **@deepracticex/config-preset** - 18/18 tests passing
- **@deepracticex/nodespec-cli** - 99/131 tests passing (76%)

### Changes
- Converted World from Cucumber.js classes to factory functions
- Updated all step definitions to use @deepracticex/testing-utils
- Fixed DataTable API usage (rawTable → raw() method)
- Added vitest.config.ts with cucumber plugin configuration
- Updated hooks to use vitest-cucumber imports

## App refactoring

### @deepracticex/example-cli (renamed from app-example)
- Renamed directory: apps/app-example → apps/example-cli
- Updated package name to @deepracticex/example-cli
- Updated bin command to example-cli
- Updated all documentation and source files

## Configuration standardization

### Subpath imports
- All packages now use subpath imports:
  - @deepracticex/config-preset/tsup
  - @deepracticex/config-preset/vitest
  - @deepracticex/config-preset/eslint
  - @deepracticex/config-preset/prettier

### Scripts standardization
- Removed unnecessary NODE_OPTIONS flags
- Unified test scripts to use vitest
- Removed cucumber-tsx in favor of vitest-cucumber

## Documentation

- Updated packages/README.md with correct config examples
- Rewrote testing-utils/README.md with comprehensive guide
- Added proper World Factory pattern examples
- Documented DataTable usage with hashes() and raw()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Detect test environment (NODE_ENV=test or VITEST=true)
- Force sync mode in test environment to avoid worker thread issues
- Fixes MODULE_NOT_FOUND error for pino worker.js in vitest
- Bump version to 0.2.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

Add explicit platform entry points to enable tree-shaking and avoid bundling incompatible dependencies in edge runtimes.

Changes:
- Add platform-specific entry points:
  - @deepracticex/logger (default: Node.js with pino)
  - @deepracticex/logger/nodejs (explicit Node.js)
  - @deepracticex/logger/cloudflare-workers (Workers with console)
  - @deepracticex/logger/browser (Browser with console)
- Create console-adapter for edge/browser environments
- Update package.json exports for platform entry points
- Refactor adapter-factory to support dynamic platform detection
- Bump version to 1.0.0 (breaking change: new architecture)

Benefits:
- Users explicitly specify platform → perfect tree-shaking
- No Node.js dependencies in Workers/browser bundles
- Unified API across all platforms
- Backward compatible (default = Node.js)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Import Logger type from types/index.js instead of types/config.js to fix TypeScript compilation error.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…try points

Update README to reflect new universal logging architecture:
- Emphasize cross-platform support (Node.js, Workers, Browser)
- Document platform-specific entry points as core feature
- Add comprehensive examples for each platform
- Include architecture diagram and bundle size comparison
- Add FAQ section for common questions
- Remove migration section (focus on building forward)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@deepracticexs deepracticexs merged commit 715d85e into main Oct 12, 2025
1 of 4 checks passed
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.

Refactor generators to use NodeSpec's own files as templates (dogfooding approach)

2 participants