diff --git a/packages/dev/codemods/src/s1-to-s2/README.md b/packages/dev/codemods/src/s1-to-s2/README.md index 0751b1c1a80..fbc71b472cb 100644 --- a/packages/dev/codemods/src/s1-to-s2/README.md +++ b/packages/dev/codemods/src/s1-to-s2/README.md @@ -9,6 +9,9 @@ Run `npx @react-spectrum/codemods s1-to-s2` from the directory you want to upgra ### Options - `-c, --components `: Comma separated list of components to upgrade (ex: `Button,TableView`). If not specified, all available components will be upgraded. +- `--path `: The path to the directory to run the codemod in. Defaults to the current directory (`.`). +- `-d, --dry`: Run the codemod without writing any changes to disk. Use this to preview migrations before applying. +- `--agent`: Run in non-interactive mode. Skips interactive prompts, package installation, and macro setup. Required when running in CI or from an agent tool. Note: `@react-spectrum/s2` must still be installed and resolvable. ## How it works diff --git a/packages/dev/codemods/src/s1-to-s2/UPGRADE.md b/packages/dev/codemods/src/s1-to-s2/UPGRADE.md index 185ec8886a9..360b97efd7c 100644 --- a/packages/dev/codemods/src/s1-to-s2/UPGRADE.md +++ b/packages/dev/codemods/src/s1-to-s2/UPGRADE.md @@ -265,7 +265,7 @@ Example: ### Border width -Affected style props: `borderWidth`, `borderStartWidth`, `borderEndWidth`, `borderTopWidth`, `orderBottomWidth`, `borderXWidth`, `borderYWidth`. +Affected style props: `borderWidth`, `borderStartWidth`, `borderEndWidth`, `borderTopWidth`, `borderBottomWidth`, `borderXWidth`, `borderYWidth`. Example: diff --git a/packages/dev/s2-docs/migration-references/focused-app-setup.md b/packages/dev/s2-docs/migration-references/focused-app-setup.md new file mode 100644 index 00000000000..9f9063b5877 --- /dev/null +++ b/packages/dev/s2-docs/migration-references/focused-app-setup.md @@ -0,0 +1,25 @@ +# App setup and style macro + +## Full-page apps + +- Import `@react-spectrum/s2/page.css` in the app entrypoint so the page background and color scheme are applied before JavaScript runs. +- Do not carry over a v3 root `Provider theme={defaultTheme}` wrapper just to make the app render. S2 does not require that pattern. +- Do not assume there is only one app root. Standalone pages, alternate entrypoints, utility apps, embedded sub-apps, and test-only render targets may each need their own `page.css` or Provider cleanup. + +## Embedded sections + +- If S2 renders as part of a larger page instead of owning the whole document, keep the S2 subtree inside `` rather than importing `page.css` globally. + +## Provider decisions + +- Use S2 `Provider` when you need locale overrides, router integration, explicit color-scheme/background control, or SSR with `elementType="html"`. +- Remove `theme={defaultTheme}` and other v3 theme props. They do not carry forward to S2. +- Preserve surrounding app-shell providers such as routing, Redux/store, analytics, i18n/intl, and product-framework host providers. Replace only the React Spectrum-specific wrapper instead of flattening the whole provider stack. +- For tests, wrap only the cases that actually need S2 context such as locale or background. Do not recreate a full v3 theme wrapper by default. + +## Bundlers + +- Detect the bundler at the migration target level, especially in monorepos. The workspace root may include Storybook, Vite, or other tooling that does not represent the runtime bundler for the package being migrated. +- Parcel v2.12.0+ already supports S2 macros. Most Parcel repos only need the package install and the right entrypoint CSS import. +- Non-Parcel bundlers need `unplugin-parcel-macros` and the appropriate framework setup. Keep plugin ordering correct so macros run before the rest of the toolchain. +- If the repo already has a framework-specific S2 setup, preserve it instead of layering a second macro configuration on top. diff --git a/packages/dev/s2-docs/migration-references/focused-manual-fixes.md b/packages/dev/s2-docs/migration-references/focused-manual-fixes.md new file mode 100644 index 00000000000..accd86a6079 --- /dev/null +++ b/packages/dev/s2-docs/migration-references/focused-manual-fixes.md @@ -0,0 +1,37 @@ +# Manual fixes after the codemod + +## Known gaps to handle explicitly + +- `@react-spectrum/toast` imports are not automatically migrated. Move them to S2, then re-check `ToastContainer` mounts and `ToastQueue` usages. +- v3 `Provider` and `defaultTheme` wrappers in apps and tests need human review. S2 does not use the v3 theme object model. + +## Imports and packages + +- Collapse v3 component imports onto `@react-spectrum/s2`. +- Keep using `@spectrum-icons/*` only when there is no S2 icon or illustration equivalent. Prefer `@react-spectrum/s2/icons/*` and `@react-spectrum/s2/illustrations` when possible. +- If the codemod leaves `TODO(S2-upgrade)` next to an icon or illustration import, pick the nearest S2 replacement manually. + +## App and Provider setup + +- Full-page apps usually add `import '@react-spectrum/s2/page.css';` at the entrypoint and no longer need a mandatory root Provider just to supply `theme={defaultTheme}`. +- Embedded sections still use S2 `Provider` with an explicit `background`. +- Keep or add S2 `Provider` only when locale, router integration, color-scheme/background overrides, or SSR `elementType="html"` behavior are needed. +- Preserve unrelated wrappers such as routing, store, analytics, i18n, and host-framework providers. Replace or remove only the React Spectrum-specific layer. + +## Style and layout follow-ups + +- Convert v3 style props and `UNSAFE_style` cases to the S2 style macro when possible. +- `Flex` and `Grid` often become `div` elements styled with the macro. +- Review `ClearSlots` and other direct `@react-spectrum/utils` imports manually. These are not part of the common S2 app surface. + +## Dialogs and collections + +- `DialogContainer` and `useDialogContainer` still exist in S2, but the import path changes and dismiss logic may need to move between `Dialog`, `DialogTrigger`, and `DialogContainer`. +- When `Item` survives the codemod, rename it based on its parent: `MenuItem`, `PickerItem`, `ComboBoxItem`, `ListBoxItem`, `Tab`, `TabPanel`, `Tag`, `Breadcrumb`, and similar. +- Preserve React `key` when mapping arrays, but ensure collection data items expose `id` when S2 expects it. +- Table and ListView migrations often need manual review for row headers, nested columns, and explicit item ids. + +## Tests + +- Replace v3 Provider/defaultTheme test wrappers with the minimal S2 `Provider` props the test actually needs, or remove the wrapper entirely if no S2 context is required. +- Update toast mocks and assertions that still reference `@react-spectrum/toast` or old dialog markup. diff --git a/packages/dev/s2-docs/migration-references/focused-toast.md b/packages/dev/s2-docs/migration-references/focused-toast.md new file mode 100644 index 00000000000..289231f006c --- /dev/null +++ b/packages/dev/s2-docs/migration-references/focused-toast.md @@ -0,0 +1,22 @@ +# Toast migration + +## Import changes + +- Move `ToastContainer` and `ToastQueue` imports from `@react-spectrum/toast` to `@react-spectrum/s2`. +- Keep a shared `ToastContainer` mounted near the app root or test harness, then update all queue calls to use the S2 import path. + +## Queue methods + +- S2 supports `ToastQueue.neutral`, `positive`, `negative`, and `info`. +- Re-check options such as `timeout`, `actionLabel`, `onAction`, `shouldCloseOnAction`, and `onClose` after the import move. +- The queue methods still return a close function. Keep programmatic dismissal logic when the existing UX depends on it. + +## Common post-codemod blind spots + +- Search for every `ToastContainer` mount and every `ToastQueue` usage after moving imports. Shared app roots, secondary entrypoints, and test harnesses are easy to miss. + +## Tests and mocks + +- Update every toast mock to point at `@react-spectrum/s2`. +- If a test mounted `ToastContainer` from the old package, swap it to the S2 import as part of the same change. +- Re-run the affected tests after the import move. Toast helpers are often mocked in many files and are easy to miss. diff --git a/packages/dev/s2-docs/migration-references/focused-workflow.md b/packages/dev/s2-docs/migration-references/focused-workflow.md new file mode 100644 index 00000000000..97b7692422f --- /dev/null +++ b/packages/dev/s2-docs/migration-references/focused-workflow.md @@ -0,0 +1,45 @@ +# Codemod-first workflow + +## Inspect before editing + +- Search package manifests and source for `@adobe/react-spectrum`, `@react-spectrum/*`, and `@spectrum-icons/*`. +- In monorepos or mixed-tooling repos, inspect the target package or app first instead of assuming the root manifest, Storybook config, or workspace tooling represents the runtime target being migrated. +- Determine the package manager from the relevant lockfile or workspace setup, then choose the codemod runner that matches that repo or package. +- Detect the bundler at the migration target level before touching setup files. Parcel v2.12.0+ already supports S2 style macros. Vite, webpack, Next.js, Rollup, ESBuild, and similar toolchains need explicit macro-plugin setup. +- Find app entrypoints, standalone pages, alternate entrypoints, embedded sub-apps, utility apps, test-only render targets, root providers, shared test wrappers, toast setup, and any direct `defaultTheme` usage before the codemod changes imports. +- Search for `ToastContainer`, `ToastQueue`, `DialogContainer`, `useDialogContainer`, `ClearSlots`, style props, and `UNSAFE_style`. These are common follow-up areas after the codemod. + +## Run the codemod first + +Prefer the repo-native non-interactive command so the upgrade stays deterministic: + +```bash +npx @react-spectrum/codemods s1-to-s2 --agent +yarn dlx @react-spectrum/codemods s1-to-s2 --agent +pnpm dlx @react-spectrum/codemods s1-to-s2 --agent +``` + +Use `npx` for npm and Yarn 1 repos, `yarn dlx` for Yarn Berry or Yarn PnP repos, and `pnpm dlx` for pnpm repos. Use the equivalent workspace-native runner if the repo uses another package manager. +Use `--path ` for monorepos or when only one package should migrate first. In a monorepo, run the codemod against the target subtree first instead of the whole workspace. +Use `--components A,B` when the user explicitly wants an incremental rollout by component family. +Use `--dry` when you need to preview scope before editing. + +## Resolve follow-up work in order + +1. Install or verify `@react-spectrum/s2` and clean up imports. +2. Add S2 app setup such as `@react-spectrum/s2/page.css` or `Provider` changes. +3. Search for `TODO(S2-upgrade)` and fix every remaining comment. +4. Resolve style prop, layout, and dialog/collection follow-ups. +5. Migrate icons, illustrations, and toast imports. +6. Update tests, mocks, and validation commands. + +## Validate with repo-native commands + +Prefer the narrowest existing scripts from `package.json`: + +- dependency install if manifests changed +- typecheck or compile +- focused tests for touched areas +- build + +For monorepos, validate the affected package or subtree first with its own scripts before escalating to workspace-wide checks. diff --git a/packages/dev/s2-docs/package.json b/packages/dev/s2-docs/package.json index b4b78e88b0b..eb14a2a3041 100644 --- a/packages/dev/s2-docs/package.json +++ b/packages/dev/s2-docs/package.json @@ -49,6 +49,8 @@ "json5": "^2.2.3", "lz-string": "^1.5.0", "markdown-to-jsx": "^6.11.0", + "mdast-util-mdx": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0", "react": "^19.2.0", "react-aria": "^3.40.0", "react-aria-components": "^1.7.1", diff --git a/packages/dev/s2-docs/scripts/generateAgentSkills.mjs b/packages/dev/s2-docs/scripts/generateAgentSkills.mjs index e9e019140bf..349a1b22752 100644 --- a/packages/dev/s2-docs/scripts/generateAgentSkills.mjs +++ b/packages/dev/s2-docs/scripts/generateAgentSkills.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * Generates Agent Skills for React Spectrum (S2) and React Aria. + * Generates Agent Skills for React Spectrum (S2), migration, and React Aria. * * This script creates skills in the Agent Skills format (https://agentskills.io/specification) * @@ -27,6 +27,7 @@ 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 MIGRATION_REFS_DIR = path.join(REPO_ROOT, 'packages/dev/s2-docs/migration-references'); const WELL_KNOWN_DIR = '.well-known'; const WELL_KNOWN_SKILLS_DIR = 'skills'; @@ -45,6 +46,20 @@ const SKILLS = { website: 'https://react-spectrum.adobe.com/' } }, + 'react-spectrum-v3-to-s2-migration': { + name: 'react-spectrum-v3-to-s2-migration', + description: + 'Upgrade React Spectrum v3 (Spectrum 1) codebases to React Spectrum S2. Use when developers mention migrating or upgrading from React Spectrum v3, Spectrum 1, S1, @adobe/react-spectrum, @react-spectrum/* packages, or codemod-assisted upgrades to @react-spectrum/s2.', + kind: 'migration', + license: 'Apache-2.0', + sourceDir: 's2', + compatibility: + 'Requires a React project currently using React Spectrum v3, @react-spectrum/* packages, or related React Spectrum v3 helpers.', + metadata: { + author: 'Adobe', + website: 'https://react-spectrum.adobe.com/' + } + }, 'react-aria': { name: 'react-aria', description: @@ -283,23 +298,25 @@ function categorizeEntries(entries, sourceDir) { return categories; } -/** - * Generate the SKILL.md content - */ -function generateSkillMd(skillConfig, categories, isS2) { - const frontmatter = `--- -name: ${skillConfig.name} -description: ${skillConfig.description} -license: ${skillConfig.license} -compatibility: ${skillConfig.compatibility} +function generateFrontmatter(skillConfig) { + return `--- +name: "${skillConfig.name}" +description: "${skillConfig.description}" +license: "${skillConfig.license}" +compatibility: "${skillConfig.compatibility}" metadata: - author: ${skillConfig.metadata.author} - website: ${skillConfig.metadata.website} + author: "${skillConfig.metadata.author}" + website: "${skillConfig.metadata.website}" --- `; +} - let content = frontmatter; +/** + * Generate the SKILL.md content + */ +function generateDocsSkillMd(skillConfig, categories, isS2) { + let content = generateFrontmatter(skillConfig); if (isS2) { content += `# React Spectrum S2 (Spectrum 2) @@ -386,10 +403,54 @@ The \`references/\` directory contains detailed documentation organized as follo return content.trimEnd() + '\n'; } +function generateMigrationSkillMd(skillConfig) { + return `${generateFrontmatter(skillConfig)}# React Spectrum v3 to S2 migration + +Use this skill to upgrade React Spectrum v3 codebases to React Spectrum S2. Work codemod-first by default: inspect the repo, run the non-interactive codemod, then resolve the manual follow-up work it cannot finish safely. + +## Inspect first + +- Search package manifests and source for \`@adobe/react-spectrum\`, \`@react-spectrum/*\` packages, and \`@spectrum-icons/*\`. +- In monorepos or mixed-tooling repos, inspect the target package or app instead of assuming the workspace root has all the information. Determine the package manager and runtime bundler at the migration target level when needed. +- Find app entrypoints, standalone pages, alternate render roots, embedded sub-apps, utility apps, test-only render targets, root providers, shared test wrappers, toast setup, dialog helpers, and any direct \`defaultTheme\` usage before running the codemod. +- Prefer \`--path\` for monorepos or partial rollouts. Use \`--components\` only when the user explicitly wants an incremental migration. + +## Upgrade workflow + +- Follow [Focused codemod workflow](references/focused-workflow.md) for the exact codemod-first sequence and validation order. +- Search for \`TODO(S2-upgrade)\` immediately after the codemod. Treat every remaining comment as a required manual review. +- Resolve follow-up work in this order: + 1. imports and package changes + 2. app setup, \`page.css\`, and Provider decisions + 3. style props, layout primitives, and \`UNSAFE_style\` + 4. dialog, collection, and table follow-ups + 5. icons, illustrations, and toast migration + 6. test wrappers, mocks, and repo-native validation + +## Common blind spots + +- \`@react-spectrum/toast\` imports need explicit migration to S2 toast APIs. +- v3 \`Provider\` and \`defaultTheme\` wrappers in apps and tests require human review rather than a blind rename. Preserve unrelated providers and shared test harnesses while replacing only the React Spectrum-specific layer. +- \`DialogContainer\`/\`useDialogContainer\`, \`ClearSlots\`, style prop conversions, icons, illustrations, and collection \`Item\` rewrites are common follow-up areas even after a successful codemod run. + +## References + +- [Focused codemod workflow](references/focused-workflow.md): inspection checklist, \`--agent\` usage, and validation order. +- [Focused manual fixes](references/focused-manual-fixes.md): the highest-signal follow-up areas after \`TODO(S2-upgrade)\`. +- [Focused app setup](references/focused-app-setup.md): \`page.css\`, Parcel vs non-Parcel macro setup, Provider decisions, and test wrappers. +- [Focused toast migration](references/focused-toast.md): how to replace \`@react-spectrum/toast\` imports and update test mocks. +- [Docs: migrating](references/docs-migrating.md): the broader migration reference when you need more component-by-component detail. +- [Docs: getting started](references/docs-getting-started.md): framework setup and macro guidance. +- [Docs: Provider](references/docs-provider.md): locale, router, color-scheme, and SSR usage. +- [Docs: style macro](references/docs-style-macro.md): exact style macro syntax and constraints. +- [Docs: Toast](references/docs-toast.md): full S2 toast API and examples. +`.trimEnd() + '\n'; +} + /** * Copy documentation files to the skill's references directory */ -function copyDocumentation(skillConfig, categories, skillDir) { +function copyDocsDocumentation(skillConfig, categories, skillDir) { const refsDir = path.join(skillDir, 'references'); const sourceDir = path.join(MARKDOWN_DOCS_DIST, skillConfig.sourceDir); @@ -463,6 +524,50 @@ function copyDocumentation(skillConfig, categories, skillDir) { } } +function copyFocusedDocs(sourceDir, skillDir, docs) { + for (const [sourceName, outputName] of docs) { + const sourcePath = path.join(MARKDOWN_DOCS_DIST, sourceDir, sourceName); + if (!fs.existsSync(sourcePath)) { + console.warn(`Warning: expected migration reference not found: ${sourcePath}`); + continue; + } + + const outputPath = path.join(skillDir, 'references', outputName); + fs.mkdirSync(path.dirname(outputPath), {recursive: true}); + fs.copyFileSync(sourcePath, outputPath); + } +} + +function writeMigrationReferences(skillDir, sourceDir) { + // Copy focused reference docs from source files + const focusedRefs = [ + 'focused-workflow.md', + 'focused-manual-fixes.md', + 'focused-app-setup.md', + 'focused-toast.md' + ]; + + for (const filename of focusedRefs) { + const sourcePath = path.join(MIGRATION_REFS_DIR, filename); + if (!fs.existsSync(sourcePath)) { + console.warn(`Warning: expected migration reference not found: ${sourcePath}`); + continue; + } + + const outputPath = path.join(skillDir, 'references', filename); + fs.mkdirSync(path.dirname(outputPath), {recursive: true}); + fs.copyFileSync(sourcePath, outputPath); + } + + copyFocusedDocs(sourceDir, skillDir, [ + ['migrating.md', 'docs-migrating.md'], + ['getting-started.md', 'docs-getting-started.md'], + ['Provider.md', 'docs-provider.md'], + ['style-macro.md', 'docs-style-macro.md'], + ['Toast.md', 'docs-toast.md'] + ]); +} + function collectSkillFiles(skillDir) { const files = []; @@ -506,11 +611,27 @@ function writeIndexJson(wellKnownRoot, skills) { */ function generateSkill(skillConfig, wellKnownRoot) { const skillDir = path.join(wellKnownRoot, skillConfig.name); - const isS2 = skillConfig.name === 'react-spectrum-s2'; // Create skill directory fs.mkdirSync(skillDir, {recursive: true}); + if (skillConfig.kind === 'migration') { + const skillMdContent = generateMigrationSkillMd(skillConfig); + fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillMdContent); + console.log( + `Generated ${path.relative(REPO_ROOT, path.join(skillDir, 'SKILL.md'))}` + ); + + writeMigrationReferences(skillDir, skillConfig.sourceDir); + console.log( + `Copied migration references to ${path.relative(REPO_ROOT, path.join(skillDir, 'references'))}` + ); + + return skillDir; + } + + const isS2 = skillConfig.name === 'react-spectrum-s2'; + // Parse documentation entries const llmsTxtPath = path.join( MARKDOWN_DOCS_DIST, @@ -526,14 +647,14 @@ function generateSkill(skillConfig, wellKnownRoot) { const categories = categorizeEntries(entries, skillConfig.sourceDir); // Generate SKILL.md - const skillMdContent = generateSkillMd(skillConfig, categories, isS2); + const skillMdContent = generateDocsSkillMd(skillConfig, categories, isS2); fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillMdContent); console.log( `Generated ${path.relative(REPO_ROOT, path.join(skillDir, 'SKILL.md'))}` ); // Copy documentation to references - copyDocumentation(skillConfig, categories, skillDir); + copyDocsDocumentation(skillConfig, categories, skillDir); console.log( `Copied documentation to ${path.relative(REPO_ROOT, path.join(skillDir, 'references'))}` ); @@ -542,7 +663,7 @@ function generateSkill(skillConfig, wellKnownRoot) { } -async function main() { +function main() { console.log( 'Generating Agent Skills for React Spectrum (S2) and React Aria...\n' ); @@ -570,11 +691,15 @@ async function main() { console.log(`\nGenerating skill: ${config.name}`); const skillDir = generateSkill(config, wellKnownRoot); const files = collectSkillFiles(skillDir); - indexEntries.push({ + const entry = { name: config.name, description: config.description, files - }); + }; + if (config.kind) { + entry.kind = config.kind; + } + indexEntries.push(entry); } writeIndexJson(wellKnownRoot, indexEntries); @@ -586,7 +711,9 @@ async function main() { console.log('\nAgent Skills generation complete!'); } -main().catch((err) => { +try { + main(); +} catch (err) { console.error(err); process.exit(1); -}); +} diff --git a/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs b/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs index 22bd7f76b3f..6646c52677c 100644 --- a/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs +++ b/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs @@ -4,11 +4,12 @@ import * as babel from '@babel/parser'; import {fileURLToPath} from 'url'; import fs from 'fs'; import glob from 'fast-glob'; +import {mdxToMarkdown} from 'mdast-util-mdx'; import path from 'path'; import {Project} from 'ts-morph'; import remarkMdx from 'remark-mdx'; import remarkParse from 'remark-parse'; -import remarkStringify from 'remark-stringify'; +import {toMarkdown} from 'mdast-util-to-markdown'; import {unified} from 'unified'; import {visit} from 'unist-util-visit'; @@ -1532,6 +1533,7 @@ function remarkRemoveImportsExports() { const docsSource = statement.source.value.slice(5); for (const specifier of statement.specifiers) { + // eslint-disable-next-line max-depth if (specifier.local?.name) { docsImports[specifier.local.name] = docsSource; } @@ -2916,6 +2918,7 @@ function generateClassAPITable(className, file) { const returnTag = methodDocs[0].getTags().find(t => t.getTagName() === 'returns' || t.getTagName() === 'return'); if (returnTag) { const returnDesc = returnTag.getCommentText() || ''; + // eslint-disable-next-line max-depth if (returnDesc) { sections.push(`**Returns:** ${returnDesc}\n`); } @@ -3220,14 +3223,17 @@ async function main() { .use(remarkParse) .use(remarkMdx) .use(remarkRemoveImportsExports) - .use(remarkDocsComponentsToMarkdown) - .use(remarkStringify, { - fences: true, - bullets: '-', - listItemIndent: 'one' - }); - - let markdown = String(await processor.process({value: mdContent, path: filePath})); + .use(remarkDocsComponentsToMarkdown); + + const file = {value: mdContent, path: filePath}; + const tree = processor.parse(file); + const transformed = await processor.run(tree, file); + let markdown = toMarkdown(transformed, { + fences: true, + bullet: '-', + listItemIndent: 'one', + extensions: mdxToMarkdown.extensions + }); // Convert markdown links ending in .html to .md (relative links only) markdown = markdown.replace(/\[([^\]]+)\]\(([^)]+\.html)\)/g, (match, text, url) => { diff --git a/yarn.lock b/yarn.lock index 5356e6f8bef..730ef49544e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7171,6 +7171,8 @@ __metadata: json5: "npm:^2.2.3" lz-string: "npm:^1.5.0" markdown-to-jsx: "npm:^6.11.0" + mdast-util-mdx: "npm:^1.0.0" + mdast-util-to-markdown: "npm:^1.0.0" playwright: "npm:^1.57.0" react: "npm:^19.2.0" react-aria: "npm:^3.40.0"