Skip to content
← Public packages

@kentcdodds/codemod-runner

Run codemods over your own saved packages: scan, dry-run with real publish checks and diffs, apply, and revert.

src/apply.ts

49 lines · 1.6 KB · TypeScript
import { requireCodemod, runStep, type StepInput } from './step.ts'
import { type CodemodRef } from './contract.ts'
import { listRunSummaries } from './ledger.ts'
import { transformPackage } from './process-package.ts'

async function assertDryRunCompleted(codemod: CodemodRef): Promise<void> {
	const exportName = codemod.exportName ?? 'codemod'
	const summaries = await listRunSummaries()
	const completedDryRun = summaries.some(
		(run) =>
			run.mode === 'dry-run' &&
			run.status === 'completed' &&
			run.codemod?.kodyId === codemod.kodyId &&
			run.codemod?.specifier === codemod.specifier &&
			run.codemod?.exportName === exportName,
	)
	if (!completedDryRun) {
		throw new Error(
			`No completed dry-run found for codemod "${codemod.kodyId}/${exportName}". Run dry-run first and review the diffs before applying.`,
		)
	}
}

/**
 * Applies a codemod to your packages: same gates as dry-run (publish
 * checks, idempotency verification, drift skips), then snapshots each
 * original tree into the run ledger and republishes the transformed tree.
 * Requires a completed dry-run for the same codemod first. Keep the
 * returned runId — it is what `revert` needs. Paged: keep calling with the
 * returned runId + cursor until nextCursor is null.
 */
export default async function apply(input: StepInput = {}) {
	const codemod = requireCodemod(input)
	if (!input.runId) {
		await assertDryRunCompleted(codemod)
	}
	return await runStep({
		mode: 'apply',
		step: input,
		defaults: { limit: 1, max: 2 },
		processPackage: async (run, pkg) =>
			await transformPackage({
				mode: 'apply',
				runId: run.id,
				pkg,
				codemod,
			}),
	})
}