-
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
Add support for import defer
proposal
#60757
base: main
Are you sure you want to change the base?
Conversation
Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page. Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up. |
3f2d947
to
942385c
Compare
Co-authored-by: Nicolò Ribaudo <hell@nicr.dev>
942385c
to
de82ce7
Compare
I guess this goes in favor of using |
src/compiler/factory/nodeFactory.ts
Outdated
@@ -4723,11 +4724,12 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode | |||
} | |||
|
|||
// @api | |||
function createImportClause(isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause { | |||
function createImportClause(isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, phase: ImportPhase): ImportClause { |
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.
Preemptively noting that this is a breaking API change, and would require a deprecation helper in the deprecations project to tack onto our public API something that will set a default for the new parameter.
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 may also be the case that phase
needs to go after isTypeOnly
since AST node builder parameters and properties are intended to be in source order.
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 we want to mix type
imports with defer
or source
. Something like import type source foo from "foo"
doesn't make sense if all source
imports will have the same type, and import type defer * as foo from "foo"
would essentially be the same as import type * as foo from "foo"
.
If we consider type
, defer
, and source
to be mutually exclusive, then I would suggest we replace the isTypeOnly
parameter with something like importModifier: SyntaxKind.TypeKeyword | SyntaxKind.DeferKeyword | SyntaxKind.SourceKeyword | boolean | undefined
and have ImportClause
be:
export interface ImportClause extends NamedDeclaration {
readonly kind: SyntaxKind.ImportClause;
readonly parent: ImportDeclaration | JSDocImportTag;
/** @deprecated */
readonly isTypeOnly: boolean;
readonly importModifier: SyntaxKind.TypeKeyword | SyntaxKind.DeferKeyword | SyntaxKind.SourceKeyword | undefined;
readonly name?: Identifier; // Default binding
readonly namedBindings?: NamedImportBindings;
}
For back-compat purposes, we can set isTypeOnly
to true
if importModifier
is SyntaxKind.TypeKeyword
.
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 think the issue with that scheme is that it's a little weirder to capture multiple modifiers being used in the cases of error recovery.
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 was originally going with something very similar to Ron's suggestion, and consider "type" just as another phase. I ended up not doing it because of the AST breaking change, but if just keeping the old property for backwards compat is ok then I'd go for it.
I think the issue with that scheme is that it's a little weirder to capture multiple modifiers being used in the cases of error recovery.
Error recovery is going to be tricky anyway, because each modifier is a valid identifier so things like import type defer
are a potentially valid start of an import declaration. I wonder how likely it is for people to accidentally write two modifiers in an import.
I'm not an expert per se, but I would expect that these need to be modifiers so that they are nodes that can be walked, since people can stick comments between them and so on... |
Modifiers would precede the But this is close syntactically to |
We have |
That said, |
4ad3ba8
to
b1f506b
Compare
I updated the PR to go with the suggestion in #60757 (comment), which is what I found the cleanest. Happy to do differently if needed. The API change is now backwards compatible. Probably I should do the same change for I also added a test showing that comments are properly preserved. |
emitTokenWithComment(node.phaseModifier, node.pos, writeKeyword, node); | ||
writeSpace(); | ||
} | ||
else if (node.isTypeOnly) { |
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 have this in case somebody passes in an object from an old TS version. Is this relevant for TS' API contract, or would removing it not be considered a breaking change?
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.
We don't support mixing TS versions between a given Node
and the emitter, so that shouldn't be a concern. Inside the compiler we should avoid depending on deprecated functionality.
This proposal is currently at Stage 2.7: the last changes have been approved at the TC39 meeting last week, and I'm planning on proposing Stage 3 at the TC39 meeting on February 18th (the only blocker is me finishing writing test262 tests, and they will for sure be ready by then). I'm already opening this PR because, depending on when TS5.8 will be released, it's possible that the proposal will already be at Stage 3 by then :)
This PR only needs to add parsing support for the proposal:
import defer * as ns
is meant to "look like" the one created byimport * as ns
As you'll see from the code, whether a module is deferred or not is not a boolean but an enum. That's because of the
import source
proposal: there is no PR for it yet, but once it will be implemented it should be done by adding one more possible "import phase" to this enum. A question I have is if that should be an enum, or just an union ofundefined | DeferKeyword
(which will one day becomeundefined | DeferKeyword | SourceKeyword
).Fixes #59391
This patch was originally written by @ryzokuken