Skip to content
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 refactor to convert namespace to named imports and back #24469

Merged
6 commits merged into from
May 30, 2018

Conversation

ghost
Copy link

@ghost ghost commented May 29, 2018

Fixes #24044 and half of #19260

@ghost ghost requested a review from amcasey May 29, 2018 21:16
const importDecl = getParentNodeInSpan(token, file, span);
if (!importDecl || !isImportDeclaration(importDecl)) return undefined;
const { importClause } = importDecl;
return importClause && importClause.namedBindings;
Copy link
Contributor

Choose a reason for hiding this comment

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

should we verify that the namedBindings is within the span..

e.g. selecting d in import d, * as ns from "./mod" should not trigger any action..

Copy link
Author

Choose a reason for hiding this comment

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

Added a test. If just d is selected, getParentNodeInSpan will return just d and not the entire import declaration.


forEachFreeIdentifier(sourceFile, id => {
if (!toConvert.elements.some(e => e.name === id) && contains(importedSymbols, checker.getSymbolAtLocation(id))) {
changes.replaceNode(sourceFile, id, createPropertyAccess(createIdentifier(namespaceImportName), nameToPropertyName.get(id.text) || id.text));
Copy link
Contributor

Choose a reason for hiding this comment

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

what if thsi is a type position? this needs to be a qualifiedName instead.

Copy link
Author

Choose a reason for hiding this comment

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

Added a test -- this should work as well either way since we're just changing text and not creating a new tree.

function isFreeIdentifier(node: Identifier): boolean {
const { parent } = node;
switch (parent.kind) {
case SyntaxKind.PropertyAccessExpression:
Copy link
Contributor

Choose a reason for hiding this comment

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

i am not sure i understand what this does.. and why not the name?

Copy link
Author

Choose a reason for hiding this comment

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

Added a comment.


const importedSymbols = toConvert.elements.map(e => checker.getSymbolAtLocation(e.name)!);

forEachFreeIdentifier(sourceFile, id => {
Copy link
Contributor

Choose a reason for hiding this comment

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

why not just find all references on the ImportSpecifier and change these instead of looking at all identifiers..

Copy link
Contributor

Choose a reason for hiding this comment

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

also we need to handle cases where the propertyAccess expression will not work.. e.g. shorthandproperties, var x = {a}, changing it to var x = { m.a } will not work, you need to change it to var x = { a: m.a }. similarly the export declarations need to be handled separately.. so export {a} will have to be rewritten either as export {a} from "mod" or import a = m.a; export {a};

forEachFreeIdentifier(sourceFile, id => {
if (id === toConvert.name || checker.getSymbolAtLocation(id) !== namespaceSymbol) return;

if (!isPropertyAccessExpression(id.parent)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

what if it is used in a call expression.. import * as n from "mod"; n();?
if allowSyntheticDefaultImport or esModuleInterop are on, then you can change it to import {default as n} from "mod"; n();, if not, we need to keep the namespace import around.. do not think there is another way to handle that.

@mhegazy
Copy link
Contributor

mhegazy commented May 30, 2018

thanks for the comment, but still why not findAllRefs?


function doChange(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, toConvert: NamedImportBindings): void {
const usedIdentifiers = createMap<true>();
forEachFreeIdentifier(sourceFile, id => usedIdentifiers.set(id.text, true));
Copy link
Author

Choose a reason for hiding this comment

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

Looks like isFileLevelUniqueName isn't suitable for this purpose since sourceFile.identifiers includes the text of every Identifier node in the source file, including the names in property accesses. So if we access import * as m from "m"; m.x then that would make us convert it to import { x as _ x } from "m"; unnecessarily.

Copy link
Contributor

Choose a reason for hiding this comment

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

D'oh.. did not think of this one.. i guess we need a walk after all.

Copy link
Contributor

Choose a reason for hiding this comment

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

one other option is to call resolveName for the property name on every reference for the namespace import, if it resolves to anything then you know you need to alias it.

@ghost ghost force-pushed the refactorConvertImport branch from f40242d to bab662d Compare May 30, 2018 20:27
@ghost ghost merged commit 43bf039 into master May 30, 2018
@ghost ghost deleted the refactorConvertImport branch May 30, 2018 21:11
ghost pushed a commit that referenced this pull request Jun 4, 2018
ghost pushed a commit that referenced this pull request Jun 4, 2018
* moveToNewFile: Update namespace imports (#24612)

* Port needed changes from #24469
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
1 participant