-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reprioritize cross-project module specifier suggestions for auto-import #40253
Conversation
This sounds great! Thanks for the hard work on this issue!! |
@andrewbranch Sorry for the noice I made yesterday in different issues/PRs, I wasn't sure which one is more relevant and made a number of communication mistakes. Impressed how responsive you folks are: tracking every single comment in all issues of such a huge project is unbelievably cool. Regarding the change in this particular PR - it sounds like the answer to the original question. I just have several clarification questions if you don't mind. I think that the related topic is not yet covered much in the TS documentation pages.
|
No worries @dko-slapdash, I just wanted to have a single place to continue the discussion.
I’m also really curious about your runtime setup—this PR was just one part of a broader effort I’ve been working on to make TypeScript work better for monorepos with all different kinds of infrastructure. You mentioned
How do these resolve at runtime? Are you using Webpack or another bundler? A custom module loader in Node? Is the project you’re working on an open source repo that I could take a look at? It would be great to add this sort of thing to my list of real-world monorepo setups so I can consider it in future work. |
Thanks for your reply! A) I tried baseUrl=src and all sorts of ../.. in paths, but it didn't work, IntelliSense still proposes src and not dist. I'll try again and post a screenshot. |
Yeah, basically, with There is miscompatibility in this setting with typescript-transform-paths though (because it also picks this |
Related to the topic (the info for future readers): here is #40101 which allows to use the above technique even without having baseUrl defined (if so, Motivation: we switched to within-module-all-relative paths (../../..), and we wanted to have no baseUrl defined anywhere at all. Because it's safer: without baseUrl, VSCode won't even propose non-relative imports in auto-import IntelliSense feature, it would always propose ../.. paths, and there would be no risk of leaking "wrong" paths to generated dist/*.js files. (In the past we used typescript-transform-paths, but now we moved off it to reduce complexity.) Here is the final workable layout. It also illustrates the case when folders in packages/* have different names than package names in package.json files ("my-" prefix) - not something to be proud of though, but in case it's needed, it works too. |
Supersedes #39267.
Fixes #38856.
Best way to understand this PR:
autoImportCrossProject_paths_sharedOutDir
for now. Particularly, note there is one group of tests that simulate lerna-style symlinks, and one group that relies only onpaths
, which simulates a bundler/post-build step that transforms module specifiers. Note that theverify
assertions in each group show that I can tweakpaths
to make auto-import give me whichever module specifier I want, choosing between"dep/src/sub/folder"
,"dep/dist/sub/folder"
, and"dep/sub/folder"
.autoImportCrossProject_paths_sharedOutDir
if desired—it was a representation of a real-world project structure from a user with a slightly different setup, but it’s functionally redundant.getModuleSpecifiers
.Background
outDir
). This always holds for files in a single project. However, it may not hold for paths from an input file in one project to an input file in another composite project. Whether it holds is dependent upon the project settings of each project. As a rule, TypeScript does not care whether it holds. This is because the actual runtime organization of composite projects may not be similar to their organization on disk during development. For example, a user might package each composite project individually and publish each to a package registry. Post-compilation build steps and deployment strategies are outside TypeScript’s scope of concern.../node_modules/symlinked-project/src/index.ts
has a corresponding output like../node_modules/symlinked-project/dist/index.d.ts
, such that if../node_modules/symlinked-project/package.json
includes"types": "dist/index.d.ts"
, we could generate the bare package specifier"symlinked-project"
.Problem
Generating a bare package specifier like
"symlinked-project"
instead of"symlinked-project/src"
, when possible, is almost always the specifier the user prefers. The problem exhibited in #38856 is the effect of #37482 when the output file is not the file specified by the"types"
field of the package’spackage.json
(or when the path to the output file did not go throughnode_modules
at all). In these cases, we generate a path that explicitly maps from input (TS) to output (.d.ts, typically colocated with JS). Moreover, this path from input to output is the only module specifier we would suggest. My first PR addressing this problem, #39267, claimed that such a path was always undesirable. However, that’s not the case. You can trivially imagine publishing/deployment strategies where, at runtime, the correct specifier is"some-dependency/dist/foo"
. Such a path is quite possibly more likely to be correct than the alternative"some-dependency/src/foo"
, but it depends on variables outside TypeScript’s scope of concern.Further, the desired specifier may be
"some-dependency/foo"
, ifsome-dependency
is published from itsdist
directory. Whensome-dependency
is a composite project symlinked through node_modules (as with lerna or yarn workspaces), TypeScript could be configured to resolvesome-dependency/foo
correctly by setting uppaths
, but auto-import would never suggestsome-dependency/foo
, because when a possible path throughnode_modules
is found,paths
are not considered.Solution
"symlinked-dependency"
) by merit of the output file matching its package.json’s"types"
field.paths
can always be used to customize the preferred specifier to a file in another project, even when a path through node_modules (e.g. "symlinked-dependency/src/foo") is available.Point (1) preserves the original intent of #37482, while eliminating paths that explicitly point to a composite project’s
outDir
by default (fixing #38856). Point (2) covers, I believe, all ambiguity between"symlinked-dependency/src/foo"
,"symlinked-dependency/dist/foo"
, and"symlinked-dependency/foo"
, which ought to account for any objections that fixing #38856 may actually be wrong for some users.