From 427b06221e558c7bda308c24a37c04616048f558 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 7 Mar 2023 19:14:59 +0100 Subject: [PATCH] feat: use custom colors implementation instead of picocolors --- packages/expect/src/index.ts | 2 +- packages/utils/package.json | 1 - packages/utils/src/colors.ts | 81 +++++++++++++++++++++-- packages/vitest/src/runtime/setup.node.ts | 14 ++-- pnpm-lock.yaml | 58 ++++++++-------- 5 files changed, 110 insertions(+), 46 deletions(-) diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index f57106312f83..b0f3466f831d 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -5,4 +5,4 @@ export * from './types' export { getState, setState } from './state' export { JestChaiExpect } from './jest-expect' export { JestExtend } from './jest-extend' -export { setColors as setupColors } from '@vitest/utils' +export { setupColors } from '@vitest/utils' diff --git a/packages/utils/package.json b/packages/utils/package.json index b9f2228c1ea8..ae9beae215cb 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -40,7 +40,6 @@ "cli-truncate": "^3.1.0", "diff": "^5.1.0", "loupe": "^2.3.6", - "picocolors": "^1.0.0", "pretty-format": "^27.5.1" }, "devDependencies": { diff --git a/packages/utils/src/colors.ts b/packages/utils/src/colors.ts index 6c31d625d98f..f40cb0de8136 100644 --- a/packages/utils/src/colors.ts +++ b/packages/utils/src/colors.ts @@ -1,7 +1,7 @@ -import type p from 'picocolors' -import type { Formatter } from 'picocolors/types' import { SAFE_COLORS_SYMBOL } from './constants' +type Colors = ReturnType + const colors = [ 'reset', 'bold', @@ -30,17 +30,84 @@ const colors = [ 'bgWhite', ] as const -const formatter: Formatter = str => String(str) +const string = (str: unknown) => String(str) +string.open = '' +string.close = '' const defaultColors = colors.reduce((acc, key) => { - acc[key] = formatter + acc[key] = string return acc -}, { isColorSupported: false } as typeof p) +}, {} as Colors) + +export function getDefaultColors(): Colors { + return { ...defaultColors } +} -export function getColors(): typeof p { +export function getColors(): Colors { return (globalThis as any)[SAFE_COLORS_SYMBOL] || defaultColors } -export function setColors(colors: typeof p) { +export function createColors(isTTY = false) { + const enabled = typeof process !== 'undefined' + && !('NO_COLOR' in process.env || process.argv.includes('--no-color')) + && !('GITHUB_ACTIONS' in process.env) + && ('FORCE_COLOR' in process.env + || process.argv.includes('--color') + || process.platform === 'win32' + || (isTTY && process.env.TERM !== 'dumb') + || 'CI' in process.env) + + const replaceClose = (string: string, close: string, replace: string, index: number): string => { + const start = string.substring(0, index) + replace + const end = string.substring(index + close.length) + const nextIndex = end.indexOf(close) + return ~nextIndex ? start + replaceClose(end, close, replace, nextIndex) : start + end + } + + const formatter = (open: string, close: string, replace = open) => { + const fn = (input: unknown) => { + const string = `${input}` + const index = string.indexOf(close, open.length) + return ~index + ? open + replaceClose(string, close, replace, index) + close + : open + string + close + } + fn.open = open + fn.close = close + return fn + } + + // based on "https://github.com/alexeyraspopov/picocolors", but browser-friendly + return { + isColorSupported: enabled, + reset: enabled ? (s: string) => `\x1B[0m${s}\x1B[0m` : string, + bold: enabled ? formatter('\x1B[1m', '\x1B[22m', '\x1B[22m\x1B[1m') : string, + dim: enabled ? formatter('\x1B[2m', '\x1B[22m', '\x1B[22m\x1B[2m') : string, + italic: enabled ? formatter('\x1B[3m', '\x1B[23m') : string, + underline: enabled ? formatter('\x1B[4m', '\x1B[24m') : string, + inverse: enabled ? formatter('\x1B[7m', '\x1B[27m') : string, + hidden: enabled ? formatter('\x1B[8m', '\x1B[28m') : string, + strikethrough: enabled ? formatter('\x1B[9m', '\x1B[29m') : string, + black: enabled ? formatter('\x1B[30m', '\x1B[39m') : string, + red: enabled ? formatter('\x1B[31m', '\x1B[39m') : string, + green: enabled ? formatter('\x1B[32m', '\x1B[39m') : string, + yellow: enabled ? formatter('\x1B[33m', '\x1B[39m') : string, + blue: enabled ? formatter('\x1B[34m', '\x1B[39m') : string, + magenta: enabled ? formatter('\x1B[35m', '\x1B[39m') : string, + cyan: enabled ? formatter('\x1B[36m', '\x1B[39m') : string, + white: enabled ? formatter('\x1B[37m', '\x1B[39m') : string, + gray: enabled ? formatter('\x1B[90m', '\x1B[39m') : string, + bgBlack: enabled ? formatter('\x1B[40m', '\x1B[49m') : string, + bgRed: enabled ? formatter('\x1B[41m', '\x1B[49m') : string, + bgGreen: enabled ? formatter('\x1B[42m', '\x1B[49m') : string, + bgYellow: enabled ? formatter('\x1B[43m', '\x1B[49m') : string, + bgBlue: enabled ? formatter('\x1B[44m', '\x1B[49m') : string, + bgMagenta: enabled ? formatter('\x1B[45m', '\x1B[49m') : string, + bgCyan: enabled ? formatter('\x1B[46m', '\x1B[49m') : string, + bgWhite: enabled ? formatter('\x1B[47m', '\x1B[49m') : string, + } +} + +export function setupColors(colors: Colors) { (globalThis as any)[SAFE_COLORS_SYMBOL] = colors } diff --git a/packages/vitest/src/runtime/setup.node.ts b/packages/vitest/src/runtime/setup.node.ts index 6bb7f64ca248..9b39bf836401 100644 --- a/packages/vitest/src/runtime/setup.node.ts +++ b/packages/vitest/src/runtime/setup.node.ts @@ -1,7 +1,7 @@ import { createRequire } from 'node:module' -import p from 'picocolors' +import { isatty } from 'node:tty' import { installSourcemapsSupport } from 'vite-node/source-map' -import { setColors } from '@vitest/utils' +import { createColors, setupColors } from '@vitest/utils' import { environments } from '../integrations/env' import type { Environment, ResolvedConfig } from '../types' import { getSafeTimers, getWorkerState } from '../utils' @@ -29,13 +29,13 @@ export async function setupGlobalEnv(config: ResolvedConfig) { globalSetup = true setupSnapshotEnvironment(new NodeSnapshotEnvironment()) - setColors(p) + setupColors(createColors(isatty(1))) - const require = createRequire(import.meta.url) + const _require = createRequire(import.meta.url) // always mock "required" `css` files, because we cannot process them - require.extensions['.css'] = () => ({}) - require.extensions['.scss'] = () => ({}) - require.extensions['.sass'] = () => ({}) + _require.extensions['.css'] = () => ({}) + _require.extensions['.scss'] = () => ({}) + _require.extensions['.sass'] = () => ({}) const state = getWorkerState() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b51cf5ab930..ff803919cb7d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -240,7 +240,7 @@ importers: react-dom: 18.0.0_react@18.0.0 devDependencies: '@testing-library/react': 13.3.0_zpnidt7m3osuk7shl3s4oenomq - '@types/node': 18.14.1 + '@types/node': 18.14.6 '@types/react': 18.0.28 '@vitejs/plugin-react': 3.1.0 jsdom: 21.1.0 @@ -802,13 +802,11 @@ importers: cli-truncate: ^3.1.0 diff: ^5.1.0 loupe: ^2.3.6 - picocolors: ^1.0.0 pretty-format: ^27.5.1 dependencies: cli-truncate: 3.1.0 diff: 5.1.0 loupe: 2.3.6 - picocolors: 1.0.0 pretty-format: 27.5.1 devDependencies: '@types/diff': 5.0.2 @@ -5005,7 +5003,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.14.1 + '@types/node': 18.14.6 '@types/yargs': 15.0.14 chalk: 4.1.2 dev: true @@ -5017,7 +5015,7 @@ packages: '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.14.1 + '@types/node': 18.14.6 '@types/yargs': 17.0.12 chalk: 4.1.2 dev: true @@ -5529,7 +5527,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 playwright-core: 1.28.0 dev: true @@ -7355,7 +7353,7 @@ packages: /@types/cheerio/0.22.31: resolution: {integrity: sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/codemirror/5.60.6: @@ -7422,33 +7420,33 @@ packages: resolution: {integrity: sha512-zdV5odfHf95B4qr6bdpshG4VMm/3xgnPhSJLa3xh75CYr35e34k+4FQli82Q48sPqwHazJGy+6+jl4T+Vw1AMg==} dependencies: '@types/jsonfile': 6.1.1 - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/glob/7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.1 - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/glob/8.0.0: resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} dependencies: '@types/minimatch': 5.1.1 - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/hast/2.3.4: @@ -7509,7 +7507,7 @@ packages: /@types/jsdom/21.1.0: resolution: {integrity: sha512-leWreJOdnuIxq9Y70tBVm/bvTuh31DSlF/r4l7Cfi4uhVQqLHD0Q4v301GMisEMwwbMgF7ZKxuZ+Jbd4NcdmRw==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 '@types/tough-cookie': 4.0.2 parse5: 7.1.1 dev: true @@ -7525,7 +7523,7 @@ packages: /@types/jsonfile/6.1.1: resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/lodash/4.14.191: @@ -7559,7 +7557,7 @@ packages: /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 form-data: 3.0.1 dev: true @@ -7575,8 +7573,8 @@ packages: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /@types/node/18.14.1: - resolution: {integrity: sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==} + /@types/node/18.14.6: + resolution: {integrity: sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==} dev: true /@types/node/18.7.13: @@ -7609,7 +7607,7 @@ packages: /@types/prompts/2.4.2: resolution: {integrity: sha512-TwNx7qsjvRIUv/BCx583tqF5IINEVjCNqg9ofKHRlSoUHE62WBHrem4B1HGXcIrG511v29d1kJ9a/t2Esz7MIg==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 kleur: 3.0.3 dev: true @@ -7682,7 +7680,7 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/resolve/1.20.2: @@ -7699,7 +7697,7 @@ packages: /@types/set-cookie-parser/2.4.2: resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/sinonjs__fake-timers/8.1.1: @@ -7773,7 +7771,7 @@ packages: /@types/webpack-sources/3.2.0: resolution: {integrity: sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 '@types/source-list-map': 0.1.2 source-map: 0.7.4 dev: true @@ -7781,7 +7779,7 @@ packages: /@types/webpack/4.41.32: resolution: {integrity: sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 '@types/tapable': 1.0.8 '@types/uglify-js': 3.17.0 '@types/webpack-sources': 3.2.0 @@ -7792,7 +7790,7 @@ packages: /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true /@types/yargs-parser/21.0.0: @@ -7815,7 +7813,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 dev: true optional: true @@ -14923,7 +14921,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.5 - '@types/node': 18.14.1 + '@types/node': 18.14.6 anymatch: 3.1.2 fb-watchman: 2.0.1 graceful-fs: 4.2.10 @@ -14991,7 +14989,7 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 graceful-fs: 4.2.10 dev: true @@ -15000,7 +14998,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 18.14.1 + '@types/node': 18.14.6 chalk: 4.1.2 graceful-fs: 4.2.10 is-ci: 2.0.0 @@ -15012,7 +15010,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.0.1 - '@types/node': 18.14.1 + '@types/node': 18.14.6 chalk: 4.1.2 ci-info: 3.7.0 graceful-fs: 4.2.10 @@ -15023,7 +15021,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -15032,7 +15030,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.14.1 + '@types/node': 18.14.6 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true