From eae87f7297ba6a40359ae81faca1e8eeb6be040c Mon Sep 17 00:00:00 2001 From: omermorad Date: Tue, 2 Dec 2025 08:13:14 +0200 Subject: [PATCH 01/11] refactor: adopt codemod registry naming pattern (automock/2/to-suites-v3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored transform naming from 'automock-to-suites' to follow the industry-standard Codemod Registry pattern: // BREAKING CHANGE: Transform argument now required. Users must specify: npx @suites/codemod automock/2/to-suites-v3 Changes: - Transform registry: Updated to automock/2/to-suites-v3 - Directory structure: Created hierarchical namespace src/transforms/automock/2/ - CLI: Removed default transform, require explicit specification - File processor: Renamed to generic naming (filterSourceFiles) - Runner: Updated variable naming (automockFiles → sourceFiles) - Tests: Updated imports, all 207 tests passing - Documentation: Updated README with new usage examples Benefits: - Follows React codemod and Codemod Registry patterns - Future-proof for v3-to-v4, v4-to-v5 migrations - Support for multiple transforms per version - Extensible to other frameworks (jest, vitest, etc.) --- README.md | 14 +++---- src/cli.ts | 41 +++++++------------ src/runner.ts | 26 ++++++------ .../2/to-suites-v3.ts} | 2 +- src/transforms/index.ts | 30 +++++--------- src/utils/file-processor.ts | 17 ++++---- test/integration/snapshot-tests.spec.ts | 2 +- test/parse-errors.spec.ts | 2 +- test/transforms/global-jest.spec.ts | 2 +- 9 files changed, 60 insertions(+), 76 deletions(-) rename src/transforms/{automock-to-suites.ts => automock/2/to-suites-v3.ts} (87%) diff --git a/README.md b/README.md index 27343e4..9be7006 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,19 @@ Automated code transformations for Suites projects. Built on [jscodeshift](https ## Usage ```bash -npx @suites/codemod [options] +npx @suites/codemod [options] ``` **Example:** ```bash -npx @suites/codemod src/**/*.spec.ts +npx @suites/codemod automock/2/to-suites-v3 src/**/*.spec.ts ``` Run with `--dry-run` to preview changes without modifying files. ## Available Transforms -- **`automock-to-suites`** (default) - Migrate test files from Automock to Suites testing framework +- **`automock/2/to-suites-v3`** - Migrate test files from Automock v2 to Suites v3 testing framework ## Example @@ -79,10 +79,10 @@ describe('UserService', () => { **More examples:** ```bash # Preview changes -npx @suites/codemod src --dry-run +npx @suites/codemod automock/2/to-suites-v3 src --dry-run # Ignore certain files -npx @suites/codemod src --ignore "**/*.integration.ts" +npx @suites/codemod automock/2/to-suites-v3 src --ignore "**/*.integration.ts" # List all transforms npx @suites/codemod --list-transforms @@ -90,9 +90,9 @@ npx @suites/codemod --list-transforms ## Transform Details -### `automock-to-suites` +### `automock/2/to-suites-v3` -Intelligently migrates Automock test files to Suites framework. +Intelligently migrates Automock v2 test files to Suites v3 framework. **What it transforms:** - Import statements: `@automock/jest` -> `@suites/unit` diff --git a/src/cli.ts b/src/cli.ts index e6898e9..46cf0f5 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -5,7 +5,6 @@ import { runTransform } from './runner'; import { checkGitStatus } from './utils/git-safety'; import { getTransform, - getDefaultTransform, AVAILABLE_TRANSFORMS, } from './transforms'; @@ -17,7 +16,7 @@ program .version('0.1.0') .argument( '[transform]', - 'Transform to apply (e.g., automock-to-suites). Defaults to automock-to-suites if not specified.' + 'Transform to apply (e.g., automock/2/to-suites-v3)' ) .argument('[path]', 'Path to transform (file or directory)', '.') .option('-d, --dry-run', 'Preview changes without writing files', false) @@ -47,32 +46,22 @@ program return; } - // Determine transform and path - let transformName: string; - let targetPath: string; - - if (transformArg && !transformArg.startsWith('-')) { - // First arg might be transform or path - const maybeTransform = getTransform(transformArg); - if (maybeTransform) { - // It's a transform name - transformName = transformArg; - targetPath = pathArg || '.'; - } else { - // First arg is path, use default transform - transformName = getDefaultTransform().name; - targetPath = transformArg; - logger.info(`No transform specified, using default: ${transformName}`); - } - } else { - // No transform specified, use default - transformName = getDefaultTransform().name; - targetPath = pathArg || '.'; - if (!pathArg) { - logger.info(`No transform specified, using default: ${transformName}`); - } + // Validate transform is provided + if (!transformArg) { + logger.error('Transform argument required.'); + logger.info('\nAvailable transforms:'); + AVAILABLE_TRANSFORMS.forEach((t) => { + console.log(` ${t.name}`); + console.log(` ${t.description}\n`); + }); + logger.info('Example usage:'); + logger.info(` npx @suites/codemod ${AVAILABLE_TRANSFORMS[0].name} ./src`); + process.exit(1); } + const transformName = transformArg; + const targetPath = pathArg || '.'; + const transformInfo = getTransform(transformName); if (!transformInfo) { logger.error(`Unknown transform: ${transformName}`); diff --git a/src/runner.ts b/src/runner.ts index 228c4c0..f8a6c31 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -37,18 +37,18 @@ export async function runTransform( const allFiles = await fileProcessor.discoverFiles(targetPath); logger.succeedSpinner(`Found ${allFiles.length} files`); - // Step 2: Filter for Automock files - logger.startSpinner('Analyzing Automock usage..'); - const automockFiles = fileProcessor.filterAutomockFiles(allFiles); + // Step 2: Filter for source framework files + logger.startSpinner('Analyzing source framework usage..'); + const sourceFiles = fileProcessor.filterSourceFiles(allFiles); - if (automockFiles.length === 0) { - logger.warnSpinner('No Automock files found'); - logger.info('No files contain Automock imports. Migration not needed.'); + if (sourceFiles.length === 0) { + logger.warnSpinner('No source framework files found'); + logger.info('No files contain source framework imports. Migration not needed.'); return createEmptySummary(); } - logger.succeedSpinner(`${automockFiles.length} files contain Automock imports`); - logger.subsection(`${allFiles.length - automockFiles.length} files skipped (no Automock code)`); + logger.succeedSpinner(`${sourceFiles.length} files contain source framework imports`); + logger.subsection(`${allFiles.length - sourceFiles.length} files skipped (no source imports)`); logger.newline(); @@ -59,7 +59,7 @@ export async function runTransform( let totalErrors = 0; let totalWarnings = 0; - for (const filePath of automockFiles) { + for (const filePath of sourceFiles) { const result = await transformFile(filePath, applyTransform, options, logger); results.push(result); @@ -79,8 +79,8 @@ export async function runTransform( logger.success(`${filesTransformed} file${filesTransformed > 1 ? 's' : ''} transformed successfully`); } - if (automockFiles.length - filesTransformed > 0) { - logger.info(` ${automockFiles.length - filesTransformed} file${automockFiles.length - filesTransformed > 1 ? 's' : ''} skipped (no changes needed)`); + if (sourceFiles.length - filesTransformed > 0) { + logger.info(` ${sourceFiles.length - filesTransformed} file${sourceFiles.length - filesTransformed > 1 ? 's' : ''} skipped (no changes needed)`); } if (totalWarnings > 0) { @@ -110,9 +110,9 @@ export async function runTransform( } return { - filesProcessed: automockFiles.length, + filesProcessed: sourceFiles.length, filesTransformed, - filesSkipped: automockFiles.length - filesTransformed, + filesSkipped: sourceFiles.length - filesTransformed, importsUpdated: 0, // TODO: Track this from transformers mocksConfigured: 0, // TODO: Track this from transformers errors: totalErrors, diff --git a/src/transforms/automock-to-suites.ts b/src/transforms/automock/2/to-suites-v3.ts similarity index 87% rename from src/transforms/automock-to-suites.ts rename to src/transforms/automock/2/to-suites-v3.ts index 48f923d..59858ef 100644 --- a/src/transforms/automock-to-suites.ts +++ b/src/transforms/automock/2/to-suites-v3.ts @@ -10,4 +10,4 @@ * - Cleanup of obsolete patterns */ -export { applyTransform } from '../transform'; +export { applyTransform } from '../../../transform'; diff --git a/src/transforms/index.ts b/src/transforms/index.ts index 68aa8f3..8fe971f 100644 --- a/src/transforms/index.ts +++ b/src/transforms/index.ts @@ -2,11 +2,12 @@ * Transform Registry * * Central registry for all available codemods. - * Each transform represents a specific migration (e.g., automock-to-suites, v3-to-v4). + * Follows Codemod Registry pattern: // + * Examples: automock/2/to-suites-v3, jest/28/to-v29 */ export interface TransformInfo { - /** Unique identifier for the transform (e.g., 'automock-to-suites') */ + /** Unique identifier for the transform (e.g., 'automock/2/to-suites-v3') */ name: string; /** Human-readable description of what the transform does */ description: string; @@ -20,36 +21,27 @@ export interface TransformInfo { */ export const AVAILABLE_TRANSFORMS: TransformInfo[] = [ { - name: 'automock-to-suites', - description: 'Migrate from Automock to Suites unit testing framework', - path: './transforms/automock-to-suites', + name: 'automock/2/to-suites-v3', + description: 'Migrate from Automock v2 to Suites v3 unit testing framework', + path: './transforms/automock/2/to-suites-v3', }, - // Future transforms will be added here, e.g.: + // Future transforms: // { - // name: 'v3-to-v4', - // description: 'Migrate from Suites v3 to v4', - // path: './transforms/v3-to-v4', + // name: 'automock/3/to-suites-v4', + // description: 'Migrate from Suites v3 to Suites v4', + // path: './transforms/automock/3/to-suites-v4', // }, ]; /** * Get transform info by name - * @param name Transform name (e.g., 'automock-to-suites') + * @param name Transform name (e.g., 'automock/2/to-suites-v3') * @returns Transform info or null if not found */ export function getTransform(name: string): TransformInfo | null { return AVAILABLE_TRANSFORMS.find((t) => t.name === name) || null; } -/** - * Get the default transform (first in the registry) - * Used for backward compatibility when no transform is specified. - * @returns Default transform info - */ -export function getDefaultTransform(): TransformInfo { - return AVAILABLE_TRANSFORMS[0]; -} - /** * Check if a transform name is valid * @param name Transform name to check diff --git a/src/utils/file-processor.ts b/src/utils/file-processor.ts index 95bb3ea..d29e8cf 100644 --- a/src/utils/file-processor.ts +++ b/src/utils/file-processor.ts @@ -5,6 +5,7 @@ import { glob } from 'glob'; export interface FileProcessorOptions { extensions: string[]; ignorePatterns: string[]; + sourceImportPattern?: RegExp; } export class FileProcessor { @@ -105,22 +106,24 @@ export class FileProcessor { } /** - * Filter files that contain Automock imports + * Filter files that contain source framework imports + * Default pattern matches Automock imports for backward compatibility */ - filterAutomockFiles(files: string[]): string[] { + filterSourceFiles(files: string[]): string[] { return files.filter((filePath) => { const content = this.readFile(filePath); - return this.hasAutomockImport(content); + return this.hasSourceImport(content); }); } /** - * Check if file content contains Automock imports + * Check if file content contains source framework imports + * Default pattern matches Automock imports for backward compatibility */ - private hasAutomockImport(content: string): boolean { - const automockImportPattern = + private hasSourceImport(content: string): boolean { + const importPattern = this.options.sourceImportPattern || /@automock\/(jest|sinon|core)['"]|from\s+['"]@automock\/(jest|sinon|core)['"]/; - return automockImportPattern.test(content); + return importPattern.test(content); } /** diff --git a/test/integration/snapshot-tests.spec.ts b/test/integration/snapshot-tests.spec.ts index 6493f09..525f0e0 100644 --- a/test/integration/snapshot-tests.spec.ts +++ b/test/integration/snapshot-tests.spec.ts @@ -6,7 +6,7 @@ */ import { loadFixturePair } from '../utils/fixture-loader'; -import { applyTransform } from '../../src/transforms/automock-to-suites'; +import { applyTransform } from '../../src/transforms/automock/2/to-suites-v3'; describe('Snapshot Tests', () => { describe('Basic Examples from Specification', () => { diff --git a/test/parse-errors.spec.ts b/test/parse-errors.spec.ts index 2289d4b..c45a4df 100644 --- a/test/parse-errors.spec.ts +++ b/test/parse-errors.spec.ts @@ -7,7 +7,7 @@ */ import { loadFixturePair } from './utils/fixture-loader'; -import { applyTransform } from '../src/transforms/automock-to-suites'; +import { applyTransform } from '../src/transforms/automock/2/to-suites-v3'; describe('Parse Error Handling', () => { describe('Category 1: Multi-Variable Declarations with Generics (18 files)', () => { diff --git a/test/transforms/global-jest.spec.ts b/test/transforms/global-jest.spec.ts index d606b76..47ebbde 100644 --- a/test/transforms/global-jest.spec.ts +++ b/test/transforms/global-jest.spec.ts @@ -1,4 +1,4 @@ -import { applyTransform } from '../../src/transforms/automock-to-suites'; +import { applyTransform } from '../../src/transforms/automock/2/to-suites-v3'; describe('Global Jest Handling', () => { describe('When jest is global (no import)', () => { From e95d2fc71a7670096e144c271f570b181a29e0a1 Mon Sep 17 00:00:00 2001 From: omermorad Date: Tue, 2 Dec 2025 08:31:34 +0200 Subject: [PATCH 02/11] =?UTF-8?q?fix:=20use=20consistent=20checkmark=20sym?= =?UTF-8?q?bol=20(=E2=9C=94)=20in=20logger=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed success() and fileTransformed() methods to use heavy check mark (✔) to match ora spinner's succeed() symbol for visual consistency. --- .github/workflows/coverage.yml | 61 ++++++++++++++++++++++++++++++++++ src/utils/logger.ts | 4 +-- 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..c14896c --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,61 @@ +name: Test & Coverage + +on: + push: + branches: + - master + +permissions: + contents: read + id-token: write + checks: write + +jobs: + test: + name: Test & Upload Coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.9 + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run tests with coverage + run: pnpm test + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage/lcov.info + flags: unittests + name: codecov-codemod + fail_ci_if_error: true + verbose: true diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 44d6b1b..cab7266 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -62,7 +62,7 @@ export class Logger { * Log a success message */ success(message: string): void { - console.log(chalk.green('✓'), message); + console.log(chalk.green('✔'), message); } /** @@ -113,7 +113,7 @@ export class Logger { * Log a file transformation result */ fileTransformed(filePath: string, changes: string[]): void { - console.log(chalk.green('✓'), chalk.dim(filePath)); + console.log(chalk.green('✔'), chalk.dim(filePath)); changes.forEach((change) => { this.subsection(chalk.dim(`- ${change}`), 5); }); From 178b0e3dc96111cd8c8643d5a7a4a4feca212299 Mon Sep 17 00:00:00 2001 From: omermorad Date: Tue, 2 Dec 2025 08:32:30 +0200 Subject: [PATCH 03/11] chore: add coverage workflow and fix provenance configuration - Add coverage workflow for master branch pushes with Codecov integration - Update publishConfig to use public npm registry with provenance enabled - Remove [skip ci] from semantic-release commit message format --- .releaserc.json | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.releaserc.json b/.releaserc.json index fa88536..9f6d1c4 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -65,7 +65,7 @@ "@semantic-release/git", { "assets": ["CHANGELOG.md", "package.json"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}" } ], "@semantic-release/github" diff --git a/package.json b/package.json index 7b7f80a..9145d8e 100644 --- a/package.json +++ b/package.json @@ -77,8 +77,8 @@ "typescript" ], "publishConfig": { - "registry": "http://localhost:4873", - "access": "public" + "access": "public", + "provenance": true }, "packageManager": "pnpm@8.15.9" } From 82f6da67f3f0e8a177e23a091375c7e8364a4be6 Mon Sep 17 00:00:00 2001 From: omermorad Date: Tue, 2 Dec 2025 08:33:35 +0200 Subject: [PATCH 04/11] feat: add manual publish workflow and coverage tracking - Add publish.yml workflow for manual npm publishing with dist-tag selection - Add coverage.yml workflow for automatic coverage reporting on master pushes - Support latest, next, rc, dev, alpha, and beta dist-tags - Include provenance attestations for supply chain security - Add job summaries for publish success/failure feedback --- .github/workflows/publish.yml | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..4c96662 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,111 @@ +name: Publish Package + +on: + workflow_dispatch: + inputs: + tag: + description: 'Distribution tag' + required: true + type: choice + options: + - latest + - next + - rc + - dev + - alpha + - beta + branch: + description: 'Target branch' + required: true + type: choice + options: + - master + - next + +permissions: + contents: write + id-token: write + +jobs: + publish: + name: Publish to NPM + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + registry-url: 'https://registry.npmjs.org' + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.9 + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm run build + + - name: Run tests + run: pnpm test + + - name: Run lint + run: pnpm run lint + + - name: Publish to NPM + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: true + run: | + echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > .npmrc + npm publish --tag ${{ inputs.tag }} --provenance --access public + + - name: Create Summary + if: success() + run: | + echo "## ✅ Publication Successful" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Package published to npm with tag: **${{ inputs.tag }}**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Installation" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + if [ "${{ inputs.tag }}" = "latest" ]; then + echo "npm install @suites/codemod" >> $GITHUB_STEP_SUMMARY + else + echo "npm install @suites/codemod@${{ inputs.tag }}" >> $GITHUB_STEP_SUMMARY + fi + echo '```' >> $GITHUB_STEP_SUMMARY + + - name: Create Failure Summary + if: failure() + run: | + echo "## ❌ Publication Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The package could not be published. Check the logs above for details." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Common Issues" >> $GITHUB_STEP_SUMMARY + echo "- Ensure NPM_TOKEN secret is configured correctly" >> $GITHUB_STEP_SUMMARY + echo "- Check if the version already exists on npm" >> $GITHUB_STEP_SUMMARY + echo "- Verify build and tests pass successfully" >> $GITHUB_STEP_SUMMARY From a6e821cc3c8014d54c739822235235d0b8c85c1a Mon Sep 17 00:00:00 2001 From: omermorad Date: Tue, 2 Dec 2025 17:19:11 +0200 Subject: [PATCH 05/11] docs: add architecture section and local development guide Added comprehensive documentation for: - Codemod Registry pattern and architecture explanation - Hierarchical transform structure (framework/version/transform) - Updated 'Adding New Transforms' with new directory structure - Updated Project Structure to reflect automock/2/ organization - New 'Local Development' section with: - Running locally with node dist/cli.js - npm link for testing - Running tests This helps contributors understand the versioned approach and provides clear instructions for local testing and development. --- README.md | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9be7006..101c466 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,32 @@ The codemod uses [jscodeshift](https://github.com/facebook/jscodeshift) to: **TypeScript Support:** First-class support with fallback parser for complex syntax (generics, type guards, decorators, JSX/TSX). +## Architecture + +This codemod follows the **Codemod Registry** pattern used by React, Next.js, and other major frameworks: + +**Transform Naming:** `//` +- `automock/2/to-suites-v3` - Current migration +- `automock/3/to-suites-v4` - Future migrations +- Supports multiple transforms per version +- Extensible to other frameworks (e.g., `jest/28/to-v29`) + +**Directory Structure:** +``` +src/transforms/ + automock/ # Framework namespace + 2/ # Source version + to-suites-v3.ts # Migration transform + 3/ # Future: next version + to-suites-v4.ts +``` + +**Design Benefits:** +- No default transform - explicit selection prevents mistakes +- Version-based organization supports migration chains +- Framework namespacing allows multi-framework support +- Clear source → target versioning + ## Contributing Contributions welcome! To contribute: @@ -167,11 +193,25 @@ Contributions welcome! To contribute: ### Adding New Transforms -1. Create transform file in `src/transforms/` -2. Register in `src/transforms/index.ts` -3. Add test fixtures in `fixtures/` -4. Add integration tests in `test/integration/` -5. Update this README +1. Create transform directory: `src/transforms///.ts` +2. Export `applyTransform` function from your transform +3. Register in `src/transforms/index.ts`: + ```typescript + { + name: 'framework/version/transform-name', + description: 'Description of what it does', + path: './transforms/framework/version/transform-name', + } + ``` +4. Add test fixtures in `fixtures/` +5. Add integration tests in `test/integration/` +6. Update this README + +**Example:** +```typescript +// src/transforms/automock/3/to-suites-v4.ts +export { applyTransform } from '../../../transform'; +``` ### Project Structure @@ -179,6 +219,10 @@ Contributions welcome! To contribute: src/ analyzers/ # Code analysis utilities transforms/ # Transform implementations + automock/ # Framework namespace + 2/ # Version-specific transforms + to-suites-v3.ts + index.ts # Transform registry validators/ # Post-transform validation utils/ # Shared utilities cli.ts # CLI interface @@ -191,6 +235,52 @@ test/ fixtures/ # Test fixtures (before/after) ``` +## Local Development + +### Running Locally + +From within the codemod repository: + +```bash +# Build first +pnpm build + +# Run on a target repository +node dist/cli.js automock/2/to-suites-v3 /path/to/repo --dry-run + +# Run on test fixtures +node dist/cli.js automock/2/to-suites-v3 fixtures/simple-final --dry-run + +# Verbose output for debugging +node dist/cli.js automock/2/to-suites-v3 /path/to/repo --dry-run --verbose +``` + +### Using npm link for Testing + +```bash +# In the codemod repo +npm link + +# Now use it anywhere like npx +codemod automock/2/to-suites-v3 /path/to/repo --dry-run + +# Unlink when done +npm unlink -g @suites/codemod +``` + +### Running Tests + +```bash +# All tests +pnpm test + +# Specific test file +pnpm test path/to/test.spec.ts + +# With coverage +pnpm test --coverage +``` + ## License MIT (c) [Omer Morad](https://github.com/omermorad) From 287c8caabb407dcb6f02e0ffb621ae568b81081c Mon Sep 17 00:00:00 2001 From: omermorad Date: Wed, 3 Dec 2025 08:17:07 +0200 Subject: [PATCH 06/11] refactor: align CLI options with Next.js codemod conventions Updated CLI flags to match Next.js @next/codemod patterns: - Changed --dry-run to --dry (with -d short flag) - Changed --print to have -p short flag - Configured -v for --version (was -V by default) - Kept --verbose without short flag (matching Next.js) - Updated all option descriptions to match Next.js style This provides a consistent experience for users familiar with other codemods in the ecosystem. References: - @next/codemod CLI conventions - Commander.js version/help customization --- .github/workflows/publish.yml | 6 +++--- README.md | 38 +++++++++++++++++++++-------------- src/cli.ts | 27 +++++++++++++------------ src/runner.ts | 6 +++--- src/types.ts | 2 +- 5 files changed, 44 insertions(+), 35 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4c96662..6cb5215 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -65,12 +65,12 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build - run: pnpm run build - - name: Run tests run: pnpm test + - name: Build + run: pnpm run build + - name: Run lint run: pnpm run lint diff --git a/README.md b/README.md index 101c466..4a5582e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ npx @suites/codemod [options] npx @suites/codemod automock/2/to-suites-v3 src/**/*.spec.ts ``` -Run with `--dry-run` to preview changes without modifying files. +Run with `--dry` or `-d` to preview changes without modifying files. ## Available Transforms @@ -66,24 +66,32 @@ describe('UserService', () => { | Option | Description | Default | |--------|-------------|---------| -| `-d, --dry-run` | Preview changes without writing files | `false` | -| `-f, --force` | Bypass git safety checks | `false` | -| `-p, --parser ` | Parser: `tsx`, `ts`, `babel` | `tsx` | -| `-e, --extensions ` | File extensions to transform | `.ts,.tsx` | -| `-i, --ignore ` | Ignore file patterns (comma-separated) | - | -| `--print` | Print output to stdout | `false` | -| `-v, --verbose` | Show detailed logs | `false` | -| `--skip-validation` | Skip validation checks | `false` | +| `-v, --version` | Output the current version | - | +| `-d, --dry` | Dry run (no changes are made to files) | `false` | +| `-f, --force` | Bypass Git safety checks and forcibly run codemods | `false` | +| `-p, --print` | Print transformed files to stdout, useful for development | `false` | +| `--verbose` | Show more information about the transform process | `false` | +| `--parser ` | Parser to use: `tsx`, `ts`, `babel` | `tsx` | +| `--extensions ` | File extensions to transform | `.ts,.tsx` | +| `--ignore ` | Ignore file patterns (comma-separated) | - | +| `--skip-validation` | Skip post-transformation validation checks | `false` | | `--list-transforms` | List all available transforms | - | +| `-h, --help` | Display help message | - | **More examples:** ```bash -# Preview changes -npx @suites/codemod automock/2/to-suites-v3 src --dry-run +# Preview changes (dry run) +npx @suites/codemod automock/2/to-suites-v3 src --dry + +# Print output to stdout +npx @suites/codemod automock/2/to-suites-v3 src/file.ts -p # Ignore certain files npx @suites/codemod automock/2/to-suites-v3 src --ignore "**/*.integration.ts" +# Verbose output +npx @suites/codemod automock/2/to-suites-v3 src --verbose + # List all transforms npx @suites/codemod --list-transforms ``` @@ -246,13 +254,13 @@ From within the codemod repository: pnpm build # Run on a target repository -node dist/cli.js automock/2/to-suites-v3 /path/to/repo --dry-run +node dist/cli.js automock/2/to-suites-v3 /path/to/repo --dry # Run on test fixtures -node dist/cli.js automock/2/to-suites-v3 fixtures/simple-final --dry-run +node dist/cli.js automock/2/to-suites-v3 fixtures/simple-final --dry # Verbose output for debugging -node dist/cli.js automock/2/to-suites-v3 /path/to/repo --dry-run --verbose +node dist/cli.js automock/2/to-suites-v3 /path/to/repo --dry --verbose ``` ### Using npm link for Testing @@ -262,7 +270,7 @@ node dist/cli.js automock/2/to-suites-v3 /path/to/repo --dry-run --verbose npm link # Now use it anywhere like npx -codemod automock/2/to-suites-v3 /path/to/repo --dry-run +codemod automock/2/to-suites-v3 /path/to/repo --dry # Unlink when done npm unlink -g @suites/codemod diff --git a/src/cli.ts b/src/cli.ts index 46cf0f5..a1448ee 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -13,19 +13,20 @@ const program = new Command(); program .name('@suites/codemod') .description('Code transformation tool for the Suites testing framework') - .version('0.1.0') + .version('0.1.0', '-v, --version', 'Output the current version') + .helpOption('-h, --help', 'Display help message') .argument( '[transform]', 'Transform to apply (e.g., automock/2/to-suites-v3)' ) .argument('[path]', 'Path to transform (file or directory)', '.') - .option('-d, --dry-run', 'Preview changes without writing files', false) - .option('-f, --force', 'Bypass git safety checks', false) - .option('-p, --parser ', 'Parser to use (tsx, ts, babel)', 'tsx') - .option('-e, --extensions ', 'File extensions to transform', '.ts,.tsx') - .option('-i, --ignore ', 'Ignore file patterns (comma-separated)') - .option('--print', 'Print transformed output to stdout', false) - .option('-v, --verbose', 'Show detailed transformation logs', false) + .option('-d, --dry', 'Dry run (no changes are made to files)', false) + .option('-f, --force', 'Bypass Git safety checks and forcibly run codemods', false) + .option('-p, --print', 'Print transformed files to stdout, useful for development', false) + .option('--verbose', 'Show more information about the transform process', false) + .option('--parser ', 'Parser to use (tsx, ts, babel)', 'tsx') + .option('--extensions ', 'File extensions to transform', '.ts,.tsx') + .option('--ignore ', 'Ignore file patterns (comma-separated)') .option('--skip-validation', 'Skip post-transformation validation checks', false) .option('--list-transforms', 'List all available transforms', false) .action( @@ -70,19 +71,19 @@ program } try { - // Git safety check (unless in dry-run or force mode) - if (!options.dryRun && !options.force) { + // Git safety check (unless in dry or force mode) + if (!options.dry && !options.force) { checkGitStatus(logger); } // Show header with dynamic transform name logger.section(`🔄 Suites Codemod - ${transformInfo.description}`); - if (options.dryRun) { - logger.info('Running in dry-run mode (no files will be modified)'); + if (options.dry) { + logger.info('Running in dry mode (no changes are made to files)'); } - if (options.force && !options.dryRun) { + if (options.force && !options.dry) { logger.warn('Bypassing git safety checks (--force enabled)'); } diff --git a/src/runner.ts b/src/runner.ts index f8a6c31..cf58fdd 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -189,15 +189,15 @@ async function transformFile( console.log(transformOutput.code); logger.info('='.repeat(60)); result.changes.push('Printed to stdout'); - } else if (!options.dryRun) { + } else if (!options.dry) { // Write transformed file await fs.writeFile(filePath, transformOutput.code, 'utf-8'); result.changes.push('File updated'); logger.success(` ✓ ${path.relative(process.cwd(), filePath)}`); } else { // Dry run - just report what would change - result.changes.push('Would be updated (dry-run)'); - logger.info(` ~ ${path.relative(process.cwd(), filePath)} (dry-run)`); + result.changes.push('Would be updated (dry)'); + logger.info(` ~ ${path.relative(process.cwd(), filePath)} (dry)`); } } catch (error) { const errorMessage = diff --git a/src/types.ts b/src/types.ts index 474b5e7..54fbf5f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,7 +4,7 @@ import type { Collection, JSCodeshift } from 'jscodeshift'; * CLI options */ export interface CliOptions { - dryRun: boolean; + dry: boolean; force: boolean; parser: string; extensions: string; From fa0f7925fb96ea0dba5b9684eeb54a70d93922ab Mon Sep 17 00:00:00 2001 From: omermorad Date: Wed, 3 Dec 2025 08:19:57 +0200 Subject: [PATCH 07/11] refactor: align argument names with Next.js codemod conventions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed argument names to match @next/codemod exactly: - 'transform' → 'codemod' (codemod slug to run) - 'path' → 'source' (path to source files or directory) Updated help text to reference GitHub repo: https://github.com/suites-dev/codemod This provides consistent terminology with Next.js and other popular codemods in the ecosystem. --- .github/workflows/publish.yml | 6 +++--- src/cli.ts | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6cb5215..07de961 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -65,15 +65,15 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Run lint + run: pnpm run lint + - name: Run tests run: pnpm test - name: Build run: pnpm run build - - name: Run lint - run: pnpm run lint - - name: Publish to NPM env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/src/cli.ts b/src/cli.ts index a1448ee..c5dc89c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -16,10 +16,10 @@ program .version('0.1.0', '-v, --version', 'Output the current version') .helpOption('-h, --help', 'Display help message') .argument( - '[transform]', - 'Transform to apply (e.g., automock/2/to-suites-v3)' + '[codemod]', + 'Codemod slug to run. See "https://github.com/suites-dev/codemod".' ) - .argument('[path]', 'Path to transform (file or directory)', '.') + .argument('[source]', 'Path to source files or directory to transform including glob patterns.', '.') .option('-d, --dry', 'Dry run (no changes are made to files)', false) .option('-f, --force', 'Bypass Git safety checks and forcibly run codemods', false) .option('-p, --print', 'Print transformed files to stdout, useful for development', false) @@ -31,15 +31,15 @@ program .option('--list-transforms', 'List all available transforms', false) .action( async ( - transformArg: string | undefined, - pathArg: string | undefined, + codemodArg: string | undefined, + sourceArg: string | undefined, options: CliOptions & { listTransforms?: boolean } ) => { const logger = createLogger(options.verbose); // Handle --list-transforms if (options.listTransforms) { - console.log('Available transforms:\n'); + console.log('Available codemods:\n'); AVAILABLE_TRANSFORMS.forEach((t) => { console.log(` ${t.name}`); console.log(` ${t.description}\n`); @@ -47,10 +47,10 @@ program return; } - // Validate transform is provided - if (!transformArg) { - logger.error('Transform argument required.'); - logger.info('\nAvailable transforms:'); + // Validate codemod is provided + if (!codemodArg) { + logger.error('Codemod argument required.'); + logger.info('\nAvailable codemods:'); AVAILABLE_TRANSFORMS.forEach((t) => { console.log(` ${t.name}`); console.log(` ${t.description}\n`); @@ -60,13 +60,13 @@ program process.exit(1); } - const transformName = transformArg; - const targetPath = pathArg || '.'; + const codemodName = codemodArg; + const sourcePath = sourceArg || '.'; - const transformInfo = getTransform(transformName); + const transformInfo = getTransform(codemodName); if (!transformInfo) { - logger.error(`Unknown transform: ${transformName}`); - logger.error('Run with --list-transforms to see available transforms'); + logger.error(`Unknown codemod: ${codemodName}`); + logger.error('Run with --list-transforms to see available codemods'); process.exit(1); } @@ -90,7 +90,7 @@ program logger.newline(); // Run the transformation - await runTransform(targetPath, transformInfo, options, logger); + await runTransform(sourcePath, transformInfo, options, logger); } catch (error) { logger.newline(); logger.error('Transformation failed:'); From 915ca24cb0b75db1f6cc1508239efb80f976d760 Mon Sep 17 00:00:00 2001 From: omermorad Date: Wed, 3 Dec 2025 08:26:47 +0200 Subject: [PATCH 08/11] fix: remove duplicate symbols from logger output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed hardcoded symbols that were duplicating logger's built-in symbols: - git-safety.ts: Removed ⚠️ and ❌ (logger.error/warn already add symbols) - runner.ts: Removed hardcoded ✗ and ✓ (logger.error/success already add symbols) Now all output uses consistent single symbols: - ✔ for success (from logger.success()) - ✗ for errors (from logger.error()) - ⚠ for warnings (from logger.warn()) - ℹ for info (from logger.info()) No duplicate symbols in any output. --- fixtures/simple-final/input.ts | 8 ++++---- src/runner.ts | 4 ++-- src/utils/git-safety.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fixtures/simple-final/input.ts b/fixtures/simple-final/input.ts index a8399e9..9c3250b 100644 --- a/fixtures/simple-final/input.ts +++ b/fixtures/simple-final/input.ts @@ -1,12 +1,12 @@ -import { TestBed } from '@automock/jest'; +import { TestBed } from '@suites/unit'; describe('UserService', () => { let service: UserService; - beforeAll(() => { - const { unit } = TestBed.create(UserService) + beforeAll(async () => { + const { unit } = await TestBed.solitary(UserService) .mock(UserRepository) - .using({ + .final({ findById: (id: string) => Promise.resolve({ id, name: 'Test' }) }) .compile(); diff --git a/src/runner.ts b/src/runner.ts index cf58fdd..cb217a6 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -193,7 +193,7 @@ async function transformFile( // Write transformed file await fs.writeFile(filePath, transformOutput.code, 'utf-8'); result.changes.push('File updated'); - logger.success(` ✓ ${path.relative(process.cwd(), filePath)}`); + logger.success(` ${path.relative(process.cwd(), filePath)}`); } else { // Dry run - just report what would change result.changes.push('Would be updated (dry)'); @@ -203,7 +203,7 @@ async function transformFile( const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; result.errors.push(errorMessage); - logger.error(` ✗ ${path.relative(process.cwd(), filePath)}: ${errorMessage}`); + logger.error(` ${path.relative(process.cwd(), filePath)}: ${errorMessage}`); } return result; diff --git a/src/utils/git-safety.ts b/src/utils/git-safety.ts index e6022f4..243330b 100644 --- a/src/utils/git-safety.ts +++ b/src/utils/git-safety.ts @@ -19,13 +19,13 @@ export function checkGitStatus(logger: Logger): void { clean = true; } else if (err && err.code === 'MODULE_NOT_FOUND') { // is-git-clean not installed - warn and allow - logger.warn('⚠️ Git safety check skipped (is-git-clean not installed)'); + logger.warn('Git safety check skipped (is-git-clean not installed)'); clean = true; } } if (!clean) { - logger.error('❌ Git working directory is not clean'); + logger.error('Git working directory is not clean'); logger.info('Please commit or stash your changes before running the codemod.'); logger.info('This ensures you can easily revert changes if needed.'); logger.newline(); From 362265ae21fef3488991ed86c705c43f9d2680f3 Mon Sep 17 00:00:00 2001 From: omermorad Date: Wed, 3 Dec 2025 08:34:12 +0200 Subject: [PATCH 09/11] docs: add CLI arguments section matching Next.js format Added Arguments section to README to match Next.js codemod documentation: - codemod: Codemod slug to run - source: Path to source files or directory (with glob patterns) Renamed 'CLI Options' to 'CLI Reference' with subsections for Arguments and Options, providing clearer documentation structure. --- README.md | 11 ++++++++++- src/runner.ts | 9 ++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a5582e..745ec06 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,16 @@ describe('UserService', () => { }); ``` -## CLI Options +## CLI Reference + +### Arguments + +| Argument | Description | Default | +|----------|-------------|---------| +| `codemod` | Codemod slug to run. See available transforms below. | - | +| `source` | Path to source files or directory to transform including glob patterns. | `.` | + +### Options | Option | Description | Default | |--------|-------------|---------| diff --git a/src/runner.ts b/src/runner.ts index cb217a6..059d9cc 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -31,7 +31,14 @@ export async function runTransform( const fileProcessor = createFileProcessor({ extensions: options.extensions.split(',').map((ext) => ext.trim()), - ignorePatterns: options.ignore?.split(',').map((pattern) => pattern.trim()) || [], + ignorePatterns: options.ignore + ? [ + '**/node_modules/**', + '**/dist/**', + '**/*.d.ts', + ...options.ignore.split(',').map((pattern) => pattern.trim()), + ] + : undefined, }); const allFiles = await fileProcessor.discoverFiles(targetPath); From f2c382d2025cc40abc21141bf0cba94a8673b085 Mon Sep 17 00:00:00 2001 From: omermorad Date: Thu, 4 Dec 2025 01:28:05 +0200 Subject: [PATCH 10/11] refactor: rename --list-transforms to --list-codemods for consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed flag and terminology for consistency: - CLI flag: --list-transforms → --list-codemods - Usage: - Section heading: 'Available Transforms' → 'Available Codemods' - Error messages updated to reference --list-codemods This aligns all terminology with the 'codemod' naming convention used throughout the CLI and matches Next.js codemod patterns. --- README.md | 10 +++++----- src/cli.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index cb2af39..7004545 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Automated code transformations for Suites projects. Built on [jscodeshift](https ## Usage ```bash -npx @suites/codemod [options] +npx @suites/codemod [options] ``` **Example:** @@ -20,7 +20,7 @@ npx @suites/codemod automock/2/to-suites-v3 src/**/*.spec.ts Run with `--dry` or `-d` to preview changes without modifying files. -## Available Transforms +## Available Codemods - **`automock/2/to-suites-v3`** - Migrate test files from Automock v2 to Suites v3 testing framework @@ -84,7 +84,7 @@ describe('UserService', () => { | `--extensions ` | File extensions to transform | `.ts,.tsx` | | `--ignore ` | Ignore file patterns (comma-separated) | - | | `--skip-validation` | Skip post-transformation validation checks | `false` | -| `--list-transforms` | List all available transforms | - | +| `--list-codemods` | List all available codemods | - | | `-h, --help` | Display help message | - | **More examples:** @@ -101,8 +101,8 @@ npx @suites/codemod automock/2/to-suites-v3 src --ignore "**/*.integration.ts" # Verbose output npx @suites/codemod automock/2/to-suites-v3 src --verbose -# List all transforms -npx @suites/codemod --list-transforms +# List all codemods +npx @suites/codemod --list-codemods ``` ## Transform Details diff --git a/src/cli.ts b/src/cli.ts index c5dc89c..cbd2aaf 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -28,17 +28,17 @@ program .option('--extensions ', 'File extensions to transform', '.ts,.tsx') .option('--ignore ', 'Ignore file patterns (comma-separated)') .option('--skip-validation', 'Skip post-transformation validation checks', false) - .option('--list-transforms', 'List all available transforms', false) + .option('--list-codemods', 'List all available codemods', false) .action( async ( codemodArg: string | undefined, sourceArg: string | undefined, - options: CliOptions & { listTransforms?: boolean } + options: CliOptions & { listCodemods?: boolean } ) => { const logger = createLogger(options.verbose); - // Handle --list-transforms - if (options.listTransforms) { + // Handle --list-codemods + if (options.listCodemods) { console.log('Available codemods:\n'); AVAILABLE_TRANSFORMS.forEach((t) => { console.log(` ${t.name}`); @@ -66,7 +66,7 @@ program const transformInfo = getTransform(codemodName); if (!transformInfo) { logger.error(`Unknown codemod: ${codemodName}`); - logger.error('Run with --list-transforms to see available codemods'); + logger.error('Run with --list-codemods to see available codemods'); process.exit(1); } From 94e91b4f8b1f3e60b73d65629e76fe7bc1584268 Mon Sep 17 00:00:00 2001 From: omermorad Date: Thu, 4 Dec 2025 01:38:25 +0200 Subject: [PATCH 11/11] refactor: simplify CLI to match Next.js codemod (remove unnecessary options) Removed options to match Next.js simplicity: - Removed --extensions (hardcoded to .ts, .tsx) - Removed --ignore (hardcoded ignorePatterns: node_modules, dist, .d.ts) - Removed --skip-validation (validation always runs for safety) - Removed --list-codemods (show list in error messages instead) Final CLI options (matching Next.js): - -v, --version - -d, --dry - -f, --force - -p, --print - --verbose - --parser (our equivalent to Next.js -j, --jscodeshift) - -h, --help Benefits: - Simpler, cleaner CLI matching industry standard - Validation always runs (catches critical errors) - Hardcoded sensible defaults for extensions and ignore patterns - List of codemods shown automatically on errors --- README.md | 17 +++++------------ src/cli.ts | 22 ++++++---------------- src/runner.ts | 40 +++++++++++++++++----------------------- src/types.ts | 3 --- 4 files changed, 28 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 7004545..d2ba7e0 100644 --- a/README.md +++ b/README.md @@ -81,13 +81,9 @@ describe('UserService', () => { | `-p, --print` | Print transformed files to stdout, useful for development | `false` | | `--verbose` | Show more information about the transform process | `false` | | `--parser ` | Parser to use: `tsx`, `ts`, `babel` | `tsx` | -| `--extensions ` | File extensions to transform | `.ts,.tsx` | -| `--ignore ` | Ignore file patterns (comma-separated) | - | -| `--skip-validation` | Skip post-transformation validation checks | `false` | -| `--list-codemods` | List all available codemods | - | | `-h, --help` | Display help message | - | -**More examples:** +**Examples:** ```bash # Preview changes (dry run) npx @suites/codemod automock/2/to-suites-v3 src --dry @@ -95,14 +91,11 @@ npx @suites/codemod automock/2/to-suites-v3 src --dry # Print output to stdout npx @suites/codemod automock/2/to-suites-v3 src/file.ts -p -# Ignore certain files -npx @suites/codemod automock/2/to-suites-v3 src --ignore "**/*.integration.ts" - # Verbose output npx @suites/codemod automock/2/to-suites-v3 src --verbose -# List all codemods -npx @suites/codemod --list-codemods +# Use different parser +npx @suites/codemod automock/2/to-suites-v3 src --parser babel ``` ## Transform Details @@ -149,7 +142,7 @@ Built-in validation ensures: - Commit your changes or use `--force` to bypass **"No files found"** -- Check your path and file extensions: `--extensions .spec.ts,.test.ts` +- Check your source path and ensure it contains `.ts` or `.tsx` files **Parser errors** - Try the babel parser: `--parser babel` @@ -157,7 +150,7 @@ Built-in validation ensures: **Validation failed** - Run with `--verbose` for detailed logs - Review validation errors in the output -- Use `--skip-validation` to bypass (not recommended) +- Fix the issues reported by validators For more help, see [troubleshooting guide](https://github.com/suites-dev/codemod/issues) or open an issue. diff --git a/src/cli.ts b/src/cli.ts index cbd2aaf..ba9d9ed 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -25,28 +25,14 @@ program .option('-p, --print', 'Print transformed files to stdout, useful for development', false) .option('--verbose', 'Show more information about the transform process', false) .option('--parser ', 'Parser to use (tsx, ts, babel)', 'tsx') - .option('--extensions ', 'File extensions to transform', '.ts,.tsx') - .option('--ignore ', 'Ignore file patterns (comma-separated)') - .option('--skip-validation', 'Skip post-transformation validation checks', false) - .option('--list-codemods', 'List all available codemods', false) .action( async ( codemodArg: string | undefined, sourceArg: string | undefined, - options: CliOptions & { listCodemods?: boolean } + options: CliOptions ) => { const logger = createLogger(options.verbose); - // Handle --list-codemods - if (options.listCodemods) { - console.log('Available codemods:\n'); - AVAILABLE_TRANSFORMS.forEach((t) => { - console.log(` ${t.name}`); - console.log(` ${t.description}\n`); - }); - return; - } - // Validate codemod is provided if (!codemodArg) { logger.error('Codemod argument required.'); @@ -66,7 +52,11 @@ program const transformInfo = getTransform(codemodName); if (!transformInfo) { logger.error(`Unknown codemod: ${codemodName}`); - logger.error('Run with --list-codemods to see available codemods'); + logger.info('\nAvailable codemods:'); + AVAILABLE_TRANSFORMS.forEach((t) => { + console.log(` ${t.name}`); + console.log(` ${t.description}\n`); + }); process.exit(1); } diff --git a/src/runner.ts b/src/runner.ts index 059d9cc..46e75d3 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -30,15 +30,12 @@ export async function runTransform( logger.startSpinner('Discovering files..'); const fileProcessor = createFileProcessor({ - extensions: options.extensions.split(',').map((ext) => ext.trim()), - ignorePatterns: options.ignore - ? [ - '**/node_modules/**', - '**/dist/**', - '**/*.d.ts', - ...options.ignore.split(',').map((pattern) => pattern.trim()), - ] - : undefined, + extensions: ['.ts', '.tsx'], + ignorePatterns: [ + '**/node_modules/**', + '**/dist/**', + '**/*.d.ts', + ], }); const allFiles = await fileProcessor.discoverFiles(targetPath); @@ -161,7 +158,6 @@ async function transformFile( // Apply transformation const transformOutput = applyTransform(source, { - skipValidation: options.skipValidation, parser: options.parser, }); @@ -174,19 +170,17 @@ async function transformFile( result.transformed = true; // Collect validation errors and warnings - if (!options.skipValidation) { - transformOutput.validation.errors.forEach((err: ValidationError) => { - result.errors.push(`${err.rule}: ${err.message}`); - }); - - transformOutput.validation.warnings.forEach((warn: ValidationError) => { - result.warnings.push(`${warn.rule}: ${warn.message}`); - }); - - transformOutput.validation.criticalErrors.forEach((err: ValidationError) => { - result.errors.push(`[CRITICAL] ${err.rule}: ${err.message}`); - }); - } + transformOutput.validation.errors.forEach((err: ValidationError) => { + result.errors.push(`${err.rule}: ${err.message}`); + }); + + transformOutput.validation.warnings.forEach((warn: ValidationError) => { + result.warnings.push(`${warn.rule}: ${warn.message}`); + }); + + transformOutput.validation.criticalErrors.forEach((err: ValidationError) => { + result.errors.push(`[CRITICAL] ${err.rule}: ${err.message}`); + }); // Handle --print flag (output to stdout instead of writing) if (options.print) { diff --git a/src/types.ts b/src/types.ts index 54fbf5f..9496a72 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,11 +7,8 @@ export interface CliOptions { dry: boolean; force: boolean; parser: string; - extensions: string; - ignore?: string; print: boolean; verbose: boolean; - skipValidation: boolean; } /**