Repository: TypeScript
Issue: Module resolution - four issues (specific and simple to general)
Labels: Suggestion,Needs Proposal

I know there have been plenty of questions around module resolution, I read some of them (SO and here). I still have these issues: The most general one last, the most specific one first.

# 1 Fix documentation

From page https://www.typescriptlang.org/docs/handbook/module-resolution.html

> **_Classic_**
>
> This used to be TypeScript’s default resolution strategy. Nowadays, this strategy is mainly present for backward compatibility.

But "Classic" still **is** the default, no?

This sounds like it should not be the default.

# 2 Don't ascend above project directory

*(At least not by default, or provide an option to prevent it.)*

Why is TS allowed, by default, to ascend up out of the project directory?

I have a folder `Projects/` with various projects, some included by others, but via packages and not directly from those directories but they have the same name locally. What happens is that when I run `tsc` in one of those projects it ends up including definitions from another project instead of from `node_modules/`, which makes no sense to me. I mean I get it if that was an optional behavior, but by default going into folders that don't belong to the project?

How can I prevent tsc from looking for anything outside the current project's directory?

# 3 Include node_modules in module resolution

What I mean is that it should always be used, or at least make it configurable but not as it is now where it depends on module resolution "node". The use of `node_modules` is not node.js specific at this time.

I'm also confused because TS _does_ find things `<PROJECT_ROOT>/node_modules`, it only finds `@types` imports. I bundle .d.ts files in my own library, which since it's internal and private has no way to get its .d.ts published via DefintelyTyped. When I import from "myLib/lib/someFile", for which a .js and a .d.ts and a .d.ts.map file exists, it only finds it in "node" module resolution mode.

It may as well include that folder in its search even in "Classic" mode, that IMHO makes more sense than the search outside the project directory.

# 4 Module resolution depends on build target

*First I checked if this still applies, and it does.*

In reference to: 
- https://github.com/Microsoft/TypeScript/issues/8189#issuecomment-212082894
- https://github.com/Microsoft/TypeScript/issues/18865#issuecomment-333412703 ("_An issue here is that the module resolution strategy we use is determined by your compiler options..._ ")

1. I don't see that mentioned in the docs (page linked above)
2. I don't understand the purpose

At least for me, the build and development are orthogonal. I build for browser, react-native, node.js and experimentally even for two embedded platforms (Moddable, low.js) and I don't see why my code structure used for development should depend on the build target! After transpiling (here using Babel, but that would be the same if we used TS for that) module resolution on the various platforms is vastly different. From webpack-packages to a "flat" module namespace (Moddable, many platforms use their own very different module handling. Whether Webpack or Moddable find a module has nothing whatsoever to do with TypeScript used while writing the code and how it type-checks and finds modules. 

IMHO development time module resolution should not depend on the build target.

My own build target is "esnext" (I also have `"noEmit": true` because I only use TS at development time, if there is any transpilation it is done by Babel, usually that's just removing the type annotations and maybe a &mdash; flexible &mdash; module system target), but even node supports ES2015 modules these days, so I don't think there is the direct connection between "commonjs" and "node_modules" any more, if ever there was.

Especially since the support of the Babel option the distinction that already and always existed between development with type checks (when the type checker needs to be able to find a) included modules, but b) also just type info like .d.ts files which have no runtime equivalent and there location surely should not depend on what the build target is), transpiling and packaging, and running the code, probably deserves another look to remove unnecessary cross-concern bindings like module resolution (used for type checking) depending on transpilation (which does not even need to know about where to find modules since it is in between type checking and runtime).

On a more general note, I think TS has too tight a coupling because those concerns, which despite all efforts I can see even here in the "Issues" in reaction to feature requests where anything that would make TS different from JS is routinely rejected, leads to a lot of confusion esp. among new(er) developers (to the ecosystem, not necessarily by age) that TS is a different language. Especially since Babel workflows are officially supported too now the more clear separation of development time (type checking) and transpilation might be beneficial. It's not even a code change, maybe a few tsconfig layout changes at most.

&nbsp;

&nbsp;

As an aside, there is even more of a disconnect between development and runtime. I, for example, use TSypeScript only for type checking. It's a setup I could also run with Flow, same for anyone that uses Babel for transpilation (many people don't seem to know, all the Babel-TS plugin does is remove the types, because then TypeScript code is just "ESnext" *(if one doesn't use namespaces, enums, import=/export=)*.

That means I use tsc only from the IDE as language service and for type checks. It (tsc) only needs to understand the layout of the code *during development*. It does not need to concern itself with either transpilation or where modules might be found during runtime &mdash; especially the latter, in the days of Webpack & Co., is something `tsc` does not need to know. So finding files and types (.d.ts) during development should be separate from finding code modules during runtime, and TS should focus on the former (the latter is the domain of various build systems anyway, which have their very own ideas).

That means that, for example, I can use TS-only features like namespaces for my *types* (inside a .d.ts file) even if I would not or can't use it in my code. In my particular case it helps with type checks (declaration merging between a private lib an apps using it, just my particular private case and example).

So in my &mdash; I would claim not unusual &mdash; setup, **1)** type checking (by tsc), **2)** transpiling (by Babel), **3)** packaging (Webpack, Moddable compiler, React Native, and others) and then **4)** the actual runtime all are disconnected. This is in line with the architecture of TS I think, the merging of concerns is, I think, a product of user-facing components. I don't think tying those components together, for example by binding module resolution (used during development for type checks) on a build target (used only when actually building, and even that does not necessarily need to be done with tsc since there is the Babel option) is not good.