[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix(vitest): strip non-serializable functions from inline diff config (#10573)

authored by

Minh Lê and committed by
GitHub
(Jun 12, 2026, 3:11 PM +0200) 5b81a63f 3d421d4e

+70 -2
+28
test/e2e/test/reporters/custom-diff-config.test.ts
··· 21 21 expect(stderr).toContain('invalid diff config file') 22 22 expect(stderr).toContain('Must have a default export with config object') 23 23 }) 24 + 25 + // https://github.com/vitest-dev/vitest/issues/8663 26 + // An inline `diff` object may carry non-serializable color functions. They must 27 + // be stripped before the config is sent to workers, otherwise the `threads` 28 + // pool throws a `DataCloneError` (structured clone) and the `forks` pool drops 29 + // them over IPC. The serializable annotations must still be applied. 30 + test.each(['forks', 'threads'] as const)( 31 + 'inline diff config object with color functions works in %s pool', 32 + async (pool) => { 33 + const filename = resolve('./fixtures/reporters/custom-diff-config.test.ts') 34 + const { stderr } = await runVitest({ 35 + root: './fixtures/reporters', 36 + pool, 37 + diff: { 38 + aAnnotation: 'Expected to be', 39 + bAnnotation: 'But got', 40 + // @ts-expect-error color functions are not part of the public diff type, 41 + // but users pass them at runtime — this is what used to crash the worker. 42 + aColor: (s: string) => s, 43 + }, 44 + }, [filename]) 45 + 46 + expect(stderr).not.toContain('could not be cloned') 47 + expect(stderr).not.toContain('DataCloneError') 48 + expect(stderr).toContain('Expected to be') 49 + expect(stderr).toContain('But got') 50 + }, 51 + )
+42 -2
packages/vitest/src/node/config/serializeConfig.ts
··· 1 + import type { SerializedDiffOptions } from '@vitest/utils/diff' 1 2 import type { SerializedConfig } from '../../runtime/config' 2 3 import type { TestProject } from '../project' 3 4 import type { ApiConfig } from '../types/config' ··· 42 43 allowWrite: api?.allowWrite, 43 44 } 44 45 })(project.isBrowserEnabled() ? config.browser.api : config.api), 45 - // TODO: non serializable function? 46 - diff: config.diff, 46 + diff: serializeDiffOptions(config.diff), 47 47 retry: config.retry, 48 48 repeats: config.repeats, 49 49 disableConsoleIntercept: config.disableConsoleIntercept, ··· 161 161 ?? configDefaults.slowTestThreshold, 162 162 disableColors: isAgent && !isForceColor(), 163 163 } 164 + } 165 + 166 + const serializableDiffKeys = [ 167 + 'aAnnotation', 168 + 'aIndicator', 169 + 'bAnnotation', 170 + 'bIndicator', 171 + 'commonIndicator', 172 + 'contextLines', 173 + 'emptyFirstOrLastLinePlaceholder', 174 + 'expand', 175 + 'includeChangeCounts', 176 + 'omitAnnotationLines', 177 + 'printBasicPrototype', 178 + 'maxDepth', 179 + 'truncateThreshold', 180 + 'truncateAnnotation', 181 + ] satisfies (keyof SerializedDiffOptions)[] 182 + 183 + // `diff` can be an inline object containing color/compareKeys functions 184 + // (`DiffOptions`). Those functions are not structured-cloneable (threads pool) 185 + // and are silently dropped over `child_process` IPC (forks pool), so passing 186 + // the raw object to workers throws `DataCloneError`. Forward only the 187 + // serializable fields declared by `SerializedDiffOptions`, and only the ones 188 + // actually set — explicit `undefined` values would override the diff defaults 189 + // when the worker merges the options. The function-based options still 190 + // require the file-path form, which workers import locally. 191 + function serializeDiffOptions( 192 + diff: string | SerializedDiffOptions | undefined, 193 + ): string | SerializedDiffOptions | undefined { 194 + if (diff == null || typeof diff === 'string') { 195 + return diff 196 + } 197 + const result: SerializedDiffOptions = {} 198 + for (const key of serializableDiffKeys) { 199 + if (diff[key] !== undefined) { 200 + (result as Record<string, unknown>)[key] = diff[key] 201 + } 202 + } 203 + return result 164 204 }