132 lines
3.8 KiB
TypeScript
132 lines
3.8 KiB
TypeScript
import { ParserInterface } from './parser.interface';
|
|
import { AbstractAstParser } from './abstract-ast.parser';
|
|
import { TranslationCollection } from '../utils/translation.collection';
|
|
|
|
import * as ts from 'typescript';
|
|
|
|
export class ServiceParser extends AbstractAstParser implements ParserInterface {
|
|
|
|
protected _sourceFile: ts.SourceFile;
|
|
|
|
public extract(contents: string, path?: string): TranslationCollection {
|
|
let collection: TranslationCollection = new TranslationCollection();
|
|
|
|
this._sourceFile = this._createSourceFile(path, contents);
|
|
const classNodes = this._findClassNodes(this._sourceFile);
|
|
classNodes.forEach(classNode => {
|
|
const constructorNode = this._findConstructorNode(classNode);
|
|
if (!constructorNode) {
|
|
return;
|
|
}
|
|
|
|
const propertyName: string = this._findTranslateServicePropertyName(constructorNode);
|
|
if (!propertyName) {
|
|
return;
|
|
}
|
|
|
|
const callNodes = this._findCallNodes(classNode, propertyName);
|
|
callNodes.forEach(callNode => {
|
|
const keys: string[] = this._getCallArgStrings(callNode);
|
|
if (keys && keys.length) {
|
|
collection = collection.addKeys(keys);
|
|
}
|
|
});
|
|
});
|
|
|
|
return collection;
|
|
}
|
|
|
|
/**
|
|
* Detect what the TranslateService instance property
|
|
* is called by inspecting constructor arguments
|
|
*/
|
|
protected _findTranslateServicePropertyName(constructorNode: ts.ConstructorDeclaration): string {
|
|
if (!constructorNode) {
|
|
return null;
|
|
}
|
|
|
|
const result = constructorNode.parameters.find(parameter => {
|
|
// Skip if visibility modifier is not present (we want it set as an instance property)
|
|
if (!parameter.modifiers) {
|
|
return false;
|
|
}
|
|
|
|
// Parameter has no type
|
|
if (!parameter.type) {
|
|
return false;
|
|
}
|
|
|
|
// Make sure className is of the correct type
|
|
const parameterType: ts.Identifier = (parameter.type as ts.TypeReferenceNode).typeName as ts.Identifier;
|
|
if (!parameterType) {
|
|
return false;
|
|
}
|
|
const className: string = parameterType.text;
|
|
if (className !== 'TranslateService') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (result) {
|
|
return (result.name as ts.Identifier).text;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find class nodes
|
|
*/
|
|
protected _findClassNodes(node: ts.Node): ts.ClassDeclaration[] {
|
|
return this._findNodes(node, ts.SyntaxKind.ClassDeclaration) as ts.ClassDeclaration[];
|
|
}
|
|
|
|
/**
|
|
* Find constructor
|
|
*/
|
|
protected _findConstructorNode(node: ts.ClassDeclaration): ts.ConstructorDeclaration {
|
|
const constructorNodes = this._findNodes(node, ts.SyntaxKind.Constructor) as ts.ConstructorDeclaration[];
|
|
if (constructorNodes) {
|
|
return constructorNodes[0];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find all calls to TranslateService methods
|
|
*/
|
|
protected _findCallNodes(node: ts.Node, propertyIdentifier: string): ts.CallExpression[] {
|
|
let callNodes = this._findNodes(node, ts.SyntaxKind.CallExpression) as ts.CallExpression[];
|
|
callNodes = callNodes
|
|
.filter(callNode => {
|
|
// Only call expressions with arguments
|
|
if (callNode.arguments.length < 1) {
|
|
return false;
|
|
}
|
|
|
|
const propAccess = callNode.getChildAt(0).getChildAt(0) as ts.PropertyAccessExpression;
|
|
if (!propAccess || propAccess.kind !== ts.SyntaxKind.PropertyAccessExpression) {
|
|
return false;
|
|
}
|
|
if (!propAccess.getFirstToken() || propAccess.getFirstToken().kind !== ts.SyntaxKind.ThisKeyword) {
|
|
return false;
|
|
}
|
|
if (propAccess.name.text !== propertyIdentifier) {
|
|
return false;
|
|
}
|
|
|
|
const methodAccess = callNode.getChildAt(0) as ts.PropertyAccessExpression;
|
|
if (!methodAccess || methodAccess.kind !== ts.SyntaxKind.PropertyAccessExpression) {
|
|
return false;
|
|
}
|
|
if (!methodAccess.name || (methodAccess.name.text !== 'get' && methodAccess.name.text !== 'instant' && methodAccess.name.text !== 'stream')) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
return callNodes;
|
|
}
|
|
|
|
}
|