React Spectrum s1-to-s2 升级助手:基于 jscodeshift 将 v3 组件批量迁移到 Spectrum 2 React Spectrum s1-to-s2 升级助手基于 jscodeshift 将 v3 组件批量迁移到 Spectrum 2【免费下载链接】react-spectrumA collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrumReact Spectrum 仓库内置了一个名为s1-to-s2 升级助手的 CLI 工具位于 codemods 包。它通过 jscodeshift AST 转换把使用 React Spectrum v3S1组件的存量代码批量升级到基于 Style Macro 的 Spectrum 2S2并自动处理导入改写、样式属性转换、图标映射等最繁琐的部分。读完本文你将掌握该工具的完整命令行用法与参数含义理解其从包安装、宏配置到 AST 转换的完整执行链路并能按照仓库既有模式为自己的组件新增一个 codemod。一、工具定位与快速开始s1-to-s2 README 将其定义为 “A CLI tool for upgrading React Spectrum components to Spectrum 2”。在需要升级的项目目录下运行npx react-spectrum/codemods s1-to-s2支持的全部命令行选项如下与 README 的 Options 一节一一对应选项说明-c, --components components逗号分隔的待升级组件列表例如Button,TableView。不指定时所有可用组件都会被升级--path path执行 codemod 的目录路径默认当前目录.-d, --dry干跑模式不向磁盘写入任何改动用于在正式应用前预览迁移结果--agent非交互模式跳过交互提示、包安装和宏配置步骤适用于 CI 或 Agent 工具调用。注意该模式下react-spectrum/s2必须已安装且可解析从 CLI 入口 可以确认这些选项的解析方式入口通过node:util的parseArgs解析参数并按名称分发到四个内置 codemods1-to-s2、use-monopackages、use-subpaths、test-utils-rc-update。若未指定 codemod 名称或名称未知会打印可用列表并退出入口文件。入口同时为所有 codemod 注入了统一的 jscodeshift 默认值parser: tsx、ignorePattern: **/node_modules/**、extensions: js,jsx,mjs,cjs,ts,tsx即默认覆盖全部 JS/TS 源文件并忽略node_modules。二、执行流程交互模式与 Agent 模式s1-to-s2的实现入口是 s1_to_s2 函数。两种模式的差异在源码中非常清晰。交互模式默认默认流程按顺序执行四步欢迎与确认用 boxen 打印欢迎框说明工具将“安装react-spectrum/s2并配置打包器的 Style Macro 支持 → 升级当前目录组件 → 给出后续步骤”并等待按回车继续安装 S2 包调用installPackage(react-spectrum/s2)配置宏支持调用addMacroSupport()检测/安装 Style Macro 支持执行转换并输出后续步骤await transform(options)后打印 Next Steps 框内容包括在应用入口组件中加入import react-spectrum/s2/page.css;与 v3 不同S2 不再需要Provider若宏支持未自动配置提示在 Webpack/Next.js/Vite/Rollup/ESBuild 中配置unplugin-parcel-macrosParcel v2.12.0 原生支持宏全局搜索TODO(S2-upgrade)注释手动处理 codemod 无法自动完成的剩余升级并建议运行 Prettier/ESLint 清理格式。Agent 模式--agent从 agent 模式分支 可以看到--agent下直接调用transform(options)完全不执行包安装和宏配置随后输出五步后续指引确认react-spectrum/s2已安装、按需配置打包器的宏支持、按需引入page.css、搜索TODO(S2-upgrade)、参考迁移指南。这解释了为什么 README 特别强调该模式下react-spectrum/s2必须可解析——因为组件清单的推导依赖它见下一节。转换的调用链transform()本身很薄它把 CLI 选项剥离后直接调用 jscodeshift 的 Runner以 codemod.js 为转换入口对--path指定的目录执行// packages/dev/codemods/src/s1-to-s2/src/transform.ts const transformPath path.join(__dirname, codemods, codemod.js); return await jscodeshift(transformPath, [filePath], jscodeshiftOptions);这也是为什么-d, --dry可以直接透传给 jscodeshift Runner——干跑能力由 jscodeshift 原生提供而非工具自己实现。三、升级哪些组件从 S2 的 exports 推导可用组件清单工具不会硬编码组件列表。getComponents() 的机制是先require.resolve(react-spectrum/s2)定位其exports/index.ts测试环境下直接使用包内index.ts若解析不到已安装的包则回退到 monorepo 工作区内的react-spectrum/s2/exports/index.ts两者都不存在时抛出Could not resolve react-spectrum/s2 source for codemods用 Babel 解析该 index 文件遍历所有exportKind value的具名导出收集组件名。在这个基线集合之上主转换文件 做了若干修正这些细节直接影响升级行为v3 中被div替代的组件被显式加入View、Flex、Grid、Well被集合型专属 Item 替代的泛型组件Item、Sectionv3 的Provider被移出清单availableComponents.delete(Provider)即不会去改写 v3 Provider 的导入改名组件ContextualHelpTrigger→UnavailableMenuItemTrigger通过renamedComponents映射处理少数组件Accordion、Card、CardView、ActionBar在skipped集合中——它们虽从 S2 导出但尚未编写对应 codemod导入暂不替换。-c选项的“关联组件扩展”机制单独指定组件时不能只看字面量。例如只升级Menu其内部的Item/Section以及MenuTrigger等也必须一起处理。relatedComponentGroups 定义了这种关联关系例如Menu关联ContextualHelpTrigger、MenuTrigger、SubmenuTrigger且Item/Section仅在Menu作用域内转换TableView关联Cell、Column、Row、TableBody、TableHeaderTooltipTrigger关联TooltipTabs关联TabList、TabPanels。getComponentSelection()会把显式组件展开为完整转换集合对于未被显式指定的关联组件如单独传-c Menu时自动带入的Item会记录其“允许的父组件”shouldTransformElement()随后通过path.findParent检查 JSX 父链只转换位于允许父组件内部的元素——这避免了把无关的Item比如TagGroup里的误改。四、转换都做了什么导入改写、Style Macro 与图标映射核心转换逻辑集中在 codemod.ts。它以 recast Babel parser 解析每个文件保证未改动部分格式不变主要处理以下几类1. 组件导入统一指向react-spectrum/s2命中adobe/react-spectrum或任何react-spectrum/*子包react-spectrum/s2除外的ImportDeclaration会被记录其中属于转换清单的命名导入、以及import * as RSP from ...的命名空间导入RSP.Button这类 JSX 成员表达式都会被收集转换完成后所有待导入组件被合并进一条import {Button, ...} from react-spectrum/s2若文件中已有该导入则去重追加原 v3 导入在被移除后如为空会整条删除Item、Section等泛型导入若仍被引用例如在ActionMenu等未转换的父组件内会保留动态import(adobe/react-spectrum)暂不自动处理源码中直接标注为TODO(S2-upgrade): check this dynamic import见 动态导入分支。2. v3 Style Props 迁移到 Style Macrov3 的布局类样式属性marginStartsize-100、borderWidththin、响应式对象值等没有 S2 同名 API需要改写成styles{style({...})}。这一步由 shared/styleProps 完成转换成功后工具会在文件中插入import {style} from react-spectrum/s2/style with {type: macro};若代码中使用了lightDark辅助函数会一并导入。该导入使用with {type: macro}属性源码生成assert {type: macro}后统一替换为with语法。若某个 style prop 无法自动转换元素上会被标注TODO(S2-upgrade): Could not transform style prop automatically: error而不是让整个文件失败。v3 到 S2 的值映射规则如size-100→8、borderRadiussmall→sm、断点base/S/M/L→default/sm/md/lg完整列在同目录的 UPGRADE.md 的 “Style props” 章节可作为手动迁移时的对照表。3. 图标与插画导入重映射spectrum-icons/workflow/*和spectrum-icons/illustrations/*的默认导入会被重写到 S2 的等价路径图标react-spectrum/s2/icons/newName映射表为 iconMap插画react-spectrum/s2/illustrations/linear/newName映射表为 illustrationMap。若本地导入名恰好等于旧模块名且新名字在作用域内无冲突引用点会同步改名。找不到 S2 等价物时元素上标注TODO(S2-upgrade): A Spectrum 2 equivalent to name was not found. Please update this icon manually.。4. 组件级专属转换动态加载通用处理之后元素转换循环 会按组件名动态require(./components/Name/transform)并调用其默认导出。转换目录不存在时静默跳过——这正是“每个组件一个 transform 文件、可增量扩展”的设计。五、为组件新增一个 CodemodREADME 的 “Adding a new codemod” 一节以Button为例给出了三步流程路径相对于 s1-to-s2 codemod 根目录。结合仓库现状完整操作如下创建转换函数新建src/codemods/components/Button/transform.ts导出default函数签名为(path: NodePatht.JSXElement) voidpath指向单个 JSX 元素节点实现转换逻辑优先复用 shared/transforms 中的工具函数removeProp、updatePropName、updatePropNameAndValue、updatePropValueAndAddNewPropName、updateComponentIfPropPresent等添加测试在__tests__/button.test.ts中为转换编写用例仓库现有 50 个组件测试与对应 snapshot 文件均位于该目录例如 button 测试。Button 的真实实现 展示了典型写法// variantcta → variantaccent updatePropNameAndValue(path, { oldPropName: variant, oldPropValue: cta, newPropName: variant, newPropValue: accent }); // variantoverBackground → variantprimary staticColorwhite updatePropValueAndAddNewPropName(path, { oldPropName: variant, oldPropValue: overBackground, newPropName: variant, newPropValue: primary, additionalPropName: staticColor, additionalPropValue: white }); // style → fillStyle updatePropName(path, {oldPropName: style, newPropName: fillStyle}); // 移除 isQuiet、elementType removeProp(path, {propName: isQuiet}); removeProp(path, {propName: elementType}); // 含 href 时 Button → LinkButton updateComponentIfPropPresent(path, {propName: href, newComponentName: LinkButton});这些映射与 UPGRADE.md 中 “Button” 小节列出的手工迁移规则一致说明 codemod 实现是迁移指南的代码化落地。六、自动化辅助包安装与 Style Macro 配置交互模式下的两个自动化步骤各有明确的实现边界包安装installPackage先检查当前目录是否有package.json没有则提示手动安装依次探测yarn.lock/package-lock.json/pnpm-lock.yaml来识别包管理器分别执行yarn add、npm install、pnpm add通过execa运行安装命令失败时给出手动安装的降级提示dev: true选项会附加-D开发依赖。宏支持配置addMacroSupport若package.json的依赖中出现parcel提示 “Macros are supported by default in v2.12.0 and newer” 并结束否则自动以 dev 依赖安装unplugin-parcel-macros注意当前版本不会自动修改打包器配置源码中留有TODO: Try to automatically update bundle configisMacroSupportEnabled恒为false因此 Next Steps 中始终会附带各打包器的宏配置提示。使用--agent时此步骤被整体跳过。七、测试与验证方式该工具的测试覆盖两个层次组件级快照测试tests目录下每个组件一个.test.ts与一个.snap快照button、dialog、table、tabs、styleProps、imports等 50 个固化每个转换的精确输出CLI 端到端测试cli.e2e.test.ts 配合testfixtures/cli 下的full-project含App.tsx、Form.tsx的 input/output 对照与subset-project仅Form.tsx验证-c子集选择两组 fixture验证从输入源码到输出源码的完整 CLI 行为。升级自己的项目时推荐的工作流是先npx react-spectrum/codemods s1-to-s2 -ddry run预览 diff确认无误后正式执行再全局搜索TODO(S2-upgrade)处理残留项最后跑一遍 lint/format。八、与手动迁移指南的配合关系codemod 覆盖不了的部分由同目录的 UPGRADE.md 承接它按组件逐条列出属性变更如Dialog的 render props 位置调整、TooltipTrigger的 placement 值映射、Item在不同父组件下应改成的具体组件名并用[PENDING]标注最终发布前仍会变动、当前方案属于临时性质的条目如暂时注释掉尚未实现的isPending、loadingState。CLI 的 Next Steps 也明确把TODO(S2-upgrade)标记与迁移指南作为收尾手段因此“codemod 自动转换 TODO 注释兜底 UPGRADE 指南手动对照”构成完整的升级闭环。九、适用前提与限制运行位置必须在你想要升级的项目目录下运行或显式--path因为工具会在当前目录寻找package.json和 lockfile组件清单依赖react-spectrum/s2非交互模式下它必须已安装且可解析monorepo 开发场景下工具会回退到工作区内的 S2 源码默认转换范围js,jsx,mjs,cjs,ts,tsx扩展名node_modules被忽略尚未自动化的部分v3Provider不处理、Accordion/Card/CardView/ActionBar暂无 codemod、动态import仅打 TODO、打包器宏配置需手动完成收尾必做升级后运行项目的 linter/formatter 清理 codemod 产生的格式差异并按 Next Steps 引入react-spectrum/s2/page.cssS2 不需要 v3 的Provider。【免费下载链接】react-spectrumA collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考