Skip to content
This repository has been archived by the owner on Oct 10, 2018. It is now read-only.

Commit

Permalink
fix(organize-imports): Don't remove exported elements and react calle…
Browse files Browse the repository at this point in the history
…d functions (#413)


Closes #380. Closes #364.
  • Loading branch information
buehler authored Mar 5, 2018
1 parent 44fb21c commit 9348b40
Show file tree
Hide file tree
Showing 10 changed files with 530 additions and 301 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"EXT_DEBUG": "true",
"LOCAL_TEST": "true",
"COVERAGE": "true",
"CHAI_JEST_SNAPSHOT_UPDATE_ALL": "true"
"CHAI_JEST_SNAPSHOT_UPDATE_ALL": ""
},
"stopOnEntry": false,
"sourceMaps": true,
Expand All @@ -75,7 +75,7 @@
"EXT_DEBUG": "true",
"LOCAL_TEST": "true",
"COVERAGE": "true",
"CHAI_JEST_SNAPSHOT_UPDATE_ALL": "true"
"CHAI_JEST_SNAPSHOT_UPDATE_ALL": ""
},
"stopOnEntry": false,
"sourceMaps": true,
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@
]
},
"devDependencies": {
"@smartive/tslint-config": "^2.0.0",
"@smartive/tslint-config": "^2.1.0",
"@types/chai": "^4.1.2",
"@types/istanbul": "^0.4.29",
"@types/mocha": "^2.2.48",
"@types/node": "^9.4.0",
"@types/node": "^9.4.6",
"@types/reflect-metadata": "0.1.0",
"@types/sinon": "^4.1.3",
"@types/sinon": "^4.3.0",
"@types/sinon-chai": "^2.7.29",
"chai": "^4.1.2",
"chai-jest-snapshot": "^2.0.0",
Expand All @@ -103,19 +103,19 @@
"remap-istanbul": "^0.10.1",
"semantic-release": "^12.4.1",
"semantic-release-vsce": "^2.0.0",
"sinon": "^4.2.2",
"sinon": "^4.4.2",
"sinon-chai": "^2.14.0",
"tslint": "^5.9.1",
"tsutils": "^2.21.0",
"tsutils": "^2.22.2",
"vscode": "^1.1.10"
},
"dependencies": {
"fs-extra": "^5.0.0",
"inversify": "^4.10.0",
"inversify": "^4.11.1",
"reflect-metadata": "^0.1.12",
"tslib": "^1.9.0",
"typescript": "~2.7.1",
"typescript-parser": "^2.2.2",
"typescript": "~2.7.2",
"typescript-parser": "^2.3.0",
"winston": "^3.0.0-rc1"
},
"activationEvents": [
Expand Down
4 changes: 3 additions & 1 deletion src/configuration/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { inject, injectable } from 'inversify';
import { TypescriptGenerationOptions } from 'typescript-parser';
import { MultiLineImportRule, TypescriptGenerationOptions } from 'typescript-parser';
import { Event, EventEmitter, ExtensionContext, Uri, window, workspace } from 'vscode';

import iocSymbols from '../ioc-symbols';
Expand Down Expand Up @@ -50,13 +50,15 @@ export default class Configuration {
public typescriptGeneratorOptions(resource: Uri): TypescriptGenerationOptions {
return {
eol: this.imports.insertSemicolons(resource) ? ';' : '',
insertSpaces: true,
multiLineTrailingComma: this.imports.multiLineTrailingComma(resource),
multiLineWrapThreshold: this.imports.multiLineWrapThreshold(resource),
spaceBraces: this.imports.insertSpaceBeforeAndAfterImportBraces(resource),
stringQuoteStyle: this.imports.stringQuoteStyle(resource),
tabSize: window.activeTextEditor && window.activeTextEditor.options.tabSize ?
(window.activeTextEditor.options.tabSize as any) * 1 :
workspace.getConfiguration('editor', resource).get('tabSize', 4),
wrapMethod: MultiLineImportRule.oneImportPerLineOnlyAfterThreshold,
};
}
}
5 changes: 4 additions & 1 deletion src/imports/import-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ export default class ImportManager {
const defaultSpec = actImport.defaultAlias;
const libraryAlreadyImported = keep.find(d => d.libraryName === actImport.libraryName);
if (actImport.specifiers.length ||
(!!defaultSpec && this._parsedDocument.nonLocalUsages.indexOf(defaultSpec) >= 0)) {
(!!defaultSpec && [
...this._parsedDocument.nonLocalUsages,
...this._parsedDocument.usages,
].indexOf(defaultSpec) >= 0)) {
if (libraryAlreadyImported) {
if (actImport.defaultAlias) {
(<NamedImport>libraryAlreadyImported).defaultAlias = actImport.defaultAlias;
Expand Down
2 changes: 1 addition & 1 deletion src/imports/import-organizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class ImportOrganizer implements Activatable {
* @private
* @returns {Promise<boolean>}
*
* @memberof ImportResolveExtension
* @memberof ImportOrganizer
*/
private async organizeImports(): Promise<void> {
if (!window.activeTextEditor) {
Expand Down
Empty file.
Empty file.
74 changes: 74 additions & 0 deletions test/tests/imports/__snapshots__/import-organizer.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ImportOrganizer import-organizer-file.ts should not remove default exported default imports 1`] = `
"import Barbaz from './foo';
export default Barbaz;
"
`;

exports[`ImportOrganizer import-organizer-file.ts should not remove default exported default imports 2`] = `
"import Barbaz from './foo';
export default Barbaz;
"
`;

exports[`ImportOrganizer import-organizer-file.ts should not remove directly exported default imports 1`] = `
"import Barbaz from './foo';
export { Barbaz }
"
`;

exports[`ImportOrganizer import-organizer-file.ts should not remove directly exported default imports 2`] = `
"import Barbaz from './foo';
export { Barbaz }
"
`;

exports[`ImportOrganizer import-organizer-file.ts should not remove directly exported imports 1`] = `
"import * as Foobar from './lol';
import * as Barbaz from './foo';
export { Foobar, Barbaz }
"
`;

exports[`ImportOrganizer import-organizer-file.ts should not remove directly exported imports 2`] = `
"import * as Barbaz from './foo';
import * as Foobar from './lol';
export { Foobar, Barbaz }
"
`;

exports[`ImportOrganizer import-organizer-file.tsx should not remove function that is used in tsx 1`] = `
"import { f } from './somewhere';
import * as React from 'react';
export class Comp extends React.Component {
render() {
return (
<div>{f()}</div>
);
}
}
"
`;

exports[`ImportOrganizer import-organizer-file.tsx should not remove function that is used in tsx 2`] = `
"import * as React from 'react';
import { f } from './somewhere';
export class Comp extends React.Component {
render() {
return (
<div>{f()}</div>
);
}
}
"
`;
143 changes: 143 additions & 0 deletions test/tests/imports/import-organizer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { join } from 'path';
import { Position, Range, TextDocument, Uri, window, workspace } from 'vscode';

import { ImportOrganizer } from '../../../src/imports';
import ioc from '../../../src/ioc';
import iocSymbols from '../../../src/ioc-symbols';
import { expect } from '../setup';

describe('ImportOrganizer', () => {

describe('import-organizer-file.ts', () => {

const rootPath = workspace.workspaceFolders![0].uri.fsPath;
const file = Uri.file(join(rootPath, 'imports', 'import-organizer-file.ts'));
let document: TextDocument;
let extension: any;

before(async () => {
document = await workspace.openTextDocument(file);
await window.showTextDocument(document);

extension = new ImportOrganizer(
ioc.get(iocSymbols.extensionContext),
ioc.get(iocSymbols.logger),
ioc.get(iocSymbols.configuration),
ioc.get(iocSymbols.importManager),
);
});

afterEach(async () => {
await window.activeTextEditor!.edit((builder) => {
builder.delete(new Range(
new Position(0, 0),
document.lineAt(document.lineCount - 1).rangeIncludingLineBreak.end,
));
});
});

it('should not remove directly exported imports', async () => {
await window.activeTextEditor!.edit((builder) => {
builder.insert(
new Position(0, 0),
`import * as Foobar from './lol';
import * as Barbaz from './foo';
export { Foobar, Barbaz }
`,
);
});

expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
await extension.organizeImports();
expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
});

it('should not remove directly exported default imports', async () => {
await window.activeTextEditor!.edit((builder) => {
builder.insert(
new Position(0, 0),
`import Barbaz from './foo';
export { Barbaz }
`,
);
});

expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
await extension.organizeImports();
expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
});

it('should not remove default exported default imports', async () => {
await window.activeTextEditor!.edit((builder) => {
builder.insert(
new Position(0, 0),
`import Barbaz from './foo';
export default Barbaz;
`,
);
});

expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
await extension.organizeImports();
expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
});

});

describe('import-organizer-file.tsx', () => {

const rootPath = workspace.workspaceFolders![0].uri.fsPath;
const file = Uri.file(join(rootPath, 'imports', 'import-organizer-file.tsx'));
let document: TextDocument;
let extension: any;

before(async () => {
document = await workspace.openTextDocument(file);
await window.showTextDocument(document);

extension = new ImportOrganizer(
ioc.get(iocSymbols.extensionContext),
ioc.get(iocSymbols.logger),
ioc.get(iocSymbols.configuration),
ioc.get(iocSymbols.importManager),
);
});

afterEach(async () => {
await window.activeTextEditor!.edit((builder) => {
builder.delete(new Range(
new Position(0, 0),
document.lineAt(document.lineCount - 1).rangeIncludingLineBreak.end,
));
});
});

it('should not remove function that is used in tsx', async () => {
await window.activeTextEditor!.edit((builder) => {
builder.insert(
new Position(0, 0),
`import { f } from './somewhere';
import * as React from 'react';
export class Comp extends React.Component {
render() {
return (
<div>{f()}</div>
);
}
}
`,
);
});

expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
await extension.organizeImports();
expect(window.activeTextEditor!.document.getText()).to.matchSnapshot();
});

});

});
Loading

0 comments on commit 9348b40

Please sign in to comment.