Codemods
Ship codemods that `astryx upgrade` runs to migrate app code, and know which of them an app runs.Add a codemod
integration add codemod writes a codemod into a folder named after a version. Apps run it with astryx upgrade to migrate their code.
bashnpx astryx integration add codemod rename-delay --to 0.7.0
textcodemods/0.7.0/rename-delay.mjs # the codemodrename-delay.test.mjs # skipped: a test file__tests__/ # skipped: a test folder
The first add declares codemods: './codemods' in astryx.integration.mjs. The codemod's id is its path inside the version folder, without the extension: rename-delay. An id must be unique across all version folders in the package.
The loader skips *.test.*, *.spec.*, and *.fixture.* files and everything under __tests__/ or __fixtures__/, so tests can sit beside the codemod. The folder name decides when an app runs the codemod; see "Choose when a codemod runs".
Write the transform
A codemod default-exports a plain object with a type, a title, and a transform function. transform returns the new source, or null to leave the file as it is.
js// codemods/0.7.0/rename-delay.mjs/** @type {import('@astryxdesign/cli/authoring').AstryxCodemod} */export default {type: 'code',title: 'Rename AcmeCarousel delay to interval',description: 'Renames the delay prop on AcmeCarousel.',fileExtensions: ['.tsx', '.jsx'],transform(file, api) {const j = api.jscodeshift;const root = j(file.source);const props = root.find(j.JSXOpeningElement, {name: {name: 'AcmeCarousel'}}).find(j.JSXAttribute, {name: {name: 'delay'}});if (props.size() === 0) return null;props.forEach(path => {path.node.name.name = 'interval';});return root.toSource();},};
type: 'code' rewrites the app's source files that match fileExtensions. type: 'config' rewrites the app's astryx.config file instead, and runs before code codemods. title shows in the upgrade output, and api.jscodeshift is a jscodeshift instance for the file. Every field is in astryx docs authoring.
Choose when a codemod runs
Two rules decide whether an app's astryx upgrade runs your codemods: the app's Core versions, and whether the app names your package.
- Version folders are matched against the app's
@astryxdesign/coreversions, not your package's version.upgrade --from <version>runs each folder above--from, up to and including the Core version installed in the app. With Core 0.7.0 installed,--from 0.6.3runs0.7.0/, and--from 0.7.0runs nothing. A folder named after your own release, such as1.0.0/, waits until the app has Core 1.0.0. upgraderuns your codemods only when the app lists your package inintegrationsin itsastryx.config, or passes--integration @acme/astryx-widgets. Having your package installed is not enough: the run then skips your codemods with no warning.
Run codemods in an app
An app previews codemods with astryx upgrade and writes the changes with --apply. Without --apply, nothing on disk changes.
bash# Preview each codemod and the files it would changenpx astryx upgrade --from 0.6.3 --integration @acme/astryx-widgets# Write the changesnpx astryx upgrade --from 0.6.3 --integration @acme/astryx-widgets --apply
textIntegrations: @acme/astryx-widgets1 codemod to run (dry run)Applying integration codemods...Rename AcmeCarousel delay to interval (v0.7.0, @acme/astryx-widgets)! ~ src/Hero.tsx (would change)
The count includes Core's codemods for the same versions, which run first and print above Integrations:. To run only yours, as when you test it, add --codemod rename-delay.
--from is the Core version the app had before it upgraded. The run scans ./src unless the app passes --path, and it never writes a file the app marks as generated, vendored, or ignored; see astryx docs cli/commands/upgrade. upgrade --list shows only Core codemods.
An integration manifest has no hooks field. Commands that run after codemods, such as a formatter, are the app's to set, in hooks.postCodemod in its astryx.config.