From 5b0ef139b3427d9208aa1ff161f2c76bd7f057d9 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 27 Feb 2026 17:59:30 -0600 Subject: [PATCH 01/15] fixed TabList render function children kept Item instead of converting to Tab --- .../__tests__/__snapshots__/tabs.test.ts.snap | 38 ++++++++++++++++-- .../src/s1-to-s2/__tests__/tabs.test.ts | 40 +++++++++++++++++++ .../src/codemods/components/Tabs/transform.ts | 15 +++---- 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap index d69fa9a6648..0725e2952c3 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap @@ -1,8 +1,27 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Converts dynamic TabList Item to Tab 1`] = ` +"import { Tab, Collection, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; + +let tabs = [{name: 'Tab 1', children: 'Tab Body 1'}]; + + + {(item) => ( + + {item.name} + + )} + + {(item) => ( + + {item.children} + + )} + " +`; + exports[`Move items from Tabs to TabList 1`] = ` -"import { Collection, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; -import { Item } from '@adobe/react-spectrum'; +"import { Tab, Collection, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; let items = [ {name: 'Tab 1', children: 'Tab Body 1'}, @@ -15,9 +34,9 @@ let items = [ {(item) => ( - + {item.name} - + )} {(item) => ( @@ -96,6 +115,17 @@ let props = {isQuiet: true}; " `; +exports[`Removes TabPanels from v3 import when s2 import exists first 1`] = ` +"import { Tabs as S2Tabs, Tab, TabPanel, Tabs as RSPTabs, TabList } from '@react-spectrum/s2'; + +<> + + + A + A panel +" +`; + exports[`Update to use new API 1`] = ` "import { Tab, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts b/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts index 409a0503106..c6b8af6b70f 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts @@ -254,3 +254,43 @@ let items = [ `); + +test('Converts dynamic TabList Item to Tab', ` +import {Tabs, TabList, TabPanels, Item} from '@adobe/react-spectrum'; + +let tabs = [{name: 'Tab 1', children: 'Tab Body 1'}]; + + + + {(item) => ( + + {item.name} + + )} + + + {(item) => ( + + {item.children} + + )} + + +`); + +test('Removes TabPanels from v3 import when s2 import exists first', ` +import {Tabs as S2Tabs} from '@react-spectrum/s2'; +import {Tabs as RSPTabs, TabList, TabPanels, Item} from '@adobe/react-spectrum'; + +<> + + + + A + + + A panel + + + +`); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts index d29e52987d5..b0ef32be6f5 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts @@ -4,13 +4,14 @@ import {removeProp, updateComponentWithinCollection, updateToNewComponentName} f import * as t from '@babel/types'; function transformTabList(tabListPath: NodePath): t.JSXElement { - tabListPath.get('children').forEach(itemPath => { - if ( - t.isJSXElement(itemPath.node) && - t.isJSXIdentifier(itemPath.node.openingElement.name) && - getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' - ) { - updateComponentWithinCollection(itemPath as NodePath, {parentComponentName: 'TabList', newComponentName: 'Tab'}); + tabListPath.traverse({ + JSXElement(itemPath) { + if ( + t.isJSXIdentifier(itemPath.node.openingElement.name) && + getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' + ) { + updateComponentWithinCollection(itemPath as NodePath, {parentComponentName: 'TabList', newComponentName: 'Tab'}); + } } }); return tabListPath.node; From be66290b2e45eb360646b1eecd443d4c0b4f22a3 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 27 Feb 2026 18:03:15 -0600 Subject: [PATCH 02/15] import cleanup: removeComponentImport was only checking the first matching import and could miss v3 imports when an @react-spectrum/s2 import appears first --- .../src/s1-to-s2/src/codemods/shared/utils.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts index e3a4e6df920..9e1458c3353 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts @@ -119,20 +119,26 @@ export function addComponentImport(path: NodePath, newComponentName: } export function removeComponentImport(path: NodePath, component: string): void { - let existingImport = path.node.body.find((node) => t.isImportDeclaration(node) && node.source.value === '@adobe/react-spectrum' || t.isImportDeclaration(node) && node.source.value.startsWith('@react-spectrum/')); - if (existingImport && t.isImportDeclaration(existingImport)) { - let specifier = existingImport.specifiers.find((specifier) => { - return ( - t.isImportSpecifier(specifier) && - specifier.imported.type === 'Identifier' && - specifier.imported.name === component + let imports = path.node.body.filter((node): node is t.ImportDeclaration => { + return t.isImportDeclaration(node) + && ( + node.source.value === '@adobe/react-spectrum' + || (node.source.value.startsWith('@react-spectrum/') && node.source.value !== '@react-spectrum/s2') + ); + }); + + for (let importDecl of imports) { + let previousLength = importDecl.specifiers.length; + importDecl.specifiers = importDecl.specifiers.filter((specifier) => { + return !( + t.isImportSpecifier(specifier) + && specifier.imported.type === 'Identifier' + && specifier.imported.name === component ); }); - if (specifier) { - existingImport.specifiers = existingImport.specifiers.filter((s) => s !== specifier); - if (existingImport.specifiers.length === 0) { - path.node.body = path.node.body.filter((node) => node !== existingImport); - } + + if (importDecl.specifiers.length === 0 && previousLength > 0) { + path.node.body = path.node.body.filter((node) => node !== importDecl); } } } From 914f03cfc35656cd9ba93557bd385a5b1fc96932 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 27 Feb 2026 18:04:27 -0600 Subject: [PATCH 03/15] dynamic import('@react-spectrum/s2') was incorrectly flagged as a v3 dynamic import --- packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index ce3467f356a..729d877347e 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -147,7 +147,9 @@ export default function transformer(file: FileInfo, api: API, options: Options): return; } - if (arg.value !== '@adobe/react-spectrum' && !arg.value.startsWith('@react-spectrum/')) { + let isV3ImportSource = arg.value === '@adobe/react-spectrum' + || (arg.value.startsWith('@react-spectrum/') && arg.value !== '@react-spectrum/s2'); + if (!isV3ImportSource) { return; } From d596a6cafd60e7e8e3863f74aa473210da6b48ad Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 27 Feb 2026 18:04:54 -0600 Subject: [PATCH 04/15] Fixed in codemod options parsing --- packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index 729d877347e..3d1c2c17c18 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -46,7 +46,9 @@ export default function transformer(file: FileInfo, api: API, options: Options): } }); let root = j(file.source); - let componentsToTransform = options.components ? new Set(options.components.split(',').filter(s => availableComponents.has(s))) : availableComponents; + let componentsToTransform = options.components + ? new Set(options.components.split(',').map(s => s.trim()).filter(s => availableComponents.has(s))) + : availableComponents; let v3ComponentsToRename = new Set(Object.keys(renamedComponents)); let S2ComponentsToImport = new Set(); From 171529ab691fb76b072e8ed4c8339a606ae43495 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 27 Feb 2026 18:05:25 -0600 Subject: [PATCH 05/15] more import fixes --- .../__snapshots__/imports.test.ts.snap | 4 +++ .../src/s1-to-s2/__tests__/imports.test.ts | 7 +++++ .../src/s1-to-s2/src/codemods/codemod.ts | 30 ++++++++++++++----- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/imports.test.ts.snap b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/imports.test.ts.snap index 4b07144fd42..6bb15e8013e 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/imports.test.ts.snap +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/imports.test.ts.snap @@ -1,10 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`does not leave a comment on dynamic s2 imports 1`] = `"const LazyButton = React.lazy(() => import('@react-spectrum/s2'))"`; + exports[`leaves a comment on dynamic imports 1`] = ` "const LazyButton = React.lazy(() => // TODO(S2-upgrade): check this dynamic import import('@react-spectrum/button'))" `; +exports[`should handle empty files safely 1`] = `""`; + exports[`should keep import aliases 1`] = ` "import { Button as RSPButton } from "@react-spectrum/s2"; diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/imports.test.ts b/packages/dev/codemods/src/s1-to-s2/__tests__/imports.test.ts index 8c6fe0ce5c5..b5f67d89959 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/imports.test.ts +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/imports.test.ts @@ -18,6 +18,9 @@ import {Button} from '@react-spectrum/button'; `); +test('should handle empty files safely', ` +`); + test('should leave unimplemented components untouched', ` import {Button, Fake} from '@adobe/react-spectrum'; @@ -64,6 +67,10 @@ test('leaves a comment on dynamic imports', ` const LazyButton = React.lazy(() => import('@react-spectrum/button')) `); +test('does not leave a comment on dynamic s2 imports', ` +const LazyButton = React.lazy(() => import('@react-spectrum/s2')) +`); + test('should not import Item from S2', ` import {Menu, ListView, Item} from '@adobe/react-spectrum'; diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index 3d1c2c17c18..821eeffd5f4 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -259,16 +259,30 @@ export default function transformer(file: FileInfo, api: API, options: Options): if (existingImport.length) { let importDecl = existingImport.get(); - for (let specifier of importDecl.node.specifiers) { - if (specifier.type === 'ImportSpecifier' - && importedComponents.has(specifier.imported.name)) { - importSpecifiers.add(specifier); - } - } + let existingSpecifiers = importDecl.value.specifiers; // add importSpecifiers to existing import importDecl.value.specifiers = [...importDecl.value.specifiers, ...[...importSpecifiers].filter(specifier => { - // @ts-ignore - return specifier.imported.name !== 'Item' && ![...importDecl.value.specifiers].find(s => s.imported.name === specifier.imported.name); + if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported)) { + if (specifier.imported.name === 'Item') { + return false; + } + + let localName = specifier.local?.name || specifier.imported.name; + return !existingSpecifiers.find((s) => + t.isImportSpecifier(s) + && t.isIdentifier(s.imported) + && s.imported.name === specifier.imported.name + && (s.local?.name || s.imported.name) === localName + ); + } + + if (t.isImportNamespaceSpecifier(specifier)) { + return !existingSpecifiers.find((s) => + t.isImportNamespaceSpecifier(s) && s.local.name === specifier.local.name + ); + } + + return false; })]; } else { if (importSpecifiers.size > 0) { From a2509b5f2316b9591c936b053b47b3bb221e5691 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 6 Mar 2026 13:28:21 -0600 Subject: [PATCH 06/15] add agent mode (non-interactive, run transforms only) --- packages/dev/codemods/src/index.ts | 36 ++++++++++++------- .../src/s1-to-s2/src/codemods/codemod.ts | 9 ++--- .../src/s1-to-s2/src/getComponents.ts | 17 +++++++-- .../dev/codemods/src/s1-to-s2/src/index.ts | 28 ++++++++++++--- .../codemods/src/s1-to-s2/src/transform.ts | 9 ++--- 5 files changed, 69 insertions(+), 30 deletions(-) diff --git a/packages/dev/codemods/src/index.ts b/packages/dev/codemods/src/index.ts index 3bb350ba739..e6859e47d7d 100644 --- a/packages/dev/codemods/src/index.ts +++ b/packages/dev/codemods/src/index.ts @@ -35,7 +35,14 @@ export interface S1ToS2CodemodOptions extends JSCodeshiftOptions { * An optional subset of components to have the s1-to-s2 codemod apply to. * Provide a comma-separated list of component names. */ - components?: string + components?: string, + /** + * Whether to run the codemod in agent mode, which skips interactive prompts + * and package installation. This matches the shipped CLI behavior. + * + * @default false + */ + agent?: boolean } export interface UseMonopackagesCodemodOptions extends JSCodeshiftOptions { @@ -67,6 +74,9 @@ const options = { }, 'components': { type: 'string' + }, + 'agent': { + type: 'boolean' } }; @@ -80,22 +90,24 @@ if (positionals.length < 1) { process.exit(1); } -const codemodName = positionals[0]; -const codemodFunction = codemods[codemodName]; +async function main() { + const codemodName = positionals[0]; + const codemodFunction = codemods[codemodName]; -if (!codemodFunction) { - console.error(`Unknown codemod: ${codemodName}, available codemods: ${Object.keys(codemods).join(', ')}`); - process.exit(1); -} + if (!codemodFunction) { + console.error(`Unknown codemod: ${codemodName}, available codemods: ${Object.keys(codemods).join(', ')}`); + process.exit(1); + } -try { - codemodFunction({ + await Promise.resolve(codemodFunction({ parser: 'tsx', ignorePattern: '**/node_modules/**', path: '.', ...values - }); -} catch (error) { + })); +} + +main().catch((error) => { console.error(`Error running codemod: ${error}`); process.exit(1); -} +}); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index 821eeffd5f4..2865a032074 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -267,17 +267,18 @@ export default function transformer(file: FileInfo, api: API, options: Options): return false; } - let localName = specifier.local?.name || specifier.imported.name; - return !existingSpecifiers.find((s) => + let importedName = specifier.imported.name; + let localName = specifier.local?.name || importedName; + return !existingSpecifiers.find((s: t.ImportSpecifier | t.ImportDefaultSpecifier | t.ImportNamespaceSpecifier) => t.isImportSpecifier(s) && t.isIdentifier(s.imported) - && s.imported.name === specifier.imported.name + && s.imported.name === importedName && (s.local?.name || s.imported.name) === localName ); } if (t.isImportNamespaceSpecifier(specifier)) { - return !existingSpecifiers.find((s) => + return !existingSpecifiers.find((s: t.ImportSpecifier | t.ImportDefaultSpecifier | t.ImportNamespaceSpecifier) => t.isImportNamespaceSpecifier(s) && s.local.name === specifier.local.name ); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts b/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts index 7398553633a..e894c50b022 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts @@ -1,6 +1,6 @@ +import {existsSync, readFileSync} from 'fs'; import {parse} from '@babel/parser'; const path = require('path'); -import {readFileSync} from 'fs'; import traverse from '@babel/traverse'; // These are exported but there are no codemods written for them yet. @@ -15,8 +15,19 @@ const skipped = new Set([ export function getComponents(): Set { // Determine list of available components in S2 from index.ts let availableComponents = new Set(); - const packagePath = require.resolve('@react-spectrum/s2'); - const indexPath = path.join(path.dirname(packagePath), process.env.NODE_ENV === 'test' ? 'src/index.ts' : '../src/index.ts'); + let indexPath: string; + + try { + const packagePath = require.resolve('@react-spectrum/s2'); + indexPath = path.join(path.dirname(packagePath), process.env.NODE_ENV === 'test' ? 'src/index.ts' : '../src/index.ts'); + } catch { + const workspaceIndexPath = path.resolve(__dirname, '../../../../../@react-spectrum/s2/src/index.ts'); + if (!existsSync(workspaceIndexPath)) { + throw new Error('Could not resolve @react-spectrum/s2 source for codemods.'); + } + indexPath = workspaceIndexPath; + } + let index = parse(readFileSync(indexPath, 'utf8'), {sourceType: 'module', plugins: ['typescript']}); traverse(index, { ExportNamedDeclaration(path) { diff --git a/packages/dev/codemods/src/s1-to-s2/src/index.ts b/packages/dev/codemods/src/s1-to-s2/src/index.ts index ee42521583c..1bff92cbb05 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/index.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/index.ts @@ -7,7 +7,29 @@ import {transform} from './transform.js'; import {waitForKeypress} from './utils/waitForKeypress.js'; const boxen = require('boxen'); +function printNextSteps(nextSteps: string[]) { + console.log(boxen( + `Next steps:\n\n ${nextSteps.map((step, i) => `${i + 1}. ${step}`).join('\n\n\n')}`, + {borderStyle: 'round', padding: 1, borderColor: 'green'} + )); +} + export async function s1_to_s2(options: S1ToS2CodemodOptions): Promise { + if (options.agent) { + logger.info('Running s1-to-s2 in agent mode (non-interactive, transform-only).'); + logger.info('Upgrading components...'); + await transform(options); + logger.success('Upgrade complete!'); + printNextSteps([ + `Ensure ${chalk.bold('@react-spectrum/s2')} is installed.`, + `If your bundler is not Parcel v2.12.0+, configure the Spectrum 2 style macro support. See: ${chalk.underline('https://react-spectrum.adobe.com/s2/index.html?path=/docs/intro--docs#configuring-your-bundler')}`, + `Add ${chalk.bold('import \'@react-spectrum/s2/page.css\';')} to your entry component if needed.`, + `Search for ${chalk.bold('TODO(S2-upgrade)')} and resolve remaining manual migration updates.`, + `Reference the migration guide: ${chalk.underline('https://react-spectrum.adobe.com/s2/index.html?path=/docs/migrating--docs')}` + ]); + return; + } + console.log(boxen( 'Welcome to the React Spectrum v3 to Spectrum 2 upgrade assistant!\n\n' + 'This tool will:\n\n' + @@ -66,11 +88,7 @@ export async function s1_to_s2(options: S1ToS2CodemodOptions): Promise { `For additional help, reference the Spectrum 2 Migration Guide: ${chalk.underline('https://react-spectrum.adobe.com/s2/index.html?path=/docs/migrating--docs')}` ); - console.log(boxen( - `Next steps:\n\n ${nextSteps.map((step, i) => `${i + 1}. ${step}`).join('\n\n\n')}`, - {borderStyle: 'round', padding: 1, borderColor: 'green'} - )); + printNextSteps(nextSteps); process.exit(0); } - diff --git a/packages/dev/codemods/src/s1-to-s2/src/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/transform.ts index a410568d8ce..6c31e3e2b03 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/transform.ts @@ -5,10 +5,7 @@ import {S1ToS2CodemodOptions} from '../..'; const transformPath = path.join(__dirname, 'codemods', 'codemod.js'); export async function transform(options: S1ToS2CodemodOptions): Promise> { - let { - path: filePath = '.', - ...rest - } = options; - - return await jscodeshift(transformPath, [filePath], rest); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const {path: filePath = '.', components, agent, ...jscodeshiftOptions} = options; + return await jscodeshift(transformPath, [filePath], jscodeshiftOptions); } From 48f17680eb484788dc2531b379251d0de981c9f2 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 6 Mar 2026 13:32:10 -0600 Subject: [PATCH 07/15] add e2e tests --- eslint.config.mjs | 5 +- packages/dev/codemods/package.json | 1 + .../full-project/input/package.fixture.json | 7 + .../cli/full-project/input/src/App.tsx | 10 + .../cli/full-project/input/src/Form.tsx | 6 + .../cli/full-project/input/yarn.lock | 1 + .../full-project/output/package.fixture.json | 7 + .../cli/full-project/output/src/App.tsx | 6 + .../cli/full-project/output/src/Form.tsx | 6 + .../cli/full-project/output/yarn.lock | 1 + .../subset-project/input/package.fixture.json | 7 + .../cli/subset-project/input/src/Form.tsx | 11 + .../cli/subset-project/input/yarn.lock | 1 + .../output/package.fixture.json | 7 + .../cli/subset-project/output/src/Form.tsx | 10 + .../cli/subset-project/output/yarn.lock | 1 + .../src/s1-to-s2/__tests__/cli.e2e.test.ts | 248 ++++++++++++++++++ packages/dev/codemods/tsconfig.json | 2 +- 18 files changed, 334 insertions(+), 3 deletions(-) create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/package.fixture.json create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/App.tsx create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/Form.tsx create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/yarn.lock create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/output/package.fixture.json create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/output/src/App.tsx create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/output/src/Form.tsx create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/output/yarn.lock create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/subset-project/input/package.fixture.json create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/subset-project/input/src/Form.tsx create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/subset-project/input/yarn.lock create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/subset-project/output/package.fixture.json create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/subset-project/output/src/Form.tsx create mode 100644 packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/subset-project/output/yarn.lock create mode 100644 packages/dev/codemods/src/s1-to-s2/__tests__/cli.e2e.test.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index d7c27a8dbee..5bc31d6cf6c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -60,7 +60,8 @@ export default [{ "packages/dev/storybook-builder-parcel/*", "packages/dev/storybook-react-parcel/*", "packages/dev/s2-docs/pages/**", - "packages/dev/mcp/*/dist" + "packages/dev/mcp/*/dist", + "packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/**" ], }, ...compat.extends("eslint:recommended"), { plugins: { @@ -533,4 +534,4 @@ export default [{ ...globals.browser } } -}]; \ No newline at end of file +}]; diff --git a/packages/dev/codemods/package.json b/packages/dev/codemods/package.json index 434acaf918c..6085213ed3a 100644 --- a/packages/dev/codemods/package.json +++ b/packages/dev/codemods/package.json @@ -21,6 +21,7 @@ "url": "https://github.com/adobe/react-spectrum" }, "dependencies": { + "@adobe/react-spectrum": "^3.46.1", "@babel/parser": "^7.24.5", "@babel/traverse": "^7.24.5", "@babel/types": "^7.24.5", diff --git a/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/package.fixture.json b/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/package.fixture.json new file mode 100644 index 00000000000..edb334cd571 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/package.fixture.json @@ -0,0 +1,7 @@ +{ + "name": "s1-to-s2-cli-fixture", + "private": true, + "devDependencies": { + "parcel": "^2.12.0" + } +} diff --git a/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/App.tsx b/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/App.tsx new file mode 100644 index 00000000000..52665e97a53 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/App.tsx @@ -0,0 +1,10 @@ +import {Button} from '@adobe/react-spectrum'; +import React from 'react'; + +export function App() { + return ( + + ); +} diff --git a/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/Form.tsx b/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/Form.tsx new file mode 100644 index 00000000000..057d14955fb --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/__testfixtures__/cli/full-project/input/src/Form.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import {TextArea} from '@adobe/react-spectrum'; + +export function Form() { + return