From 80a43d5c7c1c541d3dcc0996d411fc39ecbb2e35 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 10 Jul 2024 15:08:44 +0200 Subject: [PATCH] fix(browser): inline pretty-format and replace picocolors with tinyrainbow (#6077) --- docs/config/index.md | 2 +- packages/browser/package.json | 3 + packages/browser/rollup.config.js | 15 + packages/browser/src/client/client.ts | 4 +- packages/browser/src/client/orchestrator.html | 3 +- packages/browser/src/client/orchestrator.ts | 4 +- .../src/client/public/error-catcher.js | 81 +++ packages/browser/src/client/tester/context.ts | 2 +- packages/browser/src/client/tester/mocker.ts | 4 +- packages/browser/src/client/tester/msw.ts | 4 +- packages/browser/src/client/tester/rpc.ts | 2 +- packages/browser/src/client/tester/runner.ts | 2 +- .../browser/src/client/tester/snapshot.ts | 2 +- packages/browser/src/client/tester/state.ts | 2 +- .../browser/src/client/tester/tester.html | 3 +- packages/browser/src/client/tester/tester.ts | 14 +- .../browser/src/client/tester/unhandled.ts | 25 +- packages/browser/src/client/vite.config.ts | 8 +- packages/browser/src/node/plugin.ts | 7 +- packages/browser/src/node/server.ts | 5 + .../browser/src/node/serverOrchestrator.ts | 6 +- packages/browser/src/node/serverTester.ts | 3 +- packages/coverage-istanbul/package.json | 4 +- packages/coverage-istanbul/src/provider.ts | 2 +- packages/coverage-v8/package.json | 4 +- packages/coverage-v8/src/provider.ts | 2 +- packages/expect/package.json | 4 +- packages/expect/src/index.ts | 1 - packages/expect/src/jest-expect.ts | 20 +- packages/expect/src/jest-matcher-utils.ts | 15 +- packages/expect/src/types.ts | 2 +- packages/pretty-format/package.json | 43 ++ packages/pretty-format/rollup.config.js | 64 +++ packages/pretty-format/src/collections.ts | 234 ++++++++ packages/pretty-format/src/index.ts | 538 ++++++++++++++++++ .../src/plugins/AsymmetricMatcher.ts | 97 ++++ .../src/plugins/DOMCollection.ts | 75 +++ .../pretty-format/src/plugins/DOMElement.ts | 127 +++++ .../pretty-format/src/plugins/Immutable.ts | 202 +++++++ .../pretty-format/src/plugins/ReactElement.ts | 121 ++++ .../src/plugins/ReactTestComponent.ts | 83 +++ .../src/plugins/lib/escapeHTML.ts | 10 + .../pretty-format/src/plugins/lib/markup.ts | 100 ++++ packages/pretty-format/src/types.ts | 113 ++++ packages/pretty-format/tsconfig.json | 8 + packages/snapshot/package.json | 4 +- packages/snapshot/src/port/mockSerializer.ts | 2 +- packages/snapshot/src/port/plugins.ts | 4 +- packages/snapshot/src/port/state.ts | 2 +- packages/snapshot/src/port/utils.ts | 4 +- packages/snapshot/src/types/index.ts | 2 +- packages/ui/client/constants.ts | 4 +- packages/ui/node/reporter.ts | 2 +- packages/ui/package.json | 5 +- packages/ui/vite.config.ts | 7 +- packages/utils/package.json | 5 +- packages/utils/src/colors.ts | 126 ---- packages/utils/src/diff/diffLines.ts | 5 +- packages/utils/src/diff/diffStrings.ts | 5 +- packages/utils/src/diff/index.ts | 4 +- .../utils/src/diff/normalizeDiffOptions.ts | 6 +- packages/utils/src/diff/types.ts | 4 +- packages/utils/src/highlight.ts | 7 +- packages/utils/src/index.ts | 1 - packages/utils/src/stringify.ts | 4 +- packages/vite-node/package.json | 2 +- packages/vite-node/src/cli.ts | 2 +- packages/vite-node/src/debug.ts | 2 +- packages/vite-node/src/hmr/hmr.ts | 2 +- packages/vitest/package.json | 3 +- packages/vitest/rollup.config.js | 2 +- packages/vitest/src/create/browser/creator.ts | 2 +- packages/vitest/src/node/cli/cac.ts | 2 +- packages/vitest/src/node/config.ts | 2 +- packages/vitest/src/node/error.ts | 4 +- packages/vitest/src/node/hoistMocks.ts | 2 +- packages/vitest/src/node/logger.ts | 2 +- packages/vitest/src/node/packageInstaller.ts | 2 +- packages/vitest/src/node/reporters/base.ts | 2 +- .../node/reporters/benchmark/table/index.ts | 2 +- .../reporters/benchmark/table/tableRender.ts | 2 +- packages/vitest/src/node/reporters/default.ts | 2 +- .../node/reporters/renderers/dotRenderer.ts | 2 +- .../node/reporters/renderers/listRenderer.ts | 2 +- .../src/node/reporters/renderers/utils.ts | 2 +- packages/vitest/src/node/reporters/verbose.ts | 2 +- packages/vitest/src/node/stdin.ts | 2 +- packages/vitest/src/node/watch-filter.ts | 2 +- packages/vitest/src/runtime/console.ts | 5 +- packages/vitest/src/runtime/runVmTests.ts | 4 - packages/vitest/src/runtime/setup-node.ts | 3 - packages/vitest/src/types/config.ts | 2 +- packages/vitest/src/types/global.ts | 2 +- packages/vitest/src/types/matcher-utils.ts | 2 +- packages/vitest/src/utils/colors.ts | 3 +- pnpm-lock.yaml | 100 +++- test/core/package.json | 1 + .../test/__snapshots__/mocked.test.ts.snap | 16 +- test/core/test/diff.test.ts | 58 +- test/core/test/environments/jsdom.spec.ts | 14 +- test/core/test/injector-mock.test.ts | 2 +- test/core/test/jest-expect.test.ts | 31 +- test/core/test/jest-matcher-utils.test.ts | 13 +- test/core/test/mocked.test.ts | 31 +- test/core/test/utils.spec.ts | 12 +- test/vite-node/test/server.test.ts | 8 +- test/watch/test/reporter-failed.test.ts | 2 + tsconfig.base.json | 2 + 108 files changed, 2218 insertions(+), 415 deletions(-) create mode 100644 packages/browser/src/client/public/error-catcher.js create mode 100644 packages/pretty-format/package.json create mode 100644 packages/pretty-format/rollup.config.js create mode 100644 packages/pretty-format/src/collections.ts create mode 100644 packages/pretty-format/src/index.ts create mode 100644 packages/pretty-format/src/plugins/AsymmetricMatcher.ts create mode 100644 packages/pretty-format/src/plugins/DOMCollection.ts create mode 100644 packages/pretty-format/src/plugins/DOMElement.ts create mode 100644 packages/pretty-format/src/plugins/Immutable.ts create mode 100644 packages/pretty-format/src/plugins/ReactElement.ts create mode 100644 packages/pretty-format/src/plugins/ReactTestComponent.ts create mode 100644 packages/pretty-format/src/plugins/lib/escapeHTML.ts create mode 100644 packages/pretty-format/src/plugins/lib/markup.ts create mode 100644 packages/pretty-format/src/types.ts create mode 100644 packages/pretty-format/tsconfig.json delete mode 100644 packages/utils/src/colors.ts diff --git a/docs/config/index.md b/docs/config/index.md index 7015323dcf7f..0417edf7b5fb 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -2181,7 +2181,7 @@ Path to a diff config that will be used to generate diff interface. Useful if yo :::code-group ```ts [vitest.diff.ts] import type { DiffOptions } from 'vitest' -import c from 'picocolors' +import c from 'tinyrainbow' export default { aIndicator: c.bold('--'), diff --git a/packages/browser/package.json b/packages/browser/package.json index 8930c6410d4f..0f4777c3cc82 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -28,6 +28,9 @@ "types": "./context.d.ts", "default": "./context.js" }, + "./client": { + "default": "./dist/client.js" + }, "./matchers": { "types": "./matchers.d.ts" }, diff --git a/packages/browser/rollup.config.js b/packages/browser/rollup.config.js index 28a585f18df3..64987eb9cb74 100644 --- a/packages/browser/rollup.config.js +++ b/packages/browser/rollup.config.js @@ -57,6 +57,21 @@ export default () => }), ], }, + { + input: './src/client/client.ts', + output: { + file: 'dist/client.js', + format: 'esm', + }, + plugins: [ + resolve({ + preferBuiltins: true, + }), + esbuild({ + target: 'node18', + }), + ], + }, { input: './src/client/tester/state.ts', output: { diff --git a/packages/browser/src/client/client.ts b/packages/browser/src/client/client.ts index a950bb2b52ff..5e7bb11108ed 100644 --- a/packages/browser/src/client/client.ts +++ b/packages/browser/src/client/client.ts @@ -6,7 +6,7 @@ import { getBrowserState } from './utils' const PAGE_TYPE = getBrowserState().type -export const PORT = import.meta.hot ? '51204' : location.port +export const PORT = location.port export const HOST = [location.hostname, PORT].filter(Boolean).join(':') export const SESSION_ID = PAGE_TYPE === 'orchestrator' @@ -136,4 +136,4 @@ function createClient() { export const client = createClient() -export { channel, waitForChannel } from './channel' +export * from './channel' diff --git a/packages/browser/src/client/orchestrator.html b/packages/browser/src/client/orchestrator.html index bd17271d2237..770bc5728f61 100644 --- a/packages/browser/src/client/orchestrator.html +++ b/packages/browser/src/client/orchestrator.html @@ -23,7 +23,8 @@ height: 100%; } - + {__VITEST_INJECTOR__} + {__VITEST_ERROR_CATCHER__} {__VITEST_SCRIPTS__} diff --git a/packages/browser/src/client/orchestrator.ts b/packages/browser/src/client/orchestrator.ts index 80b11efd2447..d455732401fb 100644 --- a/packages/browser/src/client/orchestrator.ts +++ b/packages/browser/src/client/orchestrator.ts @@ -1,10 +1,10 @@ import type { ResolvedConfig } from 'vitest' +import { channel, client } from '@vitest/browser/client' import { generateHash } from '@vitest/runner/utils' +import { type GlobalChannelIncomingEvent, type IframeChannelEvent, type IframeChannelIncomingEvent, globalChannel } from '@vitest/browser/client' import { relative } from 'pathe' -import { channel, client } from './client' import { getBrowserState, getConfig } from './utils' import { getUiAPI } from './ui' -import { type GlobalChannelIncomingEvent, type IframeChannelEvent, type IframeChannelIncomingEvent, globalChannel } from './channel' import { createModuleMocker } from './tester/msw' const url = new URL(location.href) diff --git a/packages/browser/src/client/public/error-catcher.js b/packages/browser/src/client/public/error-catcher.js new file mode 100644 index 000000000000..a5701f5c8e87 --- /dev/null +++ b/packages/browser/src/client/public/error-catcher.js @@ -0,0 +1,81 @@ +import { channel, client } from '/@id/@vitest/browser/client' + +function on(event, listener) { + window.addEventListener(event, listener) + return () => window.removeEventListener(event, listener) +} + +function serializeError(unhandledError) { + if (typeof unhandledError !== 'object' || !unhandledError) { + return { + message: String(unhandledError), + } + } + + return { + name: unhandledError.name, + message: unhandledError.message, + stack: String(unhandledError.stack), + } +} + +function catchWindowErrors(cb) { + let userErrorListenerCount = 0 + function throwUnhandlerError(e) { + if (userErrorListenerCount === 0 && e.error != null) { + cb(e) + } + else { + console.error(e.error) + } + } + const addEventListener = window.addEventListener.bind(window) + const removeEventListener = window.removeEventListener.bind(window) + window.addEventListener('error', throwUnhandlerError) + window.addEventListener = function (...args) { + if (args[0] === 'error') { + userErrorListenerCount++ + } + return addEventListener.apply(this, args) + } + window.removeEventListener = function (...args) { + if (args[0] === 'error' && userErrorListenerCount) { + userErrorListenerCount-- + } + return removeEventListener.apply(this, args) + } + return function clearErrorHandlers() { + window.removeEventListener('error', throwUnhandlerError) + } +} + +function registerUnexpectedErrors() { + catchWindowErrors(event => + reportUnexpectedError('Error', event.error), + ) + on('unhandledrejection', event => + reportUnexpectedError('Unhandled Rejection', event.reason)) +} + +async function reportUnexpectedError( + type, + error, +) { + const processedError = serializeError(error) + await client.rpc.onUnhandledError(processedError, type) + const state = __vitest_browser_runner__ + + if (state.type === 'orchestrator') { + return + } + + if (!state.runTests || !__vitest_worker__.current) { + channel.postMessage({ + type: 'done', + filenames: state.files, + id: state.iframeId, + }) + } +} + +registerUnexpectedErrors() diff --git a/packages/browser/src/client/tester/context.ts b/packages/browser/src/client/tester/context.ts index a4154864fb81..b23d36df426e 100644 --- a/packages/browser/src/client/tester/context.ts +++ b/packages/browser/src/client/tester/context.ts @@ -1,7 +1,7 @@ import type { Task, WorkerGlobalState } from 'vitest' +import type { BrowserRPC } from '@vitest/browser/client' import type { BrowserPage, UserEvent, UserEventClickOptions, UserEventTabOptions, UserEventTypeOptions } from '../../../context' import type { BrowserRunnerState } from '../utils' -import type { BrowserRPC } from '../client' // this file should not import anything directly, only types diff --git a/packages/browser/src/client/tester/mocker.ts b/packages/browser/src/client/tester/mocker.ts index 5aad7c1e97ea..785e0bc3292d 100644 --- a/packages/browser/src/client/tester/mocker.ts +++ b/packages/browser/src/client/tester/mocker.ts @@ -1,8 +1,8 @@ import { getType } from '@vitest/utils' import { extname, join } from 'pathe' +import type { IframeChannelOutgoingEvent } from '@vitest/browser/client' +import { channel, waitForChannel } from '@vitest/browser/client' import { getBrowserState, importId } from '../utils' -import type { IframeChannelOutgoingEvent } from '../channel' -import { channel, waitForChannel } from '../client' import { rpc } from './rpc' const now = Date.now diff --git a/packages/browser/src/client/tester/msw.ts b/packages/browser/src/client/tester/msw.ts index da7a244ec0aa..1cdc3b88ef20 100644 --- a/packages/browser/src/client/tester/msw.ts +++ b/packages/browser/src/client/tester/msw.ts @@ -1,10 +1,10 @@ +import { channel } from '@vitest/browser/client' import type { IframeChannelEvent, IframeMockEvent, IframeMockingDoneEvent, IframeUnmockEvent, -} from '../channel' -import { channel } from '../channel' +} from '@vitest/browser/client' export function createModuleMocker() { const mocks: Map = new Map() diff --git a/packages/browser/src/client/tester/rpc.ts b/packages/browser/src/client/tester/rpc.ts index 07ccbc11e5eb..ad1695c966fb 100644 --- a/packages/browser/src/client/tester/rpc.ts +++ b/packages/browser/src/client/tester/rpc.ts @@ -1,5 +1,5 @@ import { getSafeTimers } from 'vitest/utils' -import type { VitestBrowserClient } from '../client' +import type { VitestBrowserClient } from '@vitest/browser/client' const { get } = Reflect diff --git a/packages/browser/src/client/tester/runner.ts b/packages/browser/src/client/tester/runner.ts index b8b3d1cfe287..4ec5a4c9531a 100644 --- a/packages/browser/src/client/tester/runner.ts +++ b/packages/browser/src/client/tester/runner.ts @@ -5,8 +5,8 @@ import { NodeBenchmarkRunner, VitestTestRunner } from 'vitest/runners' import { loadDiffConfig, loadSnapshotSerializers, takeCoverageInsideWorker } from 'vitest/browser' import { TraceMap, originalPositionFor } from 'vitest/utils' import { page } from '@vitest/browser/context' +import { globalChannel } from '@vitest/browser/client' import { importFs, importId } from '../utils' -import { globalChannel } from '../channel' import { VitestBrowserSnapshotEnvironment } from './snapshot' import { rpc } from './rpc' import type { VitestBrowserClientMocker } from './mocker' diff --git a/packages/browser/src/client/tester/snapshot.ts b/packages/browser/src/client/tester/snapshot.ts index aa445fe4eb0d..8e1dd2f0e807 100644 --- a/packages/browser/src/client/tester/snapshot.ts +++ b/packages/browser/src/client/tester/snapshot.ts @@ -1,6 +1,6 @@ import type { SnapshotEnvironment } from 'vitest/snapshot' import { type ParsedStack, TraceMap, originalPositionFor } from 'vitest/utils' -import type { VitestBrowserClient } from '../client' +import type { VitestBrowserClient } from '@vitest/browser/client' export class VitestBrowserSnapshotEnvironment implements SnapshotEnvironment { private sourceMaps = new Map() diff --git a/packages/browser/src/client/tester/state.ts b/packages/browser/src/client/tester/state.ts index 21a2c65b6c0f..80d32bd6b2f6 100644 --- a/packages/browser/src/client/tester/state.ts +++ b/packages/browser/src/client/tester/state.ts @@ -1,7 +1,7 @@ import type { WorkerGlobalState } from 'vitest' import { parse } from 'flatted' +import type { BrowserRPC } from '@vitest/browser/client' import { getBrowserState } from '../utils' -import type { BrowserRPC } from '../client' const config = getBrowserState().config const contextId = getBrowserState().contextId diff --git a/packages/browser/src/client/tester/tester.html b/packages/browser/src/client/tester/tester.html index 60bfdd5089f1..13ae2549cf90 100644 --- a/packages/browser/src/client/tester/tester.html +++ b/packages/browser/src/client/tester/tester.html @@ -16,8 +16,9 @@ min-height: 100vh; } - + {__VITEST_INJECTOR__} + {__VITEST_ERROR_CATCHER__} {__VITEST_SCRIPTS__} void) { window.addEventListener(event, listener) return () => window.removeEventListener(event, listener) } -export function serializeError(unhandledError: any) { +function serializeError(unhandledError: any) { + if (typeof unhandledError !== 'object' || !unhandledError) { + return { + message: String(unhandledError), + } + } + return { - ...unhandledError, name: unhandledError.name, message: unhandledError.message, stack: String(unhandledError.stack), @@ -49,19 +53,20 @@ function catchWindowErrors(cb: (e: ErrorEvent) => void) { } } -export function registerUnexpectedErrors(rpc: typeof client.rpc) { +function registerUnexpectedErrors() { catchWindowErrors(event => - reportUnexpectedError(rpc, 'Error', event.error), + reportUnexpectedError('Error', event.error), ) on('unhandledrejection', event => - reportUnexpectedError(rpc, 'Unhandled Rejection', event.reason)) + reportUnexpectedError('Unhandled Rejection', event.reason)) } async function reportUnexpectedError( - rpc: typeof client.rpc, type: string, error: any, ) { - const processedError = processError(error) - await rpc.onUnhandledError(processedError, type) + const processedError = serializeError(error) + await client.rpc.onUnhandledError(processedError, type) } + +registerUnexpectedErrors() diff --git a/packages/browser/src/client/vite.config.ts b/packages/browser/src/client/vite.config.ts index 59e3ab89cde3..ab3b3dd1aa04 100644 --- a/packages/browser/src/client/vite.config.ts +++ b/packages/browser/src/client/vite.config.ts @@ -22,7 +22,13 @@ export default defineConfig({ orchestrator: resolve(__dirname, './orchestrator.html'), tester: resolve(__dirname, './tester/tester.html'), }, - external: [/^vitest\//, 'vitest', /^msw/, '@vitest/browser/context'], + external: [ + /^vitest\//, + 'vitest', + /^msw/, + '@vitest/browser/context', + '@vitest/browser/client', + ], }, }, plugins: [ diff --git a/packages/browser/src/node/plugin.ts b/packages/browser/src/node/plugin.ts index aeae86aa421e..629f1c5afc53 100644 --- a/packages/browser/src/node/plugin.ts +++ b/packages/browser/src/node/plugin.ts @@ -178,20 +178,15 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => { 'std-env', 'tinybench', 'tinyspy', + 'tinyrainbow', 'pathe', 'msw', 'msw/browser', ], include: [ - 'vitest > @vitest/utils > pretty-format', - 'vitest > @vitest/snapshot > pretty-format', 'vitest > @vitest/snapshot > magic-string', - 'vitest > pretty-format', - 'vitest > pretty-format > ansi-styles', - 'vitest > pretty-format > ansi-regex', 'vitest > chai', 'vitest > chai > loupe', - 'vitest > @vitest/utils > diff-sequences', 'vitest > @vitest/utils > loupe', '@vitest/browser > @testing-library/user-event', '@vitest/browser > @testing-library/dom', diff --git a/packages/browser/src/node/server.ts b/packages/browser/src/node/server.ts index b833418e8c43..b552648b9c61 100644 --- a/packages/browser/src/node/server.ts +++ b/packages/browser/src/node/server.ts @@ -28,6 +28,7 @@ export class BrowserServer implements IBrowserServer { public testerHtml: Promise | string public orchestratorHtml: Promise | string public injectorJs: Promise | string + public errorCatcherJs: Promise | string public stateJs: Promise | string public state: BrowserServerState @@ -86,6 +87,10 @@ export class BrowserServer implements IBrowserServer { resolve(distRoot, 'client/esm-client-injector.js'), 'utf8', ).then(js => (this.injectorJs = js)) + this.errorCatcherJs = readFile( + resolve(distRoot, 'client/error-catcher.js'), + 'utf8', + ).then(js => (this.errorCatcherJs = js)) this.stateJs = readFile( resolve(distRoot, 'state.js'), 'utf-8', diff --git a/packages/browser/src/node/serverOrchestrator.ts b/packages/browser/src/node/serverOrchestrator.ts index bfa20dc15011..e6d176cc4d6c 100644 --- a/packages/browser/src/node/serverOrchestrator.ts +++ b/packages/browser/src/node/serverOrchestrator.ts @@ -59,7 +59,8 @@ export async function resolveOrchestrator( .replace( '', [ - '', + '{__VITEST_INJECTOR__}', + '{__VITEST_ERROR_CATCHER__}', '{__VITEST_SCRIPTS__}', ``, ].join('\n'), @@ -70,7 +71,8 @@ export async function resolveOrchestrator( __VITEST_FAVICON__: server.faviconUrl, __VITEST_TITLE__: 'Vitest Browser Runner', __VITEST_SCRIPTS__: server.orchestratorScripts, - __VITEST_INJECTOR__: injector, + __VITEST_INJECTOR__: ``, + __VITEST_ERROR_CATCHER__: ``, __VITEST_CONTEXT_ID__: JSON.stringify(contextId), }) } diff --git a/packages/browser/src/node/serverTester.ts b/packages/browser/src/node/serverTester.ts index d756efa0b8ad..97c187ec34aa 100644 --- a/packages/browser/src/node/serverTester.ts +++ b/packages/browser/src/node/serverTester.ts @@ -71,7 +71,8 @@ export async function resolveTester( __VITEST_FAVICON__: server.faviconUrl, __VITEST_TITLE__: 'Vitest Browser Tester', __VITEST_SCRIPTS__: server.testerScripts, - __VITEST_INJECTOR__: injector, + __VITEST_INJECTOR__: ``, + __VITEST_ERROR_CATCHER__: ``, __VITEST_APPEND__: `