-
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
Expand auto-import to all package.json dependencies #38923
Expand auto-import to all package.json dependencies #38923
Conversation
e.g., don’t create them during cross-project find-all-refs
@typescript-bot pack this |
Heya @andrewbranch, I've started to run the tarball bundle task on this PR at 86151e1. You can monitor the build here. |
Hey @andrewbranch, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build. |
After further thought, I do think devDependencies should be excluded by default (we could add a configuration option to include them if enough people really want them). Including them by default means that virtually every project written in TypeScript would get auto-imports for the compiler, which nobody wants. If you do want to import the compiler API, |
@@ -1815,7 +1815,7 @@ namespace ts.server { | |||
name, | |||
rootFileName, | |||
compilerOptions, | |||
hostProject)); | |||
moduleResolutionHost)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You dont need to pass moduleResolutionHost here but instead you want this to also handle trace (if logging enabled going to log) and currentDIrectory
ModuleResolutionHost = hostProject.projectService ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think I understand the suggestion—ProjectService
isn’t assignable to ModuleResolutionHost
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merging for the beta, feel free to follow up with me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You want it to be {
boundMethods from hostProject.projectService.host
getCurrentDirectory : () => hostProject.getCurrentDirectory(),
trace: mayBeBind(hostProject, hostProject.trace)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a realistic example of why hostProject
itself wouldn’t work? If it wants to mimic the existence of some files, why shouldn’t it be allowed to do that during type directive resolution?
Co-authored-by: Sheetal Nandi <shkamat@microsoft.com>
* upstream/master: (58 commits) Variadic tuple types (microsoft#39094) chore: resolve suggestions Expand auto-import to all package.json dependencies (microsoft#38923) inline local functions Update bigint declaration file (microsoft#38526) Update user baselines (microsoft#39077) LEGO: check in for master to temporary branch. Add missing index.ts files to user projects (microsoft#39163) Add reason for a disabled code action (microsoft#37871) Minor fix for assertion predicates (microsoft#38710) Update LKG (microsoft#39173) Reparse top level 'await' in modules (microsoft#39084) change chore: more change chore: resolve review chore: save space fix: lint error test: add test for it chore: make isJsxAttr required chore: revert change in checker ... # Conflicts: # src/compiler/binder.ts # src/compiler/checker.ts # src/compiler/parser.ts # src/compiler/types.ts
* Start experiment * Add logging * Go back to a single program * Fix forEachExternalModuleToImportFrom * Move auxiliary program to language service * Add logging * Don’t use resolution cache * Fix(?) containingProjects for ScriptInfo in auxiliary program * Fix ScriptInfo project inclusion * Add test for default project of auto-importable ScriptInfo * Add fourslash server test * Don’t create auto import provider inside node_modules * Add monorepo-like test * WIP * Naively ensure autoImportProvider is up to date after package.json change * Start limiting when auto update provider gets updated * Respond to changes in node_modules * Don’t create auto-import provider until a file is open that would use it e.g., don’t create them during cross-project find-all-refs * Clean up naming, @internal marking, and fix empty project creation bug * Drop devDependencies, include peerDependencies * Add additional compiler options * Fix interaction with importSuggestionsCache * Move option to UserPreferences, allow inclusion of devDependencies * Don’t filter out peerDependencies * Watch unparseable package.jsons * But don’t filter packages out due to an invalid package.json * Update test * Don’t use autoImportProvider in codefixes where it can never be used (or any refactors) * Add CompletionEntry property for telemetry * Add assertion for isPackageJsonImport to fourslash * Fix missing pushSymbol argument * Add isPackageJsonImport to tests and API baselines * Fix unit test * Host auto import provider in new Project kind * Fix InferredProject attaching on AutoImportProvider-included files, load eagerly * Update Public APIs * Simplify PackageJsonCache host * Remove unneeded markAsDirty * Defer project finished event until after AutoImportProvider is created * Make AutoImportProviderProject always report isOrphan = true * Close and remove AutoImportProviderProject when host project closes * Don’t set pendingEnsureProjectForOpenFiles * Use hasAddedOrRemovedFiles instead of hasNewProgram * Use host-wide watchOptions for package.json watching * Add to `printProjects` * Clean up * Get autoImportProvider directly from LanguageServiceHost * Clean up * Clean up * Close auto import provider on disableLanguageService * Move AutoImportProvider preload to project updateGraph * Clear auto import suggestion cache when provider program changes * Fix tests * Revert yet-unneeded change * Use projectService host for module resolution host * Don’t re-resolve type directives if nothing has changed * Update src/server/project.ts Co-authored-by: Sheetal Nandi <shkamat@microsoft.com> * Use ts.emptyArray Co-authored-by: Sheetal Nandi <shkamat@microsoft.com>
sometimes we do want to auto import in devDependencies, for example, some test only package. is there some better way to avoid the "typescript" problem? maybe some kind of filter? |
Once you manually import your devDependency once, it will be available for auto-import throughout the rest of your project. So... it's kind of already a reverse filter that you configure implicitly as you code. |
Is there a way to specify which I have a monorepo, structured like this:
My However, VSCode only checks the root Is there a way to set the setting to specify a "typescript.preferences.includePackageJsonAutoImports": "on",
"typescript.preferences.packageJsonPath": "./apps/nextjs", |
@nandorojo are you primarily a TypeScript or JavaScript user? (Or more to the point, is this particular project written in TypeScript?) The expected structure is that each package of a monorepo should have a tsconfig.json file. As a JS user, you can still have a tsconfig.json or jsconfig.json file there, and even just putting |
@andrewbranch really appreciate the fast reply. I'm a TS user exclusively. I tried adding a Interestingly, autoimport does get solved if I duplicate my I don't mind having a I am able to autoimport my monorepo packages ( Let me know if you need me to clarify anything there, it might be a bit hard to follow. |
Are you able to either share the project you’re working in or reproduce this in a little example? The only possible explanation I can think of besides this being a bug is that if we discover too many unused dependencies, we disable the package.json auto imports: This would happen if you have a bunch of dependencies listed in a package.json but the project in question has not already imported them. You can try toggling this setting to |
I did already set that to |
@andrewbranch I have a repro here: https://github.com/nandorojo/vscode-monorepo-autoimport-repro It's very minimal (these are all the dependencies): Do you need me to open an issue, or is this sufficient? I added a few comments in the only two |
This is working as intended— |
I see, thanks for clarifying. It works fine if I manually import it, though, so it's as if the autoimport doesn't match the actual behavior. Both the Next.js app and the type checker work without explicitly adding these dependencies to each package. My hesitation with adding it to Is there a way to possibly extend the dependencies from
I agree that practically speaking, since they won't duplicate, there's no real downside to this. But in a large monorepo with over a dozen If there are no intentions to allow transitive dependencies (even behind a flag) for autoimports, then I'll just add each dependency to each subpackage. My guess is, you've thought about the downsides more than I have. But I thought I would leave my thoughts on the DX. I don't necessarily want all transitive dependencies to autoimport – only the ones I've explicitly put in a subpackage's |
I did try explicitly adding these to my monorepo packages, as recommended, and it does solve the autoimports. However, to prevent duplicate dependencies, I have to fix the versions in I was hoping this would result in no changes to my lockfile, but it seems it does: {
"name": "@repo/components",
"version": "0.0.0",
"main": "index.ts",
"devDependencies": {
"moti": "*",
}
} For example, I can put versions there explicitly instead to avoid duplication, but it makes updating a dependency a bit of a hassle. I now have to increment the version in 15+ folders each time I want to update. I know this is intended, but I figured it would be useful to share the downside. Thanks again! |
I think package/workspace managers have tools for doing these kind of bulk install operations. I also think that if you’re not publishing these packages individually, but rather the monorepo structure is just for code organization, there’s no great downside to putting common dependencies in the root package.json. And if you are publishing each package individually, then I think it is an antipattern to import transitive dependencies. There are people who vehemently disagree with me on that point in #38768, but I’m pretty firm on it. |
Interesting, good to know. I'm only concerned for the times that I'm using this to organize shared packages across our internal apps. When it comes to open source monorepos I publish, I do in fact copy over the dependencies in each package. But for an app with code organization, I don't know why importing transitive dependencies would really be an issue. I suppose I'll just put the deps in the root package.json, and hopefully that works fine across the apps themselves.
Maybe lerna does this, I've only used it for publishable packages so I'm not sure. If there aren't downsides to using the root package on internal projects, I might just do that. But yarn workspaces warns against it whenever you install something in the root package, so I figured it wasn't a good pattern. |
In case anyone else lands, here, I found a nice monorepo tool that extends Lerna's capabilities to keep versions tied together between packages when you install/upgrade: https://www.npmjs.com/package/lerna-update-wizard Heard through lerna/lerna#2142 (comment) I wrote up a guide here: https://github.com/nandorojo/ts-monorepo-autoimport-guide |
This PR creates an auxiliary program in the language service layer that contains node_modules packages that are specified as a direct dependency (including
devDependenciespeerDependencies) in a package.json file visible to the project’s root directory and not already present in the main program. This program is then supplied to the auto-imports machinery as a provider of additional modules that can be used for auto-import. Effectively, this means that packages that ship their own types should be available for auto-import immediately afternpm install
ing them, lifting a long-despised limitation of “you have to write out the import manually once in your project before auto-imports will work, unless it’s in@types
, because@types
is special.”Performance
I compared a few operations against master on the output of @angular/cli (with routing). (I chose this as a test because it’s a realistic scenario where a user’s package.json will contain more dependencies with self-shipped types than are actually used in the program. For example,
@angular/forms
is a listed dependency but isn’t used in the boilerplate.) Measurements are an average of three trials run in VS Code 1.46.0-insider (b1ef2bf) with extensions disabled.* This triggers completions, including import suggestions for
PatternValidator
from@angular/forms
in the PR trials, a suggestion that is not included in the master trials.** This triggers completions again without disrupting the auto-import cache populated in the previous action.
*** This changes the structure of the primary program, which triggers an update of the auto-import provider program. The time it takes to do so is included in the subsequent
getApplicableRefactors
measurement.Observations:
updateGraph
time)Limitations
Only package.jsons found in the project’s
currentDirectory
and upward are considered. So in an unusual project structure likethe
project/package.json
file won’t be found.This doesn’t currently resolve to JavaScript files—that is, to get auto imports, the package has to have
.d.ts
files. Processing plain JS for auto-imports could be explored if demand were high, but when I tried it, most of the added packages were CLI tools and plugins, not things people want to import from, so it was a performance hit for little (or even negative) utility.Memory considerations
The auxiliary program is configured to be as light as possible, but in a project references editing scenario, it’s possible to have many projects open at once. Each project has its own language service, so each open project will typically have one of these auxiliary programs. Some possibilities for mitigating high memory usage in these scenarios: (updated: decided to exclude devDependencies for UX reasons)
but an analysis of around 500 popular open source TypeScript repos shows that this rarely happens, largely due to devDependencies that ship their own types but are never used in the program (prominent example: typescript). Excluding devDependencies significantly increases the chances that creation an auxiliary program can be skipped, at the cost of those devDependencies being unavailable for auto-import (which can be useful when writing build scripts and tests).We could either hard-code into TypeScript or indicate in third-party projects that a package should opt out of being eagerly auto-importable. In one real-world project I tested, the contents of the auxiliary programs were limited to typescript, typescript-eslint, ts-jest, jest, and prettier, none of which are commonly imported.However, at this point I’m not convinced that any of these mitigations are necessary. The size of these programs is usually in the ballpark of a browser tab (around 50 MB from one early test). But we have avenues to explore if needed.
Fixes #37812
Fixes #36042
¹ “file that belongs to the project” here means “file whose default project is the project in question,” in contrast to “file that is contained by the project”