-
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. |
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. |
b1f506b
to
bdf98f5
Compare
Marking as draft until I implement |
Oh well, that was simpler than expected. Given that |
808450c
to
621eee7
Compare
621eee7
to
e8d76ea
Compare
src/compiler/checker.ts
Outdated
@@ -37642,7 +37650,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
error(node, Diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_es2020_es2022_esnext_system_node16_node18_or_nodenext); | |||
} | |||
const file = getSourceFileOfNode(node); | |||
Debug.assert(!!(file.flags & NodeFlags.PossiblyContainsImportMeta), "Containing file is missing import meta node flag."); | |||
Debug.assert(node.name.escapedText === "defer" || !!(file.flags & NodeFlags.PossiblyContainsImportMeta), "Containing file is missing import meta node flag."); | |||
return node.name.escapedText === "meta" ? getGlobalImportMetaType() : errorType; |
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.
Should import.defer
be returning errorType
here? Should we also report an error here for import.defer
outside of a call?
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 now moved the import.defer
check to the checkMetaProperty
, which also already calls checkGrammarMetaProperty
to check if import.defer
is outside of a call. I also changes isExpressionNode()
to return false
for the import.defer
in import.defer(...)
.
So now this branch is only reached if we have a "standalone" import.defer
, for which checkGrammarMetaProperty
reports an error, and so returning errorType
is correct.
ns.foo(); | ||
}); | ||
|
||
import("./a.js"); // TODO: Without this the import.defer cannot resolve ./a |
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'm going to debug this next week, but if you have any pointer as to why I would be getting an error about ./a.js
not being resolved in import.defer(...)
without this I'd appreciate it :)
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