Skip to content

tsconfig watchOptions.excludeDirectories and excludeFiles resolve relative to cwd instead of tsconfigΒ #55656

@dmichon-msft

Description

@dmichon-msft

πŸ”Ž Search Terms

watchOptions, working directory, cwd, tsconfig, excludeDirectories, excludeFiles

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about watchOptions

⏯ Playground Link

No response

πŸ’» Code

const path = require('node:path');
const ts = require('typescript');

const tsConfigPath = // Some path to a valid tsconfig with watch options defined.

const parsedConfigFile = ts.readConfigFile(tsConfigPath, sys.readFile);
const basePath = path.dirname(tsConfigPath);
const sys = {
  ...ts.sys,
  getCurrentDirectory: () => basePath // ts.sys.getCurrentDirectory() returns the memoized value of process.cwd() as of when `require('typescript')` executed
};
const tsconfig = ts.parseJsonConfigFileContent(
  parsedConfigFile.config,
  {
    fileExists: sys.fileExists,
    readFile: sys.readFile,
    readDirectory: sys.readDirectory,
    useCaseSensitiveFileNames: true
  },
  basePath,
  /*existingOptions:*/ undefined,
  tsConfigPath
);

const compilerHost = ts.createWatchCompilerHost(tsconfig.fileNames, tsconfig.options, sys, undefined, undefined, undefined, tsconfig.projectReferences, tsconfig.watchOptions);
const watchProgram = ts.createWatchProgram(compilerHost);

πŸ™ Actual behavior

Typescript evaluates watchOptions.excludeFiles and watchOptions.excludeDirectories relative to the value of process.cwd() as of the moment the require('typescript') call evaluated.
Edit: the value gets memoized upon the first invocation of ts.sys.getCurrentDirectory() (which is captured via closure and therefore not overrideable).

πŸ™‚ Expected behavior

The watchOptions.excludeFiles and watchOptions.excludeDirectories patterns are evaluated relative to the folder containing the tsconfig file.

Additional information about the issue

The expressions here evaluate excludeFiles and excludeDirectories using a value of basePath that is a memoized invocation of process.cwd() at the moment ts.sys was created:

return (options?.excludeDirectories || options?.excludeFiles) && (
matchesExclude(pathToCheck, options?.excludeFiles, useCaseSensitiveFileNames, getCurrentDirectory()) ||
matchesExclude(pathToCheck, options?.excludeDirectories, useCaseSensitiveFileNames, getCurrentDirectory())
);

This interferes with the ability to run multiple instances of the TypeScript compiler in watch mode in the same process concurrently, or even to run the compiler in watch mode via the API in a root folder other than the current process working directory (and changing the current working directory doesn't work either).

Metadata

Metadata

Assignees

Labels

Needs More InfoThe issue still hasn't been fully clarified

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions