diff --git a/packages/dev/s2-docs/scripts/generateAgentSkills.mjs b/packages/dev/s2-docs/scripts/generateAgentSkills.mjs index e9e019140bf..fad7543e989 100644 --- a/packages/dev/s2-docs/scripts/generateAgentSkills.mjs +++ b/packages/dev/s2-docs/scripts/generateAgentSkills.mjs @@ -27,6 +27,10 @@ const REPO_ROOT = path.resolve(__dirname, '../../../../'); const MARKDOWN_DOCS_DIST = path.join(REPO_ROOT, 'packages/dev/s2-docs/dist'); const MDX_PAGES_DIR = path.join(REPO_ROOT, 'packages/dev/s2-docs/pages'); const MARKDOWN_DOCS_SCRIPT = path.join(__dirname, 'generateMarkdownDocs.mjs'); +const CODEMOD_S1_TO_S2_DIR = path.join( + REPO_ROOT, + 'packages/dev/codemods/src/s1-to-s2' +); const WELL_KNOWN_DIR = '.well-known'; const WELL_KNOWN_SKILLS_DIR = 'skills'; @@ -45,6 +49,20 @@ const SKILLS = { website: 'https://react-spectrum.adobe.com/' } }, + 'react-spectrum-v3-to-s2-migration': { + name: 'react-spectrum-v3-to-s2-migration', + description: + 'Migrate React Spectrum v3 codebases to Spectrum 2. Use when upgrading from @adobe/react-spectrum or @react-spectrum/* packages to @react-spectrum/s2, running the s1-to-s2 codemod, or resolving TODO(S2-upgrade) follow-ups.', + license: 'Apache-2.0', + sourceDir: 's2', + compatibility: + 'Requires a React project migrating from React Spectrum v3 to @react-spectrum/s2.', + metadata: { + author: 'Adobe', + website: 'https://react-spectrum.adobe.com/' + }, + mode: 'migration' + }, 'react-aria': { name: 'react-aria', description: @@ -283,11 +301,8 @@ function categorizeEntries(entries, sourceDir) { return categories; } -/** - * Generate the SKILL.md content - */ -function generateSkillMd(skillConfig, categories, isS2) { - const frontmatter = `--- +function getSkillFrontmatter(skillConfig) { + return `--- name: ${skillConfig.name} description: ${skillConfig.description} license: ${skillConfig.license} @@ -298,6 +313,13 @@ metadata: --- `; +} + +/** + * Generate the SKILL.md content + */ +function generateSkillMd(skillConfig, categories, isS2) { + const frontmatter = getSkillFrontmatter(skillConfig); let content = frontmatter; @@ -386,6 +408,393 @@ The \`references/\` directory contains detailed documentation organized as follo return content.trimEnd() + '\n'; } +function generateMigrationSkillMd(skillConfig) { + let content = getSkillFrontmatter(skillConfig); + + content += `# React Spectrum v3 to S2 Migration + +Use this skill when upgrading a React Spectrum v3 codebase to Spectrum 2. + +> **Tip:** For full S2 component API documentation, install the \`react-spectrum-s2\` skill or the React Spectrum S2 MCP server alongside this migration skill. + +## Prerequisites (check before starting) + +- **Install \`@react-spectrum/s2\` FIRST** — the codemod requires it at runtime to resolve the list of available S2 components. Without it, the codemod silently produces 0 transformations. +- **Verify npm/yarn registry access** — in corporate environments with private registries, you may need to configure your scoped registry for \`@react-spectrum\`, or use \`--registry https://registry.npmjs.org\` for public packages. +- **Yarn PnP users**: use \`yarn dlx\` instead of \`npx\`. Ensure \`@react-spectrum/s2\` is resolvable by jscodeshift workers (you may need \`nodeLinker: node-modules\` or a symlink workaround, since PnP virtual resolution is not visible to jscodeshift subprocesses). + +## Migration workflow + +1. Install \`@react-spectrum/s2\` in each workspace that uses React Spectrum. +2. Run a dry codemod pass to preview migration edits. +3. Run the codemod in apply mode. +4. Optionally scope to specific components with \`--components\`. +5. Resolve remaining \`TODO(S2-upgrade)\` comments manually (see the resolution guide below). +6. Migrate components the codemod skips (see the skipped components section below). +7. Configure bundler for style macro support. +8. Add \`page.css\` import to app entrypoints. +9. Migrate icons from \`@spectrum-icons/workflow/*\` to \`@react-spectrum/s2/icons/*\`, and illustrations from \`@spectrum-icons/illustrations/*\` to \`@react-spectrum/s2/illustrations/linear/\` or \`@react-spectrum/s2/illustrations/gradient/generic1/\`. +10. Clean up imports (see the import cleanup section below). +11. Update Provider/theme setup and verify (see the verification section below). + +## Codemod commands + +\`\`\`bash +# Install S2 first (required for codemod to work) +npm install @react-spectrum/s2 +# or: yarn add @react-spectrum/s2 + +# Preview changes without writing files +npx @react-spectrum/codemods s1-to-s2 --agent --dry --path . + +# Apply migrations +npx @react-spectrum/codemods s1-to-s2 --agent --path . + +# Scope to specific components +npx @react-spectrum/codemods s1-to-s2 --agent --path . --components=Button,TextField +\`\`\` + +## Monorepo considerations + +- In a monorepo, run the codemod per-workspace or use \`--path ./packages\` to target all workspaces at once. Ensure \`@react-spectrum/s2\` is hoisted or available in each workspace's resolution scope. +- The codemod spawns jscodeshift workers as subprocesses. These workers must be able to \`require.resolve('@react-spectrum/s2')\` — ensure it is in \`node_modules\`, not only in PnP virtual resolution. +- The \`--agent\` flag skips interactive prompts, package installation, and macro setup, but still requires \`@react-spectrum/s2\` to be installed and resolvable. + +## Known codemod limitations + +- Does NOT update \`package.json\` (add S2, remove v3 packages). +- Does NOT migrate all individual \`@react-spectrum/*\` package imports (e.g., \`@react-spectrum/toast\`, \`@react-spectrum/utils\`). Rewrite these manually to \`@react-spectrum/s2\` where S2 equivalents exist. +- Does NOT handle \`@react-types/*\` imports — audit and remove after migration since types are re-exported from \`@react-spectrum/s2\`. +- \`@react-aria/*\` and \`@react-stately/*\` packages remain compatible with S2 and do not need migration. + +## Components the codemod skips + +The following components have no automated transform and require fully manual migration: + +- **Accordion** — restructure to use \`Disclosure\`, \`DisclosureTitle\`, and \`DisclosurePanel\`: + \`\`\`jsx + // Before (v3) + + Content one + + + // After (S2) + + + Section One + Content one + + + \`\`\` +- **ActionBar** — remove \`ActionBarContainer\` and move \`ActionBar\` to the \`renderActionBar\` prop of \`TableView\` or \`CardView\`. Convert \`Item\` children to \`ActionButton\`, and move \`onAction\` to individual \`onPress\` handlers. +- **Card / CardView** — no automated transform yet; migrate manually. +- **Well** — replace with a \`
\` and apply styles using the style macro: + \`\`\`jsx + // Before (v3) + Content + + // After (S2) +
Content
+ \`\`\` + +## Resolving TODO(S2-upgrade) comments + +After the codemod runs, search for \`TODO(S2-upgrade)\` comments. Each comment pattern has a specific resolution: + +### Style prop spreads +\`TODO(S2-upgrade): check this spread for style props\` + +The codemod found a JSX spread attribute (\`{...props}\`) and cannot determine whether it contains v3 style props. Inspect the spread source — if it contains v3 style props (\`margin\`, \`padding\`, \`width\`, etc.), extract them into a \`style()\` macro call and remove them from the spread. + +### Dynamic prop values +\`TODO(S2-upgrade): Prop X could not be automatically updated because Y could not be followed.\` + +The prop value is a variable or expression the codemod cannot statically analyze. Trace the variable to its source and apply the same transformation manually. Common examples: + +\`\`\`jsx +// validationState variable → isInvalid +// Before +const state = hasError ? 'invalid' : 'valid'; + +// After + + +// Dynamic variant +// Before + + {(close) => ( + + ... + + + + + )} + + +// After (S2) + + + + {({close}) => ( + <> + ... + + + + + )} + + +\`\`\` + +### Dynamic imports +\`TODO(S2-upgrade): check this dynamic import\` + +Dynamic \`import()\` statements from v3 packages cannot be automatically rewritten. Update the import path manually to \`@react-spectrum/s2\`. + +### Link with custom element +\`TODO(S2-upgrade): You may have been using a custom link component here.\` + +If you had a custom element (not \`\`) inside \`Link\`, remove it and apply props directly to \`Link\`. S2 \`Link\` renders its own anchor element. + +### Breadcrumbs nav element +\`TODO(S2-upgrade): S2 Breadcrumbs no longer includes a nav element.\` + +S2 \`Breadcrumbs\` no longer wraps in \`