diff --git a/apps/docs/__tests__/LandingPageInstallInstructions_.test.res b/apps/docs/__tests__/LandingPageInstallInstructions_.test.res new file mode 100644 index 000000000..586db9134 --- /dev/null +++ b/apps/docs/__tests__/LandingPageInstallInstructions_.test.res @@ -0,0 +1,24 @@ +open Vitest + +test("install instructions update their class prop and keep copying available", async () => { + let screen = await render() + let root = switch document->WebAPI.Document.querySelector(".initial-layout") { + | Value(root) => root + | Null => failwith("expected the install instructions root") + } + await element(root)->toHaveAttribute("class", "w-full max-w-400 initial-layout") + + await screen->rerender() + + await element(root)->toHaveAttribute("class", "w-full max-w-400 updated-layout") + let template = await screen->getByLabelText("Copy npx create-rescript-app command") + await element(template)->toBeVisible + + await screen->rerender() + + await element(root)->toHaveAttribute("class", "w-full max-w-400 ") + let copy = await screen->getByLabelText("Copy npm install rescript command") + await copy->click + let feedback = await screen->getByText("Copied!") + await element(feedback)->toBeVisible +}) diff --git a/apps/docs/package.json b/apps/docs/package.json index 7ed76418c..9607334f8 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -16,7 +16,7 @@ "check:algolia-public-env": "node _scripts/LogAlgoliaEnvStatus.mjs", "build": "yarn build:res && yarn build:scripts && yarn check:algolia-public-env && yarn build:update-index && yarn build:vite && yarn build:generate-sitemap", "ci:format": "cd ../.. && oxfmt --check", - "ci:homepage-performance": "node scripts/homepage-performance.mjs", + "ci:homepage-performance": "node --test scripts/__tests__/homepage-compiler.test.mjs && node scripts/homepage-performance.mjs", "ci:test": "vitest --run --browser.headless", "ci:test:e2e": "cypress run --config-file cypress.homepage.config.mjs --browser chrome", "ci:test:scripts": "vitest run --config vitest.scripts.config.mjs", @@ -78,14 +78,17 @@ "vfile-matter": "^5.0.1" }, "devDependencies": { + "@babel/core": "^7.29.7", "@react-router/dev": "^8.3.1", "@responsive-image/core": "2.1.0", "@responsive-image/vite-plugin": "3.0.1", + "@rolldown/plugin-babel": "^0.2.4", "@tailwindcss/vite": "^4.3.0", "@types/react": "^19.2.14", "@vitejs/plugin-react": "^6.0.1", "@vitest/browser-playwright": "^5.0.0", "auto-image-converter": "^2.2.0", + "babel-plugin-react-compiler": "^1.0.0", "chokidar": "^4.0.3", "cypress": "^15.13.1", "cypress-real-events": "^1.15.1", @@ -96,6 +99,7 @@ "oxfmt": "^0.46.0", "playwright": "^1.63.0", "remark-cli": "^12.0.1", + "rolldown": "1.2.8", "search-insights": "^2.17.3", "tailwindcss": "^4", "to-vfile": "^8.0.0", diff --git a/apps/docs/scripts/__tests__/homepage-compiler.test.mjs b/apps/docs/scripts/__tests__/homepage-compiler.test.mjs new file mode 100644 index 000000000..20c19a1ba --- /dev/null +++ b/apps/docs/scripts/__tests__/homepage-compiler.test.mjs @@ -0,0 +1,258 @@ +import assert from "node:assert/strict"; +import { readFile, readdir } from "node:fs/promises"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; +import { parseSync, transformAsync, traverse, types } from "@babel/core"; +import { homepageCompilerOptions } from "../../vite-react-compiler.mjs"; + +const optedInComponents = [ + { + name: "LandingPageIntro", + directory: "src/components", + compiledNames: ["LandingPageIntro"], + }, + { + name: "LandingPageInstallInstructions", + directory: "src/components", + compiledNames: ["LandingPageInstallInstructions"], + }, + { + name: "LandingPageTrustedBy", + directory: "src/components", + compiledNames: ["LandingPageTrustedBy"], + }, + { + name: "NavbarPrimary", + directory: "src/components", + compiledNames: [ + "NavbarPrimary", + "NavbarPrimary$LeftContent", + "NavbarPrimary$RightContent", + ], + }, +]; +const optedInNames = optedInComponents.flatMap( + ({ compiledNames }) => compiledNames, +); + +async function transformComponent({ name, directory }) { + const filename = fileURLToPath( + new URL(`../../${directory}/${name}.jsx`, import.meta.url), + ); + const result = await transformAsync(await readFile(filename, "utf8"), { + filename, + ast: true, + code: false, + babelrc: false, + configFile: false, + parserOpts: { plugins: ["jsx"] }, + presets: homepageCompilerOptions().presets.map((preset) => preset.preset), + }); + assert.ok(result?.ast, `${name} must produce a Babel AST`); + return result.ast; +} + +function cacheBindings(ast) { + return ast.program.body.flatMap((node) => { + if ( + !types.isImportDeclaration(node) || + node.source.value !== "react/compiler-runtime" + ) { + return []; + } + return node.specifiers.flatMap((specifier) => + types.isImportSpecifier(specifier) && + types.isIdentifier(specifier.imported, { name: "c" }) + ? [specifier.local.name] + : [], + ); + }); +} + +function cachedComponentNames(ast) { + const bindings = cacheBindings(ast); + const names = new Set(); + traverse(ast, { + CallExpression(path) { + if ( + types.isIdentifier(path.node.callee) && + bindings.includes(path.node.callee.name) + ) { + const component = path.getFunctionParent(); + assert.ok(component?.isFunctionDeclaration()); + assert.ok(component.node.id); + names.add(component.node.id.name); + } + }, + }); + return [...names].sort(); +} + +function isMemoCacheSentinel(node) { + return ( + types.isStringLiteral(node, { value: "react.memo_cache_sentinel" }) || + (types.isTemplateLiteral(node) && + node.expressions.length === 0 && + node.quasis[0]?.value.cooked === "react.memo_cache_sentinel") + ); +} + +function memoizedFunctionCount(ast) { + const functions = new Set(); + traverse(ast, { + CallExpression(path) { + const { callee, arguments: args } = path.node; + if ( + types.isMemberExpression(callee, { computed: false }) && + types.isIdentifier(callee.object, { name: "Symbol" }) && + types.isIdentifier(callee.property, { name: "for" }) && + args.length === 1 && + isMemoCacheSentinel(args[0]) + ) { + const component = path.getFunctionParent(); + assert.ok(component, "memoization must belong to a component"); + functions.add(component.node); + } + }, + }); + return functions.size; +} + +for (const component of optedInComponents) { + test(`${component.name} opts in to React 19 compiler memoization`, async () => { + const ast = await transformComponent(component); + assert.deepEqual(cachedComponentNames(ast), component.compiledNames); + assert.equal(memoizedFunctionCount(ast), component.compiledNames.length); + }); +} + +test("unannotated interactive homepage components remain uncompiled", async () => { + const ast = await transformComponent({ + name: "LandingPageCopyButton", + directory: "src/components", + }); + assert.deepEqual(cacheBindings(ast), []); + assert.deepEqual(cachedComponentNames(ast), []); + assert.equal(memoizedFunctionCount(ast), 0); +}); + +test("compiler file filtering includes only opted-in generated application modules", () => { + const { include, exclude } = homepageCompilerOptions(); + const matches = (filename) => + include.test(filename) && + !exclude.some((pattern) => pattern.test(filename)); + for (const filename of [ + "/repo/apps/docs/app/routes/LandingPageIntro.jsx", + "/repo/apps/docs/app/routes/LandingPageTrustedBy.jsx?import", + "C:\\repo\\apps\\docs\\app\\routes\\LandingPageIntro.jsx", + "/repo/apps/docs/src/components/LandingPageIntro.jsx", + "/repo/apps/docs/src/components/LandingPageTrustedBy.jsx?import", + "C:\\repo\\apps\\docs\\src\\components\\LandingPageIntro.jsx", + "/repo/apps/docs/src/components/NavbarPrimary.jsx", + "/repo/apps/docs/src/components/NavbarPrimary.jsx?import", + "C:\\repo\\apps\\docs\\src\\components\\NavbarPrimary.jsx", + ]) { + assert.equal(matches(filename), true, filename); + } + for (const filename of [ + "/repo/apps/docs/app/routes/LandingPageIntro.res", + "/repo/apps/docs/app/routes/LandingPageIntro.mjs", + "/repo/apps/docs/app/routes/LandingPageIntro.jsx.map", + "/repo/apps/docs/app/routes/TryRoute.jsx", + "/repo/apps/guide/app/routes/LandingPageIntro.jsx", + "/repo/node_modules/example/apps/docs/app/routes/LandingPageIntro.jsx", + "C:\\repo\\node_modules\\example\\apps\\docs\\app\\routes\\LandingPageIntro.jsx", + "/repo/apps/docs/src/components/LandingPageIntro.res", + "/repo/apps/docs/src/components/LandingPageIntro.jsx.map", + "/repo/apps/docs/src/components/Search.jsx", + "/repo/apps/docs/src/components/NavbarSecondary.jsx", + "/repo/apps/guide/src/components/LandingPageIntro.jsx", + "/repo/node_modules/example/apps/docs/src/components/LandingPageIntro.jsx", + "\0rolldown/runtime.js", + ]) { + assert.equal(matches(filename), false, filename); + } +}); + +test("the shared preset preserves annotation mode and excludes server compilation", () => { + const [{ preset, rolldown }] = homepageCompilerOptions().presets; + assert.deepEqual(preset().plugins, [ + [ + "babel-plugin-react-compiler", + { compilationMode: "annotation", target: "19" }, + ], + ]); + assert.equal( + rolldown.filter.code.test('function Example() { "use memo"; }'), + true, + ); + assert.equal(rolldown.filter.code.test("function Example() {}"), false); + assert.equal( + rolldown.applyToEnvironmentHook({ config: { consumer: "client" } }), + true, + ); + assert.equal( + rolldown.applyToEnvironmentHook({ config: { consumer: "server" } }), + false, + ); + assert.deepEqual(rolldown.optimizeDeps.include, ["react/compiler-runtime"]); +}); + +test("the production homepage bundle contains its three compiled components", async () => { + const directory = new URL("../../build/client/assets/", import.meta.url); + const filenames = (await readdir(directory)).filter((filename) => + /^LandingPageRoute-[^/]+\.js$/.test(filename), + ); + assert.equal(filenames.length, 1, "the production homepage entry must exist"); + const contents = await readFile(new URL(filenames[0], directory), "utf8"); + const ast = parseSync(contents, { babelrc: false, configFile: false }); + assert.ok(ast); + assert.equal(memoizedFunctionCount(ast), 3); +}); + +test("the production client bundle contains the compiled primary navbar", async () => { + const directory = new URL("../../build/client/assets/", import.meta.url); + const assets = await Promise.all( + (await readdir(directory)) + .filter((filename) => filename.endsWith(".js")) + .map(async (filename) => ({ + filename, + contents: await readFile(new URL(filename, directory), "utf8"), + })), + ); + const matches = assets.filter(({ contents }) => + contents.includes("navbar-primary-left-content"), + ); + assert.equal(matches.length, 1, "the production primary navbar must exist"); + const ast = parseSync(matches[0].contents, { + babelrc: false, + configFile: false, + }); + assert.ok(ast); + assert.equal(memoizedFunctionCount(ast), 3); +}); + +test("the production server leaves the annotated components uncompiled", async () => { + const contents = await readFile( + new URL("../../build/server/index.js", import.meta.url), + "utf8", + ); + const ast = parseSync(contents, { babelrc: false, configFile: false }); + assert.ok(ast); + const annotatedComponents = []; + traverse(ast, { + FunctionDeclaration(path) { + if ( + optedInNames.includes(path.node.id?.name) && + path.node.body.directives.some( + (directive) => directive.value.value === "use memo", + ) + ) { + annotatedComponents.push(path.node.id.name); + } + }, + }); + assert.deepEqual(annotatedComponents.sort(), [...optedInNames].sort()); + assert.deepEqual(cacheBindings(ast), []); + assert.equal(memoizedFunctionCount(ast), 0); +}); diff --git a/apps/docs/src/components/LandingPageInstallInstructions.res b/apps/docs/src/components/LandingPageInstallInstructions.res index f3e4d0562..45bc0cf61 100644 --- a/apps/docs/src/components/LandingPageInstallInstructions.res +++ b/apps/docs/src/components/LandingPageInstallInstructions.res @@ -8,18 +8,20 @@ let copyBox = text => { } @react.component -let make = (~className="") => { -
-

{React.string("Quick Install")}

-
- {React.string( - "You can quickly add ReScript to your existing JavaScript codebase via npm / yarn:", - )} +let make = + @directive("'use memo'") + (~className="") => { +
+

{React.string("Quick Install")}

+
+ {React.string( + "You can quickly add ReScript to your existing JavaScript codebase via npm / yarn:", + )} +
+ {copyBox("npm install rescript")} +
+ {React.string("Or generate a new project from the official template with npx:")} +
+ {copyBox("npx create-rescript-app")}
- {copyBox("npm install rescript")} -
- {React.string("Or generate a new project from the official template with npx:")} -
- {copyBox("npx create-rescript-app")} -
-} + } diff --git a/apps/docs/src/components/LandingPageInstallInstructions.resi b/apps/docs/src/components/LandingPageInstallInstructions.resi index 193c81cf3..92fce9cea 100644 --- a/apps/docs/src/components/LandingPageInstallInstructions.resi +++ b/apps/docs/src/components/LandingPageInstallInstructions.resi @@ -1,2 +1,3 @@ +/** Opt in to React Compiler for the homepage browser build. */ @react.component let make: (~className: string=?) => React.element diff --git a/apps/docs/src/components/LandingPageIntro.res b/apps/docs/src/components/LandingPageIntro.res index 17e368c4d..01767bd75 100644 --- a/apps/docs/src/components/LandingPageIntro.res +++ b/apps/docs/src/components/LandingPageIntro.res @@ -1,27 +1,29 @@ @react.component -let make = () => { -
-
-

- {React.string("JavaScript Made Simple for Humans and AI")} -

-

- {React.string(`Types > Vibes`)} -

-

- {React.string(`ReScript is a strongly typed language that compiles to clean, +let make = + @directive("'use memo'") + () => { +

+
+

+ {React.string("JavaScript Made Simple for Humans and AI")} +

+

+ {React.string(`Types > Vibes`)} +

+

+ {React.string(`ReScript is a strongly typed language that compiles to clean, efficient JavaScript that humans and AI tools can read and understand.`)} -

-

- {React.string(`Its fast compiler and static type system keep feedback loops tight, +

+

+ {React.string(`Its fast compiler and static type system keep feedback loops tight, so you can move quickly with AI assistance while maintaining confidence as your codebase grows.`)} -

- - - -
-
-} +

+ + + +
+
+ } diff --git a/apps/docs/src/components/LandingPageIntro.resi b/apps/docs/src/components/LandingPageIntro.resi index 26c2771b5..ba814dcb8 100644 --- a/apps/docs/src/components/LandingPageIntro.resi +++ b/apps/docs/src/components/LandingPageIntro.resi @@ -1,3 +1,3 @@ -/** Prefetch installation content only after hover or focus intent. */ +/** Prefetch on intent and opt in to React Compiler for the homepage browser build. */ @react.component let make: unit => React.element diff --git a/apps/docs/src/components/LandingPageTrustedBy.res b/apps/docs/src/components/LandingPageTrustedBy.res index 0ee3d8d91..3041a1134 100644 --- a/apps/docs/src/components/LandingPageTrustedBy.res +++ b/apps/docs/src/components/LandingPageTrustedBy.res @@ -1,43 +1,45 @@ @react.component -let make = () => { - let ourUsersSourcePath = "apps/docs/src/data/OurUsers.res" +let make = + @directive("'use memo'") + () => { + let ourUsersSourcePath = "apps/docs/src/data/OurUsers.res" -
-

- {React.string("Trusted by our users")} -

-
- {OurUsers.companies - ->Array.map(company => - switch company { - | Logo({name, path, url, width, height}) => - - nameInt.toString} - height={height->Int.toString} - loading=#lazy - /> - - } - ) - ->React.array} -
- - - - -
-} +
+

+ {React.string("Trusted by our users")} +

+
+ {OurUsers.companies + ->Array.map(company => + switch company { + | Logo({name, path, url, width, height}) => + + nameInt.toString} + height={height->Int.toString} + loading=#lazy + /> + + } + ) + ->React.array} +
+ + + + +
+ } diff --git a/apps/docs/src/components/LandingPageTrustedBy.resi b/apps/docs/src/components/LandingPageTrustedBy.resi index e0905fb23..6a5ddde2e 100644 --- a/apps/docs/src/components/LandingPageTrustedBy.resi +++ b/apps/docs/src/components/LandingPageTrustedBy.resi @@ -1,3 +1,3 @@ -/** Company logos retain their intrinsic aspect ratio at the existing display height. */ +/** Retain intrinsic logo ratios and opt in to React Compiler for the browser build. */ @react.component let make: unit => React.element diff --git a/apps/docs/src/components/NavbarPrimary.res b/apps/docs/src/components/NavbarPrimary.res index 5f586b3f5..a8e5d1c6a 100644 --- a/apps/docs/src/components/NavbarPrimary.res +++ b/apps/docs/src/components/NavbarPrimary.res @@ -9,119 +9,128 @@ let isActive = (~url, ~pathname: Path.t) => { module LeftContent = { @react.component - let make = () => { - let {pathname} = useLocation() -
- - ReScript Home - ReScript Home - - - {React.string("Docs")} - - - {React.string("Playground")} - - - {React.string("Blog")} - - { + let {pathname} = useLocation() +
- {React.string("Community")} - -
- } + + ReScript Home + ReScript Home + + + {React.string("Docs")} + + + {React.string("Playground")} + + + {React.string("Blog")} + + + {React.string("Community")} + +
+ } } module RightContent = { @react.component - let make = () => { - let iconClasses = "w-6 h-6 opacity-50 hover:opacity-100" - let linkClasses = "hidden md:block" -
- - - - - - - - - - - - { + let iconClasses = "w-6 h-6 opacity-50 hover:opacity-100" + let linkClasses = "hidden md:block" + - } + + + + + + + + + + + + + + +
+ } } @react.component -let make = () => { - let scrollDirection = Hooks.useScrollDirection(~topMargin=64, ~threshold=32) +let make = + @directive("'use memo'") + () => { + let scrollDirection = Hooks.useScrollDirection(~topMargin=64, ~threshold=32) - let navbarClasses = switch scrollDirection { - | Up(_) => "translate-y-0" - | Down(_) => "-translate-y-full md:translate-y-0" - } + let navbarClasses = switch scrollDirection { + | Up(_) => "translate-y-0" + | Down(_) => "-translate-y-full md:translate-y-0" + } - <> - + + + } diff --git a/apps/docs/src/components/NavbarPrimary.resi b/apps/docs/src/components/NavbarPrimary.resi index 1ca44ce26..6b2d7a887 100644 --- a/apps/docs/src/components/NavbarPrimary.resi +++ b/apps/docs/src/components/NavbarPrimary.resi @@ -1,2 +1,3 @@ +/** Shared primary navigation opts in to React Compiler for client builds. */ @react.component let make: unit => React.element diff --git a/apps/docs/vite-react-compiler.mjs b/apps/docs/vite-react-compiler.mjs new file mode 100644 index 000000000..17b320e61 --- /dev/null +++ b/apps/docs/vite-react-compiler.mjs @@ -0,0 +1,18 @@ +import babel from "@rolldown/plugin-babel"; +import { reactCompilerPreset } from "@vitejs/plugin-react"; + +export function homepageCompilerOptions() { + return { + include: + /[/\\]apps[/\\]docs[/\\](?:app[/\\]routes|src[/\\]components)[/\\](?:LandingPage[^/\\]*|NavbarPrimary)\.jsx(?:$|\?)/, + exclude: [/[/\\]node_modules[/\\]/, /^\0rolldown\/runtime\.js$/], + presets: [ + // Keep the preset's client-only guard and React 19 runtime optimization. + reactCompilerPreset({ compilationMode: "annotation", target: "19" }), + ], + }; +} + +export function homepageReactCompiler() { + return babel(homepageCompilerOptions()); +} diff --git a/apps/docs/vite.config.mjs b/apps/docs/vite.config.mjs index 9ad4544bb..5a2e93a36 100644 --- a/apps/docs/vite.config.mjs +++ b/apps/docs/vite.config.mjs @@ -6,6 +6,7 @@ import { defineConfig } from "vite"; import devtoolsJson from "vite-plugin-devtools-json"; import env from "vite-plugin-env-compatible"; import pageReload from "vite-plugin-page-reload"; +import { homepageReactCompiler } from "./vite-react-compiler.mjs"; const excludedFiles = ["lib/**", "**/*.res", "**/*.resi"]; @@ -24,6 +25,7 @@ export default defineConfig({ include: ["**/*.mjs"], exclude: excludedFiles, }), + homepageReactCompiler(), // this is to make it so babel doesn't break when trying to acess process.env in the client env({ prefix: "PUBLIC_" }), // adds dev scripts for browser devtools diff --git a/apps/docs/vitest.config.mjs b/apps/docs/vitest.config.mjs index 427123952..893c7048f 100644 --- a/apps/docs/vitest.config.mjs +++ b/apps/docs/vitest.config.mjs @@ -3,6 +3,7 @@ import { playwright } from "@vitest/browser-playwright"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; import { responsiveImage } from "@responsive-image/vite-plugin"; +import { homepageReactCompiler } from "./vite-react-compiler.mjs"; const isUpdatingSnapshots = process.argv.some( (arg) => arg === "-u" || arg === "--update" || arg.startsWith("--update="), @@ -35,7 +36,7 @@ const setupDeps = [ export default defineConfig({ envDir: "../..", - plugins: [responsiveImage(), react(), tailwindcss()], + plugins: [responsiveImage(), react(), homepageReactCompiler(), tailwindcss()], optimizeDeps: { include: setupDeps, }, diff --git a/yarn.lock b/yarn.lock index ccb3767be..d9df19e2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -224,6 +224,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.26.0": + version: 7.29.8 + resolution: "@babel/types@npm:7.29.8" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/be7c279f0abf2a086c633e21b49c7ca80275d05283cc5a268b67a708c9914bd0c944f1422b3eb3cb37682a2af5d560abf520ccf9b01b53ecbfe6b71fbc3fdde6 + languageName: node + linkType: hard + "@blazediff/core@npm:1.10.0": version: 1.10.0 resolution: "@blazediff/core@npm:1.10.0" @@ -1928,6 +1938,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rescript-lang/docs@workspace:apps/docs" dependencies: + "@babel/core": "npm:^7.29.7" "@cloudflare/pages-plugin-vercel-og": "npm:^0.1.2" "@docsearch/react": "npm:^4.6.2" "@headlessui/react": "npm:^2.2.9" @@ -1941,11 +1952,13 @@ __metadata: "@rescript/webapi": "npm:0.1.0-experimental-29db5f4" "@responsive-image/core": "npm:2.1.0" "@responsive-image/vite-plugin": "npm:3.0.1" + "@rolldown/plugin-babel": "npm:^0.2.4" "@tailwindcss/vite": "npm:^4.3.0" "@types/react": "npm:^19.2.14" "@vitejs/plugin-react": "npm:^6.0.1" "@vitest/browser-playwright": "npm:^5.0.0" auto-image-converter: "npm:^2.2.0" + babel-plugin-react-compiler: "npm:^1.0.0" chokidar: "npm:^4.0.3" cypress: "npm:^15.13.1" cypress-real-events: "npm:^1.15.1" @@ -1976,6 +1989,7 @@ __metadata: remark-gfm: "npm:^4.0.1" remark-validate-links: "npm:^13.1.0" rescript: "npm:^12.2.0" + rolldown: "npm:1.2.8" search-insights: "npm:^2.17.3" tailwindcss: "npm:^4" tinyglobby: "npm:^0.2.15" @@ -2293,6 +2307,28 @@ __metadata: languageName: node linkType: hard +"@rolldown/plugin-babel@npm:^0.2.4": + version: 0.2.4 + resolution: "@rolldown/plugin-babel@npm:0.2.4" + dependencies: + picomatch: "npm:^4.0.7" + peerDependencies: + "@babel/core": ^7.29.0 || ^8.0.0-rc.1 + "@babel/plugin-transform-runtime": ^7.29.0 || ^8.0.0-rc.1 + "@babel/runtime": ^7.27.0 || ^8.0.0-rc.1 + rolldown: ^1.0.0-rc.5 + vite: ^8.0.0 + peerDependenciesMeta: + "@babel/plugin-transform-runtime": + optional: true + "@babel/runtime": + optional: true + vite: + optional: true + checksum: 10c0/1283804c979f32c24adf3e80177ff3ebb7f8db749660235ed0a511cd4e1a3c129bea4209dee5f9ee67b1709a6efc65986349ca10ff2f5557c86623d1eebd7b42 + languageName: node + linkType: hard + "@rolldown/pluginutils@npm:1.0.0-rc.7": version: 1.0.0-rc.7 resolution: "@rolldown/pluginutils@npm:1.0.0-rc.7" @@ -3220,6 +3256,15 @@ __metadata: languageName: node linkType: hard +"babel-plugin-react-compiler@npm:^1.0.0": + version: 1.0.0 + resolution: "babel-plugin-react-compiler@npm:1.0.0" + dependencies: + "@babel/types": "npm:^7.26.0" + checksum: 10c0/9406267ada8d7dbdfe8906b40ecadb816a5f4cee2922bee23f7729293b369624ee135b5a9b0f263851c263c9787522ac5d97016c9a2b82d1668300e42b18aff8 + languageName: node + linkType: hard + "bail@npm:^2.0.0": version: 2.0.2 resolution: "bail@npm:2.0.2" @@ -9390,7 +9435,7 @@ __metadata: languageName: node linkType: hard -"rolldown@npm:~1.2.6": +"rolldown@npm:1.2.8, rolldown@npm:~1.2.6": version: 1.2.8 resolution: "rolldown@npm:1.2.8" dependencies: