Introduction
omnimod is a modular, plugin-based codemod tool for TypeScript / JavaScript / React codebases. A small core provides the codemod machinery; plugins encapsulate specific transformations.
Why omnimod
- Formatting-preserving. The core parses with
oxc-parser(ESTree AST, UTF-16 offsets) and applies edits asmagic-stringsplices, so untouched code stays byte-identical. Generated files are then run through the target project's own formatter, so output matches its conventions. - Modular. The engine knows nothing about any specific transformation. A plugin is one object with a
transformfunction (plus optionalanalyzeandfinalizefor cross-file work). - Batteries included. Seven plugins ship today — styling (
styled-to-vanilla-extract), dates (moment-to-dayjs), testing (jest-to-vitest), utilities (lodash-to-es-toolkit), state (redux-to-toolkit), React (react-class-to-hooks) and build tooling (webpack-to-vite).
Packages
| Package | Responsibility |
|---|---|
@omnimod/core | Parse, walk, the Plugin contract, run() orchestration, diffs, formatting. |
@omnimod/plugin-utils | Helpers for plugin authors (shared ESTree node types + cast, CSS-in-JS parsing, a vanilla-extract object model, import management). |
@omnimod/cli | The omnimod command. |
@omnimod/plugin-* | Plugins. |
How it works
run(plugin, options) does the orchestration:
- Discover — glob the project (respecting
.gitignore) for files matching the plugin'sinclude. - Parse each file once into an AST + a
magic-stringedit buffer. analyze— an optional first pass over every file that collects cross-file facts into a sharedProjectContext.state.transform— the main pass: each file is rewritten via its edit buffer, and new files can be emitted.finalize— an optional last pass that emits shared files (e.g. a theme contract, or a migration report).- Write / dry-run — with
write: true, changed and emitted files are written and formatted; otherwise a colored diff is returned.
Next: Getting started.
