Repository: TypeScript
Issue: Resolving multiple package.json "main" fields
Labels: Suggestion,Awaiting More Feedback,Scenario: Monorepos & Cross-Project References

**TL;DR:** A new compiler option `mainFields` for selecting multiple fields in `package.json` instead of just `package.json#main`.

---

There are lots of related issues to this one (which I link to below), but I want to focus on just this specific proposal.

---

Packages often look like this:

```js
{
  "name": "my-package",
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js"
  "source": "src/index.ts"
}
```

Notice how we have multiple fields which specify multiple entry points. These entry points all refer to the same code, just in different compile states and configurations.

Many tools use these fields in order to find the entry point that they care about. For example, tools like Webpack and Rollup will use `package.json#module` in order to find ES modules. Other tools will use fields like `package.json#source` (or `src`) for local package development.

While these fields aren't part of the official Node module resolution algorithm. They are a community convention which has proven to be useful in lots of scenarios.

For TypeScript, one such scenario that this would be useful for is with multi-package repos or "monorepos". These are repositories where the code for multiple npm packages exist and are symlinked together locally.

```
/project/
  package.json
  /packages/
    /package-one/
      package.json
      /node_modules/
        /package-two/ -> ../../package-two (symlink)
    /package-two/
      package.json
```

Inside each package, you'll generally have a `src/` directory that gets compiled to `dist/`

```
/package-two/
  package.json
  /src/
    index.ts
  /dist/
    index.js
    index.d.ts
```

Right now it is really painful to use TypeScript with one of these repos. This is because TypeScript will use the `package.json#main` to resolve to the packages `dist` folders. The problem with this is that the `dist` folders might not exist and if they do exist they might not be compiled from the most recent version of `src`. 

To work around this today you can add a `index.ts` file in the root of each of your packages to point to the right location and make sure that the root `index.ts` file does not get shipped to npm.

```
/package-two/
  index.ts
  /src/index.ts
```

```ts
// package-two/index.ts
export * from './src/index'
```

It sucks that you need this file, and if you ever forget to create it in a new package, you'll revert back to really crap behavior.

If, instead of all that, TypeScript supported a new compiler option `mainFields` which looked like:

```js
{
  "compilerOptions": {
    "mainFields": ["source", "main"]
  }
}
```

> **Note:** [Webpack has this same configuration option](https://webpack.js.org/configuration/resolve/#resolve-mainfields)

You could add `package.json#source` (in addition to `package.json#main`) and resolve it to the right location locally.

The algorithm would look like this:

For each `mainField`:

1. Check if the `package.json` has a field with that name
2. If the package.json does not have the field, continue to next `mainField`
3. If it field exists, check for a file at that location.
4. If no file at that location exists, continue to the next `mainField`
5. If the file exists, use that file as the resolved module and stop looking

I think this is the relevant code:

https://github.com/Microsoft/TypeScript/blob/b363f4f9cd6ef98f9451ccdcc7321d151195200b/src/compiler/moduleNameResolver.ts#L987-L1014

**Related Issues:**

- https://github.com/Microsoft/TypeScript/issues/21137 "Path mapping in yarn workspaces"
- https://github.com/Microsoft/TypeScript/issues/20248 "Module resolution for sub-packages picks d.ts file when .ts file is available"
- https://github.com/Microsoft/TypeScript/issues/18442 "Support `.mjs` output"