Backend / staging cleanup, performance substrate, and create-flow polish #60

Merged
an.di merged 16 commits from adilallo/Backend/StagingCleanup into main 2026-05-26 15:11:47 +00:00
8 changed files with 228 additions and 78 deletions
Showing only changes of commit edda28f95b - Show all commits
+7 -5
View File
@@ -1,20 +1,22 @@
import type { ReactNode } from "react"; import { Suspense, type ReactNode } from "react";
import ConditionalNavigation from "../components/navigation/ConditionalNavigation"; import ConditionalNavigation from "../components/navigation/ConditionalNavigation";
import { MessagesProvider } from "../contexts/MessagesContext"; import { MessagesProvider } from "../contexts/MessagesContext";
import { AuthModalProvider } from "../contexts/AuthModalContext"; import { AuthModalProvider } from "../contexts/AuthModalContext";
import messages from "../../messages/en/index"; import messages from "../../messages/en/index";
// Reads the session for admin chrome (matches the HttpOnly cookie on first // `force-dynamic` removed in favor of `experimental.cacheComponents` (Next 16).
// HTML response). Scoped here so `(marketing)` can render statically. // See `(app)/layout.tsx` for the matching `<Suspense fallback={null}>` rationale
export const dynamic = "force-dynamic"; // — the fallback can't access `usePathname()` since it sits in the static shell.
//
// Operator/admin dashboards (e.g. `/monitor`) intentionally render without the // Operator/admin dashboards (e.g. `/monitor`) intentionally render without the
// public marketing footer. Auth/access is enforced upstream. // public marketing footer. Auth/access is enforced upstream.
export default function AdminLayout({ children }: { children: ReactNode }) { export default function AdminLayout({ children }: { children: ReactNode }) {
return ( return (
<MessagesProvider messages={messages}> <MessagesProvider messages={messages}>
<AuthModalProvider> <AuthModalProvider>
<Suspense fallback={null}>
<ConditionalNavigation /> <ConditionalNavigation />
</Suspense>
<main className="flex-1">{children}</main> <main className="flex-1">{children}</main>
</AuthModalProvider> </AuthModalProvider>
</MessagesProvider> </MessagesProvider>
+13 -7
View File
@@ -1,15 +1,19 @@
import type { ReactNode } from "react"; import { Suspense, type ReactNode } from "react";
import ConditionalNavigation from "../components/navigation/ConditionalNavigation"; import ConditionalNavigation from "../components/navigation/ConditionalNavigation";
import { MessagesProvider } from "../contexts/MessagesContext"; import { MessagesProvider } from "../contexts/MessagesContext";
import { AuthModalProvider } from "../contexts/AuthModalContext"; import { AuthModalProvider } from "../contexts/AuthModalContext";
import messages from "../../messages/en/index"; import messages from "../../messages/en/index";
// Reads `cr_session` via Server Components on every navigation so the header // `force-dynamic` removed in favor of `experimental.cacheComponents` (Next 16).
// matches the HttpOnly cookie on the first HTML response (no "Log in" flash // `ConditionalNavigation` reads `cr_session` server-side (and `usePathname()`
// before `/api/auth/session`). Scoped here instead of the root layout so // transitively in `ConditionalNavigationClient`) — both are uncached, so it
// `(marketing)` can render statically. // lives behind a `<Suspense>` boundary so the rest of the layout stays in the
export const dynamic = "force-dynamic"; // static shell while the session/pathname-aware nav streams in. The fallback
// is `null` because any non-null fallback would also need to live in the
// static shell, and the nav's chromeless decision depends on the pathname
// (e.g. `/create/*` and `/login` render no top-nav). Brief blank-nav while
// the dynamic island resolves is acceptable on signed-in product surfaces.
//
// Signed-in product surfaces (`/create/*`, `/login`) run without the marketing // Signed-in product surfaces (`/create/*`, `/login`) run without the marketing
// footer. `/profile` adds it via `profile/layout.tsx`. Per-route chrome (e.g. // footer. `/profile` adds it via `profile/layout.tsx`. Per-route chrome (e.g.
// CreateFlow) is composed in nested layouts. // CreateFlow) is composed in nested layouts.
@@ -17,7 +21,9 @@ export default function AppLayout({ children }: { children: ReactNode }) {
return ( return (
<MessagesProvider messages={messages}> <MessagesProvider messages={messages}>
<AuthModalProvider> <AuthModalProvider>
<Suspense fallback={null}>
<ConditionalNavigation /> <ConditionalNavigation />
</Suspense>
<main className="flex-1">{children}</main> <main className="flex-1">{children}</main>
</AuthModalProvider> </AuthModalProvider>
</MessagesProvider> </MessagesProvider>
+8 -1
View File
@@ -1,5 +1,5 @@
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import type { ReactNode } from "react"; import { Suspense, type ReactNode } from "react";
import MarketingNavigation from "../components/navigation/MarketingNavigation"; import MarketingNavigation from "../components/navigation/MarketingNavigation";
import { MessagesProvider } from "../contexts/MessagesContext"; import { MessagesProvider } from "../contexts/MessagesContext";
import { AuthModalProvider } from "../contexts/AuthModalContext"; import { AuthModalProvider } from "../contexts/AuthModalContext";
@@ -19,7 +19,14 @@ export default function MarketingLayout({ children }: { children: ReactNode }) {
return ( return (
<MessagesProvider messages={marketingMessages}> <MessagesProvider messages={marketingMessages}>
<AuthModalProvider> <AuthModalProvider>
{/*
* MarketingNavigation reads `usePathname()` to decide chromeless paths
* (uncached data under `cacheComponents`). Suspense lets the static
* shell prerender; the nav streams in with the correct visibility.
*/}
<Suspense fallback={null}>
<MarketingNavigation /> <MarketingNavigation />
</Suspense>
<main className="flex-1">{children}</main> <main className="flex-1">{children}</main>
<Footer /> <Footer />
</AuthModalProvider> </AuthModalProvider>
+107 -59
View File
@@ -1,18 +1,19 @@
# Next 16 substrate evaluation (Phase 3) # Next 16 substrate evaluation (Phase 3)
Evaluation of `experimental.cacheComponents` (formerly `experimental.ppr`) Evaluation of `experimental.cacheComponents` (formerly `experimental.ppr`)
and React Compiler against this repo on Next.js 16.2.6. Performed as a and React Compiler against this repo on Next.js 16.2.6. Originally written
canary build pass without committing either flag to `main`. as a canary report when both flags were deferred; updated when both shipped
as follow-up work (see "Outcome" sections below).
## TL;DR ## TL;DR
| Flag | Recommendation | Why | | Flag | Recommendation | Status |
| --- | --- | --- | | --- | --- | --- |
| `cacheComponents` (PPR successor) | **Defer** — requires a follow-up refactor before it can ship | Renamed from `ppr` in Next 16; now a boolean global toggle, no per-route `experimental_ppr` opt-in. Requires removing `force-dynamic` from `(app)` and `(admin)` layouts and re-expressing session-aware dynamism via Suspense + cache primitives. | | `cacheComponents` (PPR successor) | **Ship** | **Shipped.** `force-dynamic` removed from `(app)` and `(admin)` layouts; `<ConditionalNavigation />` (and `<MarketingNavigation />`) wrapped in `<Suspense fallback={null}>`. `(app)`/`(admin)` routes are now `◐ Partial Prerender` instead of `ƒ Dynamic`. `/` static shell dropped from 45 KB → 11.7 KB gzipped. |
| React Compiler | **Defer** — config surface moved + missing dep | Moved out of `experimental` to the top-level `reactCompiler` key in Next 16. Requires installing `babel-plugin-react-compiler`. No blocking codebase incompatibilities found in the canary surface, but the install + eslint plugin setup is its own follow-up task. | | React Compiler | **Ship (annotation mode)** | **Shipped (plumbing only).** `babel-plugin-react-compiler` + `eslint-plugin-react-compiler` installed. `reactCompiler: { compilationMode: "annotation" }` enabled in `next.config.mjs`. ESLint rule wired in at "warn" — found 31 latent warnings across 8 files (none introduced by this change). Migrating containers to `"use memo"` is a future task. |
Neither flag was shippable as a pure config flip in this audit. The findings Both flags now ship in `main`. The findings below describe what changed in
below describe what changed in Next 16 and the work each would require. Next 16, what work each required, and the outcomes.
## Repo baseline (Next 16.2.6, Turbopack, no experimental flags) ## Repo baseline (Next 16.2.6, Turbopack, no experimental flags)
@@ -29,7 +30,7 @@ Note: Next 16 with Turbopack no longer prints per-route first-load JS sizes
in the build summary. Bundle analyzer (`ANALYZE=true`) is the canonical in the build summary. Bundle analyzer (`ANALYZE=true`) is the canonical
source for size data — see Phase 4a. source for size data — see Phase 4a.
## 3a. `cacheComponents` (PPR) — DEFER ## 3a. `cacheComponents` (PPR) — SHIPPED
### What changed in Next 16 ### What changed in Next 16
@@ -67,30 +68,46 @@ session-aware chrome (set in Phase 4b of the prior plan). `cacheComponents`
requires expressing that dynamism via `<Suspense>` boundaries plus requires expressing that dynamism via `<Suspense>` boundaries plus
`unstable_noStore()`/`unstable_cache()` instead of route-segment `dynamic`. `unstable_noStore()`/`unstable_cache()` instead of route-segment `dynamic`.
### Estimated work to ship ### Work performed
1. Refactor [app/(app)/layout.tsx](../../app/(app)/layout.tsx) and 1. Removed `export const dynamic = "force-dynamic"` from
[app/(admin)/layout.tsx](../../app/(admin)/layout.tsx) so the [app/(app)/layout.tsx](../../app/(app)/layout.tsx) and
`ConditionalNavigation` session fetch sits inside a `<Suspense>` boundary [app/(admin)/layout.tsx](../../app/(admin)/layout.tsx).
with a fallback that matches the generic chrome. 2. Wrapped `<ConditionalNavigation />` (server component reading
2. Mark the session-reading components with `unstable_noStore()` (or the `getNavAuthSignedIn()``cookies()`) in `<Suspense fallback={null}>` in
stable equivalent in Next 16) so they opt out of the static cache. both layouts.
3. Verify the existing static routes (`/`, `/about`, `/blog`, etc.) still 3. Same change for `<MarketingNavigation />` in
prerender; add `<Suspense>` boundaries around any future dynamic islands. [app/(marketing)/layout.tsx](../../app/(marketing)/layout.tsx) — the
4. Confirm `(marketing)` routes still serve from CDN with the static shell marketing nav reads `usePathname()` (uncached per request) and would
while the personalized nav island streams. otherwise block the static shell at routes like `/rules/[id]`.
4. Enabled `experimental.cacheComponents: true` in
[next.config.mjs](../../next.config.mjs).
This is the natural next step after Phase 4b made marketing static, but `unstable_noStore()` already sits inside `getNavAuthSignedIn()`; no
it's not a config-only change. Ticket separately. additional cache primitives were needed.
### Verification (when shipping) ### Why `fallback={null}` and not a placeholder
- `(marketing)` routes still appear as `○ Static` in build output. Any non-null fallback would also need to live in the static shell. The
- `(app)`/`(admin)` routes' static shell prerenders; the personalized nav existing `ConditionalNavigationClient` reads `usePathname()` to decide
streams (visible in `curl` of the HTML — partial shell first, then nav). chromeless paths (`/create/*`, `/login`), which is uncached data —
- TTFB on `(marketing)` unchanged or improved. disallowed in the static shell under `cacheComponents`. A truly static
placeholder is possible but would cause layout shift on routes that
ultimately render no nav. Trade-off accepted: brief blank-nav while the
dynamic island streams in.
## 3b. React Compiler — DEFER ### Outcome (measured against `npx next build`)
| Route group | Before | After |
| --- | --- | --- |
| `/`, `/about`, `/blog`, `/components-preview`, `/how-it-works`, `/learn` | `○ Static` | `○ Static` |
| `/create`, `/create/[screenId]`, `/profile`, `/monitor`, `/login`, `/rules/[id]` | `ƒ Dynamic` | `◐ Partial Prerender` |
| `/templates`, `/use-cases`, `/use-cases/[slug]`, `/blog/[slug]` | `○ Static` / `◐ SSG` | `◐ Partial Prerender` |
`/` static shell: 45 KB gzipped → 11.7 KB gzipped (74% reduction). All
196 test files / 1251 tests pass.
## 3b. React Compiler — SHIPPED (annotation mode, plumbing only)
### What changed in Next 16 ### What changed in Next 16
@@ -110,51 +127,82 @@ to the next package. Is babel-plugin-react-compiler installed in your
node_modules directory? node_modules directory?
``` ```
### Estimated work to ship ### Work performed
1. `npm install --save-dev babel-plugin-react-compiler eslint-plugin-react-compiler`. 1. `npm install --save-dev babel-plugin-react-compiler eslint-plugin-react-compiler`.
2. Add `reactCompiler: { compilationMode: "annotation" }` to `next.config.mjs` 2. Added top-level `reactCompiler: { compilationMode: "annotation" }` to
(top-level, not under `experimental`). [next.config.mjs](../../next.config.mjs).
3. Enable `eslint-plugin-react-compiler` and run it against the repo to 3. Wired `react-compiler/react-compiler` as `"warn"` in
surface components that would bail (refs mutated during render, reads [eslint.config.mjs](../../eslint.config.mjs) (both JS and TS plugin blocks).
of non-reactive globals inline, etc.). 4. No `"use memo"` directives added in this pass — the goal is plumbing,
4. Incrementally add `"use memo"` directives to high-render-frequency not migration. The compiler is a no-op until components opt in.
containers (`CreateFlowProvider`, `AuthModalProvider`, list-heavy views).
5. Once stable, flip `compilationMode: "all"` and remove hand-written
`useMemo`/`useCallback` where the compiler subsumes them.
### Why annotation mode first ### Why annotation mode first (unchanged from original plan)
We have many hand-rolled memoized containers. The risk of `compilationMode: "all"` We have many hand-rolled memoized containers. The risk of `compilationMode: "all"`
on day one is that the compiler bails on a critical component in a way that on day one is that the compiler bails on a critical component in a way that
changes render counts. Annotation mode lets us migrate one component at a changes render counts. Annotation mode lets us migrate one component at a
time with eslint enforcement. time with eslint enforcement.
### Verification (when shipping) ### ESLint audit results
- Bundle size before/after `next build` with the runtime added. `npx eslint app lib` after wiring the rule found **31 react-compiler warnings
- Test suite green (`npx vitest run` — 196 files / 1251 tests today). across 8 files**:
- Component render counts unchanged or reduced on key surfaces (use the
React DevTools profiler on `/create/informational` and `/`). | Category | Count |
| --- | --- |
| "Hooks may not be referenced as normal values" (passing hook references as values) | 25 |
| "Writing to a variable defined outside a component or hook" (module-level mutation) | 2 |
| "Hooks must always be called in a consistent order" (conditional hooks) | 2 |
| "React Compiler skipped optimizing" (file has React rules disabled) | 2 |
Files flagged:
- `app/(app)/create/context/CreateFlowContext.tsx`
- `app/(app)/create/hooks/useCompletedRuleShareExport.ts`
- `app/(marketing)/use-cases/[slug]/page.tsx`
- `app/(marketing)/use-cases/page.tsx`
- `app/(marketing-case-study)/use-cases/[slug]/rule/_components/useUseCaseCompletedRuleActions.ts`
- `app/(marketing-case-study)/use-cases/[slug]/rule/page.tsx`
- `app/components/controls/SelectInput/SelectInput.container.tsx`
- `app/components/sections/RelatedArticles/RelatedArticles.view.tsx`
All warnings are latent (not introduced by this change). The compiler runs
in annotation mode, so these files are not affected at runtime until they
opt in. Not fixed in this commit — these are the migration targets to
address before flipping to `compilationMode: "all"`.
### Verification
- Test suite green: 196 files / 1251 tests pass.
- Build green: `npx next build` clean with the new config.
- TSC clean.
- Bundle size delta minimal — no `"use memo"` annotations means no compiler
runtime calls are emitted in user code yet.
## Impact on Phase 4 (MessagesProvider) ## Impact on Phase 4 (MessagesProvider)
If we later ship `cacheComponents`, the MessagesProvider refactor's win Phase 4 (route-scoped `MessagesProvider`) shipped before this work. With
shrinks meaningfully: the messages dictionary lives in the static shell of `cacheComponents` now enabled, the marketing routes' messages dictionary
every route, and only the dynamic island re-fetches. The static prerender lives in the static shell — cached at the CDN with no per-request cost.
output is already cacheable at the CDN. So Phase 4 should be re-evaluated The route-scoping is still a win for the dynamic islands' RSC payload, but
**after** the `cacheComponents` work lands, not before. the static-shell win is now structural, not bundle-size dependent.
If we don't ship `cacheComponents`, Phase 4's bundle-size measurement ## Follow-up work
(Phase 4a) is still the right gate — measure first, refactor only if the
data justifies it.
## What to do now `cacheComponents` and React Compiler annotation mode now ship. Remaining
work (file separately when scheduled):
- Skip both flags for this performance follow-ups plan. 1. **Migrate top React Compiler bail sites.** Fix the 31 latent warnings
- File two follow-up tickets: (especially the hook-reference patterns in `CreateFlowContext` and the
1. "Enable `cacheComponents`: refactor `(app)`/`(admin)` layouts to conditional hooks in `SelectInput.container`) so those files become
Suspense + cache primitives, remove `force-dynamic` from route segments." compiler-eligible.
2. "Adopt React Compiler in annotation mode: install plugin, enable 2. **Annotate high-render containers with `"use memo"`.** Targets:
eslint rule, migrate top containers." `CreateFlowProvider`, `AuthModalProvider`, list-heavy views. Measure
- Proceed with Phase 4a (measure) and let the data drive Phase 4b. render counts before/after with React DevTools profiler.
3. **Flip React Compiler from `annotation` to `all`** once the bail list is
green and a critical mass of containers are annotated. Remove
hand-written `useMemo`/`useCallback` the compiler subsumes.
4. **Audit `<Suspense fallback={null}>` UX on (app)/(admin) routes.** If
blank-nav flash becomes noticeable on slow connections, replace the
`null` fallback with a static placeholder that doesn't read `usePathname`
(e.g. a `min-h` div sized to the nav).
+9
View File
@@ -9,6 +9,7 @@ import nextPlugin from "@next/eslint-plugin-next";
import globals from "globals"; import globals from "globals";
import react from "eslint-plugin-react"; import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks"; import reactHooks from "eslint-plugin-react-hooks";
import reactCompiler from "eslint-plugin-react-compiler";
const eslintConfig = [ const eslintConfig = [
// Base JavaScript recommended rules // Base JavaScript recommended rules
@@ -51,6 +52,7 @@ const eslintConfig = [
plugins: { plugins: {
react, react,
"react-hooks": reactHooks, "react-hooks": reactHooks,
"react-compiler": reactCompiler,
}, },
settings: { settings: {
react: { react: {
@@ -62,6 +64,9 @@ const eslintConfig = [
...reactHooks.configs.recommended.rules, ...reactHooks.configs.recommended.rules,
"react/react-in-jsx-scope": "off", // React 19 doesn't require React import "react/react-in-jsx-scope": "off", // React 19 doesn't require React import
"react/prop-types": "off", // Using TypeScript for prop validation "react/prop-types": "off", // Using TypeScript for prop validation
// Surface code the React Compiler would bail on. We run in annotation
// mode, so the rule is "warn" — informational, not a build blocker.
"react-compiler/react-compiler": "warn",
}, },
}, },
// TypeScript files configuration // TypeScript files configuration
@@ -90,6 +95,7 @@ const eslintConfig = [
"@next/next": nextPlugin, "@next/next": nextPlugin,
react, react,
"react-hooks": reactHooks, "react-hooks": reactHooks,
"react-compiler": reactCompiler,
}, },
settings: { settings: {
react: { react: {
@@ -101,6 +107,9 @@ const eslintConfig = [
...reactHooks.configs.recommended.rules, ...reactHooks.configs.recommended.rules,
"react/react-in-jsx-scope": "off", // React 19 doesn't require React import "react/react-in-jsx-scope": "off", // React 19 doesn't require React import
"react/prop-types": "off", // Using TypeScript for prop validation "react/prop-types": "off", // Using TypeScript for prop validation
// Surface code the React Compiler would bail on. We run in annotation
// mode, so the rule is "warn" — informational, not a build blocker.
"react-compiler/react-compiler": "warn",
"@typescript-eslint/no-unused-vars": [ "@typescript-eslint/no-unused-vars": [
"error", "error",
{ {
+15 -3
View File
@@ -5,6 +5,14 @@ import createMDX from "@next/mdx";
const nextConfig = { const nextConfig = {
output: "standalone", output: "standalone",
serverExternalPackages: ["@prisma/client"], serverExternalPackages: ["@prisma/client"],
// React Compiler — annotation mode: opt-in via the `"use memo"` directive at
// the top of a component/hook. With no annotations in the codebase yet, this
// is plumbing only (no behavior change). Migrate hand-written `useMemo`/
// `useCallback` containers incrementally and rely on `eslint-plugin-react-
// compiler` to surface any code that the compiler bails on.
reactCompiler: {
compilationMode: "annotation",
},
/** /**
* `next dev --turbopack` does not use `webpack()`; without this, `.svg` * `next dev --turbopack` does not use `webpack()`; without this, `.svg`
* imports resolve as asset URLs and {@link app/components/asset/icon/Icon.tsx} * imports resolve as asset URLs and {@link app/components/asset/icon/Icon.tsx}
@@ -23,10 +31,14 @@ const nextConfig = {
experimental: { experimental: {
optimizeCss: true, optimizeCss: true,
optimizePackageImports: ["react", "react-dom"], optimizePackageImports: ["react", "react-dom"],
// Cache Components (the Next 16 successor to `experimental.ppr`) — components
// without `"use cache"` are dynamic by default, and any cookies/headers
// access outside a `<Suspense>` boundary becomes a build-time error. The
// `(app)` and `(admin)` layouts wrap `<ConditionalNavigation />` in
// `<Suspense>` so the static shell prerenders while the session-aware nav
// streams in. Replaces the prior `force-dynamic` route-segment exports.
cacheComponents: true,
}, },
// Phase 3 canary stub (not enabled): React Compiler probe surfaces a missing
// `babel-plugin-react-compiler` dep — Next 16 also moved this top-level out
// of `experimental`. See `docs/perf/next16-eval.md` for evaluation results.
// Compression // Compression
compress: true, compress: true,
// Image optimization // Image optimization
+64
View File
@@ -45,8 +45,10 @@
"@typescript-eslint/parser": "^8.41.0", "@typescript-eslint/parser": "^8.41.0",
"@vitejs/plugin-react": "^5.0.2", "@vitejs/plugin-react": "^5.0.2",
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
"babel-plugin-react-compiler": "^1.0.0",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "^16.0.0", "eslint-config-next": "^16.0.0",
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"eslint-plugin-storybook": "^10.4.1", "eslint-plugin-storybook": "^10.4.1",
"globals": "^17.1.0", "globals": "^17.1.0",
"jest-axe": "^10.0.0", "jest-axe": "^10.0.0",
@@ -645,6 +647,24 @@
"@babel/core": "^7.0.0" "@babel/core": "^7.0.0"
} }
}, },
"node_modules/@babel/plugin-proposal-private-methods": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
"integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-proposal-private-property-in-object": { "node_modules/@babel/plugin-proposal-private-property-in-object": {
"version": "7.21.0-placeholder-for-preset-env.2", "version": "7.21.0-placeholder-for-preset-env.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
@@ -9164,6 +9184,16 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
} }
}, },
"node_modules/babel-plugin-react-compiler": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz",
"integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.0"
}
},
"node_modules/bail": { "node_modules/bail": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
@@ -12200,6 +12230,40 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
} }
}, },
"node_modules/eslint-plugin-react-compiler": {
"version": "19.1.0-rc.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.1.0-rc.2.tgz",
"integrity": "sha512-oKalwDGcD+RX9mf3NEO4zOoUMeLvjSvcbbEOpquzmzqEEM2MQdp7/FY/Hx9NzmUwFzH1W9SKTz5fihfMldpEYw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.24.4",
"@babel/parser": "^7.24.4",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"hermes-parser": "^0.25.1",
"zod": "^3.22.4",
"zod-validation-error": "^3.0.3"
},
"engines": {
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
},
"peerDependencies": {
"eslint": ">=7"
}
},
"node_modules/eslint-plugin-react-compiler/node_modules/zod-validation-error": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.4.tgz",
"integrity": "sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"zod": "^3.24.4"
}
},
"node_modules/eslint-plugin-react-hooks": { "node_modules/eslint-plugin-react-hooks": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
+2
View File
@@ -84,8 +84,10 @@
"@typescript-eslint/parser": "^8.41.0", "@typescript-eslint/parser": "^8.41.0",
"@vitejs/plugin-react": "^5.0.2", "@vitejs/plugin-react": "^5.0.2",
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
"babel-plugin-react-compiler": "^1.0.0",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "^16.0.0", "eslint-config-next": "^16.0.0",
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"eslint-plugin-storybook": "^10.4.1", "eslint-plugin-storybook": "^10.4.1",
"globals": "^17.1.0", "globals": "^17.1.0",
"jest-axe": "^10.0.0", "jest-axe": "^10.0.0",