mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
error code should preserve after stitching (#75929)
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
This plugin transforms Next.js source code by:
|
||||
|
||||
- Rewriting `new Error` or `Error` to include an additional property: `Object.defineProperty(new Error(...), "__NEXT_ERROR_CODE", { value: $code, enumerable: false })`. The `enumerable: false` ensures the error code won't show up in console logs while still being accessible for telemetry.
|
||||
- Rewriting `new Error` or `Error` to include an additional property: `Object.defineProperty(new Error(...), "__NEXT_ERROR_CODE", { value: $code, enumerable: false, configurable: true })`. The `enumerable: false` ensures the error code won't show up in console logs while still being accessible for telemetry. The `configurable: true` ensures the error code can be overwritten, useful for transforming errors.
|
||||
- This enables anonymous error code reporting for user feedback while keeping the message private.
|
||||
|
||||
## Error Code Mapping
|
||||
@@ -36,6 +36,7 @@ This plugin transforms Next.js source code by:
|
||||
- **Reason:** Pre-built artefacts simplify the build process (`pnpm build` runs without requiring `cargo`).
|
||||
|
||||
- **Rebuild Script Example:**
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
@@ -46,3 +47,5 @@ This plugin transforms Next.js source code by:
|
||||
mv "$NEXT_JS_ROOT/crates/next-error-code-swc-plugin/target/wasm32-wasip1/release/next_error_code_swc_plugin.wasm" "$NEXT_JS_ROOT/packages/next/"
|
||||
echo "✨ Successfully built and moved WASM plugin! 🚀"
|
||||
```
|
||||
|
||||
- **Testing:** Navigate to `/crates/next-error-code-swc-plugin` and run `cargo test` to test the plugin
|
||||
|
||||
@@ -223,6 +223,13 @@ impl VisitMut for TransformVisitor {
|
||||
value: false,
|
||||
}))),
|
||||
}))),
|
||||
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident("configurable".into()),
|
||||
value: Box::new(Expr::Lit(Lit::Bool(Bool {
|
||||
span: new_error_expr.span,
|
||||
value: true,
|
||||
}))),
|
||||
}))),
|
||||
],
|
||||
})), // { value: "$code", enumerable: false }
|
||||
},
|
||||
@@ -303,21 +310,24 @@ async function fetchUserData(userId) {
|
||||
if (!response.ok) {
|
||||
throw Object.defineProperty(new Error(`Failed to fetch user ${userId}: ${response.statusText}`), "__NEXT_ERROR_CODE", {
|
||||
value: "E1",
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
return await response.json();
|
||||
} catch (err) {
|
||||
throw Object.defineProperty(new Error(`Request failed: ${err.message}`), "__NEXT_ERROR_CODE", {
|
||||
value: "E2",
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
function test1() {
|
||||
throw Object.defineProperty(new Error("Generic error"), "__NEXT_ERROR_CODE", {
|
||||
value: "E3",
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
function test2() {
|
||||
@@ -326,14 +336,16 @@ function test2() {
|
||||
function test3() {
|
||||
throw Object.defineProperty(new Error("Generic error"), "__NEXT_ERROR_CODE", {
|
||||
value: "E3",
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
function test4() {
|
||||
throw new Error();
|
||||
throw Object.defineProperty(new Error("Pattern should define hostname but found\n" + JSON.stringify(pattern)), "__NEXT_ERROR_CODE", {
|
||||
value: "E5",
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
"#
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import isError from '../../../lib/is-error'
|
||||
import { copyNextErrorCode } from '../../../lib/error-telemetry-utils'
|
||||
|
||||
const REACT_ERROR_STACK_BOTTOM_FRAME = 'react-stack-bottom-frame'
|
||||
const REACT_ERROR_STACK_BOTTOM_FRAME_REGEX = new RegExp(
|
||||
@@ -25,6 +26,7 @@ export function getReactStitchedError<T = unknown>(err: T): Error | T {
|
||||
const newError = new Error(originMessage)
|
||||
// Copy all enumerable properties, e.g. digest
|
||||
Object.assign(newError, err)
|
||||
copyNextErrorCode(err, newError)
|
||||
newError.stack = newStack
|
||||
|
||||
// Avoid duplicate overriding stack frames
|
||||
|
||||
@@ -22,6 +22,21 @@ export const createDigestWithErrorCode = (
|
||||
return originalDigest
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the error code from one error to another by setting the __NEXT_ERROR_CODE property.
|
||||
* This allows error codes to be preserved when wrapping or transforming errors.
|
||||
*/
|
||||
export const copyNextErrorCode = (source: unknown, target: unknown): void => {
|
||||
const errorCode = extractNextErrorCode(source)
|
||||
if (errorCode && typeof target === 'object' && target !== null) {
|
||||
Object.defineProperty(target, '__NEXT_ERROR_CODE', {
|
||||
value: errorCode,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const extractNextErrorCode = (error: unknown): string | undefined => {
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
|
||||
Reference in New Issue
Block a user