Skip to content

docs: add migration guide for creating custom presets #37

Description

@aridyckovsky

Summary

Create comprehensive documentation for users who want to create custom migration presets for their organization or specific migration scenarios.

Motivation

With first-class preset support now available (feat: implement first-class preset support), users need guidance on:

  • When to create a custom preset vs using @effect-migrate/preset-basic
  • How to structure a preset package
  • How to write effective pattern and boundary rules
  • How to distribute presets (npm, monorepo, local)
  • Best practices for rule organization

Proposed Content

Location

Create docs/guides/creating-custom-presets.md

Outline

# Creating Custom Presets

## When to Create a Custom Preset

- Organization-specific patterns
- Framework-specific migrations (React, Express, etc.)
- Domain-specific rules (finance, healthcare)
- Private/proprietary migration patterns

## Preset Structure

### Package Setup
- package.json configuration
- TypeScript setup
- Exports and entry point

### Preset Object
```typescript
import type { Preset } from '@effect-migrate/core'

export const myPreset: Preset = {
  rules: [...],
  defaults: {
    paths: { exclude: [...] },
    report: { failOn: ['error'] }
  }
}

Writing Rules

Pattern Rules

  • When to use pattern rules
  • Regex best practices
  • Avoiding false positives

Boundary Rules

  • Architectural constraints
  • Import restrictions
  • Layer boundaries

Custom Rule Implementations

  • Using ctx.listFiles
  • Using ctx.readFile
  • Using ctx.getImportIndex

Testing Your Preset

Unit Tests

  • Testing individual rules
  • Mock context setup
  • Fixture files

Integration Tests

  • Testing with real codebases
  • CI/CD integration

Publishing

NPM Package

  • Naming conventions (@org/preset-name)
  • Versioning
  • README requirements

Monorepo Distribution

  • Workspace packages
  • Local presets

Private Presets

  • Internal npm registry
  • Git dependencies

Examples

Example 1: React Migration Preset

  • Detect class components
  • Find deprecated lifecycle methods
  • Suggest hooks patterns

Example 2: Express to Fastify Preset

  • Detect Express middleware patterns
  • Flag incompatible APIs
  • Suggest Fastify equivalents

Best Practices

  • Keep rules focused and specific
  • Provide actionable error messages
  • Link to documentation
  • Use negativePattern to reduce false positives
  • Test extensively with real code

Reference

  • @effect-migrate/preset-basic source code
  • Core API documentation
  • Rule interface reference

## Documentation Locations

1. **Main guide:** `docs/guides/creating-custom-presets.md`
2. **README update:** Add link to guide in root README.md
3. **preset-basic README:** Add "Creating Custom Presets" section
4. **AGENTS.md:** Add reference for AI agents

## Examples to Include

### Minimal Preset

```typescript
// my-preset/src/index.ts
import { makePatternRule, type Preset } from '@effect-migrate/core'

const noOldPattern = makePatternRule({
  id: 'no-old-pattern',
  files: ['**/*.ts'],
  pattern: /oldFunction\(/g,
  message: 'Use newFunction instead of oldFunction',
  severity: 'warning',
  docsUrl: 'https://docs.example.com/migration'
})

export const myPreset: Preset = {
  rules: [noOldPattern],
  defaults: {
    paths: { exclude: ['node_modules/**'] }
  }
}

React Preset Example

// preset-react/src/index.ts
import { makePatternRule, type Preset } from '@effect-migrate/core'

const noClassComponents = makePatternRule({
  id: 'no-class-components',
  files: ['**/*.tsx'],
  pattern: /class\s+\w+\s+extends\s+React\.Component/g,
  message: 'Migrate class component to function component with hooks',
  severity: 'warning',
  docsUrl: 'https://react.dev/reference/react/Component#alternatives'
})

const noComponentWillMount = makePatternRule({
  id: 'no-component-will-mount',
  files: ['**/*.tsx'],
  pattern: /componentWillMount\s*\(/g,
  message: 'componentWillMount is deprecated, use useEffect',
  severity: 'error',
  docsUrl: 'https://react.dev/reference/react/Component#componentwillmount'
})

export const reactPreset: Preset = {
  rules: [noClassComponents, noComponentWillMount],
  defaults: {
    paths: {
      exclude: ['node_modules/**', '*.test.tsx']
    }
  }
}

Acceptance Criteria

  • Guide written with examples
  • Code examples tested and verified
  • Screenshots/diagrams if helpful
  • Links to relevant API docs
  • Referenced in root README
  • Referenced in preset-basic README
  • Mentioned in AGENTS.md

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions