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

feat(parser): Upgrade typescript parser and update default import generation #313

Merged
merged 12 commits into from
Oct 4, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,30 +91,30 @@
"devDependencies": {
"@types/chai": "^4.0.4",
"@types/mocha": "^2.2.43",
"@types/node": "^8.0.28",
"@types/node": "^8.0.31",
"@types/reflect-metadata": "0.0.5",
"@types/sinon": "^2.3.4",
"@types/sinon": "^2.3.5",
"@types/sinon-chai": "^2.7.29",
"chai": "^4.1.2",
"del-cli": "^1.1.0",
"filewalker": "^0.1.3",
"mocha-testdata": "^1.2.0",
"semantic-release": "^8.0.0",
"semantic-release": "^8.0.3",
"semantic-release-visualstudio-marketplace-version": "^1.0.0",
"sinon": "^4.0.0",
"sinon-chai": "^2.13.0",
"sinon-chai": "^2.14.0",
"tslint": "^5.7.0",
"tslint-config-airbnb": "^5.3.0-beta",
"tsutils": "^2.8.2",
"tslint-config-airbnb": "^5.3.0",
"tsutils": "^2.11.1",
"vscode": "^1.1.5"
},
"dependencies": {
"inversify": "^4.3.0",
"inversify-inject-decorators": "^3.0.1",
"reflect-metadata": "^0.1.10",
"tslib": "^1.7.1",
"typescript": "~2.4.2",
"typescript-parser": "^1.3.3"
"typescript": "~2.5.3",
"typescript-parser": "^2.0.0"
},
"activationEvents": [
"onLanguage:typescript",
Expand Down
25 changes: 10 additions & 15 deletions src/common/helpers/DeclarationIndexHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { join, normalize, parse, relative } from 'path';
import {
DeclarationInfo,
DefaultDeclaration,
DefaultImport,
ExternalModuleImport,
Import,
NamedImport,
NamespaceImport,
} from 'typescript-parser';
import { DeclarationInfo, ExternalModuleImport, Import, NamedImport, NamespaceImport } from 'typescript-parser';

/**
* Calculates a list of declarationInfos filtered by the already imported ones in the given document.
* The result is a list of declarations that are not already imported by the document.
*
*
* @export
* @param {ResolveIndex} resolveIndex
* @param {string} documentPath
Expand All @@ -35,11 +27,14 @@ export function getDeclarationsFilteredByImports(
declarations = declarations
.filter(o => o.from !== importedLib || !(tsImport as NamedImport).specifiers
.some(s => s.specifier === o.declaration.name));
// if (tsImport.defaultAlias) {
// else if (tsImport instanceof DefaultImport) {
// declarations = declarations
// .filter(o => (!(o.declaration instanceof DefaultDeclaration) || importedLib !== o.from));
// }
// }
} else if (tsImport instanceof NamespaceImport || tsImport instanceof ExternalModuleImport) {
declarations = declarations.filter(o => o.from !== tsImport.libraryName);
} else if (tsImport instanceof DefaultImport) {
declarations = declarations
.filter(o => (!(o.declaration instanceof DefaultDeclaration) || importedLib !== o.from));
}
}

Expand All @@ -51,7 +46,7 @@ export function getDeclarationsFilteredByImports(
* If the library is a node module or a typings module, the name
* is returned. If the "lib" is in the local workspace, then the
* absolut path from the workspace root is returned.
*
*
* @param {string} library Name of the library
* @param {string} actualFilePath Filepath of the actually open file
* @param {string} [rootPath] Root path of the workspace
Expand All @@ -72,7 +67,7 @@ export function getAbsolutLibraryName(library: string, actualFilePath: string, r
* If the library is a node module or a typings module, the name
* is returned. If the "lib" is in the local workspace, then the
* relative path from the actual file is returned.
*
*
* @param {string} library Name of the library
* @param {string} actualFilePath Filepath of the actually open file
* @param {string} [rootPath] Root path of the workspace
Expand Down
24 changes: 3 additions & 21 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import 'reflect-metadata';

import { DefaultImport, GENERATORS, NamedImport, SymbolSpecifier, TypescriptCodeGenerator } from 'typescript-parser';
import { GENERATORS, TypescriptCodeGenerator } from 'typescript-parser';
import { Disposable, ExtensionContext } from 'vscode';

import { KeywordImportGroup, RegexImportGroup, RemainImportGroup } from './import-grouping';
import { Container } from './IoC';
import { iocSymbols } from './IoCSymbols';
import { ImportProxy } from './proxy-objects/ImportProxy';
import { TypeScriptHero } from './TypeScriptHero';

let extension: Disposable;
Expand All @@ -25,28 +24,11 @@ function extendGenerator(generator: TypescriptCodeGenerator): void {
GENERATORS[KeywordImportGroup.name] = simpleGenerator;
GENERATORS[RegexImportGroup.name] = simpleGenerator;
GENERATORS[RemainImportGroup.name] = simpleGenerator;
GENERATORS[ImportProxy.name] = (proxy: ImportProxy) => {
if (proxy.specifiers.length <= 0 && (proxy.defaultAlias || proxy.defaultPurposal)) {
return generator.generate(
new DefaultImport(
proxy.libraryName, (proxy.defaultAlias || proxy.defaultPurposal)!, proxy.start, proxy.end,
),
);
}
if (proxy.defaultAlias) {
proxy.specifiers.push(new SymbolSpecifier('default', proxy.defaultAlias));
}
const named = new NamedImport(
proxy.libraryName, proxy.start, proxy.end,
);
named.specifiers = proxy.specifiers;
return generator.generate(named);
};
}

/**
* Activates TypeScript Hero
*
*
* @export
* @param {ExtensionContext} context
*/
Expand All @@ -61,7 +43,7 @@ export async function activate(context: ExtensionContext): Promise<void> {

/**
* Deactivates TypeScript Hero
*
*
* @export
*/
export function deactivate(): void {
Expand Down
97 changes: 51 additions & 46 deletions src/extension/managers/ImportManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
DeclarationIndex,
DeclarationInfo,
DefaultDeclaration,
DefaultImport,
ExternalModuleImport,
File,
Import,
Expand All @@ -29,10 +28,21 @@ import { importRange } from '../helpers';
import { ImportGroup } from '../import-grouping';
import { Container } from '../IoC';
import { iocSymbols } from '../IoCSymbols';
import { ImportProxy } from '../proxy-objects/ImportProxy';
import { importSort, specifierSort } from '../utilities/utilityFunctions';
import { ObjectManager } from './ObjectManager';

function sameSpecifiers(specs1: SymbolSpecifier[], specs2: SymbolSpecifier[]): boolean {
for (const spec of specs1) {
const spec2 = specs2[specs1.indexOf(spec)];
if (!spec2 ||
spec.specifier !== spec2.specifier ||
spec.alias !== spec2.alias) {
return false;
}
}
return true;
}

/**
* Management class for the imports of a document. Can add and remove imports to the document
* and commit the virtual document to the TextEditor.
Expand Down Expand Up @@ -93,9 +103,6 @@ export class ImportManager implements ObjectManager {
*/
public static async create(document: TextDocument): Promise<ImportManager> {
const source = await ImportManager.parser.parseSource(document.getText());
source.imports = source.imports.map(
o => o instanceof NamedImport || o instanceof DefaultImport ? new ImportProxy(o) : o,
);
return new ImportManager(document, source);
}

Expand All @@ -121,44 +128,39 @@ export class ImportManager implements ObjectManager {
* @memberof ImportManager
*/
public addDeclarationImport(declarationInfo: DeclarationInfo): this {
// If there is something already imported, it must be a NamedImport or a DefaultImport
const alreadyImported: ImportProxy = this.imports.find(
// If there is something already imported, it must be a NamedImport
const alreadyImported: NamedImport = this.imports.find(
o => declarationInfo.from === getAbsolutLibraryName(
o.libraryName,
this.document.fileName,
ImportManager.rootPath,
) && o instanceof ImportProxy,
) as ImportProxy;
) && o instanceof NamedImport,
) as NamedImport;

if (alreadyImported) {
// If we found an import for this declaration, it's either a default import or a named import
// If we found an import for this declaration, it's named import (with a possible default declaration)
if (declarationInfo.declaration instanceof DefaultDeclaration) {
delete alreadyImported.defaultAlias;
alreadyImported.defaultPurposal = declarationInfo.declaration.name;
} else {
alreadyImported.addSpecifier(declarationInfo.declaration.name);
alreadyImported.defaultAlias = declarationInfo.declaration.name;
} else if (!alreadyImported.specifiers.some(o => o.specifier === declarationInfo.declaration.name)) {
alreadyImported.specifiers.push(new SymbolSpecifier(declarationInfo.declaration.name));
}
} else {
let imp: Import;
let imp: Import = new NamedImport(getRelativeLibraryName(
declarationInfo.from,
this.document.fileName,
ImportManager.rootPath,
));

if (declarationInfo.declaration instanceof ModuleDeclaration) {
imp = new NamespaceImport(
declarationInfo.from,
declarationInfo.declaration.name,
);
} else if (declarationInfo.declaration instanceof DefaultDeclaration) {
imp = new ImportProxy(getRelativeLibraryName(
declarationInfo.from,
this.document.fileName,
ImportManager.rootPath,
));
(imp as ImportProxy).defaultPurposal = declarationInfo.declaration.name;
(imp as NamedImport).defaultAlias = declarationInfo.declaration.name;
} else {
imp = new ImportProxy(getRelativeLibraryName(
declarationInfo.from,
this.document.fileName,
ImportManager.rootPath,
));
(imp as ImportProxy).specifiers.push(new SymbolSpecifier(declarationInfo.declaration.name));
(imp as NamedImport).specifiers.push(new SymbolSpecifier(declarationInfo.declaration.name));
}
this.imports.push(imp);
this.addImportsToGroups([imp]);
Expand Down Expand Up @@ -221,11 +223,11 @@ export class ImportManager implements ObjectManager {
if (this._parsedDocument.nonLocalUsages.indexOf(actImport.alias) > -1) {
keep.push(actImport);
}
} else if (actImport instanceof ImportProxy) {
} else if (actImport instanceof NamedImport) {
actImport.specifiers = actImport.specifiers
.filter(o => this._parsedDocument.nonLocalUsages.indexOf(o.alias || o.specifier) > -1)
.sort(specifierSort);
const defaultSpec = actImport.defaultAlias || actImport.defaultPurposal;
const defaultSpec = actImport.defaultAlias;
if (actImport.specifiers.length ||
(!!defaultSpec && this._parsedDocument.nonLocalUsages.indexOf(defaultSpec) >= 0)) {
keep.push(actImport);
Expand Down Expand Up @@ -273,9 +275,6 @@ export class ImportManager implements ObjectManager {
if (result) {
delete this.organize;
this._parsedDocument = await ImportManager.parser.parseSource(this.document.getText());
this._parsedDocument.imports = this._parsedDocument.imports.map(
o => o instanceof NamedImport || o instanceof DefaultImport ? new ImportProxy(o) : o,
);
this.imports = this._parsedDocument.imports.map(o => o.clone());
for (const group of this.importGroups) {
group.reset();
Expand Down Expand Up @@ -328,10 +327,14 @@ export class ImportManager implements ObjectManager {
edits.push(TextEdit.delete(importRange(this.document, imp.start, imp.end)));
}
}
const actualDocumentsProxies = this._parsedDocument.imports.filter(o => o instanceof ImportProxy);
const actualDocumentsNamed = this._parsedDocument.imports.filter(o => o instanceof NamedImport);
for (const imp of this.imports) {
if (imp instanceof ImportProxy &&
actualDocumentsProxies.some((o: ImportProxy) => o.isEqual(imp as ImportProxy))) {
if (imp instanceof NamedImport &&
actualDocumentsNamed.some((o: NamedImport) =>
o.libraryName === imp.libraryName &&
o.defaultAlias === imp.defaultAlias &&
o.specifiers.length === imp.specifiers.length &&
sameSpecifiers(o.specifiers, imp.specifiers))) {
continue;
}
if (imp.isNew) {
Expand Down Expand Up @@ -388,7 +391,7 @@ export class ImportManager implements ObjectManager {
.reduce(
(all, cur) => {
let specifiers = all;
if (cur instanceof ImportProxy) {
if (cur instanceof NamedImport) {
specifiers = specifiers.concat(cur.specifiers.map(o => o.alias || o.specifier));
if (cur.defaultAlias) {
specifiers.push(cur.defaultAlias);
Expand All @@ -399,7 +402,7 @@ export class ImportManager implements ObjectManager {
}
return specifiers;
},
<string[]>[],
[] as string[],
);

for (const decision of Object.keys(
Expand All @@ -418,12 +421,17 @@ export class ImportManager implements ObjectManager {
}
}

const proxies = this.imports.filter(o => o instanceof ImportProxy) as ImportProxy[];
const named = this.imports.filter(o => o instanceof NamedImport) as NamedImport[];

for (const imp of proxies) {
if (imp.defaultPurposal && !imp.defaultAlias) {
imp.defaultAlias = await this.getDefaultIdentifier(imp.defaultPurposal);
delete imp.defaultPurposal;
for (const imp of named) {
if (imp.defaultAlias) {
const specifiers = getSpecifiers();
if (
specifiers.filter(o => o === imp.defaultAlias).length > 1 &&
ImportManager.config.resolver.promptForSpecifiers
) {
imp.defaultAlias = await this.getDefaultIdentifier(imp.defaultAlias);
}
}

for (const spec of imp.specifiers) {
Expand Down Expand Up @@ -465,16 +473,13 @@ export class ImportManager implements ObjectManager {
* @memberof ImportManager
*/
private async getDefaultIdentifier(declarationName: string): Promise<string | undefined> {
if (!ImportManager.config.resolver.promptForSpecifiers) {
return declarationName;
}
const result = await this.vscodeInputBox({
placeHolder: 'Default export name',
prompt: 'Please enter a variable name for the default export...',
prompt: 'Please enter an alias name for the default export...',
validateInput: s => !!s ? '' : 'Please enter a variable name',
value: declarationName,
});
return !!result ? result : undefined;
return !!result ? result.replace(/[,.-_]/g, '') : undefined;
}

/**
Expand Down