#37100·nx

@nx/esbuild:esbuild resolves tsConfig relative to current working directory

Author: Hati-Created Sep 17, 2026Updated Sep 17, 2026
Labelstype: bug

Current Behavior

@nx/esbuild:esbuild resolves the tsConfig option relative to the current working directory when it initially reads the TypeScript configuration during option normalization.

This causes the same Nx build to behave differently depending on the directory from which nx is invoked.

Expected Behavior

The tsConfig path used by @nx/esbuild:esbuild should be resolved consistently relative to the Nx workspace root, regardless of the current working directory.

GitHub Repo

No response

Steps to Reproduce

Create a fresh Nx workspace:

powershell
PS D:\code> npx [email protected] demo --appName demo --preset=node-monorepo --framework=none --docker false --formatter=none --linter=none --unitTestRunner=none --analytics false --nxCloud skip

 NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

 NX   Creating your v23.2.1 workspace.

✔ Installing dependencies with npm
✔ Successfully created the workspace: demo

Run the build from the workspace root:

powershell
PS D:\code> cd demo
PS D:\code\demo> npx nx build demo --skipNxCache

> nx run @demo/demo:build:production


———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

  Run duration:      2.1s
  Cache:             Skipped (--skip-nx-cache)
  Critical path:     1.9s (1 task)
  Recoverable time:  <1ms

PS D:\code\demo>

The same build fails when run from the project's directory:

powershell
PS D:\code\demo> cd apps/demo
PS D:\code\demo\apps\demo> npx nx build demo --skipNxCache

> nx run @demo/demo:build:production

error TS5069: Option 'declarationMap' cannot be specified without specifying option 'declaration' or option 'composite'.
error TS5069: Option 'emitDeclarationOnly' cannot be specified without specifying option 'declaration' or option 'composite'.
Found 2 errors.

———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
NX   Ran target build for project @demo/demo (2s)

   ×  1/1 failed
   √  0/1 succeeded [0 read from cache]

NX   Nx detected a flaky task

  @demo/demo:build:production

Flaky tasks can disrupt your CI pipeline. Automatically retry them with Nx Cloud. Learn more at https://nx.dev/ci/features/flaky-tasks

  Run duration:      1.7s
  Cache:             Skipped (--skip-nx-cache)
  Critical path:     1.6s (1 task)
  Recoverable time:  <1ms

PS D:\code\demo\apps\demo>

Nx Report

bash
Node           : 24.18.0
OS             : win32-x64
Native Target  : x86_64-windows
npm            : 11.16.0
daemon         : Available

nx             : 23.2.1
@nx/js         : 23.2.1
@nx/eslint     : 23.2.1
@nx/workspace  : 23.2.1
@nx/jest       : 23.2.1
@nx/devkit     : 23.2.1
@nx/esbuild    : 23.2.1
@nx/node       : 23.2.1
@nx/docker     : 23.2.1
typescript     : 6.0.3
---------------------------------------
Registered Plugins:
@nx/js/typescript
---------------------------------------
Cache
- Usage: 0.00 B / 23.10 GB (0.0%)
- Location: ~\.nx\42b10a8fd28ebbad\cache

Failure Logs

bash
error TS5069: Option 'declarationMap' cannot be specified without specifying option 'declaration' or option 'composite'.
error TS5069: Option 'emitDeclarationOnly' cannot be specified without specifying option 'declaration' or option 'composite'.
Found 2 errors.

Package Manager Version

npm 11.16.0

Operating System

  • macOS
  • Linux
  • Windows
  • Other (Please specify)

Additional Information

The build target reports its tsConfig as a workspace-relative path. The following test obtains that path directly from nx show project and passes it to readTsConfig().

From the workspace root:

powershell
PS D:\code\demo> node -e "const { execSync } = require('node:child_process'); const { readTsConfig } = require('@nx/js'); const project = JSON.parse(execSync('npx nx show project demo --json', { encoding: 'utf8' })); const tsConfig = project.targets.build.options.tsConfig; const c = readTsConfig(tsConfig); console.log({ cwd: process.cwd(), tsConfig, options: c.options, errors: c.errors });"
{
  cwd: 'D:\\code\\demo',
  tsConfig: 'apps/demo/tsconfig.app.json',
  options: {
    composite: true,
    declarationMap: true,
    emitDeclarationOnly: true,
    importHelpers: true,
    isolatedModules: true,
    lib: [ 'lib.es2022.d.ts' ],
    module: 199,
    moduleResolution: 99,
    noEmitOnError: true,
    noFallthroughCasesInSwitch: true,
    noImplicitOverride: true,
    noImplicitReturns: true,
    noUnusedLocals: true,
    skipLibCheck: true,
    strict: true,
    target: 9,
    customConditions: [ '@demo/source' ],
    outDir: 'D:/code/demo/apps/demo/dist',
    types: [ 'node' ],
    rootDir: 'D:/code/demo/apps/demo/src',
    tsBuildInfoFile: 'D:/code/demo/apps/demo/dist/tsconfig.app.tsbuildinfo',
    configFilePath: undefined
  },
  errors: []
}
PS D:\code\demo>

From the project directory:

powershell
PS D:\code\demo> cd apps/demo
PS D:\code\demo\apps\demo> node -e "const { execSync } = require('node:child_process'); const { readTsConfig } = require('@nx/js'); const project = JSON.parse(execSync('npx nx show project demo --json', { encoding: 'utf8' })); const tsConfig = project.targets.build.options.tsConfig; const c = readTsConfig(tsConfig); console.log({ cwd: process.cwd(), tsConfig, options: c.options, errors: c.errors });"
{
  cwd: 'D:\\code\\demo\\apps\\demo',
  tsConfig: 'apps/demo/tsconfig.app.json',
  options: { configFilePath: undefined },
  errors: [
    {
      file: undefined,
      start: undefined,
      length: undefined,
      messageText: `No inputs were found in config file 'tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude' paths were '[]'.`,
      category: 1,
      code: 18003,
      reportsUnnecessary: undefined,
      reportsDeprecated: undefined
    }
  ]
}
PS D:\code\demo\apps\demo>

This demonstrates that the same tsConfig value returned by Nx is interpreted differently by readTsConfig() depending on the current working directory.

The relevant call in @nx/esbuild 23.2.1 is:

typescript
const tsConfig = readTsConfig(options.tsConfig);

in packages/esbuild/src/executors/esbuild/lib/normalize.ts#L25.

The readTsConfig() implementation is in packages/js/src/utils/typescript/ts-config.ts#L15.

Nx 23.2.0 also includes a related fix for esbuild paths being resolved from the workspace root instead of the cwd: PR #36830