Skip to content

Add refactor to convert named to default export and back #24878

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
9 commits merged into from
Jun 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4418,5 +4418,13 @@
"Remove braces from arrow function": {
"category": "Message",
"code": 95060
},
"Convert default export to named export": {
"category": "Message",
"code": 95061
},
"Convert named export to default export": {
"category": "Message",
"code": 95062
}
}
2 changes: 1 addition & 1 deletion src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5844,7 +5844,7 @@ namespace ts {
// Keywords

/* @internal */
export function isModifierKind(token: SyntaxKind): boolean {
export function isModifierKind(token: SyntaxKind): token is Modifier["kind"] {
switch (token) {
case SyntaxKind.AbstractKeyword:
case SyntaxKind.AsyncKeyword:
Expand Down
54 changes: 39 additions & 15 deletions src/harness/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,12 @@ namespace FourSlash {
this.selectionEnd = end.position;
}

public selectAllInFile(fileName: string) {
this.openFile(fileName);
this.goToPosition(0);
this.selectionEnd = this.activeFile.content.length;
}

public selectRange(range: Range): void {
this.goToRangeStart(range);
this.selectionEnd = range.end;
Expand Down Expand Up @@ -3090,32 +3096,44 @@ Actual: ${stringify(fullActual)}`);
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
}

const { renamePosition, newContent } = parseNewContent();
let renameFilename: string | undefined;
let renamePosition: number | undefined;

const newFileContents = typeof newContentWithRenameMarker === "string" ? { [this.activeFile.fileName]: newContentWithRenameMarker } : newContentWithRenameMarker;
for (const fileName in newFileContents) {
const { renamePosition: rp, newContent } = TestState.parseNewContent(newFileContents[fileName]);
if (renamePosition === undefined) {
renameFilename = fileName;
renamePosition = rp;
}
else {
ts.Debug.assert(rp === undefined);
}
this.verifyFileContent(fileName, newContent);

this.verifyCurrentFileContent(newContent);
}

if (renamePosition === undefined) {
if (editInfo.renameLocation !== undefined) {
this.raiseError(`Did not expect a rename location, got ${editInfo.renameLocation}`);
}
}
else {
// TODO: test editInfo.renameFilename value
assert.isDefined(editInfo.renameFilename);
this.assertObjectsEqual(editInfo.renameFilename, renameFilename);
if (renamePosition !== editInfo.renameLocation) {
this.raiseError(`Expected rename position of ${renamePosition}, but got ${editInfo.renameLocation}`);
}
}
}

function parseNewContent(): { renamePosition: number | undefined, newContent: string } {
const renamePosition = newContentWithRenameMarker.indexOf("/*RENAME*/");
if (renamePosition === -1) {
return { renamePosition: undefined, newContent: newContentWithRenameMarker };
}
else {
const newContent = newContentWithRenameMarker.slice(0, renamePosition) + newContentWithRenameMarker.slice(renamePosition + "/*RENAME*/".length);
return { renamePosition, newContent };
}
private static parseNewContent(newContentWithRenameMarker: string): { readonly renamePosition: number | undefined, readonly newContent: string } {
const renamePosition = newContentWithRenameMarker.indexOf("/*RENAME*/");
if (renamePosition === -1) {
return { renamePosition: undefined, newContent: newContentWithRenameMarker };
}
else {
const newContent = newContentWithRenameMarker.slice(0, renamePosition) + newContentWithRenameMarker.slice(renamePosition + "/*RENAME*/".length);
return { renamePosition, newContent };
}
}

Expand Down Expand Up @@ -3966,6 +3984,10 @@ namespace FourSlashInterface {
this.state.select(startMarker, endMarker);
}

public selectAllInFile(fileName: string) {
this.state.selectAllInFile(fileName);
}

public selectRange(range: FourSlash.Range): void {
this.state.selectRange(range);
}
Expand Down Expand Up @@ -4736,7 +4758,7 @@ namespace FourSlashInterface {
refactorName: string;
actionName: string;
actionDescription: string;
newContent: string;
newContent: NewFileContent;
}

export type ExpectedCompletionEntry = string | {
Expand Down Expand Up @@ -4798,9 +4820,11 @@ namespace FourSlashInterface {
filesToSearch?: ReadonlyArray<string>;
}

export type NewFileContent = string | { readonly [filename: string]: string };

export interface NewContentOptions {
// Exactly one of these should be defined.
newFileContent?: string | { readonly [filename: string]: string };
newFileContent?: NewFileContent;
newRangeContent?: string;
}

Expand Down
11 changes: 2 additions & 9 deletions src/services/documentHighlights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,8 @@ namespace ts.DocumentHighlights {
});
}

function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): Node[] {
const modifierFlag = modifierToFlag(modifier);
return mapDefined(getNodesToSearchForModifier(declaration, modifierFlag), node => {
if (getModifierFlags(node) & modifierFlag) {
const mod = find(node.modifiers!, m => m.kind === modifier);
Debug.assert(!!mod);
return mod;
}
});
function getModifierOccurrences(modifier: Modifier["kind"], declaration: Node): Node[] {
return mapDefined(getNodesToSearchForModifier(declaration, modifierToFlag(modifier)), node => findModifier(node, modifier));
}

function getNodesToSearchForModifier(declaration: Node, modifierFlag: ModifierFlags): ReadonlyArray<Node> | undefined {
Expand Down
24 changes: 24 additions & 0 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,30 @@ namespace ts.FindAllReferences.Core {
}
}

export function eachExportReference(
sourceFiles: ReadonlyArray<SourceFile>,
checker: TypeChecker,
cancellationToken: CancellationToken | undefined,
exportSymbol: Symbol,
exportingModuleSymbol: Symbol,
exportName: string,
isDefaultExport: boolean,
cb: (ref: Identifier) => void,
): void {
const importTracker = createImportTracker(sourceFiles, arrayToSet(sourceFiles, f => f.fileName), checker, cancellationToken);
const { importSearches, indirectUsers } = importTracker(exportSymbol, { exportKind: isDefaultExport ? ExportKind.Default : ExportKind.Named, exportingModuleSymbol }, /*isForRename*/ false);
for (const [importLocation] of importSearches) {
cb(importLocation);
}
for (const indirectUser of indirectUsers) {
for (const node of getPossibleSymbolReferenceNodes(indirectUser, isDefaultExport ? "default" : exportName)) {
if (isIdentifier(node) && checker.getSymbolAtLocation(node) === exportSymbol) {
cb(node);
}
}
}
}

function shouldAddSingleReference(singleRef: Identifier | StringLiteral, state: State): boolean {
if (!hasMatchingMeaning(singleRef, state)) return false;
if (!state.options.isForRename) return true;
Expand Down
10 changes: 5 additions & 5 deletions src/services/importTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace ts.FindAllReferences {
export type ImportTracker = (exportSymbol: Symbol, exportInfo: ExportInfo, isForRename: boolean) => ImportsResult;

/** Creates the imports map and returns an ImportTracker that uses it. Call this lazily to avoid calling `getDirectImportsMap` unnecessarily. */
export function createImportTracker(sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken): ImportTracker {
export function createImportTracker(sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken | undefined): ImportTracker {
const allDirectImports = getDirectImportsMap(sourceFiles, checker, cancellationToken);
return (exportSymbol, exportInfo, isForRename) => {
const { directImports, indirectUsers } = getImportersForExport(sourceFiles, sourceFilesSet, allDirectImports, exportInfo, checker, cancellationToken);
Expand Down Expand Up @@ -43,7 +43,7 @@ namespace ts.FindAllReferences {
allDirectImports: Map<ImporterOrCallExpression[]>,
{ exportingModuleSymbol, exportKind }: ExportInfo,
checker: TypeChecker,
cancellationToken: CancellationToken
cancellationToken: CancellationToken | undefined,
): { directImports: Importer[], indirectUsers: ReadonlyArray<SourceFile> } {
const markSeenDirectImport = nodeSeenTracker<ImporterOrCallExpression>();
const markSeenIndirectUser = nodeSeenTracker<SourceFileLike>();
Expand Down Expand Up @@ -80,7 +80,7 @@ namespace ts.FindAllReferences {
continue;
}

cancellationToken.throwIfCancellationRequested();
if (cancellationToken) cancellationToken.throwIfCancellationRequested();

switch (direct.kind) {
case SyntaxKind.CallExpression:
Expand Down Expand Up @@ -363,11 +363,11 @@ namespace ts.FindAllReferences {
}

/** Returns a map from a module symbol Id to all import statements that directly reference the module. */
function getDirectImportsMap(sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken): Map<ImporterOrCallExpression[]> {
function getDirectImportsMap(sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken | undefined): Map<ImporterOrCallExpression[]> {
const map = createMap<ImporterOrCallExpression[]>();

for (const sourceFile of sourceFiles) {
cancellationToken.throwIfCancellationRequested();
if (cancellationToken) cancellationToken.throwIfCancellationRequested();
forEachImport(sourceFile, (importDecl, moduleSpecifier) => {
const moduleSymbol = checker.getSymbolAtLocation(moduleSpecifier);
if (moduleSymbol) {
Expand Down
Loading