-
Notifications
You must be signed in to change notification settings - Fork 941
Improve CommonJS support #2893
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
Closed
Closed
Improve CommonJS support #2893
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 2 additions & 11 deletions
13
testdata/baselines/reference/compiler/nestedJSDocImportType.errors.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,25 +1,16 @@ | ||
| b.js(1,1): error TS1203: Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead. | ||
| b.js(3,24): error TS2306: File 'a.js' is not a module. | ||
|
|
||
|
|
||
| ==== a.js (0 errors) ==== | ||
| /** @typedef {string} A */ | ||
|
|
||
| ==== b.js (2 errors) ==== | ||
| ==== b.js (1 errors) ==== | ||
| module.exports = { | ||
| ~~~~~~~~~~~~~~~~~~ | ||
| create() { | ||
| ~~~~~~~~~~~~ | ||
| /** @param {import("./a").A} x */ | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| ~~~~~ | ||
| !!! error TS2306: File 'a.js' is not a module. | ||
| function f(x) {} | ||
| ~~~~~~~~~~~~~~~~~~~~ | ||
| return f("hi"); | ||
| ~~~~~~~~~~~~~~~~~~~ | ||
| } | ||
| ~~~ | ||
| } | ||
| ~ | ||
| !!! error TS1203: Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead. | ||
| } |
14 changes: 0 additions & 14 deletions
14
testdata/baselines/reference/conformance/typedefModuleExportsIndirect1.errors.txt
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 0 additions & 14 deletions
14
testdata/baselines/reference/conformance/typedefModuleExportsIndirect2.errors.txt
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 0 additions & 14 deletions
14
testdata/baselines/reference/conformance/typedefModuleExportsIndirect3.errors.txt
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 0 additions & 34 deletions
34
testdata/baselines/reference/submodule/compiler/amdLikeInputDeclarationEmit.errors.txt
This file was deleted.
Oops, something went wrong.
38 changes: 0 additions & 38 deletions
38
testdata/baselines/reference/submodule/compiler/amdLikeInputDeclarationEmit.errors.txt.diff
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
It's funny, because this was, hands down, the biggest thing we wanted to avoid by using the reparsing approach for JSDoc. At least in my opinion.
Supporting this in the checker, without a doubt, requires some thought on how to support it in the declaration transformer, which seems absent here thus far. Naively, we'll produce something like
which, uhhh, not valid TS. You can see that in a bunch of the
.jsdiffs. In strada, what we'd do, symbolically, was see if theexport=target was namespace-y, and, if so, make a namespace to merge with that namespace that contained all these merged-in typedefs. The declaration transformer needs logic like that now that this is in place, so it can produceThere 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.
Do note, the later can still fail if whatever the
export=points at doesn't support normal namespace merges! But that's more rare, at least.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 wish we would just make that first example legal TS already...
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.
Here's the scenario that concerns me. Say you have a CommonJS module like this:
and a client that imports it:
This works fine and you get to reference
mas a namespace alias in the@typeannotation. But if you instead use amodule.exportsassignment in the module:then the client breaks because you can't reference
m.Foo. You instead have to write@type {import("./mod").Foo}which does work (because, inconsistently, we haven't made that an error). And if we don't make it an error, then we still have to support the namespace pattern you mention in declaration emit.I don't think there are cases where it fails. The
export=symbol generated by amodule.exportsassignment will never have a namespace meaning, so it's always happy to merge with an uninstantiated namespace containing the@typedeftypes.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.
So, I think there are two options. Either have
module.exportsassignments merge with an uninstantiated namespace that contains the@typedeftypes and support that in declaration emit, or completely eliminate exporting of@typedeftypes from modules withmodule.exportsassignments. Right now we're sorta sitting in between two chairs because type annotations can useimportto get at the types.Uh oh!
There was an error while loading. Please reload this page.
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.
This is another interesting input:
if you merge
Foodown intoa's target,Fooappears inmod, which seems wrong (but iirc is what strada does), however there's afaik no uniformly valid declaration emit. Another one isie, late-bound commonjs module exports (which maybe we don't support any more, but we used to) - those are a mire with this merge in place, and strada's behavior for both is... questionable. Especially in the context of isolated or syntactic declaration emit.
Now, a lot of this can be resolved if we do the work to allow merges like those in
.d.tsfiles, too; that way we can just emitor
and not error on read-back; but the issue I had when I tried to implement that is that it exposes all the badness of this merge to all TS users. If we can support it without implementation jankiness, it would definitely be preferable, though.
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 have much to add to what Wesley said, except:
Overall I'm with Wesley and Jake--I think we should go for the least-janky implementation that covers TS and JS completely.
I'm not current on the state of reparsing anymore, but wouldn't a JS-only implementation shrink reparsing even further. Maybe all statement-level reparsing would get dropped?
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.
A JS-only implementation struggles to be represented as a declaration file for incremental or publishing purposes, which is strada's issue. It's OK insofar as the analysis works, but definitely not ideal for the whole stack. We can take that approach again, but we need to introduce a lot of errors banning many forms of it when declaration emit is on because we can't roundtrip it, which... maybe makes it much less appealing.
It'd definitely be best to get something working across languages (ideally requiring only syntactic analysis to produce declarations, for isolatedDeclaration's sake), for sure. The issue I had is that "an alias with stuff attached" does not behave well in our current checking pipelines - I was hacking stuff up left and right and it just felt suuuuuper janky - even moreso than this second-order merge - making things that look like aliases to constant values have type exports - but only if you're still at the alias!
Honestly, I think we probably do need a dedicated non-alias thing for the module to look like when you merge an
export=into a module with type exports (since it's no longer "effectively replaced by" the target of the alias, as it's augmenting it), the trouble is maintaining that and making it "look like" the target of the alias at the same time in all the places that matters without silently dropping parts of the symbol's meaning along the way. It probably does just touch a lot, since it basically requires all the handling you'd associate with a new root symbol kind flag, I think.Uh oh!
There was an error while loading. Please reload this page.
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.
Ok, I'm going to come up with an implementation that allows both TS and JS to export types from modules with export assignments. I think I will also move the merging logic to the binder so it is generally available. I don't think we need a new kind of symbol. We should be able to add an
ast.SymbolFlagsNamespaceModuleflag to theexport=symbol and then add the exported type symbols to theexport=symbol'sExportsmap (which is otherwise empty). This means that the exported types will take precedence over any exported types in theexport=target, but seeing as this whole construct is currently an error, it is not like it will break anything. At least in theory.