Skip to content

failOnWarn is coupled to logging output; silent logging will succeed #952

@tbeseda

Description

@tbeseda

Reproduction link or steps

This direct usage of build shows how when failOnWarn is true and logLevel is silent, build does not throw.
but when logLevel is warn, it throws as expected.

import {mkdtempSync, writeFileSync} from 'node:fs'
import {tmpdir} from 'node:os'
import {join} from 'node:path'
import {build} from 'tsdown'

// Create a temp project with a source file that imports a package not installed anywhere.
const dir = mkdtempSync(join(tmpdir(), 'tsdown-failonwarn-repro-'))
const entry = join(dir, 'index.ts')
writeFileSync(
  join(dir, 'package.json'),
  JSON.stringify({type: 'module', private: true}, null, 2),
)
writeFileSync(
  entry,
  `import express from 'express'\nexport const handler = () => express()\n`,
)

console.log('Test 1: failOnWarn=true, logLevel=silent')
try {
  await build({
    entry: {index: entry},
    outDir: join(dir, 'dist-silent'),
    format: 'esm',
    platform: 'node',
    dts: false,
    config: false,
    logLevel: 'silent',
    failOnWarn: true,
    deps: {alwaysBundle: () => true, onlyBundle: false},
  })
  console.log('  -> BUILD SUCCEEDED (bug: unresolved import was silently externalized)')
} catch (err) {
  console.log('  -> BUILD THREW:', err.message.split('\n')[0])
}

console.log('')

console.log('Test 2: failOnWarn=true, logLevel=warn')
try {
  await build({
    entry: {index: entry},
    outDir: join(dir, 'dist-warn'),
    format: 'esm',
    platform: 'node',
    dts: false,
    config: false,
    logLevel: 'warn',
    failOnWarn: true,
    deps: {alwaysBundle: () => true, onlyBundle: false},
  })
  console.log('  -> BUILD SUCCEEDED (unexpected)')
} catch (err) {
  console.log('  -> BUILD THREW:', err.message.split('\n')[0])
}
Test 1: failOnWarn=true, logLevel=silent
  -> BUILD SUCCEEDED (bug: unresolved import was silently externalized)

Test 2: failOnWarn=true, logLevel=warn
  -> BUILD THREW: Build failed with 1 error:

What is expected?

I would expect both cases above to throw. Opting into failOnWarn feels like a warning, whether it is logged or not, should fail the build.

What is actually happening?

build succeeds when when logging is silent and failOnWarn is enabled.

Any additional comments?

I acknowledge the option is not failOnWarning, but it's close enough that it leaves me wanting a simple way to fail all warnings even when logging is off.

Metadata

Metadata

Assignees

No one assigned

    Type

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions