-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Module conversion tracking issue / notes #49332
Comments
I'd fixed this by hand a few times in my version - I usually swap deep imports for barrel file imports and reorder the barrel imports to match our tsconfig order, since that represents the canonical execution order we desire. Sometimes some barrel imports need to be replaced with deep imports to break circularities, but it wasn't too many, iirc. This:
? I don't see a
Nothing too magic about it. The
We have a
It still makes sense for a tree of files, imo - intuitively I feel like it should be the entrypoint file, or whatever was initially executed by |
I meant to say
My overall goal was to end up with a codebase that felt like one that had been written with modules; this means eliminating these barrel imports from files. Otherwise, auto-imports will not function properly because auto-imports will prefer importing directly from the declaring modules. It also means not requiring that a given file import each file in a specific order either, given everyone (including our own import organizer) will not preserve this order.
This is going to be rough to maintain in the long term, because I don't think the average person knows that this ordering has any impact. I sure didn't; I've never used a project that listed its files explicitly, only globs. I would hope that we can stop having so few files just to not have to add more, and instead break the code apart a bit. Especially if perf improves because we're not running every file in a closure where each access to the old namespace was actually fully-qualified under the hood.
My understanding had been that if one has a
Yep, this is more of a plumbing problem. I just wanted to note it so I could delete my random TODOs. |
I mean, it already is, it's one of the hidden costs if using namespaces. It just only comes up when someone finds they want to adjust the order (because, eg, they want to start using |
So this is a style argument, really. Imo, you're supposed to import from the barrel files for everything other than your immediately adjacent files (and we've talked about our autoimport behavior re: this before, and the only way we'll get better at it is honestly using it ourselves and seeing the upsides and downsides of each approach). Still, our first concern should probably be retaining a functional execution order (T.T) and not a specific style - that can come later. |
My next step is to revert my changes that made imports really aggressive in favor of importing from the barrels, just to confirm that things run again (and to double check the state of auto-imports), so that should hopefully get me back to a working codebase. I was really hopeful that we could write things like everyone else, but it's a shame about the execution ordering. |
2022-08-29I took a few months off of this to work on other issues, but have recently picked this back up again with the intent to make this breaking packaging change for 5.0 (it seems like a good idea to not do this on a minor release). Last week, I hit a major milestone and got the test suite to run; all tests pass except the API tests (which don't pass because I haven't started on fixing the d.ts files yet).
|
I posted the above early in the day; I did get the parallel test suite working. Mocha's parallel runner unfortunately can't be used with our repo in its current state as it expects individual test files, which it then runs in parallel (never any actual tests within a file in parallel). We do our own thing and end up with effectively one file. Potentially, we can switch to something "more normal", somehow, but it's no longer a blocker. If we were using something like jest or ava, they could potentially run tests within a file in parallel, but that's a much larger change. |
2022-08-31I have the parallel test runner working; it came down to the fact that the entrypoint I was using for the test runner was the namespace file, which broke because While working on this, I prototyped a Jest runner that could run our compiler tests instead of the relatively large bit of code we wrote to make Mocha work; I think that's a good idea for the future. I only tried it because I was frustrated trying to get our own parallel runner to work (so was looking for any alternative), but I'll defer that to another day now that I have all tests running besides the API tests. Next up is api-extractor, and getting some proper entrypoints for things like |
2022-09-02I have the entrypoints set up and working for the libraries/executables, and a basic api-extractor config for one of them. Unfortunately, things seem to fail on that front. I've reported microsoft/rushstack#3612. But, I've noticed that our d.ts output is a little suboptimal; the case that we are currently breaking on is an import that shouldn't even be emitted as it can be safely imported. I'm sure we've gotten someone reporting that issue before. We'll have to eventually call api-extractor programatically; one transformation we will need to do again is to convert our |
This turned out to be part of the bug; hand-fixing these in the d.ts file allowed api-extractor to run to completion, although the output did not seem to exclude |
2022-09-12The aforementioned |
2022-09-14I've gotten things far along enough to run perf benchmarks, and I'm overwealmingly happy to share the results.
This difference is very likely due to the fact that we are going through our export helpers, which are like our old namespace accesses, but extra slow. Compare that to ESM, which is faster as it doesn't use those import helpers, but since we import things like helper functions ( Now, for esbuild:
Previously, we had intended to try and ship as individual files, a breaking change for 5.0. Since we're not likely to switch away from CJS, the performance penalty there is pretty unfortunate. But, if we just bundle our output, we would be producing a package that's still compatible with the previous one, and turns out to be 7-25% faster, and has an extremely fast build that we can make use of during development. At this point, most things are working; I have some remaining tasks:
|
2022-09-19On #50811, I tested what it'd be like to downlevel The additional performance boost was as expected, roughly matching the performance difference I noted when trying to switch our target up past ES5 on #50022, or about 3%. Most of that difference comes from improving parsing performance by 5-9%, which indicates that it's doing a lot in the TDZ. The other parts of the compiler don't improve as greatly, but do generally get a few percent faster. Running babel on the output of esbuild works, but is definitely a lot slower than esbuild itself. I don't think we need to downlevel everything to ES5 (evanw/esbuild#297), so just Benchmark results, bundled before and after let/const downleveling:
System
Hosts
Scenarios
Summary
Also of note, I'm going to have to change our bundled output format to an IIFE; typescript uses a single file for both node and the browser, conditionally using |
2022-09-29At this point, everything is mostly done. I ended up writing my own small dts bundler tool that can cover our codebase specifically; we have some unique requirements, namely that we define our own At this point, the only remaining tasks are:
|
2022-10-11Down to basically odds and ends.
We should be good to dogfood this. Also, since this effectively requires a whole new build, I've taken this opportunity to make a number of major build changes to speed up our build performance, remove dependencies, replace dependencies, and so on; changes that are hard to make during our normal dev cycle without causing churn. The module conversion itself is a huge speedbump to anyone bisecting around as it is. |
2022-10-19Things are looking good, still at odds and ends. The dogfooding has netted some useful feedback, some issues I've fixed like tsserver needing to be a UMD-module for web, and so on.
After some design meeting bikeshedding:
Remaining work:
|
2022-10-25Probably the last update before we make the switch in 5.0. First off, more performance wins;
Expand me for tsserver.js, typescript.js, tsserverlibrary.js
The script for this is located here: https://gist.github.com/jakebailey/362aedfb19bff6086585509e223b7a64 I have filed bugs for the problems cross-team, linked in the previous post. These are two other bugs in TypeScript that I've experienced during the development of the conversion:
The former is an annoyance; the latter is a half-blocker towards converting the Debug namespace into a module (which will allow bundlers to directly call our debug helpers). I also have a big writeup which will go into the final PR's description + be converted into a blog post. See you all then. |
please consider jest's snapshot test! manually update some test is pain |
I'm not sure what you're asking; the output format is still something like UMD so the same files serve Node and the browser, so those can't really go away. I don't have a good long term plan for eliminating them given our synchronous requirements.
This turned out to be a failure; custom runners are not in a good place, and we'd have to completely overhaul how we do tests. Maybe one day, but right now things are still fine. Post modules, my build is a little faster at accepting baselines. |
they're immutable even if they're let/var anyway |
The PR has been posted! #51387 |
The modules PR went in earlier today. Here's a set of follow-up issues and PRs that I am already working on which build on the modules change:
|
This wiki page is still references `protocol.d.ts": https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29 Should it just point to here then? https://github.com/microsoft/TypeScript/blob/main/src/server/protocol.ts |
It should probably just say "check the |
Hello @jakebailey The TDZ performance finding quoted here (the 5% — 9% parser win) is being quoted as one of the reasons to perform TDZ optimisation in ESBuild. Meaning ESBuild is replacing We all want faster code and so this optimization may well be justified but I'd like to dig in a bit more before concluding. Because excess downlevelling hampers debugging, as you have previously recognized. And when we abandon using modern features in production via tooling that downlevels, it reduces the incentive for engines to optimize those features - a negative feedback loop. Specifically I'd like to understand if there is a missing V8 optimisation here. So two questions:
|
Having read that linked thread, I honestly don't know how esbuild could stop using
This is true, but in this particular case, switching to var does not really hurt debugging so long as the vars that are switched don't generate any other additional code such as the extra closures needed for
The benchmark suite that tests TypeScript largely runs tsc, and tsc only runs in node, so I have not tested anything other than v8, no. Perhaps recent Bun changes mean I can do a drop-in test and see what happens. (I'm pretty sure Deno is v8 so I wouldn't test that.)
I don't; what I really need is some sort of tool that acts as a "TDZ blame" and can tell me where all of my TDZ time is being spent, but no such tool exists. The parser is very large and besides simply running our code through a transform which changes all |
That being said, I could likely do a differential profile between the two and look there. |
@robpalme Just to show the current state, #52656 (comment) is what it'd be like if we downleveled all let/const to var; the performance difference is really stunning. I still need to go try this on another engine, but the delta shows that there really is a lot of perf sitting on the table. |
I'm using this thread to dump my brain and to note problems as I solve them (or to discuss).
Issues of note:
2022-05-31
Changes since the previous effort
gerrit
, but...typeformer bugs
inlineImports
step convertsglobalThis.assert
intoassert
, because it thinks it's a global function that should be settable. Technically true, but if we wanted that, we probably wouldn't have writtenglobalThis
explicitly. This is just a bug when I rewrote the step.#region
comments are dropped. Likely a bug that can be worked around like other ts-morph oddities to do with comment preservation (which it is almost always good at, but sometimes not).// DestructuringAssignmentType
insrc/compiler/types.ts
.dbg.ts
) that is dynamically loaded in, orloggedIO
.Unsolved problems
debug.ts
importssemver.ts
, but theSemver
class creates a static propertyzero
, which calls theSemver
constructor, which then containsDebug.assert
calls.debug.ts
containsimport { Semver } from './semver
, whereas before, the code didn't do this but instead referred to the namespace, so never actually caused that file to be evaluated.System
interface (akats.sys
) is set (ts.sys = ...
) in order to support the browser, but in the module version, modules doimport { sys } from '...'
), which makes it very difficult to do like this. What can do do here?System
interface hasgetExecutingFilePath
(among other functions), which only makes sense for TS packaged as a self contained file.TODOs
outDir
, but I don't think we intend to just allow everyone to import our internals directly.outFile
made a file that did the right thing before, and after we just need to write something liketsc.js
which exports*
from_namespaces/ts.ts
or something.exportAsModule
, which hacks together amodule.exports = ...
line rather than emitting it viaexport
.registerRefactor
uses side effects of file loading to register refactorings. This technically works OK after modification if the fake namespace modules still import these files, but it'd be better to refactor this to work like the transforms (which are just imported in other files instead).fakeHosts.ts
implements certain interfaces, but does so with classes named the same as the interface. Maybe just doFakeSomeInterface
?Other stuff
.git-blame-ignore
file. See:The text was updated successfully, but these errors were encountered: