Skip to content

Commit

Permalink
fix(vitest): print warnings form Vite plugins (#6724)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va authored Oct 18, 2024
1 parent 0a2132b commit 121b161
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 25 deletions.
4 changes: 3 additions & 1 deletion packages/browser/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ export async function createBrowserServer(

const logLevel = (process.env.VITEST_BROWSER_DEBUG as 'info') ?? 'info'

const logger = createViteLogger(logLevel)
const logger = createViteLogger(project.logger, logLevel, {
allowClearScreen: false,
})

const vite = await createViteServer({
...project.options, // spread project config inlined in root workspace config
Expand Down
34 changes: 20 additions & 14 deletions packages/browser/src/node/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import { lstatSync, readFileSync } from 'node:fs'
import type { Stats } from 'node:fs'
import { basename, extname, resolve } from 'pathe'
import { basename, dirname, extname, resolve } from 'pathe'
import sirv from 'sirv'
import type { WorkspaceProject } from 'vitest/node'
import { getFilePoolName, resolveApiServerConfig, resolveFsAllow, distDir as vitestDist } from 'vitest/node'
Expand All @@ -23,6 +23,12 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
const distRoot = resolve(pkgRoot, 'dist')
const project = browserServer.project

function isPackageExists(pkg: string, root: string) {
return browserServer.project.ctx.packageInstaller.isPackageExists?.(pkg, {
paths: [root],
})
}

return [
{
enforce: 'pre',
Expand Down Expand Up @@ -211,14 +217,14 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
const coverage = project.ctx.config.coverage
const provider = coverage.provider
if (provider === 'v8') {
const path = tryResolve('@vitest/coverage-v8', [project.ctx.config.root])
const path = tryResolve('@vitest/coverage-v8', [project.config.root])
if (path) {
entries.push(path)
exclude.push('@vitest/coverage-v8/browser')
}
}
else if (provider === 'istanbul') {
const path = tryResolve('@vitest/coverage-istanbul', [project.ctx.config.root])
const path = tryResolve('@vitest/coverage-istanbul', [project.config.root])
if (path) {
entries.push(path)
exclude.push('@vitest/coverage-istanbul')
Expand All @@ -239,18 +245,18 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
'@vitest/browser > @testing-library/dom',
]

const react = tryResolve('vitest-browser-react', [project.ctx.config.root])
if (react) {
include.push(react)
}
const vue = tryResolve('vitest-browser-vue', [project.ctx.config.root])
if (vue) {
include.push(vue)
}
const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root

const svelte = tryResolve('vitest-browser-svelte', [project.ctx.config.root])
const svelte = isPackageExists('vitest-browser-svelte', fileRoot)
if (svelte) {
exclude.push(svelte)
exclude.push('vitest-browser-svelte')
}

// since we override the resolution in the esbuild plugin, Vite can no longer optimizer it
// have ?. until Vitest 3.0 for backwards compatibility
const vueTestUtils = isPackageExists('@vue/test-utils', fileRoot)
if (vueTestUtils) {
include.push('@vue/test-utils')
}

return {
Expand Down Expand Up @@ -398,7 +404,7 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
{
name: 'test-utils-rewrite',
setup(build) {
build.onResolve({ filter: /@vue\/test-utils/ }, (args) => {
build.onResolve({ filter: /^@vue\/test-utils$/ }, (args) => {
const _require = getRequire()
// resolve to CJS instead of the browser because the browser version expects a global Vue object
const resolved = _require.resolve(args.path, {
Expand Down
1 change: 0 additions & 1 deletion packages/vitest/src/node/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export async function createVitest(
options.config = configPath

const config: ViteInlineConfig = {
logLevel: 'error',
configFile: configPath,
// this will make "mode": "test" | "benchmark" inside defineConfig
mode: options.mode || mode,
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/node/packageInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { isCI } from '../utils/env'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))

export class VitestPackageInstaller {
isPackageExists(name: string, options?: { paths?: string[] }) {
return isPackageExists(name, options)
}

async ensureInstalled(dependency: string, root: string, version?: string) {
if (process.env.VITEST_SKIP_INSTALL_CHECKS) {
return true
Expand Down
9 changes: 9 additions & 0 deletions packages/vitest/src/node/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { resolveApiServerConfig } from '../config/resolveConfig'
import { Vitest } from '../core'
import { generateScopedClassName } from '../../integrations/css/css-modules'
import { defaultPort } from '../../constants'
import { createViteLogger } from '../viteLogger'
import { SsrReplacerPlugin } from './ssrReplacer'
import { CSSEnablerPlugin } from './cssEnabler'
import { CoverageTransform } from './coverageTransform'
Expand Down Expand Up @@ -132,6 +133,14 @@ export async function VitestPlugin(
},
}

config.customLogger = createViteLogger(
ctx.logger,
viteConfig.logLevel || 'warn',
{
allowClearScreen: false,
},
)

// If "coverage.exclude" is not defined by user, add "test.include" to "coverage.exclude" automatically
if (userConfig.coverage?.enabled && !userConfig.coverage.exclude && userConfig.include && config.test) {
config.test.coverage = {
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/plugins/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function resolveOptimizerConfig(

// `optimizeDeps.disabled` is deprecated since v5.1.0-beta.1
// https://github.com/vitejs/vite/pull/15184
if (major >= 5 && minor >= 1) {
if ((major >= 5 && minor >= 1) || major >= 6) {
if (newConfig.optimizeDeps.disabled) {
newConfig.optimizeDeps.noDiscovery = true
newConfig.optimizeDeps.include = []
Expand Down
8 changes: 8 additions & 0 deletions packages/vitest/src/node/plugins/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { configDefaults } from '../../defaults'
import { generateScopedClassName } from '../../integrations/css/css-modules'
import type { WorkspaceProject } from '../workspace'
import type { ResolvedConfig, UserWorkspaceConfig } from '../types/config'
import { createViteLogger } from '../viteLogger'
import { CoverageTransform } from './coverageTransform'
import { CSSEnablerPlugin } from './cssEnabler'
import { SsrReplacerPlugin } from './ssrReplacer'
Expand Down Expand Up @@ -124,6 +125,13 @@ export function WorkspaceVitestPlugin(
}
}
}
config.customLogger = createViteLogger(
project.logger,
viteConfig.logLevel || 'warn',
{
allowClearScreen: false,
},
)

return config
},
Expand Down
5 changes: 1 addition & 4 deletions packages/vitest/src/node/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ export async function createViteServer(inlineConfig: InlineConfig) {
error(...args)
}

const server = await createServer({
logLevel: 'error',
...inlineConfig,
})
const server = await createServer(inlineConfig)

console.error = error
return server
Expand Down
137 changes: 137 additions & 0 deletions packages/vitest/src/node/viteLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import type { LogErrorOptions, LogLevel, LogType, Logger, LoggerOptions } from 'vite'
import type { RollupError } from 'rollup'
import colors from 'tinyrainbow'
import type { Logger as VitestLogger } from './logger'

const LogLevels: Record<LogLevel, number> = {
silent: 0,
error: 1,
warn: 2,
info: 3,
}

function clearScreen(logger: VitestLogger) {
const repeatCount = process.stdout.rows - 2
const blank = repeatCount > 0 ? '\n'.repeat(repeatCount) : ''
logger.clearScreen(blank)
}

let lastType: LogType | undefined
let lastMsg: string | undefined
let sameCount = 0

// Only initialize the timeFormatter when the timestamp option is used, and
// reuse it across all loggers
let timeFormatter: Intl.DateTimeFormat
function getTimeFormatter() {
timeFormatter ??= new Intl.DateTimeFormat(undefined, {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
})
return timeFormatter
}

// This is copy-pasted and needs to be synced from time to time. Ideally, Vite's `createLogger` should accept a custom `console`
// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/logger.ts?rgh-link-date=2024-10-16T23%3A29%3A19Z
// When Vitest supports only Vite 6 and above, we can use Vite's `createLogger({ console })`
// https://github.com/vitejs/vite/pull/18379
export function createViteLogger(
console: VitestLogger,
level: LogLevel = 'info',
options: LoggerOptions = {},
): Logger {
const loggedErrors = new WeakSet<Error | RollupError>()
const { prefix = '[vite]', allowClearScreen = true } = options
const thresh = LogLevels[level]
const canClearScreen
= allowClearScreen && process.stdout.isTTY && !process.env.CI
const clear = canClearScreen ? clearScreen : () => {}

function format(type: LogType, msg: string, options: LogErrorOptions = {}) {
if (options.timestamp) {
let tag = ''
if (type === 'info') {
tag = colors.cyan(colors.bold(prefix))
}
else if (type === 'warn') {
tag = colors.yellow(colors.bold(prefix))
}
else {
tag = colors.red(colors.bold(prefix))
}
const environment = (options as any).environment ? `${(options as any).environment} ` : ''
return `${colors.dim(getTimeFormatter().format(new Date()))} ${tag} ${environment}${msg}`
}
else {
return msg
}
}

function output(type: LogType, msg: string, options: LogErrorOptions = {}) {
if (thresh >= LogLevels[type]) {
const method = type === 'info' ? 'log' : type

if (options.error) {
loggedErrors.add(options.error)
}
if (canClearScreen) {
if (type === lastType && msg === lastMsg) {
sameCount++
clear(console)
console[method](
format(type, msg, options),
colors.yellow(`(x${sameCount + 1})`),
)
}
else {
sameCount = 0
lastMsg = msg
lastType = type
if (options.clear) {
clear(console)
}
console[method](format(type, msg, options))
}
}
else {
console[method](format(type, msg, options))
}
}
}

const warnedMessages = new Set<string>()

const logger: Logger = {
hasWarned: false,
info(msg, opts) {
output('info', msg, opts)
},
warn(msg, opts) {
logger.hasWarned = true
output('warn', msg, opts)
},
warnOnce(msg, opts) {
if (warnedMessages.has(msg)) {
return
}
logger.hasWarned = true
output('warn', msg, opts)
warnedMessages.add(msg)
},
error(msg, opts) {
logger.hasWarned = true
output('error', msg, opts)
},
clearScreen(type) {
if (thresh >= LogLevels[type]) {
clear(console)
}
},
hasErrorLogged(error) {
return loggedErrors.has(error)
},
}

return logger
}
1 change: 0 additions & 1 deletion packages/vitest/src/node/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export async function initializeProject(
const config: ViteInlineConfig = {
...options,
root,
logLevel: 'error',
configFile,
// this will make "mode": "test" | "benchmark" inside defineConfig
mode: options.test?.mode || options.mode || ctx.config.mode,
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/public/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { createDebugger } from '../utils/debugger'
export { resolveFsAllow } from '../node/plugins/utils'
export { resolveApiServerConfig, resolveConfig } from '../node/config/resolveConfig'
export { TestSpecification } from '../node/spec'
export { createViteLogger } from '../node/viteLogger'

export { GitNotFoundError, FilesNotFoundError as TestsNotFoundError } from '../node/errors'

Expand Down Expand Up @@ -54,7 +55,6 @@ export {
isFileServingAllowed,
parseAst,
parseAstAsync,
createLogger as createViteLogger,
} from 'vite'
/** @deprecated use `createViteServer` instead */
export const createServer = _createServer
Expand Down
5 changes: 3 additions & 2 deletions test/coverage-test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { stripVTControlCharacters } from 'node:util'
import { normalize } from 'pathe'
import libCoverage from 'istanbul-lib-coverage'
import type { FileCoverageData } from 'istanbul-lib-coverage'
import type { TestFunction, UserConfig } from 'vitest'
import type { TestFunction } from 'vitest'
import { vi, describe as vitestDescribe, test as vitestTest } from 'vitest'
import type { UserConfig } from 'vitest/node'
import * as testUtils from '../test-utils'

export function test(name: string, fn: TestFunction, skip = false) {
Expand Down Expand Up @@ -55,7 +56,7 @@ export async function runVitest(config: UserConfig, options = { throwOnError: tr

if (options.throwOnError) {
if (result.stderr !== '') {
throw new Error(result.stderr)
throw new Error(`stderr:\n${result.stderr}\n\nstdout:\n${result.stdout}`)
}
}

Expand Down

0 comments on commit 121b161

Please sign in to comment.