@via-ds/codemods
v0.0.5
Published
Codemods for migrating to Via Design System (from LeafyGreen) and for upgrading between Via versions
Downloads
12,615
Readme
@via-ds/codemods
Codemods for migrating to Via Design System from other MongoDB component libraries (e.g. LeafyGreen), and for upgrading between Via versions as the API evolves.
Available codemods
| Codemod | Engine | Source | Description |
| ------------------------ | ------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| lg-button-to-via | jscodeshift | @leafygreen-ui/{button,icon-button} | Rewrites LeafyGreen Button / IconButton to Via Button / LinkButton / ToggleButton. |
| lg-icon-to-via | jscodeshift | @leafygreen-ui/icon | Rewrites LeafyGreen Icon usage to Via Icon (@via-ds/icons). |
| lg-provider-to-via | jscodeshift | @leafygreen-ui/leafygreen-provider | Rewrites LeafyGreen LeafyGreenProvider to Via ViaProvider. |
| lg-typography-to-via | jscodeshift | @leafygreen-ui/typography | Rewrites LeafyGreen typography (H1–H3, Body, Subtitle, Overline, Disclaimer, InlineCode, InlineKeyCode, Description, Error, Label, Link, BackLink) to Via Text / Label / Link. |
| css-vars-via-namespace | regex | @via-ds/tokens | Rewrites Via token CSS var references (var(--color-black)) to the --via- prefixed names introduced in UXE-479 (var(--via-color-black)). |
Composites
| Composite | Runs |
| ----------- | ---------------------------------- |
| lg-to-via | All LG → Via codemods in sequence. |
Notes on css-vars-via-namespace
- Rewrites vars matching a Style Dictionary token category (
border,color,duration,font,shadow,size,space,typography) — component-local CSS vars (e.g.--button-text-color-light,--cell-size) are untouched, since none of them share those category names. The category list is generated at build time frompackages/tokens/src/*.tokens.json(scripts/generateTokenCategories.ts), not hand-copied. - Idempotent — running it twice is a no-op; it skips anything already
--via--prefixed. - Applies to
.css,.scss,.less,.ts,.tsx, and.mdxfiles, since token var references show up as plain text in inline styles and docs too, not just stylesheets. - Uses the
regexengine (plain text replacement), notjscodeshift— jscodeshift can't parse non-JS files like.css. Run it viavia-codemodorrunRegexCodemod(see "Running aregex-engine codemod" below), notjscodeshift -t.
Known limitations (accepted tradeoffs)
- Can't distinguish a Via token var from a consumer's own var that happens to share a category prefix.
var(--color-warning-bg)gets rewritten tovar(--via-color-warning-bg)even if--color-warning-bgis the consumer's own var, not one Via generates. Matching against the complete set of real Via token names (rather than just their category prefixes) would avoid this, but requires the tokens package to be built (Style Dictionary output, not just its source JSON) at codemod-build-time, which the codemod's dependency-free build step doesn't do. Seetests/consumer-var-collision.*. - No awareness of CSS comments or non-CSS string/comment content. Because the match is a plain-text regex, it rewrites
var(--color-black)even inside a CSS comment or inside a.ts/.tsx/.mdxcomment or prose string — it isn't parsing CSS or JS syntax, just text. Seetests/inline-style-string.*.
Notes on lg-button-to-via
- LG
Buttonwithhrefbecomes ViaLinkButton. Plain LGButtonbecomes ViaButton. - LG
IconButtonbecomes ViaButtonwithvariant="tertiary". Withactive, it becomes ViaToggleButton(active→isSelected). disabled→isDisabled,onClick→onPress.variant="dangerOutline"→variant="secondaryDanger",variant="baseGreen"→variant="brand"; other LG variants (default,primary,primaryOutline,danger) match Via variant names of the same name and pass through unchanged. Unrecognized variants are left as-is with a// TODO(via-codemod): …comment for review.darkMode→colorScheme.leftGlyph/rightGlyphmove from props to children (before / after the existing children).isLoading→isPending(Via's native RAC pending state, which renders its own progress indicator alongside the existing children).loadingTextandloadingIndicatorare dropped with a// TODO(via-codemod): …comment becauseisPendingdoesn't swap children.sizeandbaseFontSizeare stripped and a// TODO(via-codemod): …comment is left above the element — Via does not yet support them.- Aliased imports (
import LgButton from '@leafygreen-ui/button') are detected, but the rewritten JSX uses canonical Via names; review the diff if you relied on aliases. - If the file also imports non-component symbols from
@leafygreen-ui/buttonor@leafygreen-ui/icon-button(e.g.ButtonProps,Size), those specifiers are preserved and a// TODO(via-codemod): residual imports …comment is added above the surviving import so the partial migration is visible.
Consumer-facing usage
Run a jscodeshift-engine codemod via @via-ds/cli (preferred):
npx @via-ds/cli codemod <name> ./srcOr directly with jscodeshift:
npx jscodeshift \
-t node_modules/@via-ds/codemods/dist/codemods/<name>/transform.js \
--extensions=ts,tsx \
--parser=tsx \
src/Running a regex-engine codemod
jscodeshift can only parse JS/TS ASTs, so it can't run codemods that target .css (e.g. css-vars-via-namespace). Run those with the via-codemod bin instead:
npx --package=@via-ds/codemods via-codemod css-vars-via-namespace ./srcOr programmatically:
import { runRegexCodemod } from '@via-ds/codemods/run-codemod';
const results = await runRegexCodemod('css-vars-via-namespace', './src');Both rewrite matched files in place and report which files changed.
Authoring a codemod
Every codemod's meta must declare an engine: 'jscodeshift' (a JS/TS AST transform) or 'regex' (a plain-text find/replace, for files jscodeshift can't parse).
- Create
src/codemods/<name>/transform.ts. The file must:- named-export
meta(the manifest entry, sanstransformPath), includingengine. - default-export a
jscodeshiftTransform(ifengine: 'jscodeshift') or aRegexCodemodModule—{ filePatterns, replacements }(ifengine: 'regex').
- named-export
- Add fixture pairs at
src/codemods/<name>/tests/<case>.{input,output}.<ext>:jscodeshiftcodemods: atransform.spec.tsthat loops them throughrunFixture(fromsrc/utils/tests/transformTest.ts).regexcodemods: atransform.spec.tsthat loops them throughrunRegexFixture(fromsrc/utils/tests/regexFixtureTest.ts).
- (Optional) Add
src/composites/<name>.tsexportingcompositeto bundle this codemod into a multi-step run.
The src/index.ts manifest is generated — prebuild runs scripts/buildCodemodIndex.ts, which scans src/codemods/* and src/composites/* and writes a typed codemods / composites record. Missing meta exports are a hard build error.
Example transform.ts (jscodeshift engine):
import type { Transform } from 'jscodeshift';
import type { CodemodMeta } from '../../types';
export const meta = {
name: 'lg-button-to-via',
description:
'Migrate LeafyGreen Button/IconButton to Via Button/LinkButton/ToggleButton',
fromPackages: [
'@leafygreen-ui/button',
'@leafygreen-ui/icon-button',
] as const,
engine: 'jscodeshift',
} satisfies Omit<CodemodMeta, 'transformPath'>;
const transform: Transform = (file, api) => {
// …
};
export default transform;Example transform.ts (regex engine):
import type { CodemodMeta, RegexCodemodModule } from '../../types';
export const meta = {
name: 'css-vars-via-namespace',
description:
'Rewrite Via token CSS var references to their --via- prefixed names.',
fromPackages: ['@via-ds/tokens'] as const,
engine: 'regex',
} satisfies Omit<CodemodMeta, 'transformPath'>;
const module: RegexCodemodModule = {
filePatterns: ['**/*.css'],
replacements: [
{ pattern: /var\(--color-/g, replacement: 'var(--via-color-' },
],
};
export default module;