Repository: TypeScript
Issue: Establish, document and maintain a reference monorepo for building npm packages
Labels: Suggestion,Help Wanted

# Suggestion

Establish and document **reference** npm packages built from Typescript source in a monorepo which achieve the following three fundamentals. This seems no longer possible since `node16`, `nodenext` ...

* A monorepo (with select package names resolving to local folders)
* Package references resolve to source files, not built assets (e.g. local tooling such as IDE, bundlers, jest, can resolve local packages to  `.ts` files not `.js` or `.d.ts` files which may be out of date)
* Publishable (package has `"main"`, `"module"`, `"types"`, `"exports"` pointing to CommonJS and ESM build artefacts)

For modern usage, I have also been attempting to achieve the following, which don't seem to be compatible with the above.

* Uses moduleResolution `node16` or `nodenext`)
* Source-mapped (the package contains source and sourcemaps)

## 🔍 Search Terms

exports main module types npm publishable source resolution package.json node16 nodenext

## ✅ 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

As a Typescript user I am in need of a minimal reference example in which a monorepo resolves local packages to Typescript source in a recommended way. It's possible there is currently no correct way to do it. Discovering this for sure could lead to proposals for core features to fix it (like https://github.com/microsoft/TypeScript/issues/51750 ).

The purpose of packages (rather than folders) is that they can be published, depended upon and bundled in isolation (else I'd just use folders). In modern terms this means the package.json is annotated with the `main`, `module`, `types`, and `exports` fields correctly. The purpose of having a monorepo is to handle the coupling between source files in different packages. So local packages should be resolved to their actual source files (not a build artefact from those files which may be out of date depending which tooling steps have been run). I should be able to follow the recommendations to adopt `node16` or `nodenext` without breaking this core feature (of resolving packages to local source).

## 📃 Motivating Example

My (working) minimal repo of two interdependent npm packages in a monorepo was at https://github.com/cefn/starter/tree/ef3759946046de7c2223ce75959d64d1c86f6610

However, it was broken by a regression arising in the new resolution modes `node16` and `nodenext`.

In particular with the moduleResolution mode of `node` I get the following resolution of the local package, traced to the local `.ts` source file as expected...

```sh
[packages/multiply]$ npx tsc --traceResolution | grep @starter/sum | more
======== Resolving module '@starter/sum' from '/home/cefn/Documents/github/starter/packages/multiply/src/index.ts'. ========
======== Module name '@starter/sum' was successfully resolved to '/home/cefn/Documents/github/starter/packages/sum/src/index.ts'. ========
```

However, toggling the top level `tsconfig.json` to moduleResolution `node16` or `nodenext` the local package resolves as follows, to the `.d.ts` artefact which is fundamentally problematic to workflow in a monorepo (requires build targets to be permanently synchronised, for no obvious reason, when the actual source is right there).

```
[packages/multiply]$ npx tsc --traceResolution | grep @starter/sum | more
======== Resolving module '@starter/sum' from '/home/cefn/Documents/github/starter/packages/multiply/src/index.ts'. ========
======== Module name '@starter/sum' was successfully resolved to '/home/cefn/Documents/github/starter/packages/sum/dist/esm/index.d.ts' with Package ID '@starter/sum/dist/esm/ind
ex.d.ts@1.0.0'. ========
```

If the Typescript team were to take responsibility for defining a reference approach, it would minimise the complexity of future development of Typescript too. Currently the team has to deal with all manner of attempts people have made to satisfy the requirements of the ecosystem without support, leading to multiple incompatible strategies now needing to be supported in Typescript itself. I cannot speak for all other package authors, but their configuration 'choices' which the team attempts to support are probably not a choice at all, but are just the result of endlessly tracking and experimenting with ill-documented ecosystem features until the errors go away and they can do some REAL work. 

With a reference repo in place that populates an npm package having source resolution, source mapping, CommonJS and ESM outputs, people would no doubt just follow that example. From that starting point it's always possible to further bundle and minify with downstream tooling.

## 💻 Use Cases

Authoring type-safe npm packages in Typescript is a core use case. Monorepos are a mainstream mechanism for projects which have multiple interdependent chunks of functionality. My attempts to achieve a reference repo fulfilling the above for one of my monorepo-based projects was a failure, and the internet is full of blogs having a blend of legacy and inappropriate configurations with no obvious canonical example. A reference monorepo would also be an effective way for the community to collaborate on further Typescript features.