@coinbase/cds-migrator
v1.1.0
Published
Coinbase Design System - Code Migration Tools
Readme
@coinbase/cds-migrator
Code migration tools for the Coinbase Design System.
Installation
# Install as dev dependency (recommended)
yarn add -D @coinbase/cds-migrator
# Or use npx without installing
npx @coinbase/cds-migratorQuick Start
Your First Migration
1. Run the CLI
npx @coinbase/cds-migrator
# Or with path: npx @coinbase/cds-migrator ./src2. Follow the Prompts
? Enter the path to your codebase: ./src
? Which migration preset do you need? v8 to v9That's it! Runs all transforms by default. Use --partial to select specific ones.
3. Review Changes
The migrator will:
- Run transforms and display progress
- Create
migration.logwith details - Add TODO comments where manual work is needed
4. Commit
git add .
git commit -m "Migrate from CDS v8 to v9"Common Workflows
Quick & Complete:
# Preview
npx @coinbase/cds-migrator ./src -p v8-to-v9 --dry-run
# Review log
cat migration.log
# Apply
npx @coinbase/cds-migrator ./src -p v8-to-v9One Transform:
npx @coinbase/cds-migrator ./src -t button-variant-values --dry-runMultiple Transforms:
npx @coinbase/cds-migrator ./src -t button-variant -t input-size --dry-runFeatures
- 🎯 Smart Prompting - Only prompts for missing information
- 📦 Flexible Execution - Use presets for grouped migrations or run transforms directly
- 📝 History Tracking - Automatically prevents duplicate migrations
- 🌊 Dry-Run Mode - Preview all changes before applying
- 📊 Comprehensive Logging - Track successes, warnings, and manual TODOs
- 💬 TODO Comments - Automatically marks code that needs manual review
- 🔀 Import Rewrites - Cover wrapper packages that re-export CDS so transforms apply to call sites that don't import from CDS directly
How It Works
Presets are curated collections of related transforms (e.g., all transforms needed for v8→v9 migration). You can use a preset to run multiple transforms at once, or run individual transforms directly.
| Preset | Description | Status | | ------------ | ---------------------------------------------------- | ------------ | | v8-to-v9 | Component API updates, hook changes, utility updates | ✅ Available |
Note: Presets aren't just for version migrations - you can create presets for any collection of code transforms (deprecations, refactors, style updates, etc.).
Usage
Preset Mode
Run all transforms in a preset (default) or select specific ones:
# Run all transforms in preset (default)
npx @coinbase/cds-migrator ./src -p v8-to-v9
# Select specific transforms with multi-select
npx @coinbase/cds-migrator ./src -p v8-to-v9 --partialDirect Transform Mode
Run transforms directly without a preset:
# Single transform
npx @coinbase/cds-migrator ./src -t button-variant-values
# Multiple transforms
npx @coinbase/cds-migrator ./src -t button-variant-values -t input-size-values
# Transform in subdirectory
npx @coinbase/cds-migrator ./src -t components/button-variantStandalone Transforms
These aren't part of any preset — run them directly with -t.
| Transform | What it does |
| ----------------- | ---------------------------------------------------------------------------------------------- |
| compact-to-size | Replaces the deprecated compact prop with the t-shirt size prop (expected removal in v10). |
compact-to-size
npx @coinbase/cds-migrator ./src -t compact-to-size --dry-runcompact is deprecated in favour of size on the components below, and maps to a different
size depending on which scale the component uses:
| Components | compact becomes |
| ----------------------------------------------------------------------------------------------------------------------- | ----------------- |
| Button, IconButton, SlideButton, TextInput, SearchInput, DateInput, DatePicker, alpha Select/Combobox | size="s" |
| Chip, InputChip, MediaChip, SelectChip, alpha SelectChip, alpha TabbedChips | size="xs" |
The transform matches on (import path, component name) pairs, so the many other components
that happen to have a compact prop meaning something unrelated — AvatarButton, NativeInput,
NativeTextArea, InputIcon, the legacy controls/Select, ContentCell/ListCell (whose
compact became spacingVariant in v9), Table, CardBody, and the alpha dropdown/option
components — are left untouched even when imported from the same barrel.
It also preserves behaviour that compact carried beyond spacing:
IconButton'scompactdefaults totrue, socompact={false}becomessize="l"instead of being dropped.- On the input and select families,
compact(set withoutsize) also forced the label into the inline start slot, solabelVariant="inside"is added when the element has a label. Multi-select (type="multi") is excluded, because legacycompactdeliberately kept its label outside. - A dynamic
compact={expr}becomessize={expr ? 's' : 'l'}(or'xs' : 's'for chips) rather than being left behind.
TODO [cds-migrator:compact-to-size] comments are inserted for the cases it can't fully resolve —
a dynamic size, a dynamic compact on a labelled input, a label arriving through a spread, and
a dynamic type on a Select (where the label rule differs between single- and multi-select).
The alpha Select dropdown needs no migration: it keeps a binary compact density toggle, and
Select derives it from its own resolved size, so setting size="s" still yields a compact
dropdown.
Wrapper Packages / Import Rewrites
If your repo re-exports CDS through a wrapper package (e.g. @acme/shared/cds/buttons/Button instead of @coinbase/cds-web/buttons/Button), transforms will skip those call sites by default because they don't match the CDS package names.
Use --import-mapping to tell the migrator to treat one import prefix as another before testing any transform regex:
# One-off via CLI flag
npx @coinbase/cds-migrator ./src -p v8-to-v9 -ir '@acme/shared/cds=@coinbase/cds-web'
# Multiple rewrites
npx @coinbase/cds-migrator ./src -p v8-to-v9 \
-ir '@acme/shared/cds=@coinbase/cds-web' \
-ir '@acme/ui/cds=@coinbase/cds-web'Or drop a cds-migrator.config.json at your repo root so every run picks it up automatically—no flag required:
{
"packageScope": "coinbase",
"importMappings": [{ "from": "@acme/shared/cds", "to": "@coinbase/cds-web" }]
}CLI flags always take precedence over the config file when the same from key appears in both. The rewrite affects only import matching—it never changes the import path written to disk.
Fully Interactive
Omit flags to be prompted for everything:
npx @coinbase/cds-migrator
# Prompts for: path → preset → transforms (if --partial)Note: Path is the first positional argument. The CLI prompts only for missing information.
Documentation
📘 For Users
- CLI Reference - Complete CLI usage guide
- History Guide - Advanced history tracking
🔧 For Contributors
- Presets & Transforms - Creating presets, transforms, and APIs
What Gets Created
After running a migration:
your-project/
├── src/
│ ├── .cds-migration-history.json # ← Tracks applied migrations (commit this!)
│ └── ... (your modified files)
├── cds-migrator.config.json # ← Optional: standing defaults (commit this!)
└── migration.log # ← Detailed log (review then delete)Migration History
The migrator automatically tracks which transforms have been run to prevent duplicates.
How It Works
When you run a migration (not in dry-run mode):
- Records which transform was run and when
- Stores in
.cds-migration-history.jsonin the target directory - Checks history before running to prevent duplicates
The history file should be committed to version control so team members don't re-run the same migrations.
What Happens with Duplicates
If you try to run already-applied transforms:
⚠️ Warning: Some transforms have already been run on this path:
• button-variant-values
? How would you like to proceed?
❯ Skip already-run transforms (recommended)
Re-run all transforms
CancelUse --skip-confirmation to auto-filter duplicates without prompting.
Clearing History
# With confirmation
npx @coinbase/cds-migrator ./src --clear-history
# Skip confirmation
npx @coinbase/cds-migrator ./src --clear-history --skip-confirmationWhen to clear:
- Failed migration that needs to be re-run
- Testing/development
- After rolling back code changes
See docs/HISTORY.md for advanced usage.
Project Structure
packages/migrator/
├── docs/
│ ├── CLI_REFERENCE.md # CLI usage guide
│ ├── PRESETS_AND_TRANSFORMS.md # Creating presets and transforms
│ └── HISTORY.md # History tracking
├── src/
│ ├── cli.ts # Main CLI
│ ├── runner.ts # Migration executor
│ ├── types.ts # TypeScript types
│ ├── presets/ # Preset configurations (auto-discovered!)
│ │ └── v8-to-v9/
│ │ └── manifest.json
│ ├── transforms/ # Jscodeshift codemods (a module, or a folder with index.ts)
│ │ ├── v9/ # Grouped by target version when part of a preset
│ │ │ └── migrate-use-merge-refs.ts
│ │ └── compact-to-size/ # Self-contained standalone transform
│ │ ├── __testfixtures__/
│ │ ├── __tests__/
│ │ └── index.ts
│ └── utils/ # Shared utilities
│ ├── import-mapping.ts # Import prefix rewrites (-ir / config)
│ ├── package-scope.ts # Package scope filtering (-ps)
│ └── ...
└── package.jsonDevelopment
Build & Test
# Build the package
yarn nx run migrator:build
# Type check
yarn nx run migrator:typecheck
# Lint
yarn nx run migrator:lint
# Test the CLI
cd packages/migrator
node cjs/cli.jsAdding New Presets
See docs/PRESETS_AND_TRANSFORMS.md for complete instructions.
Quick steps:
- Create
src/presets/v9-to-v10/directory - Add
manifest.jsonwith a flat list of transforms:{ "preset": "v9-to-v10", "description": "Migration from CDS v9 to v10", "transforms": [ { "name": "my-transform", "description": "What it does (@package/name)", "file": "my-transform" } ] } - Create transform file in
src/transforms/my-transform.ts
Note: Presets are auto-discovered from the src/presets/ directory!
License
See LICENSE in the root of the repository.
