๐ ์น ์ฑ ๋ฒ๋ค ์ฌ์ด์ฆ ์ต์ ํ (198kB โ 160kB)#416
๐ ์น ์ฑ ๋ฒ๋ค ์ฌ์ด์ฆ ์ต์ ํ (198kB โ 160kB)#416
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough
Estimated code review effort๐ฏ 3 (Moderate) | โฑ๏ธ ~35 minutes Suggested reviewers
๐ฅ Pre-merge checks | โ 2 | โ 1โ Failed checks (1 warning)
โ Passed checks (2 passed)
โ๏ธ Tip: You can configure your own custom pre-merge checks in the settings. โจ Finishing touches๐งช Generate unit tests (beta)
No actionable comments were generated in the recent review. ๐ Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
๐ค Fix all issues with AI agents
In `@apps/admin/src/components/features/scores/GpaScoreTable.tsx`:
- Around line 214-223: Fix the inconsistent indentation in the pagination block
inside GpaScoreTable: align the Array.from({ length: totalPages }, ...)
expression to the same indentation level as the surrounding <Button> siblings
and make the props on the Button (key, onClick, variant) vertically aligned
(ensure onClick is at the same indent level as key and variant). Locate the
pagination map using Array.from and the Button elements that call
handlePageChange(idx + 1) and reference page to set the variant, then reformat
those lines so indentation is consistent and symmetric.
In `@apps/admin/src/components/features/scores/LanguageScoreTable.tsx`:
- Around line 236-245: Fix the indentation in LanguageScoreTable's pagination
block to match GpaScoreTable: align the {Array.from({ length: totalPages }, (_,
idx) => ( ... ))} block with the surrounding <Button> container and ensure the
attributes inside each Button (key={idx + 1}, onClick={() =>
handlePageChange(idx + 1)}, variant={page === idx + 1 ? "default" :
"secondary"}) have consistent indentation levels (make onClick aligned with key
and variant). Locate the pagination code in LanguageScoreTable (references:
totalPages, page, handlePageChange) and reformat it so its JSX indentation
mirrors the GpaScoreTable pagination section.
In `@apps/admin/src/routes/auth/login.tsx`:
- Around line 56-86: The JSX indentation inside the login form is inconsistent:
ensure each <div className="space-y-1"> is indented as a child of the
surrounding <form>, align all props of the Input components (id, type,
placeholder, value, onChange, disabled, required, className) at the same
indentation level, and align the closing `/>` for each <Input> and the closing
`</div>` tags to match their opening tags; after fixing the whitespace, run
"biome format --write" to normalize formatting across the <Label>, <Input>, and
surrounding form markup (refer to the Input and Label usage in this login
component).
In `@apps/web/next.config.mjs`:
- Around line 97-98: Remove the unsupported transpileClientSDK option from
next.config.mjs (delete the transpileClientSDK: false line and its IE11-related
comment); reference the `@sentry/nextjs` change (v8+ removed this option) and, if
IE11/older-browser support is required, instruct to implement explicit
transpilation and polyfills via Next.js webpack/babel config instead of using
transpileClientSDK.
In `@apps/web/package.json`:
- Line 50: The dependency `@next/bundle-analyzer` in apps/web package.json is
pinned to ^16.1.6 which mismatches the project's Next.js version (^14.2.35);
update the "@next/bundle-analyzer" entry to a 14.x compatible version (e.g.
^14.0.0 or the specific v14.x that matches Next 14) so the `@next/`* major
versions align, then reinstall deps (npm/yarn/pnpm install) and run the
app/build to verify compatibility; locate the "@next/bundle-analyzer" entry in
package.json to make this change.
๐งน Nitpick comments (7)
apps/web/src/components/ui/FloatingUpBtn/hooks/useFloatingUpHandler.ts (1)
10-15:handleClick๋useCallback์ผ๋ก ๊ฐ์ธ๋ ๊ฒ์ ๊ณ ๋ คํด๋ณผ ์ ์์ต๋๋ค.
handleScroll์ ๋ฉ๋ชจ์ด์ ์ด์ ํ์ง๋ง,handleClick์ ๋งค ๋ ๋๋ง๋ค ์๋ก ์์ฑ๋ฉ๋๋ค. ์ด ํจ์๊ฐ ์์ ์ปดํฌ๋ํธ์ prop์ผ๋ก ์ ๋ฌ๋๋ค๋ฉด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ฅผ ์ ๋ฐํ ์ ์์ต๋๋ค. ๋ค๋ง ํ์ฌ ์ฌ์ฉ ํจํด์์ ๋ฌธ์ ๊ฐ ์๋ค๋ฉด ๋ฌด์ํ์ ๋ ๊ด์ฐฎ์ต๋๋ค.โป๏ธ ์ ํ์ ๊ฐ์ ์
- const handleClick = () => { - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - }; + const handleClick = useCallback(() => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }, []);apps/web/src/app/university/score/page.tsx (1)
6-6:loadingํด๋ฐฑ์ ์ถ๊ฐํ๋ฉด UX๊ฐ ๋ ์ข์์ง ์ ์์ด์.
ssr: false๋ก ๋์ ์ํฌํธํ๋ฉด JS ์ฒญํฌ๊ฐ ๋ก๋๋ ๋๊น์ง ํด๋น ์์ญ์ด ๋น์ด ๋ณด์ ๋๋ค. ๊ฐ๋จํ ์ค์ผ๋ ํค์ด๋ ์คํผ๋๋ฅผloading์ต์ ์ ๋ฃ์ด์ฃผ๋ฉด ์ฌ์ฉ์ ์ฒด๊ฐ์ด ์์ฐ์ค๋ฌ์์ง๋๋ค.๐ก ์์
-const ScoreScreen = dynamic(() => import("./ScoreScreen"), { ssr: false }); +const ScoreScreen = dynamic(() => import("./ScoreScreen"), { + ssr: false, + loading: () => <div className="flex h-40 items-center justify-center">๋ก๋ฉ ์ค...</div>, +});apps/web/src/app/university/search/page.tsx (1)
6-7: ๋์ ์ํฌํธ์loadingํด๋ฐฑ์ด ์์ด์.
SearchBar์SchoolSearchForm๋ชจ๋ssr: false๋ก ์ค์ ๋์ด ์์ด์, JS๊ฐ ๋ก๋๋๊ธฐ ์ ๊น์ง ํด๋น ์์ญ์ด ๋น์ด ๋ณด์ผ ์ ์์ต๋๋ค.dynamic์loading์ต์ ์ผ๋ก ๊ฐ๋จํ ์ค์ผ๋ ํค์ด๋ ์คํผ๋๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ์ฌ์ฉ์ ์ฒด๊ฐ ๋ก๋ฉ ์๋๊ฐ ๊ฐ์ ๋ฉ๋๋ค.next/dynamic loading option usage๐ก ์์
-const SearchBar = dynamic(() => import("./SearchBar"), { ssr: false }); -const SchoolSearchForm = dynamic(() => import("./PageContent"), { ssr: false }); +const SearchBar = dynamic(() => import("./SearchBar"), { + ssr: false, + loading: () => <div className="h-10 animate-pulse rounded-lg bg-gray-100" />, +}); +const SchoolSearchForm = dynamic(() => import("./PageContent"), { + ssr: false, + loading: () => <div className="h-40 animate-pulse rounded-lg bg-gray-100" />, +});apps/admin/src/lib/api/scores.ts (1)
30-44: ์ดํ ์ฑ์ ์กฐํ ๋ฐ ์์ API ๋ก์ง ํ์ธ ์๋ฃํฌ๋งทํ ์ ๋ฆฌ๊ฐ ์ ๋์์ต๋๋ค. ๋ค๋ง ํฅํ ํ์ ์์ ์ฑ ๊ฐ์ ์ ๊ณ ๋ คํด๋ณผ ์ ์๋ ๋ถ๋ถ์ด ์์ต๋๋ค.
Line 38์
as LanguageTestTypeํ์ ๋จ์ธ์ ์๋ฒ์์ ์์์น ๋ชปํ ๊ฐ์ด ์ฌ ๊ฒฝ์ฐ ๋ฐํ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํฌ ์ ์์ต๋๋ค. ํ์์ ํ์ ๊ฐ๋๋ฅผ ํตํ ๊ฒ์ฆ์ ์ถ๊ฐํ๋ ๊ฒ์ ๊ณ ๋ คํด๋ณด์ธ์.apps/web/src/lib/react-query/useMutationWithFailure.ts (2)
77-99:mutate/mutateAsync๊ฐ ๋งค ๋ ๋๋ง๋ค ์ ์ฐธ์กฐ๋ฅผ ์์ฑํฉ๋๋ค.
mutate์mutateAsync๊ฐuseCallback์์ด ๋งค ๋ ๋์ ์ฌ์์ฑ๋ฉ๋๋ค.- React Query ์์ฒด์
mutation.mutate๋ ์์ ์ ์ฐธ์กฐ๊ฐ ์๋๋ฏ๋ก ์ค์ง์ ๋ฌธ์ ๋ ์๋์ง๋ง, ์ด ํจ์๋ค์ ์์ ์ปดํฌ๋ํธ์ prop์ผ๋ก ์ ๋ฌํ๋ ๊ฒฝ์ฐ ๋ถํ์ํ ๋ฆฌ๋ ๋๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.- ํ์ฌ ๊ท๋ชจ์์๋ ํฐ ์ํฅ์ด ์์ผ๋ฏ๋ก, ํ์ํด์ง๋ฉด
useCallback์ผ๋ก ๊ฐ์ธ๋ ๊ฒ์ ๊ณ ๋ คํด ์ฃผ์ธ์.
43-62:normalizeMutationOptions/normalizeMutateOptionsโ ํ์ ์บ์คํ ์ ๋ํ ๋ฉ๋ชจ.
- Line 49, 60์์
as์บ์คํ ์ ์ฌ์ฉํ๋๋ฐ, ์ด๋mergeOnError๊ฐTData๋ฅผunknown์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ํ์ํฉ๋๋ค.- ์์ ์ฑ ์ธก๋ฉด์์ ๋ฌธ์ ๋ ์์ง๋ง,
mergeOnError์ ์ ๋ค๋ฆญ์TData๋ฅผ ์ถ๊ฐํ๋ฉด ์บ์คํ ์์ด ํด๊ฒฐํ ์ ์์ต๋๋ค.โป๏ธ ์บ์คํ ์ ๊ฑฐ๋ฅผ ์ํ ์ ์
-const mergeOnError = <TError, TVariables, TContext>( - onError?: UseMutationOptions<unknown, TError, TVariables, TContext>["onError"], +const mergeOnError = <TData, TError, TVariables, TContext>( + onError?: UseMutationOptions<TData, TError, TVariables, TContext>["onError"], onFailure?: (error: TError, variables: TVariables, context: TContext | undefined) => void, -): UseMutationOptions<unknown, TError, TVariables, TContext>["onError"] => { +): UseMutationOptions<TData, TError, TVariables, TContext>["onError"] => { if (!onError && !onFailure) return undefined; - return (...args: Parameters<NonNullable<UseMutationOptions<unknown, TError, TVariables, TContext>["onError"]>>) => { + return (...args: Parameters<NonNullable<UseMutationOptions<TData, TError, TVariables, TContext>["onError"]>>) => { onError?.(...args); onFailure?.(args[0], args[1], args[2]); }; };์ด๋ ๊ฒ ํ๋ฉด
normalizeMutationOptions์normalizeMutateOptions๋ด๋ถ์as์บ์คํ ์ ์ ๊ฑฐํ ์ ์์ต๋๋ค.apps/web/sentry.client.config.ts (1)
35-44:lazyLoadIntegrationPromise์ ์๋ฌ ์ฒ๋ฆฌ๊ฐ ์์ต๋๋ค.๋คํธ์ํฌ ์คํจ ๋ฑ์ผ๋ก lazy load๊ฐ ์คํจํ ๊ฒฝ์ฐ unhandled promise rejection์ด ๋ฐ์ํ ์ ์์ด์. Session Replay๋ ๋นํ์ ๊ธฐ๋ฅ์ด๋ ์กฐ์ฉํ ์คํจํด๋ ๊ด์ฐฎ์ง๋ง, ๋ช ์์ ์ผ๋ก
.catch()๋ฅผ ์ถ๊ฐํด ์ฃผ๋ ๊ฒ์ด ์์ ํฉ๋๋ค.๐ก๏ธ ์๋ฌ ์ฒ๋ฆฌ ์ถ๊ฐ ์ ์
Sentry.lazyLoadIntegration("replayIntegration").then((replay) => { Sentry.addIntegration( replay({ maskAllText: true, blockAllMedia: true, }), ); - }); + }).catch(() => { + // Session Replay lazy load ์คํจ ์ ๋ฌด์ (๋นํ์ ๊ธฐ๋ฅ) + });
apps/admin/src/components/features/scores/LanguageScoreTable.tsx
Outdated
Show resolved
Hide resolved
| // IE11 ์ง์ ๋ถํ์ - ๋ฒ๋ค ์ฌ์ด์ฆ ์ต์ ํ๋ฅผ ์ํด ๋นํ์ฑํ | ||
| transpileClientSDK: false, |
There was a problem hiding this comment.
๐งฉ Analysis chain
๐ Web query:
@sentry/nextjs v10 transpileClientSDK option documentation
๐ก Result:
transpileClientSDK is not available in @sentry/nextjs v10 (or any v8+). It was removed in @sentry/nextjs 8.0.0 when Sentry dropped support for IE11/older browsers; the release notes explicitly say the option was removed and that you must down-compile the SDK yourself via your Next.js/Webpack setup if you still need that compatibility. [1]
Historically (pre-v8), transpileClientSDK: true told the Next.js SDKโs Webpack config to transpile Sentryโs client bundle to match your appโs transpilation level (to support older browsers). [2]
If your goal is โsupport older browsers,โ note that current Sentry JavaScript SDKs target modern syntax (ES2020) and Sentryโs docs recommend transpiling + polyfills on your side if you need earlier browser versions. [3]
Sources: [1] [2] [3]
๐ Web query:
Sentry SDK transpileClientSDK option removed deprecated v8 v9 v10
๐ก Result:
transpileClientSDKwas added to the Sentry Next.js SDK in the v7 line (to force Webpack to transpile Sentryโs client bundle for older browsers like IE11). (newreleases.io)- It was removed in
@sentry/nextjs8.0.0 (Sentry JS SDK v8) because v8 dropped support for IE11 / older browsers, so the SDK no longer provides this built-in transpilation switch. (newreleases.io) - Therefore, the option is not available in v8, v9, or v10 of
@sentry/nextjs. If you still need older-browser support, you must configure Next.js/Webpack to down-compile/transpile the Sentry packages yourself. (newreleases.io)
Citations:
- 1: https://newreleases.io/project/npm/%40sentry/react/release/7.8.0?utm_source=openai
- 2: https://newreleases.io/project/npm/%40sentry/nextjs/release/8.0.0?utm_source=openai
- 3: https://newreleases.io/project/npm/%40sentry/nextjs/release/8.0.0?utm_source=openai
๐ Web query:
@sentry/nextjs changelog transpileClientSDK
๐ก Result:
transpileClientSDK was added to @sentry/nextjs in sentry-javascript v7.8.0 (โfix(nextjs): Add transpileClientSDK optionโ, PR #5472) as a Next.js Webpack config flag. It makes Next/Webpack transpile the Sentry client SDK code using the same transpilation rules as your app, which helps when targeting older browsers (e.g., missing ES6/ES6+ features like object spread). [1]
In the @sentry/nextjs type definitions (v7.x), itโs documented as:
transpileClientSDK?: boolean;โ โapply the same transpilation rules to the SDK code as apply to user codeโฆโ [2]
Sources: [1] [2]
transpileClientSDK ์ต์
์ @sentry/nextjs v10์์ ์ง์๋์ง ์์ผ๋ฏ๋ก ์ ๊ฑฐ๊ฐ ํ์ํฉ๋๋ค.
๋ค์๊ณผ ๊ฐ์ ๋ณ๊ฒฝ ์ฌํญ์ด ํ์ํฉ๋๋ค:
-
์ต์ ์ ๊ฑฐ
@sentry/nextjs8.0.0๋ถํฐtranspileClientSDK์ต์ ์ด ์์ ํ ์ ๊ฑฐ๋์์ผ๋ฏ๋ก, ์ด ๋ผ์ธ์ ์ญ์ ํ์ธ์. ํ์ฌ ์ค์ ์ v10์์ ์ธ์๋์ง ์์ ์๋์ผ๋ก ๋ฌด์๋ฉ๋๋ค. -
IE11 ์ง์ ์ฌํ๊ฐ
Sentry JavaScript SDK v8+ ์ดํ IE11 ์ง์์ด ๊ณต์์ ์ผ๋ก ์ค๋จ๋์์ต๋๋ค. ๋ง์ฝ ์ด์ ๋ธ๋ผ์ฐ์ ์ง์์ด ํ์๋ผ๋ฉด, Next.js์ webpack ์ค์ ์ ํตํด ์ง์ transpilation + polyfill์ ๊ตฌ์ฑํด์ผ ํฉ๋๋ค. ํ์ฌ ์ฃผ์์ ์๋(๋ฒ๋ค ์ต์ ํ)๋ผ๋ฉด ์ฃผ์๋ ํจ๊ป ์ ๊ฑฐํ์ธ์.
๐ค Prompt for AI Agents
In `@apps/web/next.config.mjs` around lines 97 - 98, Remove the unsupported
transpileClientSDK option from next.config.mjs (delete the transpileClientSDK:
false line and its IE11-related comment); reference the `@sentry/nextjs` change
(v8+ removed this option) and, if IE11/older-browser support is required,
instruct to implement explicit transpilation and polyfills via Next.js
webpack/babel config instead of using transpileClientSDK.
| "zustand": "^5.0.7" | ||
| }, | ||
| "devDependencies": { | ||
| "@next/bundle-analyzer": "^16.1.6", |
There was a problem hiding this comment.
๐งฉ Analysis chain
๐ Script executed:
cat apps/web/package.json | grep -A 100 '"dependencies"' | head -30Repository: solid-connection/solid-connect-web
Length of output: 1085
๐ Script executed:
cat apps/web/package.json | grep -E '"next"|"@next/bundle-analyzer"'Repository: solid-connection/solid-connect-web
Length of output: 141
๐ Web query:
@next/bundle-analyzer Next.js 14 compatibility requirements version
๐ก Result:
For Next.js 14, use @next/bundle-analyzer v14.x (same major as Next). In practice, keep it in lockstep with your next version (e.g., next@14.2.30 โ @next/bundle-analyzer@14.2.30). The npm registry shows published 14.x releases (e.g., 14.2.30) for @next/bundle-analyzer. [1]
Requirements to run it with Next 14:
- Next.js 14 requires Node.js >= 18.17. [2]
- Itโs a Webpack-based analyzer; if you run
next dev --turbo(Turbopack), you may see warnings because Webpack config/plugins (like bundle analyzer) arenโt supported the same way. [3]
Install example (pin to your Next 14 version):
npm i -D `@next/bundle-analyzer`@14.2.30Sources: [1] [2] [3]
@next/bundle-analyzer ๋ฒ์ ์ด Next.js ๋ฉ์ด์ ๋ฒ์ ๊ณผ ๋ง์ง ์์ต๋๋ค.
ํ์ฌ ํ๋ก์ ํธ์์ next๋ ^14.2.35์ด์ง๋ง, @next/bundle-analyzer๋ ^16.1.6์ผ๋ก ์ค์ ๋์ด ์์ด์. @next/* ํจํค์ง๋ค์ Next.js์ ๋ฉ์ด์ ๋ฒ์ ๊ณผ ๋์ผํ๊ฒ ์ ์งํด์ผ ํธํ์ฑ์ด ๋ณด์ฅ๋ฉ๋๋ค. ๊ณต์ ๋ฌธ์์์๋ Next.js 14์๋ @next/bundle-analyzer v14.x๋ฅผ ์ฌ์ฉํ๋๋ก ๊ถ์ฅํ๊ณ ์์ต๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ์์ ์ ์ ์๋๋ฆฝ๋๋ค:
@next/bundle-analyzer๋ฒ์ ์ Next.js 14๊ณผ ๋ง์ถฐ์ ์ ๊ทธ๋ ์ด๋ํ๊ธฐ
๐ง ๋ฒ์ ์์ ๋ฐฉ์
- "@next/bundle-analyzer": "^16.1.6",
+ "@next/bundle-analyzer": "^14.2.35",๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "@next/bundle-analyzer": "^16.1.6", | |
| "@next/bundle-analyzer": "^14.2.35", |
๐ค Prompt for AI Agents
In `@apps/web/package.json` at line 50, The dependency `@next/bundle-analyzer` in
apps/web package.json is pinned to ^16.1.6 which mismatches the project's
Next.js version (^14.2.35); update the "@next/bundle-analyzer" entry to a 14.x
compatible version (e.g. ^14.0.0 or the specific v14.x that matches Next 14) so
the `@next/`* major versions align, then reinstall deps (npm/yarn/pnpm install)
and run the app/build to verify compatibility; locate the
"@next/bundle-analyzer" entry in package.json to make this change.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
There was a problem hiding this comment.
Actionable comments posted: 1
๐ค Fix all issues with AI agents
In @.npmrc:
- Around line 3-5: The .npmrc contains conflicting hoisting settings: remove the
redundant public-hoist-pattern[]='*' entries (and the negations '!@types/react'
/ '!@types/react-dom' which are ineffective when shamefully-hoist=true) or else
remove shamefully-hoist=true so public-hoist-patterns take effect; then move
bundle-size optimizations into Next.js config by adjusting next.config.js (use
transpilePackages and optimizePackageImports) rather than relying on broad pnpm
hoisting.
.npmrc
Outdated
| public-hoist-pattern[]='*' | ||
| public-hoist-pattern[]='!@types/react' | ||
| public-hoist-pattern[]='!@types/react-dom' |
There was a problem hiding this comment.
๐งฉ Analysis chain
๐ Web query:
pnpm public-hoist-pattern shamefully-hoist interaction configuration
๐ก Result:
shamefully-hoist and public-hoist-pattern both control public hoisting (putting selected packages into the root node_modules so โphantomโ deps become resolvable by app/tooling).
Interaction / precedence
shamefully-hoist=trueis effectively the same aspublic-hoist-pattern[]=*(public-hoist everything). It also overrides anypublic-hoist-patternyou set. [1][2]shamefully-hoist=falsemeans pnpm usespublic-hoist-patternnormally (only patterns you specify are publicly hoisted). [2]
Recommended configuration approach
Prefer targeted public hoisting via public-hoist-pattern (only for broken tooling that expects flat/root deps), instead of shamefully-hoist=true. [1]
Example .npmrc
# Targeted public hoisting (recommended)
public-hoist-pattern[]=*plugin*
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*pnpm also supports excluding matches with ! (so you can broadly hoist, then carve out exceptions). [1][3]
public-hoist-pattern[]=*types*
public-hoist-pattern[]=!@types/reactโHoist everythingโ (legacy/last resort)
shamefully-hoist=true
# equivalent to: public-hoist-pattern[]=*[1]
Rule of thumb: if you set shamefully-hoist=true, donโt bother setting public-hoist-patternโit will be treated as * anyway. [1][2]
Sources: pnpm .npmrc settings docs (public-hoist-pattern + shamefully-hoist) [1], pnpm 5.2.0 release notes describing override behavior [2], pnpm .npmrc docs noting exclusion patterns with ! [3].
๐ Web query:
pnpm monorepo hoisting best practices Next.js bundle optimization
๐ก Result:
pnpm monorepo hoisting: best practices (so you donโt break Next.js or hide missing deps)
- Prefer pnpmโs default โisolatedโ linker (
node-linker=isolated) to keep dependency boundaries correct and avoid โphantom dependencyโ bugs (packages importing deps they didnโt declare). Only switch tonode-linker=hoistedif you must support tooling/filesystems that canโt handle symlinks. [1] - Avoid blanket โpublicโ hoisting (
publicHoistPattern=['*']/shamefullyHoist=true) unless youโre working around a specific broken tool, because it intentionally makes phantom deps available to app code. [2] - If you need hoisting, make it targeted:
- Use
hoistPatternto hoist only the problematic toolchain deps (recommended by pnpm) instead of everything. [2] - Use
publicHoistPatternsparingly for tools that incorrectly resolve plugins/deps and require root-level visibility. [2]
- Use
Typical .npmrc / pnpm config approach
# default / recommended
node-linker=isolated
# if some tool breaks, prefer targeted hoisting (examples)
# hoist-pattern[]=*eslint*
# hoist-pattern[]=*babel*
# public-hoist-pattern[]=*plugin*(Exact config file can be .npmrc or workspace config depending on how you manage pnpm settings; the knobs are nodeLinker, hoistPattern, publicHoistPattern.) [1][2]
Next.js in a pnpm monorepo: bundle optimization checklist
- Bundle/transpile your internal workspace packages (the usual monorepo fix) using
transpilePackagesso Next can compile code coming from workspace packages /node_modules. [3][4]
// next.config.js
module.exports = {
transpilePackages: ['@acme/ui', '@acme/shared'],
}- Measure first with the official bundle analyzer so you can see whatโs actually inflating the client/server bundles. [5]
ANALYZE=true pnpm build- Fix โbarrel exportโ bloat with
experimental.optimizePackageImports(or rely on defaults for popular libs). This can reduce work during builds and avoid pulling in huge module graphs for big โnamed exportโ packages. [5][6][7]
module.exports = {
experimental: {
optimizePackageImports: ['some-big-barrel-lib'],
},
}- Keep server-only deps out of bundling when appropriate using
serverExternalPackages(useful for native/binary deps or packages that shouldnโt be bundled). [5]
How this ties together (pnpm hoisting โ Next bundling)
- Donโt use broad hoisting to โmake Next find things.โ If Next canโt compile/import a workspace package cleanly, the right fix is usually
transpilePackages(and proper dependency declarations), not making everything visible via public hoisting. [3][4][2] - Use hoisting only as a compatibility escape hatch, and keep it pattern-based. [2][1]
Sources: pnpm settings for nodeLinker/hoisting knobs [1][2]; Next.js docs for bundle analyzer, transpilePackages, and optimizePackageImports [5][3][4][6]; Vercel guidance on optimizePackageImports and barrel-export impact [7].
๊ณต์กดํ ์ ์๋ ์ค์ : ์ค๋ณต์ ์ ๊ฑฐํ๊ณ ๋ฒ๋ค ์ต์ ํ ๋ฐฉ์์ ์ฌ๊ฒํ ํ์ธ์
ํ์ฌ ์ค์ ์ ๋ ๊ฐ์ง ๋ฌธ์ ๊ฐ ์์ต๋๋ค:
-
์ค์ ์ค๋ณต์ผ๋ก ์ธํ ๋นํจ์จ
- 2๋ฒ ๋ผ์ธ์
shamefully-hoist=true๊ฐ ์ด๋ฏธ ๋ชจ๋ ์์กด์ฑ์ ๊ณต๊ฐ ํธ์ด์คํ ํฉ๋๋ค - 3~5๋ฒ ๋ผ์ธ์
public-hoist-pattern[]='*'๋ ์ ์ค์ ๊ณผ ์ ํํ ๋์ผํ ๋์์ ํ๋ฏ๋ก ๋ถํ์ํฉ๋๋ค - pnpm ๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด,
shamefully-hoist=true๋ฅผ ์ค์ ํ๋ฉด ๋ค๋ฅธpublic-hoist-pattern์ค์ ์ ๋ฌด์๋ฉ๋๋ค
- 2๋ฒ ๋ผ์ธ์
-
์๋ํ ํจ๊ณผ๋ฅผ ๋ด์ง ๋ชปํ ๊ฐ๋ฅ์ฑ
- 4~5๋ฒ ๋ผ์ธ์ React ํ์
์ ์ธ ์ค์ (
!@types/react๋ฑ)์shamefully-hoist=true์์์ ์๋ํ์ง ์์ต๋๋ค - React ํ์ ๋ ๊ฒฐ๊ตญ ๊ณต๊ฐ ํธ์ด์คํ ๋๋ฏ๋ก, ์์กด์ฑ ์ถฉ๋ ๋ฐฉ์ง ํจ๊ณผ๊ฐ ์์ต๋๋ค
- 4~5๋ฒ ๋ผ์ธ์ React ํ์
์ ์ธ ์ค์ (
-
๋ฒ๋ค ์ต์ ํ ๋ชฉํ ์ฌ๊ฒํ
- ๊ด๋ฒ์ํ ํธ์ด์คํ ๋ง์ผ๋ก๋ Next.js ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค
- ์ค์ ์ต์ ํ๋
next.config.js์transpilePackages์optimizePackageImports์ค์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋๋ค
๊ถ์ฅ ์กฐ์น:
- ๋ถํ์ํ
public-hoist-pattern[]='*'๋ฅผ ์ ๊ฑฐํ๊ฑฐ๋ shamefully-hoist=true๋ก ํต์ผํ๋, ๋ฒ๋ค ์ต์ ํ๋ Next.js ์ค์ ์ผ๋ก ์งํํ์ธ์
๐ค Prompt for AI Agents
In @.npmrc around lines 3 - 5, The .npmrc contains conflicting hoisting
settings: remove the redundant public-hoist-pattern[]='*' entries (and the
negations '!@types/react' / '!@types/react-dom' which are ineffective when
shamefully-hoist=true) or else remove shamefully-hoist=true so
public-hoist-patterns take effect; then move bundle-size optimizations into
Next.js config by adjusting next.config.js (use transpilePackages and
optimizePackageImports) rather than relying on broad pnpm hoisting.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Summary
์น ์ฑ์ First Load JS shared by all์ 198 kB โ 160 kB (-38 kB, -19.2%)๋ก ๊ฐ์์ํต๋๋ค.
๋ณ๊ฒฝ ์ฌํญ
1. Sentry transpileClientSDK ๋นํ์ฑํ
2. Sentry replayIntegration lazy load
lazyLoadIntegration์ผ๋ก ๋น๋๊ธฐ ๋ก๋ฉํ์ฌ ์ด๊ธฐ ๋ก๋ฉ ์ฑ๋ฅ ๊ฐ์3. @next/bundle-analyzer ์ถ๊ฐ
ANALYZE=true pnpm --filter @solid-connect/web run analyze)4. optimizePackageImports ์ค์
lucide-react,@radix-ui/*,zod,react-hook-form๋ฑ tree-shaking ์ต์ ํ5. score/search ํ์ด์ง dynamic import
ScoreScreen,SearchBar,SchoolSearchForm์ปดํฌ๋ํธ๋ฅผnext/dynamic์ผ๋ก ๋ณ๊ฒฝ๋น๋ ๊ฒฐ๊ณผ ๋น๊ต
/university/score/university/score/submit/gpa/university/score/submit/language-test/university/search์ฐธ๊ณ
useMutationWithFailure.ts์ ๊ธฐ์กด ํ์ ์๋ฌ๊ฐ main์๋ ์กด์ฌํฉ๋๋ค (์ด PR๊ณผ ๋ฌด๊ด)