Repository: TypeScript
Issue: API for parsing *.json config files into a complete config objects
Labels: Suggestion,Awaiting More Feedback

# Suggestion

Dedicated public beginner-friendly API (ideally, an asynchronous function on the `ts` namespace) for parsing `tsconfig.json` / `jsconfig.json` files into a complete config object.

## 🔍 Search Terms

parse JSON config tsconfig tsconfig.json jsconfig.json

## ✅ Viability Checklist

My suggestion meets these guidelines:

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).

## ⭐ Suggestion

The suggestion is to have a single function that in its simplest form (overload) would take a string, – path to the config file (either `tsconfig.json` or `jsconfig.json`, shouldn't matter), and return (either synchronously or asynchronously) the fully compiled complete configuration object with all the properties initialized (either explicitly from the provided config(s) or implicitly, from known defaults).

> See **Motivating Example** below for why I can't use existing functions on `ts` namespace.

The suggestion is related to issue #44516 and [this StackOverflow question](https://stackoverflow.com/questions/67956755/how-to-compile-tsconfig-json-into-a-config-object-using-typescript-api/67957311?noredirect=1#comment120115106_67957311).

## 📃 Motivating Example

I know that there are already a couple of functions on the imported `ts` namespace whose purpose is to read/parse/compile config, given its filepath/stringified contents/parsed contents. These include:

- [`ts.readConfigFile`](https://github.com/microsoft/TypeScript/blob/147d7211367d9f576bb227a2c40004901c5521b6/src/compiler/commandLineParser.ts#L1661)
- [`ts.parseConfigFileTextToJson `](https://github.com/microsoft/TypeScript/blob/147d7211367d9f576bb227a2c40004901c5521b6/src/compiler/commandLineParser.ts#L1671)
- [`ts.readJsonConfigFile`](https://github.com/microsoft/TypeScript/blob/147d7211367d9f576bb227a2c40004901c5521b6/src/compiler/commandLineParser.ts#L1683)
- [`ts.parseJsonConfigFileContent`](https://github.com/microsoft/TypeScript/blob/147d7211367d9f576bb227a2c40004901c5521b6/src/compiler/commandLineParser.ts#L2423)

However, they either provide incomplete functionality (e.g., `ts.readConfigFile` is just a glorified `JSON.parse`, it doesn't – for example – crawl parent configs) or are ridiculously complicated to use (e.g., `ts.parseConfigFileTextToJson` require 3-9 arguments, the third of which is some kind of `host` object, that has to have a `readDirectory` method, that in turn requires 4-5 arguments, and has to actually do something, which I honestly don't know why I can't just use `fs.readdirSync`).

I need a function that takes a path to json and returns a complete config object.

## 💻 Use Cases

I'm thinking of a couple of variants of the function (for the sake of an example, it is called `readConfig`):

```ts
// given these
type Path = string | Buffer | URL;
interface Config {
  include?: string[], exclude?: string[], files?: string[], compilerOptions: { /*...*/ }, /* other properties of *.json config files */
}
```

```ts
/**
 * Find file by path, parse it, extend parent config objects, fill in missing defaults, return the resulting config object
 * If an error occurs, throw it
 */
/* 1 */ function readConfigSync(path: Path): Config;
```

```ts
/**
 * Find file by path, parse it, extend parent config objects, fill in missing defaults, invoke `callback(null, <resulting config object>)`
 * If an error occurs, invoke `callback(<error>)`
 */
/* 2 */ function readConfig(path: Path, callback: (...args: [ err: unknown ] | [ err: null, config: Config ]) => void): void;
```

```ts
/**
 * Find file by path, parse it, extend parent config objects, fill in missing defaults, return promise that resolves to the default config object
 * If an error occurs, return promise that rejects with the error
 */
/* 3 */ function readConfig(path: Path): Promise<Config>;
```

```ts
/**
 * Return the default config object
 * If an error occurs, throw it
 */
/* 4 */ function readConfigSync(): Config;
```

```ts
/**
 * Invoke `callback(null, <default config object>)`
 * If an error occurs, invoke `callback(<error>)`
 */
/* 5 */ function readConfig(callback: (...args: [ err: unknown ] | [ err: null, config: Config ]) => void): void;
```

```ts
/**
 * Return promise that resolves to the default config object
 * If an error occurs, return promise that rejects with the error
 */
/* 6 */ function readConfig(): Promise<Config>;
```

Also, shout out to functions that return `{ config?: Config; error?: unknown }`, which, however, are not idiomatic to Node.JS.