migration-kit
v0.0.1
Published
Building project-specific migration runners.
Maintainers
Readme
migration-kit
migration-kit is a library for building project-specific migration runners. It helps migration packages check runtime and dependency requirements, scan config files, run code transforms, and stop when a change still needs human review.
Features
- Migration runner - Run environment checks, dependency checks, package version updates, config changes, and API changes in one ordered flow.
- Runtime checks - Verify Node.js, Bun, or Deno availability and semver ranges through commands and common project version files.
- Dependency requirements - Check declared package ranges in
package.jsondependencies, dev dependencies, optional dependencies, and peer dependencies. - Package version updates - Detect npm, pnpm, Yarn, or Bun and update configured package ranges before transforms run.
- Config change handling - Run a transform against the first matching config file and keep rechecking error-level blockers after files change.
- API change scanning - Find files with
tinyglobby, run transforms, and summarize updated, unchanged, failed, and needs-review files. - Transformer helpers - Wrap
jscodeshiftandast-greptransforms behind the sharedTransformerresult contract.
Getting Started
Install
Install migration-kit in the project that owns your migration runner:
pnpm add migration-kitIf you use the built-in transformer helpers, install their peer dependencies too:
pnpm add ast-grep jscodeshiftUsage
Create a migration runner by describing the checks and transforms that make up the migration.
import { readFileSync } from "node:fs";
import { createMigrationRunner, runtime, transformer } from "migration-kit";
const migrationRunner = createMigrationRunner({
name: "Migration Runner",
from: "1.x",
to: "2.x",
configPath: ["migration.config.ts"],
environment: [runtime.node({ version: ">=20.0.0" })],
peerDependencies: [{ dependency: "target-package", requiredVersion: ">=2.0.0" }],
packageVersionUpdates: [{ dependency: "target-package", to: "^2.0.0" }],
configChanges: [
{
title: "Review removed config option",
description: "legacyMode was removed in 2.x.",
level: "warning",
shouldBlock: (filePath) => {
const source = readFileSync(filePath, "utf8");
return source.includes("legacyMode")
? { reason: "Remove legacyMode from the migration config." }
: false;
},
},
],
apiChanges: [
{
title: "Replace legacy API calls",
level: "warning",
files: ["src/**/*.ts", "test/**/*.ts"],
transform: transformer.astGrep("legacyApi()", { replace: "nextApi()" }),
},
],
});
await migrationRunner.run();The runner executes work in this order:
- Print the migration name, version range, and docs URL when one is provided.
- Run environment checks. Failed environment checks are reported without stopping the run.
- Check required dependencies from
package.json. Failed dependency checks stop the migration. - Detect the package manager, update configured package ranges from the migration
fromrange to thetorange, and run the package manager install command. - Find the first existing config file from
configPath, run config transforms, and recheck error-level blockers after project files change. - Scan API change file globs, run transforms, summarize results, and recheck error-level blockers after project files change.
API
createMigrationRunner(options)
Creates a runner with a single async run() method.
const runner = createMigrationRunner({
name: "Example Migration",
from: "1",
to: "2",
});
await runner.run();The options object supports:
name,from,to, and optionaldocsmetadataenvironmentchecks that return pass/fail resultspeerDependenciesrequirements checked against the current working directory'spackage.jsonpackageVersionUpdatesfor dependency ranges that should be updated before transforms runconfigPathcandidates for config-file migrationsconfigChangesfor config transforms and blockersapiChangesfor glob-based source transforms and blockers
Each packageVersionUpdates entry defaults to the runner-level from and to values. Set entry-level from or to when the package range should be more specific than the displayed migration versions.
runtime
runtime.node(), runtime.bun(), and runtime.deno() create environment checks.
runtime.node({ version: ">=20.0.0" });
runtime.bun({ version: "^1.2.0" });
runtime.deno({ version: ">=2.0.0" });Runtime checks can read project requirements from package.json fields such as engines, volta, devEngines.runtime, and runtime-specific packageManager pins. They also check version files such as .nvmrc, .node-version, .bun-version, .deno-version, and .tool-versions before falling back to the runtime command.
transformer
transformer.jscodeshift() wraps a jscodeshift transform function and writes changed source back to disk.
transformer.jscodeshift((fileInfo, api) => {
const j = api.jscodeshift;
return j(fileInfo.source)
.find(j.Identifier, { name: "oldName" })
.replaceWith(() => j.identifier("newName"))
.toSource();
});transformer.astGrep() searches source with ast-grep. Without a replacement it returns needs-review; with a replacement it writes updated source.
transformer.astGrep({
pattern: "oldValue()",
replace: "newValue()",
});License
This package is licensed under MIT as declared in package.json.
