Skip to content

fix(42605): support refactoring for export default assignment without equal #42936

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

Merged
merged 1 commit into from
Apr 5, 2021

Conversation

Zuckjet
Copy link
Contributor

@Zuckjet Zuckjet commented Feb 24, 2021

Fixes #42605

The original issue pointed that Convert default export to named export refactoring is not available when it has specific format below.

  1. export default identifier:
  const foo = () => {};
  export default foo;
  1. anonymous function:
    export default () => {}

I can confirm the first situation is a bug, since we support export default function foo () {} refactoring currently.
But for second situation, I think it may be a feature not a bug, because export default function() {} is also not supported, and I traced the original relevant PR, this situation is not supported at very beginning.

So I just fix the bug part, as for feature part, I am not quite sure.

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Feb 24, 2021
@typescript-bot
Copy link
Collaborator

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

@andrewbranch
Copy link
Member

What are the parser and checker changes for?

@Zuckjet
Copy link
Contributor Author

Zuckjet commented Mar 4, 2021

What are the parser and checker changes for?

  1. for parser, update nextTokenCanFollowDefaultKeyword() function , since we should support export default foo.
  2. for checker, make checkGrammarModifiers() function not throw grammar error when encounter export default foo

@andrewbranch
Copy link
Member

I mean, I’m asking how that’s different from the current behavior.

@Zuckjet
Copy link
Contributor Author

Zuckjet commented Mar 5, 2021

I mean, I’m asking how that’s different from the current behavior.

this changes is for refactoring. currently select export default foo, then apply refactor, "Convert default export to named export" is not available.
2021-03-05 07 55 54

this PR make this refactor available:
2021-03-05 08 05 57

@andrewbranch
Copy link
Member

this changes is for refactoring

Right. My point is, changing the behavior of a refactor usually doesn’t require changing the parser or checker, and conversely, changing the parser and checker will usually have a much broader impact than changing the behavior of a refactor. Clearly we already parse and check export default foo without errors, so at a glance I don’t understand why those changes were necessary.

@Zuckjet
Copy link
Contributor Author

Zuckjet commented Mar 8, 2021

@andrewbranch Thanks for your patience and advice, I finally understand what you mean. The codebase has changed since this PR created, and I find there is no need to change checker any more, but the parser seems still need change, here is the reason:
The changeExport refactor function will find DefaultKeyword in exportNode's modifiers, then delete it:

function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, exportName }: ExportInfo, changes: textChanges.ChangeTracker, checker: TypeChecker): void {
if (wasDefault) {
changes.delete(exportingSourceFile, Debug.checkDefined(findModifier(exportNode, SyntaxKind.DefaultKeyword), "Should find a default keyword in modifier list"));
}

For export default function foo () {}, it's NodeObject's modifiers property is like {..., modifiers:[{TokenObject}, {TokenObject}]}. But for const foo = () => {};export default foo;, it's NodeObject's modifiers property is undefined. So I changed the parser, make the NodeObject of this format has right modifiers(array of two TokenObject).

It should be noted that export default foo; is also parsed as a ExportAssignment NodeObject.This format is a little different with export = foo(the official doc described as ExportAssignment), So I have to do some extra work.

@andrewbranch
Copy link
Member

Ah, yes. So, this is a weird part of our parse tree, but I think it’s working as intended. We parse export default foo and export = foo both as an ExportAssignment (which doesn’t exist in the ES grammar spec). You can tell them apart by the exportAssignment.isExportEquals property. If it’s undefined/false, it was export default foo. Having modifiers there would be redundant. It means you’ll have to handle a couple more cases in the refactor, but changing the parser would be a breaking API change, which we want to avoid.

@@ -4720,7 +4720,7 @@ namespace ts {
* NOTE: This function does not use `parent` pointers and will not include modifiers from JSDoc.
*/
export function getSyntacticModifierFlagsNoCache(node: Node): ModifierFlags {
let flags = modifiersToFlags(node.modifiers);
let flags = (isExportAssignment(node) && !node.isExportEquals) ? ModifierFlags.None : modifiersToFlags(node.modifiers);
Copy link
Contributor Author

@Zuckjet Zuckjet Mar 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle this, or the Convert default export to named export options will be disabled.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn’t modifiersToFlags already return None in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, same mistake #42936 (comment)

@Zuckjet
Copy link
Contributor Author

Zuckjet commented Mar 9, 2021

Ah, yes. So, this is a weird part of our parse tree, but I think it’s working as intended. We parse export default foo and export = foo both as an ExportAssignment (which doesn’t exist in the ES grammar spec). You can tell them apart by the exportAssignment.isExportEquals property. If it’s undefined/false, it was export default foo. Having modifiers there would be redundant. It means you’ll have to handle a couple more cases in the refactor, but changing the parser would be a breaking API change, which we want to avoid.

As you suggested, I have updated my PR, please help review it again.

Copy link
Member

@andrewbranch andrewbranch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, GitHub was having some issues yesterday and it looks like these two comments didn’t make it through.

@@ -4720,7 +4720,7 @@ namespace ts {
* NOTE: This function does not use `parent` pointers and will not include modifiers from JSDoc.
*/
export function getSyntacticModifierFlagsNoCache(node: Node): ModifierFlags {
let flags = modifiersToFlags(node.modifiers);
let flags = (isExportAssignment(node) && !node.isExportEquals) ? ModifierFlags.None : modifiersToFlags(node.modifiers);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn’t modifiersToFlags already return None in this case?

@Zuckjet Zuckjet changed the title fix(42605): make Convert default export to named export refactoring… fix(42605): support refactoring for export default assignment without equal Mar 23, 2021
@Zuckjet Zuckjet requested a review from andrewbranch March 23, 2021 18:37
@Zuckjet
Copy link
Contributor Author

Zuckjet commented Mar 23, 2021

Thanks for review, I have updated this PR again, ready for new round review.

@Zuckjet
Copy link
Contributor Author

Zuckjet commented Apr 1, 2021

just friendly ping

@andrewbranch andrewbranch merged commit f621d67 into microsoft:master Apr 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

"Convert default export to named export" refactoring is not always available
3 participants