Skip to content

Commit 00cb5d9

Browse files
committed
perf: revert to use the light safe stringify for is-error (#86053)
`is-error` is a helper that being used accorss client and server now, in #84909 we switched the underlying safe stringify error implementation to the precompiled dependency `safe-stable-stringify`. But it increased the client bundle size a bit. The change in #84909 was only for applying the `safe-stable-stringify` for client file logger with mcp, not really necessary need to change for is-error helper Note: Save minified around 12kb. it's actually 7.5kb on bundlephobia, but because we're using ncc to compile it to cjs dependency that the size seems slightly larger than their metrics. The saved gzip size would be around 2-3.x kb
1 parent de6bc15 commit 00cb5d9

File tree

3 files changed

+39
-4
lines changed

3 files changed

+39
-4
lines changed

packages/next/src/lib/is-error.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { isPlainObject } from '../shared/lib/is-plain-object'
2-
import safeStringify from 'next/dist/compiled/safe-stable-stringify'
32

43
// We allow some additional attached properties for Next.js errors
54
export interface NextError extends Error {
@@ -10,6 +9,28 @@ export interface NextError extends Error {
109
digest?: number
1110
}
1211

12+
/**
13+
* This is a safe stringify function that handles circular references.
14+
* We're using a simpler version here to avoid introducing
15+
* the dependency `safe-stable-stringify` into production bundle.
16+
*
17+
* This helper is used both in development and production.
18+
*/
19+
function safeStringifyLite(obj: any) {
20+
const seen = new WeakSet()
21+
22+
return JSON.stringify(obj, (_key, value) => {
23+
// If value is an object and already seen, replace with "[Circular]"
24+
if (typeof value === 'object' && value !== null) {
25+
if (seen.has(value)) {
26+
return '[Circular]'
27+
}
28+
seen.add(value)
29+
}
30+
return value
31+
})
32+
}
33+
1334
/**
1435
* Checks whether the given value is a NextError.
1536
* This can be used to print a more detailed error message with properties like `code` & `digest`.
@@ -43,5 +64,5 @@ export function getProperError(err: unknown): Error {
4364
}
4465
}
4566

46-
return new Error(isPlainObject(err) ? safeStringify(err) : err + '')
67+
return new Error(isPlainObject(err) ? safeStringifyLite(err) : err + '')
4768
}

test/development/app-dir/serialize-circular-error/serialize-circular-error.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('serialize-circular-error', () => {
1919
`)
2020
const output = next.cliOutput
2121
expect(output).toContain(
22-
'Error: {"objA":{"other":{"a":"[Circular]"}},"objB":{"a":{"other":"[Circular]"}}}'
22+
'Error: {"objA":{"other":{"a":"[Circular]"}},"objB":"[Circular]"}'
2323
)
2424
})
2525

@@ -44,7 +44,7 @@ describe('serialize-circular-error', () => {
4444

4545
const output = next.cliOutput
4646
expect(output).toContain(
47-
'Error: {"objC":{"other":{"a":"[Circular]"}},"objD":{"a":{"other":"[Circular]"}}}'
47+
'Error: {"objC":{"other":{"a":"[Circular]"}},"objD":"[Circular]"}'
4848
)
4949
})
5050
})

test/production/app-dir/browser-chunks/browser-chunks.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,18 @@ describe('browser-chunks', () => {
4848
)
4949
}
5050
})
51+
52+
it('must not include heavy dependencies into browser chunks', () => {
53+
const heavyDependencies = sources.filter((source) => {
54+
return source.includes('next/dist/compiled/safe-stable-stringify')
55+
})
56+
57+
if (heavyDependencies.length > 0) {
58+
const message = `Found the following heavy dependencies:\n ${heavyDependencies.join('\n ')}`
59+
60+
throw new Error(
61+
'Did not expect any heavy dependencies in browser chunks.\n' + message
62+
)
63+
}
64+
})
5165
})

0 commit comments

Comments
 (0)