Skip to content

Conversation

@lukesandberg
Copy link
Contributor

@lukesandberg lukesandberg commented Oct 30, 2025

Code frame uses the js-tokens library under the hood for syntax highlighting, this has known issues with things like large string literals.

So we need to be defensive when calling it. Avoid producing code frames for large, presumably pre-bundled libraries and disable highlighting functionality on large lines.

Some of these issues would be addressed by updating the version of js-tokens used by babel codeframe and ideally code-frame itself would handle large lines more gracefully than it does.

Fixes #85357
Closes PACK-5754

@ijjk ijjk added created-by: Turbopack team PRs by the Turbopack team. type: next labels Oct 30, 2025
Copy link
Contributor Author

lukesandberg commented Oct 30, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments:

packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseScss.ts (line 38):
The code uses the nullish coalescing operator (??) which doesn't correctly handle empty strings returned by the new codeFrameColumns wrapper function for edge cases. The fallback to backupFrame will not work when the wrapper returns an empty string.

View Details
📝 Patch Details
diff --git a/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseScss.ts b/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseScss.ts
index 3582477092..09875f1eeb 100644
--- a/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseScss.ts
+++ b/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseScss.ts
@@ -35,7 +35,7 @@ export function getScssError(
       `${cyan(fileName)}:${yellow(lineNumber.toString())}:${yellow(
         column.toString()
       )}`,
-      red(bold('Syntax error')).concat(`: ${reason}\n\n${frame ?? backupFrame}`)
+      red(bold('Syntax error')).concat(`: ${reason}\n\n${frame || backupFrame}`)
     )
   }
 

Analysis

Incorrect null coalescing operator allows empty code frame to bypass fallback in parseScss

What fails: parseScss.ts line 38 uses nullish coalescing operator (??) instead of logical OR (||), causing empty strings returned by codeFrameColumns() to bypass the fallback to backupFrame. When the code frame is empty (for files over 5MB, columns exceeding 300, or other edge cases), the error message displays an empty string instead of the Sass error backup frame.

How to reproduce:

  1. Create a Sass file over 5MB or with very wide column positions (>300 chars)
  2. Trigger a Sass syntax error that would call getScssError() in parseScss.ts
  3. codeFrameColumns() returns '' due to defensive limits (see code-frame.ts MAX_CODEFRAME_FILESIZE, MAX_COLUMN)
  4. Line 38 evaluates: '' ?? backupFrame → returns '' (not nullish)
  5. Error message displays with empty code frame instead of backup message

Result: In JavaScript/TypeScript, the nullish coalescing operator (??) treats only null and undefined as nullish. Empty strings are truthy values for ?? purposes, so '' ?? backupFrame returns '' instead of the fallback.

Expected: Should use logical OR (||) operator which treats empty strings as falsy, matching the pattern used in diagnosticFormatter.ts line 383. This ensures that when codeFrameColumns() returns an empty string for edge cases, the error message correctly falls back to the backupFrame from the Sass error.

Fix applied: Changed line 38 from frame ?? backupFrame to frame || backupFrame

@ijjk
Copy link
Member

ijjk commented Oct 30, 2025

Failing test suites

Commit: 4e0b90c | About building and testing Next.js

pnpm test test/integration/scroll-back-restoration/test/index.test.js

  • Scroll Back Restoration Support > development mode > should restore the scroll position on navigating back (DD)
Expand output

● Scroll Back Restoration Support › development mode › should restore the scroll position on navigating back

expect(received).toBe(expected) // Object.is equality

Expected: 0
Received: 5008

  59 |     })
  60 |
> 61 |     expect(scrollX).toBe(newScrollX)
     |                     ^
  62 |     expect(scrollY).toBe(newScrollY)
  63 |   })
  64 | }

  at Object.toBe (integration/scroll-back-restoration/test/index.test.js:61:21)

pnpm test test/integration/i18n-support-same-page-hash-change/test/index.test.js

  • Hash changes i18n > development mode > should update props on locale change with same hash (dynamic page) (DD)
Expand output

● Hash changes i18n › development mode › should update props on locale change with same hash (dynamic page)

page.waitForSelector: Timeout 5000ms exceeded.
Call log:
  - waiting for locator('#change-locale') to be visible

  519 |
  520 |     return this.startChain(async () => {
> 521 |       const el = await page.waitForSelector(selector, {
      |                             ^
  522 |         timeout,
  523 |         state,
  524 |       })

  at waitForSelector (lib/browsers/playwright.ts:521:29)
  at Playwright._chain (lib/browsers/playwright.ts:651:23)
  at Playwright._chain [as startChain] (lib/browsers/playwright.ts:632:17)
  at Playwright.startChain [as waitForElementByCss] (lib/browsers/playwright.ts:520:17)
  at Playwright.waitForElementByCss [as elementByCss] (lib/browsers/playwright.ts:405:17)
  at Object.elementByCss (integration/i18n-support-same-page-hash-change/test/index.test.js:42:19)
  at Proxy._chain (lib/browsers/playwright.ts:651:23)
  at Proxy._chain (lib/browsers/playwright.ts:627:17)
  at Proxy.continueChain (lib/browsers/playwright.ts:471:17)
  at Object.click (integration/i18n-support-same-page-hash-change/test/index.test.js:42:50)

pnpm test test/integration/app-dir-export/test/start.test.ts

  • app dir - with output export (next start) > production mode > should warn during next start with output standalone (DD)
Expand output

● app dir - with output export (next start) › production mode › should warn during next start with output standalone

thrown: "Exceeded timeout of 60000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  46 |
  47 |       // TODO: Move this test to test/production to run in isolation.
> 48 |       ;(process.env.TURBOPACK_BUILD ? it.skip : it)(
     |                                                  ^
  49 |         'should warn during next start with output standalone',
  50 |         async () => {
  51 |           nextConfig.replace(`output: 'export'`, `output: 'standalone'`)

  at integration/app-dir-export/test/start.test.ts:48:50
  at integration/app-dir-export/test/start.test.ts:21:56
  at Object.describe (integration/app-dir-export/test/start.test.ts:20:1)

pnpm test-dev test/e2e/app-dir/fallback-shells/fallback-shells.test.ts

  • fallback-shells > without IO > should start and not postpone the response (DD)
Expand output

● fallback-shells › without IO › should start and not postpone the response

thrown: "Timed out waiting for the response of /without-io/world"

   8 |
   9 |   describe('without IO', () => {
> 10 |     it('should start and not postpone the response', async () => {
     |     ^
  11 |       const { browser, response } =
  12 |         await next.browserWithResponse('/without-io/world')
  13 |

  at it (e2e/app-dir/fallback-shells/fallback-shells.test.ts:10:5)
  at describe (e2e/app-dir/fallback-shells/fallback-shells.test.ts:9:3)
  at Object.describe (e2e/app-dir/fallback-shells/fallback-shells.test.ts:4:1)

pnpm test-dev test/e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts

  • use-cache-custom-handler > should use a modern custom cache handler if provided (DD)
Expand output

● use-cache-custom-handler › should use a modern custom cache handler if provided

expect(received).toEqual(expected) // deep equality

Expected: "2025-10-30T22:58:31.528Z"
Received: "2025-10-30T22:58:34.769Z"

  47 |     let data = await browser.elementById('data').text()
  48 |     expect(data).toMatch(isoDateRegExp)
> 49 |     expect(data).toEqual(initialData)
     |                  ^
  50 |
  51 |     // Now that a cache entry exists, we expect that getExpiration() is called
  52 |     // to compare the cache entries timestamp with the expiration of the

  at Object.toEqual (e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts:49:18)

pnpm test-dev test/e2e/app-dir/interception-dynamic-segment/interception-dynamic-segment.test.ts

  • interception-dynamic-segment > should work when interception route is paired with a dynamic segment (DD)
Expand output

● interception-dynamic-segment › should work when interception route is paired with a dynamic segment

expect(received).toEqual(expected) // deep equality

Expected: "intercepted"
Received: "default"

  12 |     await browser.elementByCss('[href="/foo/1"]').click()
  13 |     await retry(async () => {
> 14 |       expect(await browser.elementById('modal').text()).toEqual('intercepted')
     |                                                         ^
  15 |     })
  16 |     await browser.refresh()
  17 |     await retry(async () => {

  at toEqual (e2e/app-dir/interception-dynamic-segment/interception-dynamic-segment.test.ts:14:57)
  at retry (lib/next-test-utils.ts:797:14)
  at Object.<anonymous> (e2e/app-dir/interception-dynamic-segment/interception-dynamic-segment.test.ts:13:5)

pnpm test-start-turbo test/e2e/app-dir/segment-cache/revalidation/segment-cache-revalidation.test.ts (turbopack)

  • segment cache (revalidation) > delay re-prefetch after revalidation to allow CDN propagation (DD)
Expand output

● segment cache (revalidation) › delay re-prefetch after revalidation to allow CDN propagation

page.waitForSelector: Timeout 5000ms exceeded.
Call log:
  - waiting for locator('#greeting') to be visible

  519 |
  520 |     return this.startChain(async () => {
> 521 |       const el = await page.waitForSelector(selector, {
      |                             ^
  522 |         timeout,
  523 |         state,
  524 |       })

  at waitForSelector (lib/browsers/playwright.ts:521:29)
  at Playwright._chain (lib/browsers/playwright.ts:651:23)
  at Playwright._chain [as startChain] (lib/browsers/playwright.ts:632:17)
  at Playwright.startChain [as waitForElementByCss] (lib/browsers/playwright.ts:520:17)
  at Playwright.waitForElementByCss [as elementByCss] (lib/browsers/playwright.ts:405:17)
  at Playwright.elementByCss [as elementById] (lib/browsers/playwright.ts:425:17)
  at elementById (e2e/app-dir/segment-cache/revalidation/segment-cache-revalidation.test.ts:363:38)
  at act (lib/router-act.ts:275:27)
  at Object.<anonymous> (e2e/app-dir/segment-cache/revalidation/segment-cache-revalidation.test.ts:358:5)

@ijjk
Copy link
Member

ijjk commented Oct 30, 2025

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary vercel/next.js code_frame_defence Change
buildDuration 32.1s 30.6s N/A
buildDurationCached 27.2s 22.8s N/A
nodeModulesSize 454 MB 454 MB ⚠️ +15.9 kB
nextStartRea..uration (ms) 703ms 656ms N/A
Client Bundles (main, webpack)
vercel/next.js canary vercel/next.js code_frame_defence Change
436-HASH.js gzip 5.32 kB 5.32 kB N/A
4779.HASH.js gzip 169 B 169 B
9760-HASH.js gzip 54.9 kB 54.6 kB N/A
c57d0559-HASH.js gzip 62 kB 62.1 kB N/A
framework-HASH.js gzip 59.8 kB 59.8 kB
main-app-HASH.js gzip 254 B 255 B N/A
main-HASH.js gzip 39.7 kB 39.8 kB N/A
webpack-HASH.js gzip 1.69 kB 1.69 kB
Overall change 61.7 kB 61.7 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js code_frame_defence Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Overall change 39.4 kB 39.4 kB
Client Pages
vercel/next.js canary vercel/next.js code_frame_defence Change
_app-HASH.js gzip 193 B 194 B N/A
_error-HASH.js gzip 182 B 182 B
css-HASH.js gzip 334 B 334 B
dynamic-HASH.js gzip 1.81 kB 1.81 kB N/A
edge-ssr-HASH.js gzip 255 B 254 B N/A
head-HASH.js gzip 350 B 351 B N/A
hooks-HASH.js gzip 384 B 384 B
image-HASH.js gzip 4.78 kB 4.77 kB N/A
index-HASH.js gzip 260 B 259 B N/A
link-HASH.js gzip 2.5 kB 2.5 kB N/A
routerDirect..HASH.js gzip 316 B 320 B N/A
script-HASH.js gzip 388 B 388 B
withRouter-HASH.js gzip 316 B 314 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 1.39 kB 1.39 kB
Client Build Manifests
vercel/next.js canary vercel/next.js code_frame_defence Change
_buildManifest.js gzip 718 B 720 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary vercel/next.js code_frame_defence Change
index.html gzip 524 B 523 B N/A
link.html gzip 538 B 538 B
withRouter.html gzip 521 B 519 B N/A
Overall change 538 B 538 B
Edge SSR bundle Size Overall increase ⚠️
vercel/next.js canary vercel/next.js code_frame_defence Change
edge-ssr.js gzip 128 kB 128 kB N/A
page.js gzip 259 kB 260 kB ⚠️ +1.12 kB
Overall change 259 kB 260 kB ⚠️ +1.12 kB
Middleware size Overall increase ⚠️
vercel/next.js canary vercel/next.js code_frame_defence Change
middleware-b..fest.js gzip 638 B 638 B
middleware-r..fest.js gzip 156 B 156 B
middleware.js gzip 32.7 kB 32.9 kB ⚠️ +264 B
edge-runtime..pack.js gzip 846 B 846 B
Overall change 34.3 kB 34.6 kB ⚠️ +264 B
Next Runtimes
vercel/next.js canary vercel/next.js code_frame_defence Change
app-page-exp...dev.js gzip 296 kB 296 kB N/A
app-page-exp..prod.js gzip 162 kB 162 kB
app-page-tur...dev.js gzip 295 kB 295 kB
app-page-tur..prod.js gzip 162 kB 162 kB
app-page-tur...dev.js gzip 292 kB 292 kB N/A
app-page-tur..prod.js gzip 159 kB 159 kB
app-page.run...dev.js gzip 292 kB 292 kB N/A
app-page.run..prod.js gzip 159 kB 159 kB
app-route-ex...dev.js gzip 70.8 kB 70.8 kB
app-route-ex..prod.js gzip 49.4 kB 49.4 kB
app-route-tu...dev.js gzip 70.8 kB 70.8 kB
app-route-tu..prod.js gzip 49.4 kB 49.4 kB
app-route-tu...dev.js gzip 70.4 kB 70.4 kB
app-route-tu..prod.js gzip 49.1 kB 49.1 kB
app-route.ru...dev.js gzip 70.4 kB 70.4 kB
app-route.ru..prod.js gzip 49.1 kB 49.1 kB
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 328 B 328 B
dist_client_...dev.js gzip 320 B 320 B
dist_client_...dev.js gzip 318 B 318 B
pages-api-tu...dev.js gzip 43.3 kB 43.3 kB
pages-api-tu..prod.js gzip 33.2 kB 33.2 kB
pages-api.ru...dev.js gzip 43.3 kB 43.3 kB
pages-api.ru..prod.js gzip 33.1 kB 33.1 kB
pages-turbo....dev.js gzip 52.8 kB 52.8 kB
pages-turbo...prod.js gzip 40.1 kB 40.1 kB
pages.runtim...dev.js gzip 52.8 kB 52.8 kB
pages.runtim..prod.js gzip 40 kB 40 kB
server.runti..prod.js gzip 78.9 kB 78.9 kB
Overall change 1.83 MB 1.83 MB
build cache Overall increase ⚠️
vercel/next.js canary vercel/next.js code_frame_defence Change
0.pack gzip 3.25 MB 3.25 MB ⚠️ +536 B
index.pack gzip 94.6 kB 95.1 kB ⚠️ +539 B
Overall change 3.34 MB 3.34 MB ⚠️ +1.07 kB
Diff details
Diff for page.js

Diff too large to display

Diff for middleware.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for dynamic-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [2291],
   {
-    /***/ 1033: /***/ (
+    /***/ 431: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/dynamic",
         function () {
-          return __webpack_require__(6490);
+          return __webpack_require__(8084);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 5323: /***/ (
+    /***/ 2699: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -60,7 +60,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       const _react = /*#__PURE__*/ _interop_require_default._(
         __webpack_require__(2223)
       );
-      const _loadablecontextsharedruntime = __webpack_require__(9289);
+      const _loadablecontextsharedruntime = __webpack_require__(3785);
       function resolve(obj) {
         return obj && obj.default ? obj.default : obj;
       }
@@ -293,73 +293,34 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       /***/
     },
 
-    /***/ 6490: /***/ (
+    /***/ 3785: /***/ (
       __unused_webpack_module,
-      __webpack_exports__,
+      exports,
       __webpack_require__
     ) => {
       "use strict";
-      __webpack_require__.r(__webpack_exports__);
-      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
-        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
-        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
-        /* harmony export */
+      /* __next_internal_client_entry_do_not_use__  cjs */
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
       });
-      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
-        __webpack_require__(1503);
-      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7320);
-      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
-        /*#__PURE__*/ __webpack_require__.n(
-          next_dynamic__WEBPACK_IMPORTED_MODULE_1__
-        );
-
-      const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
-        () =>
-          __webpack_require__
-            .e(/* import() */ 4779)
-            .then(__webpack_require__.bind(__webpack_require__, 4779))
-            .then((mod) => mod.Hello),
-        {
-          loadableGenerated: {
-            webpack: () => [/*require.resolve*/ 4779],
-          },
-        }
+      Object.defineProperty(exports, "LoadableContext", {
+        enumerable: true,
+        get: function () {
+          return LoadableContext;
+        },
+      });
+      const _interop_require_default = __webpack_require__(1532);
+      const _react = /*#__PURE__*/ _interop_require_default._(
+        __webpack_require__(2223)
       );
-      const Page = () =>
-        /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
-          react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
-          {
-            children: [
-              /*#__PURE__*/ (0,
-              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
-                children: "testing next/dynamic size",
-              }),
-              /*#__PURE__*/ (0,
-              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
-                DynamicHello,
-                {}
-              ),
-            ],
-          }
-        );
-      var __N_SSP = true;
-      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
-
-      /***/
-    },
-
-    /***/ 7320: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(7340);
+      const LoadableContext = _react.default.createContext(null);
+      if (false) {
+      } //# sourceMappingURL=loadable-context.shared-runtime.js.map
 
       /***/
     },
 
-    /***/ 7340: /***/ (module, exports, __webpack_require__) => {
+    /***/ 6828: /***/ (module, exports, __webpack_require__) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -392,7 +353,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
         __webpack_require__(2223)
       );
       const _loadablesharedruntime = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(5323)
+        __webpack_require__(2699)
       );
       const isServerSide = "object" === "undefined";
       // Normalize loader to return the module as form { default: Component } for `React.lazy`.
@@ -492,29 +453,68 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       /***/
     },
 
-    /***/ 9289: /***/ (
+    /***/ 7514: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(6828);
+
+      /***/
+    },
+
+    /***/ 8084: /***/ (
       __unused_webpack_module,
-      exports,
+      __webpack_exports__,
       __webpack_require__
     ) => {
       "use strict";
-      /* __next_internal_client_entry_do_not_use__  cjs */
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "LoadableContext", {
-        enumerable: true,
-        get: function () {
-          return LoadableContext;
-        },
+      __webpack_require__.r(__webpack_exports__);
+      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
+        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
+        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
+        /* harmony export */
       });
-      const _interop_require_default = __webpack_require__(1532);
-      const _react = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(2223)
+      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
+        __webpack_require__(1503);
+      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
+        __webpack_require__(7514);
+      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
+        /*#__PURE__*/ __webpack_require__.n(
+          next_dynamic__WEBPACK_IMPORTED_MODULE_1__
+        );
+
+      const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
+        () =>
+          __webpack_require__
+            .e(/* import() */ 9573)
+            .then(__webpack_require__.bind(__webpack_require__, 9573))
+            .then((mod) => mod.Hello),
+        {
+          loadableGenerated: {
+            webpack: () => [/*require.resolve*/ 9573],
+          },
+        }
       );
-      const LoadableContext = _react.default.createContext(null);
-      if (false) {
-      } //# sourceMappingURL=loadable-context.shared-runtime.js.map
+      const Page = () =>
+        /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
+          react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
+          {
+            children: [
+              /*#__PURE__*/ (0,
+              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
+                children: "testing next/dynamic size",
+              }),
+              /*#__PURE__*/ (0,
+              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
+                DynamicHello,
+                {}
+              ),
+            ],
+          }
+        );
+      var __N_SSP = true;
+      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
 
       /***/
     },
@@ -524,7 +524,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(1033)
+      __webpack_exec__(431)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for edge-ssr-HASH.js
@@ -1,24 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [676],
   {
-    /***/ 1819: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/edge-ssr",
-        function () {
-          return __webpack_require__(7521);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 7521: /***/ (
+    /***/ 983: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -37,13 +20,30 @@
 
       /***/
     },
+
+    /***/ 985: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/edge-ssr",
+        function () {
+          return __webpack_require__(983);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(1819)
+      __webpack_exec__(985)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for head-HASH.js
@@ -1,34 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [5350],
   {
-    /***/ 619: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/head",
-        function () {
-          return __webpack_require__(9891);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 7997: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(6705);
-
-      /***/
-    },
-
-    /***/ 9891: /***/ (
+    /***/ 1417: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -43,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7997);
+        __webpack_require__(5171);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_head__WEBPACK_IMPORTED_MODULE_1__
@@ -76,13 +49,40 @@
 
       /***/
     },
+
+    /***/ 1937: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/head",
+        function () {
+          return __webpack_require__(1417);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 5171: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(7505);
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(619)
+      __webpack_exec__(1937)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for hooks-HASH.js
@@ -1,24 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [9804],
   {
-    /***/ 1679: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/hooks",
-        function () {
-          return __webpack_require__(7036);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 7036: /***/ (
+    /***/ 1598: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -76,13 +59,30 @@
 
       /***/
     },
+
+    /***/ 3925: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/hooks",
+        function () {
+          return __webpack_require__(1598);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(1679)
+      __webpack_exec__(3925)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for image-HASH.js
@@ -1,24 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [2983],
   {
-    /***/ 797: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/image",
-        function () {
-          return __webpack_require__(5999);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 1713: /***/ (__unused_webpack_module, exports) => {
+    /***/ 881: /***/ (__unused_webpack_module, exports) => {
       "use strict";
       /**
        * A shared function, used on both client and server, to generate a SVG blur placeholder.
@@ -58,7 +41,7 @@
       /***/
     },
 
-    /***/ 2263: /***/ (module, exports, __webpack_require__) => {
+    /***/ 1511: /***/ (module, exports, __webpack_require__) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -136,7 +119,137 @@
       /***/
     },
 
-    /***/ 2728: /***/ (module, exports, __webpack_require__) => {
+    /***/ 1744: /***/ (
+      __unused_webpack_module,
+      exports,
+      __webpack_require__
+    ) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "default", {
+        enumerable: true,
+        get: function () {
+          return _default;
+        },
+      });
+      const _findclosestquality = __webpack_require__(7054);
+      function defaultLoader({ config, src, width, quality }) {
+        if (
+          src.startsWith("/") &&
+          src.includes("?") &&
+          config.localPatterns?.length === 1 &&
+          config.localPatterns[0].pathname === "**" &&
+          config.localPatterns[0].search === ""
+        ) {
+          throw Object.defineProperty(
+            new Error(
+              `Image with src "${src}" is using a query string which is not configured in images.localPatterns.` +
+                `\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns`
+            ),
+            "__NEXT_ERROR_CODE",
+            {
+              value: "E871",
+              enumerable: false,
+              configurable: true,
+            }
+          );
+        }
+        if (false) {
+        }
+        const q = (0, _findclosestquality.findClosestQuality)(quality, config);
+        return `${config.path}?url=${encodeURIComponent(
+          src
+        )}&w=${width}&q=${q}${
+          src.startsWith("/_next/static/media/") && false ? 0 : ""
+        }`;
+      }
+      // We use this to determine if the import is the default loader
+      // or a custom loader defined by the user in next.config.js
+      defaultLoader.__next_img_default = true;
+      const _default = defaultLoader; //# sourceMappingURL=image-loader.js.map
+
+      /***/
+    },
+
+    /***/ 2388: /***/ (
+      __unused_webpack_module,
+      __webpack_exports__,
+      __webpack_require__
+    ) => {
+      "use strict";
+      // ESM COMPAT FLAG
+      __webpack_require__.r(__webpack_exports__);
+
+      // EXPORTS
+      __webpack_require__.d(__webpack_exports__, {
+        __N_SSP: () => /* binding */ __N_SSP,
+        default: () => /* binding */ pages_image,
+      });
+
+      // EXTERNAL MODULE: ./node_modules/.pnpm/[email protected]/node_modules/react/jsx-runtime.js
+      var jsx_runtime = __webpack_require__(1503);
+      // EXTERNAL MODULE: ./node_modules/.pnpm/next@[email protected][email protected][email protected]/node_modules/next/image.js
+      var next_image = __webpack_require__(3866);
+      var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // ./pages/nextjs.png
+      /* harmony default export */ const nextjs = {
+        src: "/_next/static/media/nextjs.cae0b805.png",
+        height: 1347,
+        width: 1626,
+        blurDataURL:
+          "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAHCAMAAAACh/xsAAAAD1BMVEX////x8fH6+vrb29vo6Oh8o70bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAH0lEQVR4nGNgwARMjIyMjCAGCzMzMwsTRISJCcRABwAEcAAkLCQfgAAAAABJRU5ErkJggg==",
+        blurWidth: 8,
+        blurHeight: 7,
+      }; // ./pages/image.js
+      function ImagePage(props) {
+        return /*#__PURE__*/ (0, jsx_runtime.jsxs)(jsx_runtime.Fragment, {
+          children: [
+            /*#__PURE__*/ (0, jsx_runtime.jsx)("h1", {
+              children: "next/image example",
+            }),
+            /*#__PURE__*/ (0, jsx_runtime.jsx)(image_default(), {
+              src: nextjs,
+              placeholder: "blur",
+            }),
+          ],
+        });
+      }
+      var __N_SSP = true;
+      /* harmony default export */ const pages_image = ImagePage;
+
+      /***/
+    },
+
+    /***/ 3866: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(6888);
+
+      /***/
+    },
+
+    /***/ 4483: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/image",
+        function () {
+          return __webpack_require__(2388);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 6600: /***/ (module, exports, __webpack_require__) => {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -158,17 +271,17 @@
         __webpack_require__(9507)
       );
       const _head = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(6705)
+        __webpack_require__(7505)
       );
-      const _getimgprops = __webpack_require__(3556);
-      const _imageconfig = __webpack_require__(3157);
-      const _imageconfigcontextsharedruntime = __webpack_require__(9323);
-      const _warnonce = __webpack_require__(6173);
-      const _routercontextsharedruntime = __webpack_require__(6046);
+      const _getimgprops = __webpack_require__(9588);
+      const _imageconfig = __webpack_require__(2645);
+      const _imageconfigcontextsharedruntime = __webpack_require__(5451);
+      const _warnonce = __webpack_require__(7549);
+      const _routercontextsharedruntime = __webpack_require__(5470);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3744)
+        __webpack_require__(1744)
       );
-      const _usemergedref = __webpack_require__(2263);
+      const _usemergedref = __webpack_require__(1511);
       // This is replaced by webpack define plugin
       const configEnv = {
         deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
@@ -495,7 +608,96 @@
       /***/
     },
 
-    /***/ 3556: /***/ (
+    /***/ 6888: /***/ (
+      __unused_webpack_module,
+      exports,
+      __webpack_require__
+    ) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      0 && 0;
+      function _export(target, all) {
+        for (var name in all)
+          Object.defineProperty(target, name, {
+            enumerable: true,
+            get: all[name],
+          });
+      }
+      _export(exports, {
+        default: function () {
+          return _default;
+        },
+        getImageProps: function () {
+          return getImageProps;
+        },
+      });
+      const _interop_require_default = __webpack_require__(1532);
+      const _getimgprops = __webpack_require__(9588);
+      const _imagecomponent = __webpack_require__(6600);
+      const _imageloader = /*#__PURE__*/ _interop_require_default._(
+        __webpack_require__(1744)
+      );
+      function getImageProps(imgProps) {
+        const { props } = (0, _getimgprops.getImgProps)(imgProps, {
+          defaultLoader: _imageloader.default,
+          // This is replaced by webpack define plugin
+          imgConf: {
+            deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
+            imageSizes: [32, 48, 64, 96, 128, 256, 384],
+            qualities: [75],
+            path: "/_next/image",
+            loader: "default",
+            dangerouslyAllowSVG: false,
+            unoptimized: false,
+          },
+        });
+        // Normally we don't care about undefined props because we pass to JSX,
+        // but this exported function could be used by the end user for anything
+        // so we delete undefined props to clean it up a little.
+        for (const [key, value] of Object.entries(props)) {
+          if (value === undefined) {
+            delete props[key];
+          }
+        }
+        return {
+          props,
+        };
+      }
+      const _default = _imagecomponent.Image; //# sourceMappingURL=image-external.js.map
+
+      /***/
+    },
+
+    /***/ 7054: /***/ (__unused_webpack_module, exports) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "findClosestQuality", {
+        enumerable: true,
+        get: function () {
+          return findClosestQuality;
+        },
+      });
+      function findClosestQuality(quality, config) {
+        const q = quality || 75;
+        if (!config?.qualities?.length) {
+          return q;
+        }
+        return config.qualities.reduce(
+          (prev, cur) => (Math.abs(cur - q) < Math.abs(prev - q) ? cur : prev),
+          0
+        );
+      } //# sourceMappingURL=find-closest-quality.js.map
+
+      /***/
+    },
+
+    /***/ 9588: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -511,9 +713,9 @@
           return getImgProps;
         },
       });
-      const _warnonce = __webpack_require__(6173);
-      const _imageblursvg = __webpack_require__(1713);
-      const _imageconfig = __webpack_require__(3157);
+      const _warnonce = __webpack_require__(7549);
+      const _imageblursvg = __webpack_require__(881);
+      const _imageconfig = __webpack_require__(2645);
       const VALID_LOADING_VALUES =
         /* unused pure expression or super */ null && [
           "lazy",
@@ -943,215 +1145,13 @@
 
       /***/
     },
-
-    /***/ 3744: /***/ (
-      __unused_webpack_module,
-      exports,
-      __webpack_require__
-    ) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "default", {
-        enumerable: true,
-        get: function () {
-          return _default;
-        },
-      });
-      const _findclosestquality = __webpack_require__(8494);
-      function defaultLoader({ config, src, width, quality }) {
-        if (
-          src.startsWith("/") &&
-          src.includes("?") &&
-          config.localPatterns?.length === 1 &&
-          config.localPatterns[0].pathname === "**" &&
-          config.localPatterns[0].search === ""
-        ) {
-          throw Object.defineProperty(
-            new Error(
-              `Image with src "${src}" is using a query string which is not configured in images.localPatterns.` +
-                `\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns`
-            ),
-            "__NEXT_ERROR_CODE",
-            {
-              value: "E871",
-              enumerable: false,
-              configurable: true,
-            }
-          );
-        }
-        if (false) {
-        }
-        const q = (0, _findclosestquality.findClosestQuality)(quality, config);
-        return `${config.path}?url=${encodeURIComponent(
-          src
-        )}&w=${width}&q=${q}${
-          src.startsWith("/_next/static/media/") && false ? 0 : ""
-        }`;
-      }
-      // We use this to determine if the import is the default loader
-      // or a custom loader defined by the user in next.config.js
-      defaultLoader.__next_img_default = true;
-      const _default = defaultLoader; //# sourceMappingURL=image-loader.js.map
-
-      /***/
-    },
-
-    /***/ 4292: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(7896);
-
-      /***/
-    },
-
-    /***/ 5999: /***/ (
-      __unused_webpack_module,
-      __webpack_exports__,
-      __webpack_require__
-    ) => {
-      "use strict";
-      // ESM COMPAT FLAG
-      __webpack_require__.r(__webpack_exports__);
-
-      // EXPORTS
-      __webpack_require__.d(__webpack_exports__, {
-        __N_SSP: () => /* binding */ __N_SSP,
-        default: () => /* binding */ pages_image,
-      });
-
-      // EXTERNAL MODULE: ./node_modules/.pnpm/[email protected]/node_modules/react/jsx-runtime.js
-      var jsx_runtime = __webpack_require__(1503);
-      // EXTERNAL MODULE: ./node_modules/.pnpm/next@[email protected][email protected][email protected]/node_modules/next/image.js
-      var next_image = __webpack_require__(4292);
-      var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // ./pages/nextjs.png
-      /* harmony default export */ const nextjs = {
-        src: "/_next/static/media/nextjs.cae0b805.png",
-        height: 1347,
-        width: 1626,
-        blurDataURL:
-          "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAHCAMAAAACh/xsAAAAD1BMVEX////x8fH6+vrb29vo6Oh8o70bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAH0lEQVR4nGNgwARMjIyMjCAGCzMzMwsTRISJCcRABwAEcAAkLCQfgAAAAABJRU5ErkJggg==",
-        blurWidth: 8,
-        blurHeight: 7,
-      }; // ./pages/image.js
-      function ImagePage(props) {
-        return /*#__PURE__*/ (0, jsx_runtime.jsxs)(jsx_runtime.Fragment, {
-          children: [
-            /*#__PURE__*/ (0, jsx_runtime.jsx)("h1", {
-              children: "next/image example",
-            }),
-            /*#__PURE__*/ (0, jsx_runtime.jsx)(image_default(), {
-              src: nextjs,
-              placeholder: "blur",
-            }),
-          ],
-        });
-      }
-      var __N_SSP = true;
-      /* harmony default export */ const pages_image = ImagePage;
-
-      /***/
-    },
-
-    /***/ 7896: /***/ (
-      __unused_webpack_module,
-      exports,
-      __webpack_require__
-    ) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      0 && 0;
-      function _export(target, all) {
-        for (var name in all)
-          Object.defineProperty(target, name, {
-            enumerable: true,
-            get: all[name],
-          });
-      }
-      _export(exports, {
-        default: function () {
-          return _default;
-        },
-        getImageProps: function () {
-          return getImageProps;
-        },
-      });
-      const _interop_require_default = __webpack_require__(1532);
-      const _getimgprops = __webpack_require__(3556);
-      const _imagecomponent = __webpack_require__(2728);
-      const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3744)
-      );
-      function getImageProps(imgProps) {
-        const { props } = (0, _getimgprops.getImgProps)(imgProps, {
-          defaultLoader: _imageloader.default,
-          // This is replaced by webpack define plugin
-          imgConf: {
-            deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
-            imageSizes: [32, 48, 64, 96, 128, 256, 384],
-            qualities: [75],
-            path: "/_next/image",
-            loader: "default",
-            dangerouslyAllowSVG: false,
-            unoptimized: false,
-          },
-        });
-        // Normally we don't care about undefined props because we pass to JSX,
-        // but this exported function could be used by the end user for anything
-        // so we delete undefined props to clean it up a little.
-        for (const [key, value] of Object.entries(props)) {
-          if (value === undefined) {
-            delete props[key];
-          }
-        }
-        return {
-          props,
-        };
-      }
-      const _default = _imagecomponent.Image; //# sourceMappingURL=image-external.js.map
-
-      /***/
-    },
-
-    /***/ 8494: /***/ (__unused_webpack_module, exports) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "findClosestQuality", {
-        enumerable: true,
-        get: function () {
-          return findClosestQuality;
-        },
-      });
-      function findClosestQuality(quality, config) {
-        const q = quality || 75;
-        if (!config?.qualities?.length) {
-          return q;
-        }
-        return config.qualities.reduce(
-          (prev, cur) => (Math.abs(cur - q) < Math.abs(prev - q) ? cur : prev),
-          0
-        );
-      } //# sourceMappingURL=find-closest-quality.js.map
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(797)
+      __webpack_exec__(4483)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for link-HASH.js
@@ -1,7 +1,338 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [4672],
   {
-    /***/ 69: /***/ (module, exports, __webpack_require__) => {
+    /***/ 1511: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "useMergedRef", {
+        enumerable: true,
+        get: function () {
+          return useMergedRef;
+        },
+      });
+      const _react = __webpack_require__(2223);
+      function useMergedRef(refA, refB) {
+        const cleanupA = (0, _react.useRef)(null);
+        const cleanupB = (0, _react.useRef)(null);
+        // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
+        // (this happens often if the user doesn't pass a ref to Link/Form/Image)
+        // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
+        // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
+        // (because it hasn't been updated for React 19)
+        // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
+        // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
+        return (0, _react.useCallback)(
+          (current) => {
+            if (current === null) {
+              const cleanupFnA = cleanupA.current;
+              if (cleanupFnA) {
+                cleanupA.current = null;
+                cleanupFnA();
+              }
+              const cleanupFnB = cleanupB.current;
+              if (cleanupFnB) {
+                cleanupB.current = null;
+                cleanupFnB();
+              }
+            } else {
+              if (refA) {
+                cleanupA.current = applyRef(refA, current);
+              }
+              if (refB) {
+                cleanupB.current = applyRef(refB, current);
+              }
+            }
+          },
+          [refA, refB]
+        );
+      }
+      function applyRef(refA, current) {
+        if (typeof refA === "function") {
+          const cleanup = refA(current);
+          if (typeof cleanup === "function") {
+            return cleanup;
+          } else {
+            return () => refA(null);
+          }
+        } else {
+          refA.current = current;
+          return () => {
+            refA.current = null;
+          };
+        }
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=use-merged-ref.js.map
+
+      /***/
+    },
+
+    /***/ 2025: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/link",
+        function () {
+          return __webpack_require__(4591);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 3267: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "getDomainLocale", {
+        enumerable: true,
+        get: function () {
+          return getDomainLocale;
+        },
+      });
+      const _normalizetrailingslash = __webpack_require__(2371);
+      const basePath =
+        /* unused pure expression or super */ null && (false || "");
+      function getDomainLocale(path, locale, locales, domainLocales) {
+        if (false) {
+        } else {
+          return false;
+        }
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=get-domain-locale.js.map
+
+      /***/
+    },
+
+    /***/ 4591: /***/ (
+      __unused_webpack_module,
+      __webpack_exports__,
+      __webpack_require__
+    ) => {
+      "use strict";
+      __webpack_require__.r(__webpack_exports__);
+      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
+        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
+        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
+        /* harmony export */
+      });
+      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
+        __webpack_require__(1503);
+      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
+        __webpack_require__(6929);
+      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
+        /*#__PURE__*/ __webpack_require__.n(
+          next_link__WEBPACK_IMPORTED_MODULE_1__
+        );
+
+      function aLink(props) {
+        return /*#__PURE__*/ (0,
+        react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
+          children: [
+            /*#__PURE__*/ (0,
+            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h3", {
+              children: "A Link page!",
+            }),
+            /*#__PURE__*/ (0,
+            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
+              next_link__WEBPACK_IMPORTED_MODULE_1___default(),
+              {
+                href: "/",
+                children: "Go to /",
+              }
+            ),
+          ],
+        });
+      }
+      var __N_SSP = true;
+      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = aLink;
+
+      /***/
+    },
+
+    /***/ 6929: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(8885);
+
+      /***/
+    },
+
+    /***/ 7686: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "useIntersection", {
+        enumerable: true,
+        get: function () {
+          return useIntersection;
+        },
+      });
+      const _react = __webpack_require__(2223);
+      const _requestidlecallback = __webpack_require__(901);
+      const hasIntersectionObserver =
+        typeof IntersectionObserver === "function";
+      const observers = new Map();
+      const idList = [];
+      function createObserver(options) {
+        const id = {
+          root: options.root || null,
+          margin: options.rootMargin || "",
+        };
+        const existing = idList.find(
+          (obj) => obj.root === id.root && obj.margin === id.margin
+        );
+        let instance;
+        if (existing) {
+          instance = observers.get(existing);
+          if (instance) {
+            return instance;
+          }
+        }
+        const elements = new Map();
+        const observer = new IntersectionObserver((entries) => {
+          entries.forEach((entry) => {
+            const callback = elements.get(entry.target);
+            const isVisible =
+              entry.isIntersecting || entry.intersectionRatio > 0;
+            if (callback && isVisible) {
+              callback(isVisible);
+            }
+          });
+        }, options);
+        instance = {
+          id,
+          observer,
+          elements,
+        };
+        idList.push(id);
+        observers.set(id, instance);
+        return instance;
+      }
+      function observe(element, callback, options) {
+        const { id, observer, elements } = createObserver(options);
+        elements.set(element, callback);
+        observer.observe(element);
+        return function unobserve() {
+          elements.delete(element);
+          observer.unobserve(element);
+          // Destroy observer when there's nothing left to watch:
+          if (elements.size === 0) {
+            observer.disconnect();
+            observers.delete(id);
+            const index = idList.findIndex(
+              (obj) => obj.root === id.root && obj.margin === id.margin
+            );
+            if (index > -1) {
+              idList.splice(index, 1);
+            }
+          }
+        };
+      }
+      function useIntersection({ rootRef, rootMargin, disabled }) {
+        const isDisabled = disabled || !hasIntersectionObserver;
+        const [visible, setVisible] = (0, _react.useState)(false);
+        const elementRef = (0, _react.useRef)(null);
+        const setElement = (0, _react.useCallback)((element) => {
+          elementRef.current = element;
+        }, []);
+        (0, _react.useEffect)(() => {
+          if (hasIntersectionObserver) {
+            if (isDisabled || visible) return;
+            const element = elementRef.current;
+            if (element && element.tagName) {
+              const unobserve = observe(
+                element,
+                (isVisible) => isVisible && setVisible(isVisible),
+                {
+                  root: rootRef?.current,
+                  rootMargin,
+                }
+              );
+              return unobserve;
+            }
+          } else {
+            if (!visible) {
+              const idleCallback = (0,
+              _requestidlecallback.requestIdleCallback)(() => setVisible(true));
+              return () =>
+                (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
+            }
+          }
+          // eslint-disable-next-line react-hooks/exhaustive-deps
+        }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
+        const resetVisible = (0, _react.useCallback)(() => {
+          setVisible(false);
+        }, []);
+        return [setElement, visible, resetVisible];
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=use-intersection.js.map
+
+      /***/
+    },
+
+    /***/ 8101: /***/ (__unused_webpack_module, exports) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "errorOnce", {
+        enumerable: true,
+        get: function () {
+          return errorOnce;
+        },
+      });
+      let errorOnce = (_) => {};
+      if (false) {
+      } //# sourceMappingURL=error-once.js.map
+
+      /***/
+    },
+
+    /***/ 8885: /***/ (module, exports, __webpack_require__) => {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -28,17 +359,17 @@
       const _react = /*#__PURE__*/ _interop_require_wildcard._(
         __webpack_require__(2223)
       );
-      const _resolvehref = __webpack_require__(2275);
-      const _islocalurl = __webpack_require__(3179);
-      const _formaturl = __webpack_require__(5486);
-      const _utils = __webpack_require__(3708);
-      const _addlocale = __webpack_require__(8225);
-      const _routercontextsharedruntime = __webpack_require__(6046);
-      const _useintersection = __webpack_require__(2678);
-      const _getdomainlocale = __webpack_require__(4499);
-      const _addbasepath = __webpack_require__(7434);
-      const _usemergedref = __webpack_require__(2263);
-      const _erroronce = __webpack_require__(2197);
+      const _resolvehref = __webpack_require__(7379);
+      const _islocalurl = __webpack_require__(4843);
+      const _formaturl = __webpack_require__(9374);
+      const _utils = __webpack_require__(3116);
+      const _addlocale = __webpack_require__(8065);
+      const _routercontextsharedruntime = __webpack_require__(5470);
+      const _useintersection = __webpack_require__(7686);
+      const _getdomainlocale = __webpack_require__(3267);
+      const _addbasepath = __webpack_require__(1450);
+      const _usemergedref = __webpack_require__(1511);
+      const _erroronce = __webpack_require__(8101);
       const prefetched = new Set();
       function prefetch(router, href, as, options) {
         if (false) {
@@ -416,344 +747,13 @@
 
       /***/
     },
-
-    /***/ 2197: /***/ (__unused_webpack_module, exports) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "errorOnce", {
-        enumerable: true,
-        get: function () {
-          return errorOnce;
-        },
-      });
-      let errorOnce = (_) => {};
-      if (false) {
-      } //# sourceMappingURL=error-once.js.map
-
-      /***/
-    },
-
-    /***/ 2263: /***/ (module, exports, __webpack_require__) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "useMergedRef", {
-        enumerable: true,
-        get: function () {
-          return useMergedRef;
-        },
-      });
-      const _react = __webpack_require__(2223);
-      function useMergedRef(refA, refB) {
-        const cleanupA = (0, _react.useRef)(null);
-        const cleanupB = (0, _react.useRef)(null);
-        // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
-        // (this happens often if the user doesn't pass a ref to Link/Form/Image)
-        // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
-        // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
-        // (because it hasn't been updated for React 19)
-        // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
-        // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
-        return (0, _react.useCallback)(
-          (current) => {
-            if (current === null) {
-              const cleanupFnA = cleanupA.current;
-              if (cleanupFnA) {
-                cleanupA.current = null;
-                cleanupFnA();
-              }
-              const cleanupFnB = cleanupB.current;
-              if (cleanupFnB) {
-                cleanupB.current = null;
-                cleanupFnB();
-              }
-            } else {
-              if (refA) {
-                cleanupA.current = applyRef(refA, current);
-              }
-              if (refB) {
-                cleanupB.current = applyRef(refB, current);
-              }
-            }
-          },
-          [refA, refB]
-        );
-      }
-      function applyRef(refA, current) {
-        if (typeof refA === "function") {
-          const cleanup = refA(current);
-          if (typeof cleanup === "function") {
-            return cleanup;
-          } else {
-            return () => refA(null);
-          }
-        } else {
-          refA.current = current;
-          return () => {
-            refA.current = null;
-          };
-        }
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=use-merged-ref.js.map
-
-      /***/
-    },
-
-    /***/ 2369: /***/ (
-      __unused_webpack_module,
-      __webpack_exports__,
-      __webpack_require__
-    ) => {
-      "use strict";
-      __webpack_require__.r(__webpack_exports__);
-      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
-        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
-        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
-        /* harmony export */
-      });
-      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
-        __webpack_require__(1503);
-      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(6691);
-      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
-        /*#__PURE__*/ __webpack_require__.n(
-          next_link__WEBPACK_IMPORTED_MODULE_1__
-        );
-
-      function aLink(props) {
-        return /*#__PURE__*/ (0,
-        react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
-          children: [
-            /*#__PURE__*/ (0,
-            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h3", {
-              children: "A Link page!",
-            }),
-            /*#__PURE__*/ (0,
-            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
-              next_link__WEBPACK_IMPORTED_MODULE_1___default(),
-              {
-                href: "/",
-                children: "Go to /",
-              }
-            ),
-          ],
-        });
-      }
-      var __N_SSP = true;
-      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = aLink;
-
-      /***/
-    },
-
-    /***/ 2678: /***/ (module, exports, __webpack_require__) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "useIntersection", {
-        enumerable: true,
-        get: function () {
-          return useIntersection;
-        },
-      });
-      const _react = __webpack_require__(2223);
-      const _requestidlecallback = __webpack_require__(4213);
-      const hasIntersectionObserver =
-        typeof IntersectionObserver === "function";
-      const observers = new Map();
-      const idList = [];
-      function createObserver(options) {
-        const id = {
-          root: options.root || null,
-          margin: options.rootMargin || "",
-        };
-        const existing = idList.find(
-          (obj) => obj.root === id.root && obj.margin === id.margin
-        );
-        let instance;
-        if (existing) {
-          instance = observers.get(existing);
-          if (instance) {
-            return instance;
-          }
-        }
-        const elements = new Map();
-        const observer = new IntersectionObserver((entries) => {
-          entries.forEach((entry) => {
-            const callback = elements.get(entry.target);
-            const isVisible =
-              entry.isIntersecting || entry.intersectionRatio > 0;
-            if (callback && isVisible) {
-              callback(isVisible);
-            }
-          });
-        }, options);
-        instance = {
-          id,
-          observer,
-          elements,
-        };
-        idList.push(id);
-        observers.set(id, instance);
-        return instance;
-      }
-      function observe(element, callback, options) {
-        const { id, observer, elements } = createObserver(options);
-        elements.set(element, callback);
-        observer.observe(element);
-        return function unobserve() {
-          elements.delete(element);
-          observer.unobserve(element);
-          // Destroy observer when there's nothing left to watch:
-          if (elements.size === 0) {
-            observer.disconnect();
-            observers.delete(id);
-            const index = idList.findIndex(
-              (obj) => obj.root === id.root && obj.margin === id.margin
-            );
-            if (index > -1) {
-              idList.splice(index, 1);
-            }
-          }
-        };
-      }
-      function useIntersection({ rootRef, rootMargin, disabled }) {
-        const isDisabled = disabled || !hasIntersectionObserver;
-        const [visible, setVisible] = (0, _react.useState)(false);
-        const elementRef = (0, _react.useRef)(null);
-        const setElement = (0, _react.useCallback)((element) => {
-          elementRef.current = element;
-        }, []);
-        (0, _react.useEffect)(() => {
-          if (hasIntersectionObserver) {
-            if (isDisabled || visible) return;
-            const element = elementRef.current;
-            if (element && element.tagName) {
-              const unobserve = observe(
-                element,
-                (isVisible) => isVisible && setVisible(isVisible),
-                {
-                  root: rootRef?.current,
-                  rootMargin,
-                }
-              );
-              return unobserve;
-            }
-          } else {
-            if (!visible) {
-              const idleCallback = (0,
-              _requestidlecallback.requestIdleCallback)(() => setVisible(true));
-              return () =>
-                (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
-            }
-          }
-          // eslint-disable-next-line react-hooks/exhaustive-deps
-        }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
-        const resetVisible = (0, _react.useCallback)(() => {
-          setVisible(false);
-        }, []);
-        return [setElement, visible, resetVisible];
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=use-intersection.js.map
-
-      /***/
-    },
-
-    /***/ 4499: /***/ (module, exports, __webpack_require__) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "getDomainLocale", {
-        enumerable: true,
-        get: function () {
-          return getDomainLocale;
-        },
-      });
-      const _normalizetrailingslash = __webpack_require__(1379);
-      const basePath =
-        /* unused pure expression or super */ null && (false || "");
-      function getDomainLocale(path, locale, locales, domainLocales) {
-        if (false) {
-        } else {
-          return false;
-        }
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=get-domain-locale.js.map
-
-      /***/
-    },
-
-    /***/ 6691: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(69);
-
-      /***/
-    },
-
-    /***/ 6771: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/link",
-        function () {
-          return __webpack_require__(2369);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(6771)
+      __webpack_exec__(2025)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for routerDirect-HASH.js
@@ -1,7 +1,34 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [188],
   {
-    /***/ 97: /***/ (
+    /***/ 417: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/routerDirect",
+        function () {
+          return __webpack_require__(5491);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 1840: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(3252);
+
+      /***/
+    },
+
+    /***/ 5491: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -16,7 +43,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7798);
+        __webpack_require__(1840);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -35,40 +62,13 @@
 
       /***/
     },
-
-    /***/ 4283: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/routerDirect",
-        function () {
-          return __webpack_require__(97);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 7798: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(9300);
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(4283)
+      __webpack_exec__(417)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for script-HASH.js
@@ -1,17 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [1209],
   {
-    /***/ 5964: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(2010);
-
-      /***/
-    },
-
-    /***/ 7758: /***/ (
+    /***/ 1312: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -26,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(5964);
+        __webpack_require__(2398);
       /* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_script__WEBPACK_IMPORTED_MODULE_1__
@@ -59,7 +49,17 @@
       /***/
     },
 
-    /***/ 8803: /***/ (
+    /***/ 2398: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(8954);
+
+      /***/
+    },
+
+    /***/ 4305: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -67,7 +67,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/script",
         function () {
-          return __webpack_require__(7758);
+          return __webpack_require__(1312);
         },
       ]);
       if (false) {
@@ -81,7 +81,7 @@
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(8803)
+      __webpack_exec__(4305)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for withRouter-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [3263],
   {
-    /***/ 184: /***/ (
+    /***/ 358: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -16,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7798);
+        __webpack_require__(1840);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -35,7 +35,17 @@
       /***/
     },
 
-    /***/ 3163: /***/ (
+    /***/ 1840: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(3252);
+
+      /***/
+    },
+
+    /***/ 4041: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -43,7 +53,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/withRouter",
         function () {
-          return __webpack_require__(184);
+          return __webpack_require__(358);
         },
       ]);
       if (false) {
@@ -51,23 +61,13 @@
 
       /***/
     },
-
-    /***/ 7798: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(9300);
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(3163)
+      __webpack_exec__(4041)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for 436-HASH.js

Diff too large to display

Diff for 9760-HASH.js
failed to diff
Diff for main-HASH.js

Diff too large to display

Commit: 4e0b90c

@lukesandberg lukesandberg changed the base branch from canary to graphite-base/85592 November 5, 2025 23:39
@ijjk ijjk added the Turbopack Related to Turbopack with Next.js. label Nov 5, 2025
@lukesandberg lukesandberg changed the base branch from graphite-base/85592 to sync_bindings November 5, 2025 23:40
Copy link
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

export function formatIssue(issue: Issue) {
export async function formatIssue(issue: Issue) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export async function formatIssue(issue: Issue) {
export function formatIssue(issue: Issue): string {

formatIssue was incorrectly changed to be async, but it contains no async operations. This causes Promise objects to be treated as strings in processIssues, resulting in [object Promise] being logged instead of actual error messages.

View Details

Analysis

formatIssue() incorrectly declared as async without any async operations

What fails: The formatIssue() function in packages/next/src/shared/lib/turbopack/utils.ts (line 94) is declared as async but contains no await statements. When called without await in processIssues() (lines 78, 83) and in test files, it returns a Promise object instead of a string, causing error messages to display as [object Promise].

How to reproduce:

// In packages/next/src/shared/lib/turbopack/utils.ts, the function is currently:
export async function formatIssue(issue: Issue) {
  // ... completely synchronous operations, no await ...
  return message
}

// Called without await in processIssues():
const formatted = formatIssue(issue)  // Returns Promise, not string
relevantIssues.add(formatted)         // Adds Promise to Set
Log.error(formatted)                  // Logs "[object Promise]"

Result:

  • Error messages display as [object Promise] instead of actual error descriptions
  • Tests calling formatIssue() without await (e.g., line 39 in utils.test.ts) would receive Promises instead of strings
  • Error reporting functionality is broken for paths through processIssues()

Expected behavior: formatIssue() should be synchronous since all operations within it are synchronous:

  • renderCodeFrame() uses getBindingsSync() - synchronous
  • renderStyledStringToErrorAnsi() - synchronous
  • All string manipulations and conditionals - synchronous
  • No async operations or await statements anywhere in the function

The fix is to remove the async keyword and add explicit return type : string to the function declaration.

}

const scss = getScssError(sourceFilename, sourceContent, err)
const scss = await getScssError(sourceFilename, sourceContent, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const scss = await getScssError(sourceFilename, sourceContent, err)
const scss = getScssError(sourceFilename, sourceContent, err)

The getScssError function is being called with await on line 92, but the function is synchronous (not async), making the await incorrect and unnecessary.

View Details

Analysis

Unnecessary await on synchronous getScssError() function call

What fails: getScssError() is a synchronous function (returns SimpleWebpackError | false), but line 92 of webpackModuleError.ts calls it with await, which is unnecessary and inconsistent with similar error handlers in the same function.

How to reproduce:

# The code is in: packages/next/src/build/webpack/plugins/wellknown-errors-plugin/webpackModuleError.ts
# Line 92: const scss = await getScssError(sourceFilename, sourceContent, err)

Result: While JavaScript/TypeScript will not error at runtime (await on non-Promise values simply returns the value), this:

  • Breaks consistency with other synchronous error handlers (getBabelError, getCssError, getNextFontError, getNextAppLoaderError, getNextInvalidImportError) which are called without await
  • Suggests incorrect refactoring or developer intent mismatch
  • May mislead future maintainers into thinking the function is async

Expected: Remove the await keyword to match the pattern of other synchronous error handlers:

  • Line 69: const babel = getBabelError(sourceFilename, err) (no await)
  • Line 74: const css = getCssError(sourceFilename, err) (no await)
  • Line 76: const scss = getScssError(sourceFilename, sourceContent, err) (no await - fixed)

The function definition in parseScss.ts:8 confirms it is synchronous: export function getScssError(fileName: string, fileContent: string | null, err: Error): SimpleWebpackError | false

@lukesandberg lukesandberg force-pushed the code_frame_defence branch 6 times, most recently from fee0d1a to ae5325c Compare November 7, 2025 23:51
@lukesandberg lukesandberg changed the base branch from sync_bindings to graphite-base/85592 November 8, 2025 22:42
lukesandberg and others added 7 commits November 8, 2025 22:43
Implements Phase 1-3 of the babel-code-frame replacement:

- Core frame rendering with line numbers, gutters, and error markers
- Terminal width detection with 140 char default
- Smart line truncation that scrolls all lines consistently
- Ellipsis markers (...) for truncated content
- ColorScheme abstraction with colored() and plain() variants
- 15 comprehensive tests covering edge cases

Key features:
- Sans-IO design (no file IO, WASM-compatible)
- Handles arbitrary file sizes (tested with 50k lines, 5MB files)
- Centers error spans in viewport with uniform line truncation
- UTF-8 safe string slicing at character boundaries
- Follows rustc's line truncation UX patterns

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Improvements:
- Multiline errors now show markers on both first and last lines
  - First line: marker at start_column
  - Last line: marker at end_column with message
  - Middle lines: no marker (just > indicator)
- Messages only render once on the last error line's marker
- Column clamping allows positions one past line length (standard behavior)
- Single-line errors with end_column show spanning markers (^^^)

New test coverage:
- test_multiline_error_with_message: validates message placement
- test_invalid_column_start_out_of_bounds: column past line end
- test_invalid_column_end_before_start: reversed columns
- test_invalid_column_both_out_of_bounds: both columns invalid
- test_invalid_multiline_end_column_out_of_bounds: multiline clamping

All 20 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Multiline errors now display spanning markers to clearly show the full extent of the error:

- First line: ^^^ from start_column to end of line
- Last line: ^^^ from start to end_column
- Single-line errors with end_column: ^^^ spanning the range
- Single-line errors without end_column: single ^ (unchanged)

This makes multiline errors much easier to understand at a glance, similar to how rustc and other modern compilers display errors.

Examples:
```
> 2 |   console.log('hello')
    |   ^^^^^^^^^^^^^^^^^^^
> 3 |   return 42
    | ^^^^^^^^^^^^ Unexpected expression
```

All 20 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Major refactoring to make marker rendering clearer and more maintainable:

**Key improvements:**
1. Normalize end_column upfront (before rendering loop)
   - Single-line without end_column: defaults to start_column + 1
   - Multiline without end_column: error (ambiguous)
   - This eliminates many conditional branches later

2. Unified range calculation for all marker types
   - Single-line: (start_column, end_column)
   - First line of multiline: (start_column, line_end)
   - Last line of multiline: (1, end_column)
   - All use same truncation and clamping logic

3. Cleaner clamping strategy
   - Allow end to extend 1 char past line (handles off-by-one)
   - Prevents excessive spans from invalid input
   - Consistent behavior across all cases

**Results:**
- 41 fewer lines of code (-19%)
- Eliminated complex nested conditionals
- All 20 tests passing
- Same behavior, clearer intent

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Replace nested conditionals with concise saturating subtraction:

Before:
```rust
let marker_col = if column_offset > 0 {
    if range_start > column_offset {
        range_start - column_offset + 1
    } else {
        1
    }
} else {
    range_start
};
```

After:
```rust
let marker_col = range_start
    .saturating_sub(column_offset.saturating_sub(1))
    .max(1);
```

This formula correctly handles:
- No truncation (offset=0): returns range_start
- Truncated visible: returns adjusted position
- Scrolled out of view: clamps to 1

Same behavior, much clearer intent. All 20 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Replace `String::repeat()` with direct character pushing to eliminate
temporary allocations:

Before:
```rust
output.push_str(&" ".repeat(marker_col - 1));
output.push_str(&"^".repeat(marker_length));
```

After:
```rust
for _ in 0..(marker_col - 1) {
    output.push(' ');
}
for _ in 0..marker_length {
    output.push('^');
}
```

Benefits:
- No temporary string allocations
- Direct appending to output buffer
- Same behavior, better performance
- Especially impactful for large marker spans

All 20 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
lukesandberg and others added 22 commits November 8, 2025 22:43
Created a reusable helper function that:
- Pre-reserves capacity to avoid multiple allocations
- Pushes characters directly into the buffer
- Marked inline for zero-cost abstraction

Applied to all 3 repetition sites:
1. Message indentation (gutter_total_width spaces)
2. Marker column spacing (marker_col - 1 spaces)
3. Error marker carets (marker_length '^' chars)

Benefits:
- Single implementation of the optimization pattern
- Easier to maintain and understand
- Better performance with upfront reservation
- Cleaner call sites

Example:
```rust
// Before
output.push_str(&" ".repeat(gutter_total_width));

// After
repeat_char_into(&mut output, ' ', gutter_total_width);
```

All 20 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Fixed 6 clippy warnings:

1. **Single-char string literals** → `push(char)`
   - `output.push_str(" ")` → `output.push(' ')`

2. **Collapsed nested if statements** with `if let ... &&`
   - More concise and idiomatic
   - Applied in 3 locations

3. **Length comparison** → `is_empty()`
   - `line_content.len() > 0` → `!line_content.is_empty()`

All changes are zero-cost and improve readability. Tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Improved readability by extracting complex logic into well-named helpers:

**1. apply_line_truncation()**
Centralizes the truncation decision logic:
- Global truncation offset applied to all lines
- Individual line truncation for lines exceeding width
- No truncation for lines that fit

Before: 8 lines of conditional logic inline
After: Single function call

**2. calculate_marker_position()**
Encapsulates all marker positioning logic:
- Determines column range based on error type (single/multi-line)
- Clamps to valid bounds
- Accounts for truncation offsets
- Calculates visible span length

Before: 45+ lines of complex nested logic
After: Single function call with clear parameters

**Benefits:**
- Main rendering loop is much easier to follow
- Logic can be tested independently
- Clear separation of concerns
- Self-documenting through function names

All 20 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added comprehensive documentation explaining the inclusive vs exclusive
semantics of end_column:

**Single-line errors:**
- After normalization, end_column is EXCLUSIVE (one past last char)
- Example: start=5, end=6 marks exactly column 5
- This matches Rust's range semantics [start, end)

**Multiline errors:**
- User provides end_column as INCLUSIVE (the last column to mark)
- Example: end_column=12 means mark UP TO AND INCLUDING column 12
- We convert to exclusive internally with +1 for consistent calculation

**Why the difference?**
- Single-line: Matches typical API expectations (like string slicing)
- Multiline: More intuitive for users to specify "mark to column 12"
- Internal calc uses exclusive ranges for consistency

This explains the seemingly inconsistent +1 logic in calculate_marker_position.

All 20 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changes:
- Introduce Location struct with line and column fields
- Update CodeFrameLocation to use nested start/end Location fields
- Update calculate_truncation_offset to center on error range midpoint
- Fix gutter formatting (remove extra leading space)
- Fix marker column calculation for truncated content
- Update all tests to use new nested API
- Export Location from public API

Benefits:
- Makes end line/column pairing explicit and type-safe
- Prevents invalid states where end_line exists without end_column
- Centers truncation on entire error range, not just start column
- More consistent with exclusive end_column semantics

17/21 tests passing. Remaining 4 failures are test expectations that
need updating to match new error range centering behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changes:
- Fix Cargo.toml dev-dependencies typo (dev.dependencies → dev-dependencies)
- Add insta with yaml features for snapshot testing
- Convert all 21 tests from assert_eq! to assert_snapshot!
- Generate snapshot files for all test cases

Benefits:
- Much easier to review and update test expectations
- Better diff output when tests fail
- Can use `cargo insta review` to update snapshots
- Snapshots capture the actual rendered output format

All 21 tests passing with new snapshot approach.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changes:
- Replace SWC with OXC parser for better API accessibility
- Implement offset/marker pair approach for style tracking
- Extract highlights by visiting OXC AST (strings, numbers, regex, templates)
- Split style markers across line boundaries (no spans cross newlines)
- Implement apply_line_highlights() for late ANSI code insertion

Architecture:
- `extract_highlights()` - Parse entire file once with OXC, extract all tokens
- `StyleMarker` - Records start/end of styled regions by byte offset
- `LineHighlight` - Per-line highlighting info with line-relative offsets
- `split_markers_by_line()` - Ensures markers never cross line boundaries
- `apply_line_highlights()` - Applies ANSI codes during rendering

Benefits:
- OXC parser is fast and has accessible public API
- Line-oriented rendering never needs to handle newlines in styles
- Late insertion prevents column offset issues
- Supports multiline tokens (template literals, strings) properly

Dependencies added:
- oxc_parser, oxc_ast, oxc_span, oxc_allocator (0.42.0)
- Removed swc_core dependency

Next: Integrate into frame rendering and add ANSI stripping for tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Fixed all import and API usage issues:
- Use swc_common 15.0.0 to match swc_ecma_lexer 24.0.0
- Import Token from swc_ecma_lexer::token::Token
- Import TsSyntax from swc_ecma_lexer
- Use .into() for FileName Arc wrapping
- lexer.next() returns Option<TokenAndSpan>, not Result
- Use Token enum correctly in pattern matching

The highlight module now compiles successfully. The dead code warnings
are expected - next step is to integrate with render_code_frame().

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added comprehensive tests for the highlighting module:
- test_extract_highlights_basic: Verify single-line highlighting
- test_extract_highlights_multiline: Verify multi-line handling
- test_apply_line_highlights_basic: Verify ANSI code insertion
- test_apply_line_highlights_plain: Verify plain mode

Fixed critical bug: BytePos in SWC is 1-indexed (BytePos(0) is reserved),
so we need to subtract 1 to get 0-indexed offsets for our markers.

All 25 tests now pass (21 original + 4 highlighting tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added complete syntax highlighting integration:

Highlighting Architecture:
- Extract highlights once at the start of render_code_frame()
- Apply highlights "late" after line truncation to avoid column offset issues
- adjust_highlights_for_truncation() handles truncated views correctly
- Markers are filtered and adjusted for the visible range

ANSI Stripping:
- Added strip_ansi_codes() utility for testing
- Strips escape sequences while preserving text content

Testing:
- Added test_highlighting_doesnt_break_formatting() integration test
- Verifies highlighting with use_colors=false produces identical output
- Verifies ANSI-stripped output matches plain output
- Ensures highlighting doesn't mess up basic formatting

Additional Tests:
- test_strip_ansi_codes: Verify ANSI stripping works
- test_strip_ansi_codes_preserves_plain_text: No-op on plain text
- test_adjust_highlights_for_truncation: Verify marker adjustment

All 29 tests pass. Highlighting is fully functional and doesn't
break any existing functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added examples/highlighting_demo.rs to showcase the syntax highlighting
feature. The demo shows:
- Code frame without highlighting
- Code frame with syntax highlighting enabled

Keywords, identifiers, strings, and numbers are color-coded while
maintaining correct error marker positioning.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added comprehensive token highlighting to match Babel's behavior:

Comments Support:
- Integrated SWC's SingleThreadedComments API
- Extract both leading and trailing comments
- Supports single-line (//) and multi-line (/* */) comments
- Comments styled in gray matching Babel

Punctuation Support:
- All punctuation tokens highlighted in yellow (Babel style)
- Includes: = ; , . ... => : ? + - * / % operators etc
- Brackets ()[]{}  remain unstyled (matching Babel)
- Added @ # $ { for template literals

Token Classification:
- Keywords (cyan): const, let, var, if, etc.
- Identifiers (yellow): variable names
- Strings (green): "...", '...', template literals
- Numbers (magenta): 123, 0x42, bigints
- Regex (magenta): /pattern/flags
- Punctuation (yellow): all non-bracket punctuation
- Comments (gray): // and /* */
- Brackets: unstyled

Tests Added:
- test_comments_and_punctuation: Verifies inline comments and operators
- test_multiline_comment: Verifies multi-line comment handling

Demo:
- comments_punctuation_demo.rs: Shows comments and punctuation highlighting

All 31 tests pass. Highlighting now matches Babel's babel-code-frame.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added complete_highlighting_demo.rs showing all highlighting features:
- Comments (single-line and multi-line)
- Keywords
- Identifiers
- Strings
- Numbers
- Regex
- Punctuation
- Brackets (unstyled)
- JSX syntax

Includes a color key showing all token types and their colors,
demonstrating full Babel babel-code-frame compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…ests

Changes:
- Moved strip_ansi_codes() from public API to test-only helper
- Made highlight::tests module public for cross-module test access
- Added render_for_snapshot() helper that enables highlighting and strips ANSI
- Updated all 21 render_code_frame() calls in tests to use render_for_snapshot()

Result:
- All tests now verify highlighting doesn't break formatting
- No snapshot changes required (proving highlighting works correctly!)
- All 31 tests pass

This ensures that every code frame rendering path is tested with
highlighting enabled, giving us confidence that the feature works
correctly across all edge cases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
… line splitting

Changes:
- Removed complex split_markers_by_line function
- Added simpler group_markers_by_line that distributes markers to lines
- Updated add_token_markers to accept TokenAndSpan.had_line_break (for future optimization)
- Kept build_line_offset_map for now (SourceFile.lookup_line would add overhead)
- Simplified marker distribution logic

Benefits:
- Cleaner code with less duplication
- Easier to understand marker-to-line mapping
- Foundation for future optimization using had_line_break
- All 31 tests still pass

Note: We explored using SourceFile.lookup_line() but kept our offset map
approach as it's more efficient for iterating through all lines.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Fixed algorithmic complexity and leveraged SWC's efficient APIs:

Performance improvements:
- add_token_markers: Split multiline tokens using had_line_break detection
  - Only calls lookup_line() when had_line_break is true (rare case)
  - Uses line_bounds() to get accurate line boundaries
  - O(log n) lookups only for tokens after newlines

- group_markers_by_line: Fixed O(markers × lines) → O(markers + lines)
  - Single pass through sorted markers using marker index
  - Never revisits earlier markers (they're sorted by offset)
  - Uses fm.count_lines() and fm.line_bounds() instead of manual scanning

Removed:
- build_line_offset_map(): Replaced by fm.line_bounds() API

Code quality:
- Fixed clippy warnings (collapsed nested if statements with let-chains)
- Cleaner, more idiomatic Rust code

All 31 tests pass with improved performance!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Reduce memory usage by accepting a line range parameter in extract_highlights()
and only producing style markers for lines that will be displayed in the code
frame. This is especially beneficial for large files where only ~10-20 lines
are visible.

Changes:
- Add Range<usize> parameter to extract_highlights() for 0-indexed line ranges
- Add byte-range filtering to skip marker production outside visible range
- Still lex entire file to maintain correct parsing state
- Update frame.rs to pass first_line..last_line range
- Add tests for line range filtering and marker count reduction

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Expose the Rust code-frame implementation to Node.js via NAPI:
- Create code_frame.rs module with codeFrameColumns function
- Use From trait for clean conversions instead of manual copying
- Add Default derive to CodeFrameOptionsJs
- Document default values in doc comments (lines_above: 2, lines_below: 3)
- Add next-code-frame dependency to napi crate

This allows Next.js to use the faster, more scalable Rust implementation
instead of Babel's code-frame for error reporting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@graphite-app graphite-app bot changed the base branch from graphite-base/85592 to canary November 8, 2025 22:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Turbopack team PRs by the Turbopack team. Turbopack Related to Turbopack with Next.js. type: next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Turbopack stack overflow when compiling a page with @techstark/opencv-js import

3 participants