Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: error code #73332

Merged
merged 3 commits into from
Dec 11, 2024
Merged

feat: error code #73332

merged 3 commits into from
Dec 11, 2024

Conversation

gaojude
Copy link
Contributor

@gaojude gaojude commented Nov 29, 2024

This update introduces a custom swc-plugin for Next.js that appends a non-enumerable __NEXT_ERROR_CODE property to all Error instances, enabling anonymous telemetry while keeping error codes hidden from console logs. The plugin manages error codes through an append-only /packages/next/errors.json file, which is automatically updated during the pnpm build process using a two-pass system to ensure consistent error mapping, even during concurrent builds.

@ijjk ijjk added created-by: Next.js team PRs by the Next.js team. Turbopack Related to Turbopack with Next.js. type: next labels Nov 29, 2024
packages/next/taskfile.js Outdated Show resolved Hide resolved
@ijjk ijjk added the tests label Dec 3, 2024
@gaojude gaojude force-pushed the jude/error-coding branch 2 times, most recently from d0ddec6 to 9a4e8a8 Compare December 3, 2024 21:28
@gaojude gaojude changed the title draft: error code system feat: error code Dec 3, 2024
@gaojude gaojude force-pushed the jude/error-coding branch 6 times, most recently from 9b88304 to 23aa093 Compare December 4, 2024 20:19
@vercel vercel deleted a comment from ijjk Dec 4, 2024
@gaojude gaojude force-pushed the jude/error-coding branch 2 times, most recently from 53f2290 to 6b68825 Compare December 5, 2024 20:48
@ijjk
Copy link
Member

ijjk commented Dec 5, 2024

Tests Passed

@ijjk
Copy link
Member

ijjk commented Dec 5, 2024

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary vercel/next.js jude/error-coding Change
buildDuration 19.2s 15.6s N/A
buildDurationCached 14.6s 12.5s N/A
nodeModulesSize 409 MB 409 MB ⚠️ +679 kB
nextStartRea..uration (ms) 469ms 469ms
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary vercel/next.js jude/error-coding Change
1187-HASH.js gzip 50.4 kB 50.7 kB ⚠️ +314 B
8276.HASH.js gzip 169 B 168 B N/A
8377-HASH.js gzip 5.3 kB 5.36 kB N/A
bccd1874-HASH.js gzip 53 kB 53 kB N/A
framework-HASH.js gzip 57.5 kB 57.5 kB N/A
main-app-HASH.js gzip 232 B 235 B N/A
main-HASH.js gzip 33.8 kB 34 kB ⚠️ +260 B
webpack-HASH.js gzip 1.71 kB 1.71 kB N/A
Overall change 84.2 kB 84.8 kB ⚠️ +574 B
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js jude/error-coding 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 jude/error-coding Change
_app-HASH.js gzip 193 B 193 B
_error-HASH.js gzip 193 B 193 B
amp-HASH.js gzip 512 B 510 B N/A
css-HASH.js gzip 343 B 342 B N/A
dynamic-HASH.js gzip 1.84 kB 1.84 kB
edge-ssr-HASH.js gzip 265 B 265 B
head-HASH.js gzip 363 B 362 B N/A
hooks-HASH.js gzip 393 B 392 B N/A
image-HASH.js gzip 4.44 kB 4.49 kB N/A
index-HASH.js gzip 268 B 268 B
link-HASH.js gzip 2.35 kB 2.34 kB N/A
routerDirect..HASH.js gzip 328 B 328 B
script-HASH.js gzip 397 B 397 B
withRouter-HASH.js gzip 323 B 326 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 3.59 kB 3.59 kB
Client Build Manifests
vercel/next.js canary vercel/next.js jude/error-coding Change
_buildManifest.js gzip 747 B 746 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary vercel/next.js jude/error-coding Change
index.html gzip 524 B 524 B
link.html gzip 539 B 539 B
withRouter.html gzip 520 B 520 B
Overall change 1.58 kB 1.58 kB
Edge SSR bundle Size Overall increase ⚠️
vercel/next.js canary vercel/next.js jude/error-coding Change
edge-ssr.js gzip 127 kB 128 kB ⚠️ +940 B
page.js gzip 202 kB 203 kB ⚠️ +1.13 kB
Overall change 330 kB 332 kB ⚠️ +2.07 kB
Middleware size Overall increase ⚠️
vercel/next.js canary vercel/next.js jude/error-coding Change
middleware-b..fest.js gzip 670 B 666 B N/A
middleware-r..fest.js gzip 155 B 156 B N/A
middleware.js gzip 31 kB 31.2 kB ⚠️ +152 B
edge-runtime..pack.js gzip 844 B 844 B
Overall change 31.9 kB 32 kB ⚠️ +152 B
Next Runtimes Overall increase ⚠️
vercel/next.js canary vercel/next.js jude/error-coding Change
523-experime...dev.js gzip 322 B 322 B
523.runtime.dev.js gzip 314 B 314 B
app-page-exp...dev.js gzip 322 kB 323 kB ⚠️ +820 B
app-page-exp..prod.js gzip 127 kB 127 kB ⚠️ +492 B
app-page-tur..prod.js gzip 140 kB 140 kB ⚠️ +502 B
app-page-tur..prod.js gzip 135 kB 135 kB ⚠️ +506 B
app-page.run...dev.js gzip 312 kB 313 kB ⚠️ +844 B
app-page.run..prod.js gzip 122 kB 123 kB ⚠️ +508 B
app-route-ex...dev.js gzip 37.1 kB 37.3 kB ⚠️ +196 B
app-route-ex..prod.js gzip 25.1 kB 25.4 kB ⚠️ +252 B
app-route-tu..prod.js gzip 25.1 kB 25.4 kB ⚠️ +252 B
app-route-tu..prod.js gzip 24.9 kB 25.2 kB ⚠️ +255 B
app-route.ru...dev.js gzip 38.7 kB 38.9 kB ⚠️ +198 B
app-route.ru..prod.js gzip 24.9 kB 25.2 kB ⚠️ +255 B
pages-api-tu..prod.js gzip 9.56 kB 9.67 kB ⚠️ +105 B
pages-api.ru...dev.js gzip 11.4 kB 11.6 kB ⚠️ +109 B
pages-api.ru..prod.js gzip 9.56 kB 9.66 kB ⚠️ +104 B
pages-turbo...prod.js gzip 21.3 kB 21.7 kB ⚠️ +336 B
pages.runtim...dev.js gzip 27 kB 27.4 kB ⚠️ +378 B
pages.runtim..prod.js gzip 21.3 kB 21.7 kB ⚠️ +336 B
server.runti..prod.js gzip 915 kB 916 kB ⚠️ +757 B
Overall change 2.35 MB 2.36 MB ⚠️ +7.21 kB
build cache Overall increase ⚠️
vercel/next.js canary vercel/next.js jude/error-coding Change
0.pack gzip 2.04 MB 2.05 MB ⚠️ +15.1 kB
index.pack gzip 72 kB 72.1 kB ⚠️ +117 B
Overall change 2.11 MB 2.12 MB ⚠️ +15.2 kB
Diff details
Diff for middleware.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for image-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [2983],
   {
-    /***/ 3705: /***/ (
+    /***/ 8255: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/image",
         function () {
-          return __webpack_require__(8448);
+          return __webpack_require__(8926);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 7342: /***/ (module, exports, __webpack_require__) => {
+    /***/ 4369: /***/ (module, exports, __webpack_require__) => {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -40,17 +40,17 @@
         __webpack_require__(6049)
       );
       const _head = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(7196)
+        __webpack_require__(956)
       );
-      const _getimgprops = __webpack_require__(2661);
-      const _imageconfig = __webpack_require__(72);
-      const _imageconfigcontextsharedruntime = __webpack_require__(6386);
-      const _warnonce = __webpack_require__(4496);
-      const _routercontextsharedruntime = __webpack_require__(6443);
+      const _getimgprops = __webpack_require__(485);
+      const _imageconfig = __webpack_require__(4664);
+      const _imageconfigcontextsharedruntime = __webpack_require__(1250);
+      const _warnonce = __webpack_require__(1648);
+      const _routercontextsharedruntime = __webpack_require__(907);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3433)
+        __webpack_require__(6489)
       );
-      const _usemergedref = __webpack_require__(1942);
+      const _usemergedref = __webpack_require__(5942);
       // This is replaced by webpack define plugin
       const configEnv = {
         deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
@@ -371,7 +371,7 @@
       /***/
     },
 
-    /***/ 1942: /***/ (module, exports, __webpack_require__) => {
+    /***/ 5942: /***/ (module, exports, __webpack_require__) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -432,7 +432,7 @@
       /***/
     },
 
-    /***/ 2661: /***/ (
+    /***/ 485: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -448,9 +448,9 @@
           return getImgProps;
         },
       });
-      const _warnonce = __webpack_require__(4496);
-      const _imageblursvg = __webpack_require__(4796);
-      const _imageconfig = __webpack_require__(72);
+      const _warnonce = __webpack_require__(1648);
+      const _imageblursvg = __webpack_require__(4812);
+      const _imageconfig = __webpack_require__(4664);
       const VALID_LOADING_VALUES =
         /* unused pure expression or super */ null && [
           "lazy",
@@ -617,8 +617,15 @@
           };
         }
         if (typeof defaultLoader === "undefined") {
-          throw new Error(
-            "images.loaderFile detected but the file is missing default export.\nRead more: https://nextjs.org/docs/messages/invalid-images-config"
+          throw Object.defineProperty(
+            new Error(
+              "images.loaderFile detected but the file is missing default export.\nRead more: https://nextjs.org/docs/messages/invalid-images-config"
+            ),
+            "__NEXT_ERROR_CODE",
+            {
+              value: "E165",
+              enumerable: false,
+            }
           );
         }
         let loader = rest.loader || defaultLoader;
@@ -630,11 +637,18 @@
         const isDefaultLoader = "__next_img_default" in loader;
         if (isDefaultLoader) {
           if (config.loader === "custom") {
-            throw new Error(
-              'Image with src "' +
-                src +
-                '" is missing "loader" prop.' +
-                "\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader"
+            throw Object.defineProperty(
+              new Error(
+                'Image with src "' +
+                  src +
+                  '" is missing "loader" prop.' +
+                  "\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader"
+              ),
+              "__NEXT_ERROR_CODE",
+              {
+                value: "E255",
+                enumerable: false,
+              }
             );
           }
         } else {
@@ -685,15 +699,29 @@
         if (isStaticImport(src)) {
           const staticImageData = isStaticRequire(src) ? src.default : src;
           if (!staticImageData.src) {
-            throw new Error(
-              "An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received " +
-                JSON.stringify(staticImageData)
+            throw Object.defineProperty(
+              new Error(
+                "An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received " +
+                  JSON.stringify(staticImageData)
+              ),
+              "__NEXT_ERROR_CODE",
+              {
+                value: "E464",
+                enumerable: false,
+              }
             );
           }
           if (!staticImageData.height || !staticImageData.width) {
-            throw new Error(
-              "An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received " +
-                JSON.stringify(staticImageData)
+            throw Object.defineProperty(
+              new Error(
+                "An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received " +
+                  JSON.stringify(staticImageData)
+              ),
+              "__NEXT_ERROR_CODE",
+              {
+                value: "E50",
+                enumerable: false,
+              }
             );
           }
           blurWidth = staticImageData.blurWidth;
@@ -824,7 +852,7 @@
       /***/
     },
 
-    /***/ 4796: /***/ (__unused_webpack_module, exports) => {
+    /***/ 4812: /***/ (__unused_webpack_module, exports) => {
       "use strict";
       /**
        * A shared function, used on both client and server, to generate a SVG blur placeholder.
@@ -879,7 +907,7 @@
       /***/
     },
 
-    /***/ 1969: /***/ (
+    /***/ 8273: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -906,10 +934,10 @@
         },
       });
       const _interop_require_default = __webpack_require__(173);
-      const _getimgprops = __webpack_require__(2661);
-      const _imagecomponent = __webpack_require__(7342);
+      const _getimgprops = __webpack_require__(485);
+      const _imagecomponent = __webpack_require__(4369);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3433)
+        __webpack_require__(6489)
       );
       function getImageProps(imgProps) {
         const { props } = (0, _getimgprops.getImgProps)(imgProps, {
@@ -941,7 +969,7 @@
       /***/
     },
 
-    /***/ 3433: /***/ (__unused_webpack_module, exports) => {
+    /***/ 6489: /***/ (__unused_webpack_module, exports) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -976,7 +1004,7 @@
       /***/
     },
 
-    /***/ 8448: /***/ (
+    /***/ 8926: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -993,8 +1021,8 @@
 
       // EXTERNAL MODULE: ./node_modules/.pnpm/react@19.0.0/node_modules/react/jsx-runtime.js
       var jsx_runtime = __webpack_require__(5105);
-      // EXTERNAL MODULE: ./node_modules/.pnpm/next@file+..+main-repo+packages+next+next-packed.tgz_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image.js
-      var next_image = __webpack_require__(8140);
+      // EXTERNAL MODULE: ./node_modules/.pnpm/next@file+..+diff-repo+packages+next+next-packed.tgz_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image.js
+      var next_image = __webpack_require__(5434);
       var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // ./pages/nextjs.png
       /* harmony default export */ const nextjs = {
         src: "/_next/static/media/nextjs.cae0b805.png",
@@ -1024,12 +1052,12 @@
       /***/
     },
 
-    /***/ 8140: /***/ (
+    /***/ 5434: /***/ (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) => {
-      module.exports = __webpack_require__(1969);
+      module.exports = __webpack_require__(8273);
 
       /***/
     },
@@ -1039,7 +1067,7 @@
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(3705)
+      __webpack_exec__(8255)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for 1187-HASH.js

Diff too large to display

Diff for 8377-HASH.js
@@ -1,8 +1,8 @@
 "use strict";
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
-  [8377],
+  [2727],
   {
-    /***/ 8377: /***/ (module, exports, __webpack_require__) => {
+    /***/ 2727: /***/ (module, exports, __webpack_require__) => {
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
         value: true,
@@ -13,27 +13,27 @@
           return Image;
         },
       });
-      const _interop_require_default = __webpack_require__(2952);
-      const _interop_require_wildcard = __webpack_require__(333);
-      const _jsxruntime = __webpack_require__(3094);
+      const _interop_require_default = __webpack_require__(384);
+      const _interop_require_wildcard = __webpack_require__(4261);
+      const _jsxruntime = __webpack_require__(7048);
       const _react = /*#__PURE__*/ _interop_require_wildcard._(
-        __webpack_require__(1446)
+        __webpack_require__(228)
       );
       const _reactdom = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(8307)
+        __webpack_require__(9221)
       );
       const _head = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(8050)
+        __webpack_require__(1116)
       );
-      const _getimgprops = __webpack_require__(3201);
-      const _imageconfig = __webpack_require__(6678);
-      const _imageconfigcontextsharedruntime = __webpack_require__(578);
-      const _warnonce = __webpack_require__(1971);
-      const _routercontextsharedruntime = __webpack_require__(7795);
+      const _getimgprops = __webpack_require__(5763);
+      const _imageconfig = __webpack_require__(6224);
+      const _imageconfigcontextsharedruntime = __webpack_require__(6720);
+      const _warnonce = __webpack_require__(894);
+      const _routercontextsharedruntime = __webpack_require__(9093);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(1315)
+        __webpack_require__(2809)
       );
-      const _usemergedref = __webpack_require__(592);
+      const _usemergedref = __webpack_require__(1329);
       // This is replaced by webpack define plugin
       const configEnv = {
         deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
@@ -355,7 +355,7 @@
       /***/
     },
 
-    /***/ 592: /***/ (module, exports, __webpack_require__) => {
+    /***/ 1329: /***/ (module, exports, __webpack_require__) => {
       Object.defineProperty(exports, "__esModule", {
         value: true,
       });
@@ -365,7 +365,7 @@
           return useMergedRef;
         },
       });
-      const _react = __webpack_require__(1446);
+      const _react = __webpack_require__(228);
       function useMergedRef(refA, refB) {
         const cleanupA = (0, _react.useRef)(() => {});
         const cleanupB = (0, _react.useRef)(() => {});
@@ -414,7 +414,7 @@
       /***/
     },
 
-    /***/ 5318: /***/ (
+    /***/ 272: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -428,9 +428,9 @@
           return AmpStateContext;
         },
       });
-      const _interop_require_default = __webpack_require__(2952);
+      const _interop_require_default = __webpack_require__(384);
       const _react = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(1446)
+        __webpack_require__(228)
       );
       const AmpStateContext = _react.default.createContext({});
       if (false) {
@@ -439,7 +439,7 @@
       /***/
     },
 
-    /***/ 1210: /***/ (__unused_webpack_module, exports) => {
+    /***/ 1467: /***/ (__unused_webpack_module, exports) => {
       Object.defineProperty(exports, "__esModule", {
         value: true,
       });
@@ -461,7 +461,7 @@
       /***/
     },
 
-    /***/ 3201: /***/ (
+    /***/ 5763: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -475,9 +475,9 @@
           return getImgProps;
         },
       });
-      const _warnonce = __webpack_require__(1971);
-      const _imageblursvg = __webpack_require__(3482);
-      const _imageconfig = __webpack_require__(6678);
+      const _warnonce = __webpack_require__(894);
+      const _imageblursvg = __webpack_require__(5868);
+      const _imageconfig = __webpack_require__(6224);
       const VALID_LOADING_VALUES =
         /* unused pure expression or super */ null && [
           "lazy",
@@ -644,8 +644,15 @@
           };
         }
         if (typeof defaultLoader === "undefined") {
-          throw new Error(
-            "images.loaderFile detected but the file is missing default export.\nRead more: https://nextjs.org/docs/messages/invalid-images-config"
+          throw Object.defineProperty(
+            new Error(
+              "images.loaderFile detected but the file is missing default export.\nRead more: https://nextjs.org/docs/messages/invalid-images-config"
+            ),
+            "__NEXT_ERROR_CODE",
+            {
+              value: "E165",
+              enumerable: false,
+            }
           );
         }
         let loader = rest.loader || defaultLoader;
@@ -657,11 +664,18 @@
         const isDefaultLoader = "__next_img_default" in loader;
         if (isDefaultLoader) {
           if (config.loader === "custom") {
-            throw new Error(
-              'Image with src "' +
-                src +
-                '" is missing "loader" prop.' +
-                "\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader"
+            throw Object.defineProperty(
+              new Error(
+                'Image with src "' +
+                  src +
+                  '" is missing "loader" prop.' +
+                  "\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader"
+              ),
+              "__NEXT_ERROR_CODE",
+              {
+                value: "E255",
+                enumerable: false,
+              }
             );
           }
         } else {
@@ -712,15 +726,29 @@
         if (isStaticImport(src)) {
           const staticImageData = isStaticRequire(src) ? src.default : src;
           if (!staticImageData.src) {
-            throw new Error(
-              "An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received " +
-                JSON.stringify(staticImageData)
+            throw Object.defineProperty(
+              new Error(
+                "An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received " +
+                  JSON.stringify(staticImageData)
+              ),
+              "__NEXT_ERROR_CODE",
+              {
+                value: "E464",
+                enumerable: false,
+              }
             );
           }
           if (!staticImageData.height || !staticImageData.width) {
-            throw new Error(
-              "An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received " +
-                JSON.stringify(staticImageData)
+            throw Object.defineProperty(
+              new Error(
+                "An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received " +
+                  JSON.stringify(staticImageData)
+              ),
+              "__NEXT_ERROR_CODE",
+              {
+                value: "E50",
+                enumerable: false,
+              }
             );
           }
           blurWidth = staticImageData.blurWidth;
@@ -851,8 +879,8 @@
       /***/
     },
 
-    /***/ 8050: /***/ (module, exports, __webpack_require__) => {
-      /* provided dependency */ var process = __webpack_require__(6611);
+    /***/ 1116: /***/ (module, exports, __webpack_require__) => {
+      /* provided dependency */ var process = __webpack_require__(9829);
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
         value: true,
@@ -873,19 +901,19 @@
           return defaultHead;
         },
       });
-      const _interop_require_default = __webpack_require__(2952);
-      const _interop_require_wildcard = __webpack_require__(333);
-      const _jsxruntime = __webpack_require__(3094);
+      const _interop_require_default = __webpack_require__(384);
+      const _interop_require_wildcard = __webpack_require__(4261);
+      const _jsxruntime = __webpack_require__(7048);
       const _react = /*#__PURE__*/ _interop_require_wildcard._(
-        __webpack_require__(1446)
+        __webpack_require__(228)
       );
       const _sideeffect = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(4607)
+        __webpack_require__(4101)
       );
-      const _ampcontextsharedruntime = __webpack_require__(5318);
-      const _headmanagercontextsharedruntime = __webpack_require__(7060);
-      const _ampmode = __webpack_require__(1210);
-      const _warnonce = __webpack_require__(1971);
+      const _ampcontextsharedruntime = __webpack_require__(272);
+      const _headmanagercontextsharedruntime = __webpack_require__(1790);
+      const _ampmode = __webpack_require__(1467);
+      const _warnonce = __webpack_require__(894);
       function defaultHead(inAmpMode) {
         if (inAmpMode === void 0) inAmpMode = false;
         const head = [
@@ -1069,7 +1097,7 @@
       /***/
     },
 
-    /***/ 3482: /***/ (__unused_webpack_module, exports) => {
+    /***/ 5868: /***/ (__unused_webpack_module, exports) => {
       /**
        * A shared function, used on both client and server, to generate a SVG blur placeholder.
        */
@@ -1123,7 +1151,7 @@
       /***/
     },
 
-    /***/ 578: /***/ (
+    /***/ 6720: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -1137,11 +1165,11 @@
           return ImageConfigContext;
         },
       });
-      const _interop_require_default = __webpack_require__(2952);
+      const _interop_require_default = __webpack_require__(384);
       const _react = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(1446)
+        __webpack_require__(228)
       );
-      const _imageconfig = __webpack_require__(6678);
+      const _imageconfig = __webpack_require__(6224);
       const ImageConfigContext = _react.default.createContext(
         _imageconfig.imageConfigDefault
       );
@@ -1151,7 +1179,7 @@
       /***/
     },
 
-    /***/ 6678: /***/ (__unused_webpack_module, exports) => {
+    /***/ 6224: /***/ (__unused_webpack_module, exports) => {
       Object.defineProperty(exports, "__esModule", {
         value: true,
       });
@@ -1199,7 +1227,7 @@
       /***/
     },
 
-    /***/ 1315: /***/ (__unused_webpack_module, exports) => {
+    /***/ 2809: /***/ (__unused_webpack_module, exports) => {
       Object.defineProperty(exports, "__esModule", {
         value: true,
       });
@@ -1232,7 +1260,7 @@
       /***/
     },
 
-    /***/ 7795: /***/ (
+    /***/ 9093: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -1246,9 +1274,9 @@
           return RouterContext;
         },
       });
-      const _interop_require_default = __webpack_require__(2952);
+      const _interop_require_default = __webpack_require__(384);
       const _react = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(1446)
+        __webpack_require__(228)
       );
       const RouterContext = _react.default.createContext(null);
       if (false) {
@@ -1257,7 +1285,7 @@
       /***/
     },
 
-    /***/ 4607: /***/ (
+    /***/ 4101: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -1271,7 +1299,7 @@
           return SideEffect;
         },
       });
-      const _react = __webpack_require__(1446);
+      const _react = __webpack_require__(228);
       const isServer = typeof window === "undefined";
       const useClientOnlyLayoutEffect = isServer
         ? () => {}
Diff for main-HASH.js

Diff too large to display

Diff for app-page-exp..ntime.dev.js
failed to diff
Diff for app-page-exp..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page.runtime.dev.js
failed to diff
Diff for app-page.runtime.prod.js

Diff too large to display

Diff for app-route-ex..ntime.dev.js

Diff too large to display

Diff for app-route-ex..time.prod.js

Diff too large to display

Diff for app-route-tu..time.prod.js

Diff too large to display

Diff for app-route-tu..time.prod.js

Diff too large to display

Diff for app-route.runtime.dev.js

Diff too large to display

Diff for app-route.ru..time.prod.js

Diff too large to display

Diff for pages-api-tu..time.prod.js

Diff too large to display

Diff for pages-api.runtime.dev.js

Diff too large to display

Diff for pages-api.ru..time.prod.js

Diff too large to display

Diff for pages-turbo...time.prod.js

Diff too large to display

Diff for pages.runtime.dev.js

Diff too large to display

Diff for pages.runtime.prod.js

Diff too large to display

Diff for server.runtime.prod.js
failed to diff
Commit: 7f9317e

@gaojude gaojude force-pushed the jude/error-coding branch 2 times, most recently from 408dfbb to 062fb55 Compare December 5, 2024 21:32
@gaojude gaojude marked this pull request as ready for review December 5, 2024 22:01
@gaojude gaojude requested a review from a team December 5, 2024 22:11
Error codes need to be ingested into our analytics platform for monitoring and analysis. Here's how the process works:

1. Extract Git Commit Hash
Given an error code like `E2ab666d70ae3cc620277dcf822`, the first 10 characters (`2ab666d70a`) represent the Git commit hash where the error was introduced.
Copy link
Member

Choose a reason for hiding this comment

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

Usually gut sha use 8 chars. And could be put a separator char between both hashes, e.g. a C.
Also the second hash could be truncated to 8 chars...

echo "✨ Successfully built and moved WASM plugin! 🚀"
```

Make sure to commit the WASM file to the repo.
Copy link
Member

@lubieowoce lubieowoce Dec 6, 2024

Choose a reason for hiding this comment

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

hmm that seems a bit surprising, why is this necessary? do we do this for anything else?

Error codes need to be ingested into our analytics platform for monitoring and analysis. Here's how the process works:

1. Extract Git Commit Hash
Given an error code like `E2ab666d70ae3cc620277dcf822`, the first 10 characters (`2ab666d70a`) represent the Git commit hash where the error was introduced.
Copy link
Member

Choose a reason for hiding this comment

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

How will that work when we introduce errors in PRs? There's no meaningful commit yet to use.

Copy link
Member

@lubieowoce lubieowoce Dec 9, 2024

Choose a reason for hiding this comment

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

hmm well if the codes were just another output in dist (i.e. not comitted into the repo), then it wouldn't be a problem - they'd just get generated separately for each commit. would we be okay with that? bc if not then you're right, we can't really use the commit in a meaningful way
(edit: this makes no sense if we want the commit to stay stable)

Copy link
Member

@lubieowoce lubieowoce Dec 9, 2024

Choose a reason for hiding this comment

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

also, taking a step back, why is "the commit when the error was introduced" even particularly useful? how does that help us? wouldn't doing a git blame on the json file already give us that info? (assuming those files are checked in).


if let Expr::New(new_expr) = expr {
if let Expr::Ident(ident) = &*new_expr.callee {
if ident.sym.to_string() == "Error" {
Copy link
Member

@eps1lon eps1lon Dec 6, 2024

Choose a reason for hiding this comment

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

These are all the Error subclasses we need to check:

const errorConstructors = [
  // built-ints
  'AggregateError',
  'Error',
  'EvalError',
  'RangeError',
  'ReferenceError',
  'SyntaxError',
  'TypeError',
  'URIError',
  // custom
  'ApiError',
  'BailoutToCSRError',
  'BubbledError',
  'CanaryOnlyError',
  'Cancel',
  'CompileError',
  'CssSyntaxError',
  'DecodeError',
  'DynamicServerError',
  'ExportError',
  'FatalError',
  'ImageError',
  'InvariantError',
  'ModuleBuildError',
  'NestedMiddlewareError',
  'NoFallbackError',
  'NoSuchDeclarationError',
  'PageSignatureError',
  'PostCSSSyntaxError',
  'ReadonlyHeadersError',
  'ReadonlyRequestCookiesError',
  'ReadonlyRequestCookiesError',
  'ReadonlyURLSearchParamsError',
  'ResponseAborted',
  'SerializableError',
  'StaticGenBailoutError',
  'TimeoutError',
  'Warning',
]

found by checking which classes actually subclass Error via [collect-error-constructors.mjs](https://gist.github.com/eps1lon/6cce3059dfa061f2a7dc28305fdaddae#file-collect-error-constructors-mjs)

But we can do this in a follow-up. This should be a proof-of-concept.

packages/next/src/server/next-server.ts Outdated Show resolved Hide resolved
packages/next/errors.json Outdated Show resolved Hide resolved
Comment on lines +15 to +17
"rust-check-clippy": "cargo clippy --exclude next-error-code-swc-plugin --workspace --all-targets -- -D warnings -A deprecated",
"rust-check-napi": "cargo check -p next-swc-napi",
"test-cargo-unit": "cargo nextest run --workspace --exclude next-swc-napi --release --no-fail-fast"
"test-cargo-unit": "cargo nextest run --workspace --exclude next-swc-napi --exclude next-error-code-swc-plugin --release --no-fail-fast"
Copy link
Member

Choose a reason for hiding this comment

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

is this needed? why? or is it just a leftover of when we thought we had to exclude the package from the workspace?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is part of the workspace. But we had to exclude it here for the unit test, because next-error-code-swc-plugin uses a different target than the rest, i.e., it would require --target wasm32-wasip1. Afaik, there is no way to specify a different target for a specific project, thus the exclude here.

Comment on lines +6 to +8
// This script checks for new error codes in .errors directory and consolidates them into errors.json.
// It will fail if new error codes are found, after consolidating them, to ensure error codes are
// properly reviewed and committed.
Copy link
Member

@lubieowoce lubieowoce Dec 10, 2024

Choose a reason for hiding this comment

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

hmm maybe i'm missing something but this seems a bit convoluted. can't we just write errors.json directly from the transform, without doing this whole .errors/ directory thing? the transform is already reading errors.json after all.

and if we want to e.g. make sure changes are committed, we can do the same thing we do in scripts/check-pre-compiled.sh and just run a git diff on the file after build

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, most of the complexity is caused by concurrent compilation not able to read a single errors.json without race condition. That's what motivates this whole "two-phase" strategy.

Copy link
Member

@lubieowoce lubieowoce Dec 10, 2024

Choose a reason for hiding this comment

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

hmmm. i feel like ideally we'd just keep all the errors in memory and write them all out at the end, if everything succeeded, which'd avoid this problem. i don't like that we have to jump through all these hoops, feels like we shouldn't have to

...but i also don't wanna block the PR on that, we can try to improve this later

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, each transform has a sandboxed WASM instance, thus no way to share global memory that keeps track of errors...

@gaojude gaojude requested a review from devjiwonchoi December 10, 2024 18:01
Copy link
Member

Choose a reason for hiding this comment

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

is this file intended to be commited or it's generated by build

Copy link
Contributor Author

Choose a reason for hiding this comment

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

packages/next/src/lib/error-telemetry-utils.ts Outdated Show resolved Hide resolved
packages/next/src/lib/error-telemetry-utils.ts Outdated Show resolved Hide resolved
packages/next/src/lib/error-telemetry-utils.ts Outdated Show resolved Hide resolved
@gaojude gaojude requested a review from huozhi December 11, 2024 15:06
Copy link
Member

@eps1lon eps1lon left a comment

Choose a reason for hiding this comment

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

Love it.

What happens if a I push a branch without updated errors? Where does it error in the CI pipeline and what is the message?

Comment on lines +11 to +13
const errorCode = await browser.waitForElementByCss(
'[data-nextjs-error-code]'
)
Copy link
Member

Choose a reason for hiding this comment

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

We'll eventually upstream these into toDisplayRedbox and then we can remove these specific tests.

Comment on lines +263 to +265
throw Object.defineProperty(new Error(`Failed to fetch user ${userId}: ${response.statusText}`), "__NEXT_ERROR_CODE", {
value: "E1",
enumerable: false
Copy link
Member

Choose a reason for hiding this comment

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

We could consider making it enumerable with a more human-readable property name like code and then go with Next1 for error codes so that they're distinguished from other error codes. That way the error code would automatically show up when logged to the console

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we have a use case for that, we can consider it. Right now, because we only use the code for telemetry, keeping it implicit should be fine.

@gaojude
Copy link
Contributor Author

gaojude commented Dec 11, 2024

Love it.

What happens if a I push a branch without updated errors? Where does it error in the CI pipeline and what is the message?

It would fail in any part of CI where it runs pnpm build with error message check-error-codes: failed.

@eps1lon
Copy link
Member

eps1lon commented Dec 11, 2024

It would fail in any part of CI where it runs pnpm build with error message check-error-codes: failed.

Can we expand the message to include instructions how to fix it?

@gaojude
Copy link
Contributor Author

gaojude commented Dec 11, 2024

It would fail in any part of CI where it runs pnpm build with error message check-error-codes: failed.

Can we expand the message to include instructions how to fix it?

I will follow up on this!

@gaojude gaojude merged commit 306d4bd into canary Dec 11, 2024
106 of 111 checks passed
@gaojude gaojude deleted the jude/error-coding branch December 11, 2024 16:30
gaojude added a commit that referenced this pull request Dec 13, 2024
Adds support for additional error classes in the error code SWC plugin.

Follow up for
#73332 (comment)
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
created-by: Next.js team PRs by the Next.js team. locked tests Turbopack Related to Turbopack with Next.js. type: next
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants