icon

Usage Examples

Advanced Usage

This guide covers advanced techniques and patterns for using Mutates effectively in complex scenarios.

Complex Transformations

Chaining Operations

import { addMethods, addProperties, editClasses, getClasses } from '@mutates/core';

// Find and transform classes in multiple steps
const classes = getClasses({ pattern: 'src/**/*.ts' });

classes.forEach((klass) => {
  // First transformation
  editClasses(klass, () => ({
    isExported: true,
    isAbstract: true,
  }));

  // Add methods
  addMethods(klass, {
    name: 'initialize',
    isAbstract: true,
    returnType: 'Promise<void>',
  });

  // Add properties
  addProperties(klass, {
    name: 'isInitialized',
    type: 'boolean',
    initializer: 'false',
  });
});

Conditional Transformations

import { getClasses, Node } from '@mutates/core';

const classes = getClasses();

classes.forEach((klass) => {
  // Check if class extends a specific base class
  const baseClass = klass.getBaseClass();
  if (baseClass && Node.isIdentifier(baseClass) && baseClass.getText() === 'BaseComponent') {
    // Apply specific transformations for components
  }

  // Check for specific decorators
  const hasInject = klass.getDecorators().some((d) => d.getName() === 'Inject');
  if (hasInject) {
    // Apply transformations for injectable classes
  }
});

Working with Types

Type Management

import { addInterfaces, addTypeAliases } from '@mutates/core';

// Add complex interface
addInterfaces('types.ts', {
  name: 'Config',
  properties: [
    {
      name: 'api',
      type: '{
        endpoint: string;
        version: number;
        options?: RequestInit;
      }'
    }
  ]
});

// Add type alias with generic
addTypeAliases('types.ts', {
  name: 'Result',
  typeParameters: ['T'],
  type: '{ data: T; error?: Error }'
});

Generic Type Handling

import { addClasses } from '@mutates/core';

addClasses('service.ts', {
  name: 'DataService',
  typeParameters: ['T extends Record<string, any>'],
  properties: [
    {
      name: 'items',
      type: 'T[]',
    },
  ],
  methods: [
    {
      name: 'find',
      typeParameters: ['K extends keyof T'],
      parameters: [
        {
          name: 'key',
          type: 'K',
        },
        {
          name: 'value',
          type: 'T[K]',
        },
      ],
      returnType: 'T | undefined',
      statements: 'return this.items.find(item => item[key] === value);',
    },
  ],
});

Advanced Pattern Matching

Complex File Patterns

import { getClasses } from '@mutates/core';

// Multiple patterns
const classes = getClasses({
  pattern: ['src/**/*.service.ts', 'src/**/*.repository.ts', '!src/**/*.spec.ts'],
});

// Pattern with alternatives
const components = getClasses({
  pattern: 'src/**/*.(component|directive).ts',
});

Content-Based Filtering

import { getClasses, Node } from '@mutates/core';

// Find classes with specific characteristics
const classes = getClasses().filter((klass) => {
  // Has specific method
  const hasMethod = klass.getMethods().some((method) => method.getName() === 'ngOnInit');

  // Has specific import
  const hasImport = klass
    .getSourceFile()
    .getImportDeclarations()
    .some((imp) => imp.getModuleSpecifier().getText().includes('@angular/core'));

  return hasMethod && hasImport;
});

Working with Comments and Documentation

JSDoc Management

import { addClasses } from '@mutates/core';

addClasses('service.ts', {
  name: 'ApiService',
  docs: [
    '/**',
    ' * Service for handling API communications',
    ' * @template T - The response data type',
    ' */',
  ],
  methods: [
    {
      name: 'fetch',
      docs: [
        '/**',
        ' * Fetches data from the API',
        ' * @param endpoint - The API endpoint',
        ' * @returns Promise with the response data',
        ' * @throws {ApiError} When the request fails',
        ' */',
      ],
    },
  ],
});

Comment Preservation

import { editClasses } from '@mutates/core';

// Preserve existing comments while modifying code
editClasses(targetClass, (structure) => ({
  ...structure,
  methods: structure.methods?.map((method) => ({
    ...method,
    // Preserve method documentation
    docs: method.docs,
  })),
}));

Performance Optimization

Batch Processing

import { createProject, editClasses, getClasses, saveProject } from '@mutates/core';

// Process files in batches
createProject();

const allClasses = getClasses();
const batchSize = 100;

for (let i = 0; i < allClasses.length; i += batchSize) {
  const batch = allClasses.slice(i, i + batchSize);
  editClasses(batch /* transformations */);
}

saveProject();

Caching Results

import { getClasses, Node } from '@mutates/core';

// Cache complex computations
const classCache = new Map<string, boolean>();

function isEligibleForTransform(klass: Node) {
  const key = klass.getFilePath();

  if (classCache.has(key)) {
    return classCache.get(key);
  }

  const isEligible = /* complex computation */;
  classCache.set(key, isEligible);

  return isEligible;
}

Error Handling and Validation

Custom Validation

import { getClasses, Node } from '@mutates/core';

function validateClass(klass: Node) {
  const errors = [];

  // Check for required methods
  const requiredMethods = ['initialize', 'dispose'];
  for (const method of requiredMethods) {
    if (!klass.getMethod(method)) {
      errors.push(`Missing required method: ${method}`);
    }
  }

  // Validate property types
  klass.getProperties().forEach((prop) => {
    const type = prop.getType();
    if (type.isAny()) {
      errors.push(`Property ${prop.getName()} has 'any' type`);
    }
  });

  return errors;
}

// Use validation in transformations
const classes = getClasses();
classes.forEach((klass) => {
  const errors = validateClass(klass);
  if (errors.length > 0) {
    console.error(`Validation failed for ${klass.getName()}:`, errors);
    // Handle errors appropriately
  }
});

Next Steps

Previous
Basic Usage