alm
Version:
The best IDE for TypeScript
142 lines (126 loc) • 5.28 kB
text/typescript
import * as types from "../../../../common/types";
export var forEachChild = ts.forEachChild;
export function forEachChildRecursive<T>(node: ts.Node, cbNode: (node: ts.Node, depth: number) => T, depth = 0): T {
var res = cbNode(node, depth);
forEachChildRecursive(node, cbNode, depth + 1);
return res;
}
/** Number to string */
export function syntaxKindToString(syntaxKind: ts.SyntaxKind): string {
return (<any>ts).SyntaxKind[syntaxKind];
}
export function getNodeByKindAndName(program: ts.Program, kind: ts.SyntaxKind, name: string): ts.Node {
let found: ts.Node = undefined;
function findNode(node: ts.Node) {
if (node.kind == kind) {
// Now lookup name:
if (node.kind == ts.SyntaxKind.ClassDeclaration) {
if ((<ts.ClassDeclaration>node).name.text == name) {
found = node;
}
}
if (node.kind == ts.SyntaxKind.InterfaceDeclaration) {
if ((<ts.InterfaceDeclaration>node).name.text == name) {
found = node;
}
}
}
if (!found) { forEachChild(node, findNode); }
}
for (let file of program.getSourceFiles()) {
forEachChild(file, findNode);
}
return found;
}
export function getSourceFileImports(srcFile: ts.SourceFile): string[] {
var modules: string[] = [];
getImports(srcFile, modules);
return modules;
}
/**
* For example for import path + the string inside that
*/
interface StringWithRange {
text: string;
range: ts.TextRange;
}
export function getSourceFileImportsWithTextRange(srcFile: ts.SourceFile)
: StringWithRange[] {
var modules: StringWithRange[] = [];
getImportsWithTextRange(srcFile, modules);
return modules;
}
// https://github.com/Microsoft/TypeScript/issues/2621#issuecomment-90986004
function getImports(searchNode: ts.Node, importedModules: string[]) {
ts.forEachChild(searchNode, node => {
// Vist top-level import nodes
if (node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration || node.kind === ts.SyntaxKind.ExportDeclaration) {
let moduleNameExpr = getExternalModuleName(node);
// if they have a name, that is a string, i.e. not alias defition `import x = y`
if (moduleNameExpr && moduleNameExpr.kind === ts.SyntaxKind.StringLiteral) {
importedModules.push((<ts.LiteralExpression>moduleNameExpr).text);
}
}
else if (node.kind === ts.SyntaxKind.ModuleDeclaration && (<ts.ModuleDeclaration>node).name.kind === ts.SyntaxKind.StringLiteral) {
// Ambient module declaration
getImports((<ts.ModuleDeclaration>node).body, importedModules);
}
});
}
function getExternalModuleName(node: ts.Node): ts.Expression {
if (node.kind === ts.SyntaxKind.ImportDeclaration) {
return (<ts.ImportDeclaration>node).moduleSpecifier;
}
if (node.kind === ts.SyntaxKind.ImportEqualsDeclaration) {
let reference = (<ts.ImportEqualsDeclaration>node).moduleReference;
if (reference.kind === ts.SyntaxKind.ExternalModuleReference) {
return (<ts.ExternalModuleReference>reference).expression;
}
}
if (node.kind === ts.SyntaxKind.ExportDeclaration) {
return (<ts.ExportDeclaration>node).moduleSpecifier;
}
}
/** Note: we exclude the quote characters */
function getImportsWithTextRange(searchNode: ts.Node, importedModules: StringWithRange[]) {
ts.forEachChild(searchNode, node => {
// Vist top-level import nodes
if (node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration || node.kind === ts.SyntaxKind.ExportDeclaration) {
let moduleNameExpr = getExternalModuleName(node);
// if they have a name, that is a string, i.e. not alias defition `import x = y`
if (moduleNameExpr && moduleNameExpr.kind === ts.SyntaxKind.StringLiteral) {
let moduleExpr = <ts.LiteralExpression>moduleNameExpr;
importedModules.push({
text: moduleExpr.text,
range: { pos: moduleExpr.getStart() + 1, end: moduleExpr.getEnd() - 1 }
});
}
}
else if (node.kind === ts.SyntaxKind.ModuleDeclaration && (<ts.ModuleDeclaration>node).name.kind === ts.SyntaxKind.StringLiteral) {
// Ambient module declaration
getImportsWithTextRange((<ts.ModuleDeclaration>node).body, importedModules);
}
});
}
/**
* Used by a few things like the documentation view
*/
export function getDocumentedTypeLocation(sourceFile: ts.SourceFile, position: number): types.DocumentedTypeLocation {
/**
* The actual position of the node will be like
*
* <here
* /** some comment
* var someNode;
*
* Call the `ts.skipTrivia` to get the true node location, but +1 is good enough
*/
const pos = ts.getLineAndCharacterOfPosition(sourceFile, position + 1);
return {
filePath: sourceFile.fileName,
position: {
line: pos.line,
ch: pos.character
}
};
}