npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@grafana/design-codemods

v0.2.0

Published

Codemod tooling for migrating consumers onto @grafana/design packages

Readme

@grafana/design-codemods

Codemod tooling for migrating consumers onto @grafana/design packages.

This is the home of:

  • A design-codemods CLI for bulk source rewrites (the heavy-lifting jscodeshift transforms).
  • Re-exports of any migration tables that codemods consume — most notably the icon migrations table from @grafana/icons, which lives here so application code never has a reason to import it directly from the icons package.

The companion to this package is @grafana/eslint-plugin-design, which carries the per-call-site ESLint rules that auto-fix the same migrations during normal editing. Both reach for the same migration data; the codemods package handles bulk + cross-file transforms, the eslint plugin handles the editor-time path.

Installation

pnpm add -D @grafana/design-codemods

CLI

pnpm dlx @grafana/design-codemods help
pnpm dlx @grafana/design-codemods <subcommand> [paths...] [options]

Code-rewriting subcommands

These subcommands read source files, apply transforms in place, and report a summary. All share the same flags and path-walking behavior:

  • --dry-run / -d — print which files would change without writing.
  • --verbose / -v — also log unchanged files.

Paths may be files or directories; directories are walked recursively for .ts/.tsx/.js/.jsx (+ .cts/.cjs/.mts/.mjs) files. node_modules, dist, build, .turbo, .next, .git and coverage directories are skipped.

Each transform preserves the source position of imports and the surrounding code's formatting: when all specifiers of an import move, the new import replaces the original in place; when only some specifiers move, the new import is inserted immediately after the trimmed original. Existing imports from the target package are merged into rather than duplicated. The codemods never reformat unrelated code (no quote-style changes outside the edits, no JSX whitespace normalization, no auto-format).

icon-migration

One-shot migration of <Icon name="..." /> from @grafana/ui to <Icon component={X} /> from @grafana/icons. Looks each name up in the @grafana/icons/migrations table and rewrites the JSX + manages the imports (adds the matched component and the Icon wrapper to @grafana/icons, removes Icon from @grafana/ui).

pnpm dlx @grafana/design-codemods icon-migration apps/plugin/src
pnpm dlx @grafana/design-codemods icon-migration . --dry-run

Stage A scope (this codemod):

  • Static literal name values (string literal or JSX-expression-wrapped string literal).
  • Adds the matched component and Icon to @grafana/icons, merging into an existing import where present.
  • Removes Icon from @grafana/ui (drops the whole import declaration when it had no other specifiers).
  • Resolves import-name collisions by aliasing as <Name>Icon, escalating to <Name>IconN.

Skipped (reported as manual review):

  • Dynamic name (variable, ternary, computed).
  • Missing name prop.
  • IgnoredLegacyIconName values (google-hangouts-alt, hipchat).
  • Unknown legacy name (not in the migrations table).
  • Aliased Icon import (import { Icon as UIIcon } from '@grafana/ui').

When any usage in a file falls into one of these categories, the whole file is skipped — half-migrating leaves the file in a non-compiling state (one Icon binding, mixed name= / component= usage). The codemod prints a per-site report so the developer knows what's left:

icon-migration: 64 changed, 126 sites migrated, 42 skipped, 2099 unchanged

52 sites need manual review:

  apps/plugin/src/components/Foo.tsx:42:14   dynamic-name
  apps/plugin/src/pages/Bar.tsx:18:10        ignored-legacy-name  (name="google-hangouts-alt")
  …

Exit code is 0 even when there are manual-review sites — they're informational, not failures. Re-running the codemod with the same input is a no-op (idempotent).

Companion to prefer-grafana-icons-component in @grafana/eslint-plugin-design: same migration, different mechanism (one-shot CLI vs continuous lint-time --fix). Use the codemod for the bulk initial migration; the ESLint rule catches drift on new code.

icon-imports

Moves icon-related named imports out of @grafana/components into @grafana/icons. Covers the Icon wrapper, every PascalCase icon component, AllIcons, iconMetaData, and the icon-related type exports (SVGComponent, SVGComponentProps, IconName, IconProps, IconSize). Non-icon imports from @grafana/components stay in place.

pnpm dlx @grafana/design-codemods icon-imports apps/plugin/src
pnpm dlx @grafana/design-codemods icon-imports . --dry-run

The name set is derived at codemod build time from Object.keys(@grafana/icons) plus a hardcoded list of type-only exports.

provider-imports

Moves the provider/hook/identifier exports out of @grafana/components into the dedicated @grafana/theme-providers package. Non-provider imports from @grafana/components stay in place. import type { … } and per-specifier type modifiers are preserved.

pnpm dlx @grafana/design-codemods provider-imports apps/plugin/src
pnpm dlx @grafana/design-codemods provider-imports . --dry-run

Names handled: ColorMode, ColorModeProvider, ColorModeChangeHandler, PortalProvider, usePortal, ThemeNameProvider, ThemeNameChangeHandler, THEME_IDS, LegacyThemeId, LEGACY_THEME_IDS, useColorMode, useColorModeChange, useThemeNameChange, useThemeId.

Reference: provider-imports migration recipe in @grafana/design-catalog.

Individual codemods land per-migration use case (see plans/codemod-architecture.md in the source tree).

Cross-file subcommands

The recommended entry point is the migrate-icons Claude Code skill (see below). It orchestrates every subcommand in this section, including the per-file icon-migration, with the right pause points for human review. The subcommands below are documented for completeness and for power users / CI scripts that want to drive individual steps directly.

The cross-file icon migration runs in three steps after icon-migration --dry-run has flagged the per-file manual-review list:

  1. Schema discoveryicon-prop-survey performs TypeScript-aware (ts-morph) analysis and emits a JSON plan of every schema endpoint that flows IconName into a non-static <Icon name={…}> site.
  2. Plan refinement — the migrate-icons skill reads the survey output, applies a fixed decision framework to each endpoint (apply / review / skip), and writes a refined plan.
  3. Applyicon-prop-apply reads the refined plan and writes the source edits: type rewrites, JSX name=component= swaps, literal supplier substitutions, and import management.

icon-prop-survey

Phase 2 of the cross-file icon migration. Walks the supplied paths with TypeScript-aware analysis, finds every <Icon name={X} /> JSX site whose X isn't a static literal (those are handled by icon-migration), and traces X back to its declaring schema endpoint — either a function parameter (Pattern B) or an object-type property (Pattern C). For each endpoint, enumerates the supplier sites that flow values into it and classifies each as a static literal we can migrate mechanically or as a dynamic expression that needs context-aware judgement.

pnpm dlx @grafana/design-codemods icon-prop-survey apps/plugin/src --out icon-prop-plan.json
pnpm dlx @grafana/design-codemods icon-prop-survey apps/plugin/src        # JSON to stdout

Output is a JSON plan with this shape (abbreviated):

{
  "version": 1,
  "migration": "migrate-icons",
  "generatedAt": "...",
  "endpoints": [
    {
      "id": "src/components/MenuItem.tsx::icon",
      "kind": "object-property",
      "currentType": "IconName",
      "proposedType": "SVGComponent",
      "typeDeclaration": {
        "file": "src/types.ts",
        "line": 12,
        "annotation": "IconName",
      },
      "jsxConsumers": [
        {
          "file": "src/Menu.tsx",
          "line": 47,
          "snippet": "<Icon name={item.icon} />",
        },
      ],
      "suppliers": [
        {
          "file": "src/menuData.ts",
          "line": 8,
          "value": "'bell-slash'",
          "classification": {
            "kind": "literal",
            "legacyName": "bell-slash",
            "componentName": "BellOff",
          },
        },
        {
          "file": "src/api.ts",
          "line": 22,
          "value": "response.iconKey",
          "classification": {
            "kind": "dynamic",
            "reason": "non-literal: PropertyAccessExpression",
          },
        },
      ],
      "decision": "undecided",
    },
  ],
  "untracedSites": [
    {
      "file": "src/legacy.tsx",
      "line": 31,
      "snippet": "<Icon name={icons[i]} />",
      "reason": "unhandled expression kind: ElementAccessExpression",
    },
  ],
}

Flags:

  • --tsconfig <file> — path to the consumer's tsconfig.json. Defaults to walking up from the first input path.
  • --out <file> / -o <file> — write the plan to a file instead of stdout.

The plan is consumed by the migrate-icons Claude Code skill (see below) to produce a refined plan, and then by icon-prop-apply to write the source edits.

Companion subcommand to icon-migration: run that first to handle every static-literal case, then run this to plan the cross-file work for what's left.

icon-prop-apply

Reads a refined plan (produced by the migrate-icons skill) and rewrites the source files it names. For each endpoint with decision: "apply":

  • The type annotation at typeDeclaration is rewritten to SVGComponent (e.g. IconName or 'foo' | 'bar'SVGComponent); SVGComponent is added as a type import from @grafana/icons, and IconName is dropped from @grafana/ui if it has no other references.
  • Every <Icon name={X}> consumer is rewritten to <Icon component={X}>. The Icon wrapper import moves from @grafana/ui to @grafana/icons.
  • Every supplier site classified as literal (with a known migration target) has its string literal replaced by the icon component reference, with the component imported from @grafana/icons. JSX-attribute literals get wrapped in {} so the post-edit JSX stays valid.
  • Suppliers classified as dynamic (or marked decision: "review" per-site) are left alone and reported as residue for human cleanup.
pnpm dlx @grafana/design-codemods icon-prop-apply icon-prop-plan.refined.json
pnpm dlx @grafana/design-codemods icon-prop-apply icon-prop-plan.refined.json --dry-run

Per-endpoint freshness checks abort the endpoint atomically if any site has drifted from the plan (type annotation differs, JSX <Icon name=…> no longer present, supplier literal moved, etc.). Half-migrating across files is never safe — the type graph would break. Drifted endpoints are reported as stale-plan; re-running icon-prop-survey followed by the migrate-icons skill refreshes them.

Re-running on already-migrated source is a no-op: every site reports as already-applied and no writes happen.

Flags:

  • --tsconfig <file> — path to the consumer's tsconfig.json. Defaults to walking up from the plan file's directory.
  • --dry-run / -d — report what would change without writing.
  • --verbose / -v — log each changed file to stderr.

Bundled Claude Code skills

This package ships skill files alongside its CLI so engineers can drive the icon-migration workflow from Claude Code in their own checkout, without needing to copy skill prompts around manually.

install-skill

Installs a bundled skill into the consumer repo's .claude/skills/:

pnpm dlx @grafana/design-codemods install-skill migrate-icons
pnpm dlx @grafana/design-codemods install-skill --list

After installation the engineer can invoke the skill in Claude Code as /migrate-icons. The same skill content is also surfaced by the @grafana/design-mcp server, so consumer repos using that MCP server can invoke it without the local-install step.

Flags:

  • --list — print the available bundled skills.
  • --dest <path> — install elsewhere than ./.claude/skills/.

migrate-icons skill

End-to-end orchestration of the @grafana/ui <Icon name=…>@grafana/icons <Icon component=…> migration. The skill is the recommended entry point — it drives every subcommand in the right order, pauses for human confirmation at the writes, and applies the cross-file decision framework on the survey output without the engineer having to read it manually.

Five phases, mapping to the underlying subcommands:

  1. Preconditions — confirm the repo has Icon imports from @grafana/ui, a reachable design-codemods CLI, and a tsconfig.json.
  2. Static-literal passdesign-codemods icon-migration <path> (dry-run first, then write).
  3. Cross-file surveydesign-codemods icon-prop-survey <path> --out icon-prop-plan.json if Phase 2 reported dynamic-name residue.
  4. Decision framework + apply — the skill annotates each endpoint with apply / review / skip per a fixed table (loose string types skipped with manualGuidance, narrow string-literal unions promoted to SVGComponent, cross-package endpoints deferred, etc.), writes icon-prop-plan.refined.json, then runs design-codemods icon-prop-apply (dry-run first, then write).
  5. Verify — type-check, grep for leftover <Icon name={…}> sites and @grafana/ui Icon imports.

The full prompt lives at skills/migrate-icons/SKILL.md; sample input + refined plans for the three main shapes (all-literal, narrowed-union, loose-type) live under skills/migrate-icons/examples/.

Invoke in a Claude Code session in the consumer repo:

/migrate-icons

If the @grafana/design-mcp server is configured in the consumer's .mcp.json, the skill is also reachable without install-skill — the agent finds it via search / list_design_skills against the design MCP server, then drives it inline.

Library

import {
  migrations,
  type LegacyIconName,
  type IgnoredLegacyIconName,
} from '@grafana/design-codemods';
  • migrations — typed Record<Exclude<LegacyIconName, IgnoredLegacyIconName>, IconName> mapping every @grafana/ui icon-name string with a resolved upgrade target to its canonical @grafana/icons PascalCase component. Re-exported verbatim from @grafana/icons/migrations.
  • LegacyIconName — the legacy icon-name union (verbatim copy of @grafana/ui's IconName re-export from @grafana/data).
  • IgnoredLegacyIconName — the explicit list of legacy names with no migration target (google-hangouts-alt, hipchat).

Codemod authors should consume the table from here rather than reaching for the @grafana/icons/migrations subpath directly — that subpath is reserved for this package and the in-editor ESLint rule.

License

Apache-2.0