-
Notifications
You must be signed in to change notification settings - Fork 2.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
re-export common packages #9124
Conversation
e19ffe3
to
6944273
Compare
Ok so the last step is to find a way to configure webpack to ignore non-browser stuff from |
6086d20
to
8266abb
Compare
Fixed CI and the frontend build by re-exporting |
Related: #7303 |
8266abb
to
049121a
Compare
|
049121a
to
b2f89fe
Compare
Updated |
3234565
to
90463e6
Compare
626ec02
to
1ffb1ac
Compare
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 confirmed the following:
The no-extraneous-dependencies
rule works well (fixes #7079 if you'd like to automatically close):
error '@theia/debug' should be listed in the project's dependencies. Run 'npm i -S @theia/debug' to add it import/no-extraneous-dependencies
The new eslint plugin properly picks up violations of not using the shared
dependencies re-exported by @theia/core
(attempting to use inversify
the old way):
"inversify" is a @theia/core shared dependency, please use "@theia/core/shared/inversify" instead. eslint(@theia/shared-dependencies)
1ffb1ac
to
d020eb9
Compare
@tsmaeder will this change cause issues with how you consume Theia? |
As it's an optional change, I'm not sure it's the best use of our effort. |
@tsmaeder does that mean that this change is fine to go as is, or are you against merging? edit:
This change goes in the way of improving the framework's stability for people consuming it through NPM, so I'd argue that it is not simply "optional". There's always the option to refuse changes in general, so that would make this change optional in that aspect? But this patch fixes a class of issues, it's not like I just want to make things different. Regarding the effort it's a do once and forget about it. Search and replace, follow the eslint errors and done. People writing Theia extensions or Theia applications by pulling packages from NPM will not even be affected. Only forks would need to align. |
One think I still don't understand is if types will be compatible when I import them directly instead of via reexports: for example, if I extend a class and override a method, will the parameter types be compatible or do I need to change my source? In brief: is the change potentially breaking? |
This change is not breaking in that regard, it makes it even more reliable: You will always use the same types than those used by edit: This alignment is currently only enforced in our repo and potentially forks, downstream extensions are encouraged to do the same but it wouldn't be enforced there. |
@marechal-p duck typing works for interfaces, but what if a return type is a class, for example? They may be structurally the same, even refer to the same version, but does it compile? |
@tsmaeder Then it is important to understand how one would end up with two distinct references to what looks like the same class. This usually happens when the same package is installed twice under different versions e.g. doing The same package installation mismatch scenario can happen if you don't rely on implicit dependencies but rather try to explicitly depend on the same package as In both scenarios, you have two packages. If both export a class and they are structurally the same, since they come from different packages they will be two different references and type compatibility will only depend on whether or not those classes declare private fields: if they do declare private fields then TS will complain at build time when you use one in place of the other. It does not matter that the private fields are identical, the class definitions are separate so TS complains. See error and success. Using re-exports prevents this completely since the types involved will always be the same and hoisting will never cause these scenarios to happen. Dependents will only need to update code if Note that type incompatibility issue will never happen if your code requires the classes from the same package version, as it will most likely mean that the package is not duplicated due to mismatched ranges. This is a complex issue due to hoisting. This change gets rid of our dependence on hoisting. |
@tsmaeder I got burned when doing one of the earliest I had to fight with how types were imported/exported, and implicit dependencies didn't help. The gist to fixing the issue was to re-export lsp types from a central Theia file and use that as source of truth everywhere else. |
But if I do this in
and I currently have code that does
I would expect the two |
@tsmaeder the issue you are describing here can already happen without my changes, even if both locations just import My patch allows us to get rid of that issue by allowing everyone to always refer to the same package thanks to re-exporting things. Here is an example showing how re-exporting is preserving types: ts-class-re-export.zip. Note how the recopied class fails, but the re-exported type works. edit: But you are right that it can still break if people keep using implicit dependencies, but only if there's hoisting issues. Just like is currently happening, this change doesn't make this case worse or better. It only improves things when |
@tsmaeder here's another way to put the benefits of this PR: This change prevents the issue you are talking about in this repository, and enforces it. After merging, if dependents keep using implicit dependencies then the issue you are mentioning can still happen, but it will be caused by their own implementations and not by the core packages anymore. Extenders that wish to also prevent the issue can now use this |
I'll resolve conflicts before merging somewhere around Thursday/Friday of this week. |
@marechal-p, this PR won't fix this problem, right? If we merge the PR, I can still create a Theia extension that has a couple of dependencies that depend on (If my example needs more clarification, let me know.) |
@kittaakos as long as the Theia extension requires See example: fs-extra-duplicated.zip Package This means that hoisting will keep happening, but it won't de-stabilize builds since packages will be able to precisely import the same shared packages from the places that explicitly depend on them. |
👍 Thanks for the example.
That would be great. |
d020eb9
to
03ae712
Compare
git diff explains better what changed than git status. Signed-off-by: Paul <paul.marechal@ericsson.com>
e871e25
to
00dd8ab
Compare
Enable errors for import/no-extraneous-dependencies. Relying on hoisting makes building Theia application somewhat unstable. In general cases, everything is fine. But as soon as Yarn decides to hoist things differently then it can become difficult to fix. This commit fixes the issue by re-exporting dependencies from @theia/core, this way instead of relying on implicit dependencies one can directly import what @theia/core uses like: import * as dep from '@theia/core/shared/dep' To make this work I added a 'shared' folder under 'packages/core' with js scripts that re-export everything from their matching library. To ease with the development there is a script named 'packages/core/scripts/generate-shared.js' that will read values from 'packages/core/package.json#theiaReExports' in order to more easily add new packages. Depending on how the package exports things we need to re-export things the same way. This same script generates the README.md to list all re-exported packages. It uses README.in.md as a base. @theia/electron now re-exports all members from electron. This prevents having to use electron as an implicit dependency. @theia/electron now also is a @theia/core optional peer dependency. This directly represent the use case we have for this package where we will only use electron features if this package is installed by application makers. If people don't know about this way to consume @theia/core's shared dependencies then nothing will change and they might keep using implicit dependencies. But the new recommended method is to use those re-exports in order to make builds more stable in the face of hoisting and other dependency tree optimizations. Noteworthy: There was an issue with sharing @theia/application-package since I only re-export the main 'index.js' it tried to include Node-specific code in frontend applications. I fixed this by re-exporting @theia/application-package/lib/environment separately. eslint: add @theia/eslint-plugin package Add a new package @theia/eslint-plugin to provide rules for downstream projects to re-use. The goal was simply to give better error messages if committers used implicit dependencies again, by notifying when a shared package is depended upon without going through @theia/core/shared/... It turned out that implementing that rule revealed places where import/no-extraneous-dependencies simply didn't catch. The reason might be that the plugin wasn't designed to work on TS sources. This commit fixes those newly found occurences of implicit dependencies. Signed-off-by: Paul <paul.marechal@ericsson.com>
00dd8ab
to
0269515
Compare
I'll keep an eye out for any breakage but I don't expect any. I'll also assist with PRs if people need help rebasing. |
What it does
The goal of this refactoring is to make downstream application builds more stable by removing implicit dependencies and exporting common packages from
@theia/core/shared/<package>
.This method should not break dependents. See commit message for explanation.
Fixes #8403
Most changes are just about updating all locations where implicit dependencies are used. I think this is a good change for the framework as it is an opt-in feature, doesn't break dependents, and improves the build stability for application developers making use of it.
How to test
This is a refactoring: Everything should work like before.
Review checklist
Reminder for reviewers