@cratis/components.migrator

中文文档
4.40.0发布于 2026/11/04MITES Module构建来源已验证近 30 天下载 3968

Migration tooling for updating @cratis/components consumers from Components 3 to Components 4.

原始使用文档
文档目录8 节
  1. remove-root-namespace-imports
  2. What it rewrites
  3. What it refuses to guess
  4. Use
  5. button-variant-tone
  6. change-handler
  7. Run the migration sequence
  8. Test

@cratis/components.migrator

The Components 4 Migrator updates a Components 3 codebase to the Components 4 public contracts. It moves root namespaces to explicit subpath imports, replaces deprecated Button appearance props, and changes legacy event-wrapper callbacks to semantic value callbacks. The CLI uses syntax-aware codemods internally, but each command is named for the migration it performs, can be run independently, and is idempotent. Components 4 keeps only package-wide provider setup at the root; every component is imported from its explicit subpath (@cratis/components/Canvas, for example). The companion @cratis/eslint-plugin-components package's no-root-barrel-import rule enforces this once a consumer has migrated.

The package is published on npm and released at the same version as @cratis/components (4.0.0 onward). Run it with npx, pnpm dlx, or yarn dlx as shown below; it is never an application runtime dependency. Contributors to this repository can run the workspace scripts directly instead.

The published package is a CLI-only tool. Its public surface is the documented cratis-components-* executable names (plus ./package.json for tooling metadata); lib/, scripts/, and individual transforms are implementation details and are blocked by the package export map. Do not import them from application code or build custom migration APIs on them.

remove-root-namespace-imports

An idempotent, AST-based codemod built on the TypeScript compiler API. The published CLI brings its own typescript runtime dependency, so its parser does not depend on the consumer application's TypeScript version. It rewrites static import declarations and named export … from re-exports only; it never guesses, and reports every case it will not touch.

What it rewrites

BeforeAfter
import { Canvas } from '@cratis/components';import * as Canvas from '@cratis/components/Canvas';
import type { Canvas } from '@cratis/components';import type * as Canvas from '@cratis/components/Canvas';
import { Canvas as C } from '@cratis/components';import * as C from '@cratis/components/Canvas';
export { Canvas } from '@cratis/components';export * as Canvas from '@cratis/components/Canvas';
export type { Canvas } from '@cratis/components';export type * as Canvas from '@cratis/components/Canvas';
export { Canvas as C } from '@cratis/components';export * as C from '@cratis/components/Canvas';

A named export … from '@cratis/components' re-export follows exactly the same rules as an import, because the transformation is equally unambiguous in both directions: the exported name identifies one specific namespace (or approved setup symbol), so the codemod can rewrite it without guessing. Only a wildcard re-export of the whole package — export * from '@cratis/components' or export * as X from '@cratis/components' — stays unsupported, for the same reason a whole-package namespace import stays unsupported: see What it refuses to guess.

The historical CommandStepper root namespace is a special case: it aliased the complete CommandDialog module. The codemod therefore maps it to @cratis/components/CommandDialog, preserving the module identity for members retained in Components 4. It cannot restore a removed module export such as the accidental CommandStepperContent surface. New code that only needs the standalone component should use import { CommandStepper } from '@cratis/components/CommandStepper' (or the equivalent re-export).

Multiple namespaces in one import or re-export become one subpath statement per namespace:

// Before
import { Canvas, Common } from '@cratis/components';

// After
import * as Canvas from '@cratis/components/Canvas';
import * as Common from '@cratis/components/Common';

A mixed import or re-export naming both an approved setup symbol and a namespace is split — the setup symbol stays at the root, the namespace moves to its subpath:

// Before
import { CratisComponentsProvider, Canvas } from '@cratis/components';

// After
import { CratisComponentsProvider } from '@cratis/components';
import * as Canvas from '@cratis/components/Canvas';

Running the codemod again on its own output is a no-op — it only ever matches an import or named re-export whose module specifier is exactly @cratis/components (or the --package override), so an already-migrated subpath import or re-export is never revisited.

The complete namespace → subpath map, known removed Components 3 compatibility exports, and approved-root-symbol allowlist live in lib/namespaceMap.js, mirrored by ESLint/lib/rootNamespaceMap.js. Contributors must update both packages together when a namespace subpath, removed boundary, or setup symbol changes. The packages share the repository release version; parity specs prevent their migration contracts from drifting.

What it refuses to guess

Each of these is left completely untouched, and reported as a diagnostic instead:

  • A namespace import of the whole package — import * as Components from '@cratis/components';. Which subpath each later Components.X access belongs to cannot be inferred from the import alone.
  • A default import — import Components from '@cratis/components';. The package has no default export.
  • A side-effect-only import — import '@cratis/components';. There is no binding to infer a subpath from.
  • A named import of a symbol that is neither an approved setup symbol nor a known namespace (for example a member like Button that only exists inside a namespace, not at the root). The whole import statement is left untouched rather than partially migrated.
  • The Components 3.6 Compatibility namespace and its direct pass-through exports. They are recognized known removals, not unknown symbols: Components 4 has no compatibility subpath, so the transform leaves the statement untouched and directs the consumer to typed Cratis parts.
  • A TypeScript import assignment — import Components = require('@cratis/components');. This is the whole package namespace, so later member access is as ambiguous as import * as.
  • A dynamic import('@cratis/components') or a CommonJS require('@cratis/components'), anywhere in the file.
  • A wildcard re-export of the whole package — export * from '@cratis/components'; — or a namespace re-export of the whole package — export * as Components from '@cratis/components';. Both are exactly as ambiguous as a whole-package namespace import: which subpath each later access needs cannot be determined from the re-export alone. A named re-export (export { Canvas } from '@cratis/components';) is not in this list — see What it rewrites.

Existing subpath imports and re-exports (@cratis/components/Canvas, …) are never touched — narrow subpath consumers are left exactly as they are.

Use

The packaged CLI requires Node.js 20 or newer and brings its own TypeScript parser; it does not depend on the consumer application's TypeScript version. It scans JavaScript, JSX, TypeScript, and TSX sources, including .mjs, .cjs, .mts, and .cts variants. Components 3 has no 3.x Migrator package. Use the Components 4 Migrator bounded to >=4 <5; never use latest.

Before any scan or write, the CLI validates its bundled compatibility manifest, checks its own version against the declared Migrator range, resolves the configured Components package from the invocation directory, and verifies that package is in the supported 3.x migration-source or 4.x migration-target window. Missing Components, unsupported versions, stale package metadata, and an invalid manifest fail closed. --help remains available without Components installed.

TOOLING_RANGE='^4.0.0' # Shell-safe equivalent of >=4 <5.

# Preview what would change, without writing anything:
npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-remove-root-namespace-imports --check path/to/app/src

# Apply the rewrite:
npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-remove-root-namespace-imports path/to/app/src

# Contributors to this repository can run the workspace source directly:
node Migrator/scripts/remove-root-namespace-imports.js --check Source

The same bounded package works in pnpm and Yarn 2+ projects:

pnpm dlx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-remove-root-namespace-imports --check path/to/app/src
yarn dlx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-remove-root-namespace-imports --check path/to/app/src

Quote any path containing spaces. Angle-bracket placeholders are intentionally not used in executable examples because <...> is shell input-redirection syntax.

Exit code is non-zero whenever an unsupported case was found (with --check or without — those always need a human), and, in --check mode only, whenever a supported rewrite has not yet been applied. Run --help for the full option list.

After running the codemod, run the project's own build/lint/test gates — the codemod only performs the mechanical import/re-export rewrite; it does not attempt to fix any resulting unused import, nor does it know whether a namespace member you use (Canvas.Foo) still exists at that subpath.

button-variant-tone

cratis-components-button-variant-tone resolves Button JSX bindings imported, including aliases and Common.Button namespace access, from @cratis/components/Common. It applies the runtime's legacy precedence (link → text → outlined → solid) and replaces deprecated props:

Deprecated propSemantic prop
textvariant='ghost'
linkvariant='link'
outlinedvariant='outline'
roundedshape='pill'
severity='secondary' / 'contrast'tone='neutral'
severity='info' / 'help'tone='accent'
severity='success'tone='positive'
severity='warn'tone='caution'
severity='danger'tone='critical'

A literal severity='contrast' also receives variant='solid' only when no explicit new variant or stronger true legacy variant applies. A literal new variant, tone, or shape wins and its redundant legacy prop is removed.

// Safe rewrite
<Button outlined rounded severity='warn' />
// becomes
<Button variant='outline' shape='pill' tone='caution' />

The transform never guesses through JSX spreads, dynamic legacy values, duplicate props, or an unknown new-prop expression that conflicts with a legacy prop. It leaves the uncertain prop group semantically unchanged, reports it, and adds one syntax-safe TODO(cratis-codemod) annotation. Subsequent runs do not duplicate that annotation.

// Refused and reported
<Button text={appearance.text} severity={appearance.severity} />

change-handler

cratis-components-change-handler resolves Components-owned JSX identifiers imported with aliases or namespaces from @cratis/components/Dropdown, @cratis/components/CommandForm, and @cratis/components/CommandForm/fields. It rewrites structurally-proven single forwarding callbacks on standalone Dropdown and CommandForm field onValueChange props:

// Before
<Dropdown onChange={(event) => setRole(event.value)} />
// After
<Dropdown onChange={(value) => setRole(value)} />

For Components 4 CommandForm fields, the codemod leaves onChange unchanged and reports an actionable diagnostic instead of changing its callback. Arc's asCommandFormField wrappers do not expose onChange as a field prop in Components 4, so keeping the attribute would produce TS2322 even if the callback were rewritten. Move its side effects to the enclosing CommandForm's onFieldChange(command, fieldName, oldValue, newValue, validationInfo?) callback. Legacy onValueChange field callbacks with a proven event payload are rewritten to semantic values; already-semantic onValueChange callbacks are untouched. The Components 3 to 4 migration guide covers this case.

Multi-statement, multi-use, wrong-payload, destructuring-with-default/rest, and native-event-dependent callbacks are refused, reported, and annotated once:

// Refused and reported: the host still depends on the old event object
<Dropdown
    onChange={(event) => {
        audit(event.originalEvent);
        setRole(event.value);
    }}
/>

Run the migration sequence

Use the bounded Components 4 tooling train for all three commands. The recommended source/target order is:

  1. With Components 3 (>=3 <4) installed, preview and apply the root-import transform. Its subpaths exist in both majors.
  2. Run the Components 3 gates and checkpoint the import-only change.
  3. Upgrade Core to Components 4 (^4.0.0).
  4. Preview and apply Button appearance, then change-handler migration; these produce Components 4 source.

Preflight also accepts the Components 4 target window so the root transform can be recovered after an upgrade. It accepts no other Core major. Preview every step before applying it:

TOOLING_RANGE='^4.0.0'

npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-remove-root-namespace-imports --check path/to/app/src
npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-remove-root-namespace-imports path/to/app/src

npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-button-variant-tone --check path/to/app/src
npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-button-variant-tone path/to/app/src

npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-change-handler --check path/to/app/src
npx --package "@cratis/components.migrator@$TOOLING_RANGE" \
  cratis-components-change-handler path/to/app/src

Never substitute latest; keep tooling within >=4 <5. Tooling packages share the repository release version with Core, and the bounded range plus bundled manifest preflight enforce the compatible migration windows without exact-patch coupling. Both new CLIs require Node.js 20 or newer, bundle their own TypeScript compiler dependency, scan .js, .jsx, .mjs, .cjs, .ts, .tsx, .mts, and .cts recursively, support --package <name>, and exit nonzero for pending check-mode edits or any refused case. Review every diagnostic and TODO(cratis-codemod), then run the consuming project's formatter, lint, type check, tests, and production build.

Contributor source commands are:

node Migrator/scripts/button-variant-tone.js --check Source
node Migrator/scripts/change-handler.js --check Source

Test

cd Migrator && yarn test

Repository fixtures for every supported and unsupported root-import case live under Migrator/test/fixtures, each as an input.ts/expected.ts pair (input.ts and expected.ts are identical for an unsupported case, since nothing should change). The Button and change-handler transforms use input.tsx/expected.tsx pairs under Migrator/test/button-fixtures and Migrator/test/change-handler-fixtures.

版本与发布记录

@cratis/components.migrator 共发布 83 个版本,首次发布于 2026/08/31;过去 12 个月发布了 83 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
4.40.0latest2026/11/046 天前
4.39.52026/11/046 天前
4.39.42026/11/047 天前
4.39.32026/11/037 天前
4.39.22026/11/037 天前
4.39.12026/11/037 天前

其他发布通道:bootstrap → 0.0.0。

关于 @cratis/components.migrator 的常见问题

@cratis/components.migrator 是什么?

Migration tooling for updating @cratis/components consumers from Components 3 to Components 4.

如何安装 @cratis/components.migrator?

在项目目录运行 npm install @cratis/components.migrator,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 cratis-components-change-handler、cratis-components-button-variant-tone、cratis-components-remove-root-namespace-imports,可以用 npx cratis-components-change-handler 直接运行。

@cratis/components.migrator 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/cratis__components.migrator 是否存在。

@cratis/components.migrator 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@cratis/components.migrator 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@cratis/components.migrator 可以商用吗?

@cratis/components.migrator 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@cratis/components.migrator 的下载量有多少?

根据 npm 官方下载统计,@cratis/components.migrator 在 2026/10/11 – 2026/11/09 这 30 天内被下载 3,968 次,日均约 132 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@cratis/components.migrator 还在维护吗?

最新版本 4.40.0 发布于 2026/11/04(6 天前);至今共发布 83 个版本,其中过去 12 个月发布了 83 个;首次发布于 2026/08/31。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。