extract strings when TranslateService is accessed directly via constructor parameter. Closes #50 and #106
This commit is contained in:
		| @@ -1,8 +1,9 @@ | ||||
| import { ClassDeclaration, CallExpression } from 'typescript'; | ||||
| import { tsquery } from '@phenomnomnominal/tsquery'; | ||||
|  | ||||
| import { ParserInterface } from './parser.interface'; | ||||
| import { TranslationCollection } from '../utils/translation.collection'; | ||||
| import { findClassDeclarations, findClassPropertyByType, findMethodCallExpressions, getStringsFromExpression } from '../utils/ast-helpers'; | ||||
| import { findClassDeclarations, findClassPropertyByType, findPropertyCallExpressions, findMethodCallExpressions, getStringsFromExpression, findMethodParameterByType, findConstructorDeclaration } from '../utils/ast-helpers'; | ||||
|  | ||||
| const TRANSLATE_SERVICE_TYPE_REFERENCE = 'TranslateService'; | ||||
| const TRANSLATE_SERVICE_METHOD_NAMES = ['get', 'instant', 'stream']; | ||||
| @@ -19,12 +20,11 @@ export class ServiceParser implements ParserInterface { | ||||
| 		let collection: TranslationCollection = new TranslationCollection(); | ||||
|  | ||||
| 		classDeclarations.forEach(classDeclaration => { | ||||
| 			const propName: string = findClassPropertyByType(classDeclaration, TRANSLATE_SERVICE_TYPE_REFERENCE); | ||||
| 			if (!propName) { | ||||
| 				return; | ||||
| 			} | ||||
| 			const callExpressions = [ | ||||
| 				...this.findConstructorParamCallExpressions(classDeclaration), | ||||
| 				...this.findPropertyCallExpressions(classDeclaration) | ||||
| 			]; | ||||
|  | ||||
| 			const callExpressions = findMethodCallExpressions(classDeclaration, propName, TRANSLATE_SERVICE_METHOD_NAMES); | ||||
| 			callExpressions.forEach(callExpression => { | ||||
| 				const [firstArg] = callExpression.arguments; | ||||
| 				if (!firstArg) { | ||||
| @@ -36,4 +36,21 @@ export class ServiceParser implements ParserInterface { | ||||
| 		}); | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| 	protected findConstructorParamCallExpressions(classDeclaration: ClassDeclaration): CallExpression[] { | ||||
| 		const constructorDeclaration = findConstructorDeclaration(classDeclaration); | ||||
| 		if (!constructorDeclaration) { | ||||
| 			return []; | ||||
| 		} | ||||
| 		const paramName = findMethodParameterByType(constructorDeclaration, TRANSLATE_SERVICE_TYPE_REFERENCE); | ||||
| 		return findMethodCallExpressions(constructorDeclaration, paramName, TRANSLATE_SERVICE_METHOD_NAMES); | ||||
| 	} | ||||
|  | ||||
| 	protected findPropertyCallExpressions(classDeclaration: ClassDeclaration): CallExpression[] { | ||||
| 		const propName: string = findClassPropertyByType(classDeclaration, TRANSLATE_SERVICE_TYPE_REFERENCE); | ||||
| 		if (!propName) { | ||||
| 			return []; | ||||
| 		} | ||||
| 		return findPropertyCallExpressions(classDeclaration, propName, TRANSLATE_SERVICE_METHOD_NAMES); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,16 @@ | ||||
| import { tsquery } from '@phenomnomnominal/tsquery'; | ||||
| import { | ||||
| 	SyntaxKind, | ||||
| 	Node, | ||||
| 	NamedImports, | ||||
| 	Identifier, | ||||
| 	ClassDeclaration, | ||||
| 	CallExpression, | ||||
| 	ConstructorDeclaration, | ||||
| 	isStringLiteralLike, | ||||
| 	isArrayLiteralExpression, | ||||
| 	CallExpression, | ||||
| 	Expression, | ||||
| 	isBinaryExpression, | ||||
| 	SyntaxKind, | ||||
| 	isConditionalExpression, | ||||
| 	PropertyAccessExpression | ||||
| } from 'typescript'; | ||||
| @@ -45,6 +46,30 @@ export function findClassPropertyByType(node: ClassDeclaration, type: string): s | ||||
| 	return findClassPropertyConstructorParameterByType(node, type) || findClassPropertyDeclarationByType(node, type); | ||||
| } | ||||
|  | ||||
| export function findConstructorDeclaration(node: ClassDeclaration): ConstructorDeclaration { | ||||
| 	const query = `Constructor`; | ||||
| 	const [result] = tsquery<ConstructorDeclaration>(node, query); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| export function findMethodParameterByType(node: Node, type: string): string | null { | ||||
| 	const query = `Parameter:has(TypeReference > Identifier[name="${type}"]) > Identifier`; | ||||
| 	const [result] = tsquery<Identifier>(node, query); | ||||
| 	if (result) { | ||||
| 		return result.text; | ||||
| 	} | ||||
| 	return null; | ||||
| } | ||||
|  | ||||
| export function findMethodCallExpressions(node: Node, propName: string, fnName: string | string[]): CallExpression[] { | ||||
| 	if (Array.isArray(fnName)) { | ||||
| 		fnName = fnName.join('|'); | ||||
| 	} | ||||
| 	const query = `CallExpression > PropertyAccessExpression:has(Identifier[name=/^(${fnName})$/]):has(PropertyAccessExpression:has(Identifier[name="${propName}"]):not(:has(ThisKeyword)))`; | ||||
| 	const nodes = tsquery<PropertyAccessExpression>(node, query).map(n => n.parent as CallExpression); | ||||
| 	return nodes; | ||||
| } | ||||
|  | ||||
| export function findClassPropertyConstructorParameterByType(node: ClassDeclaration, type: string): string | null { | ||||
| 	const query = `Constructor Parameter:has(TypeReference > Identifier[name="${type}"]):has(PublicKeyword,ProtectedKeyword,PrivateKeyword) > Identifier`; | ||||
| 	const [result] = tsquery<Identifier>(node, query); | ||||
| @@ -72,7 +97,7 @@ export function findFunctionCallExpressions(node: Node, fnName: string | string[ | ||||
| 	return nodes; | ||||
| } | ||||
|  | ||||
| export function findMethodCallExpressions(node: Node, prop: string, fnName: string | string[]): CallExpression[] { | ||||
| export function findPropertyCallExpressions(node: Node, prop: string, fnName: string | string[]): CallExpression[] { | ||||
| 	if (Array.isArray(fnName)) { | ||||
| 		fnName = fnName.join('|'); | ||||
| 	} | ||||
|   | ||||
| @@ -11,6 +11,18 @@ describe('ServiceParser', () => { | ||||
| 		parser = new ServiceParser(); | ||||
| 	}); | ||||
|  | ||||
| 	it('should extract strings when TranslateService is accessed directly via constructor parameter', () => { | ||||
| 		const contents = ` | ||||
| 			@Component({ }) | ||||
| 			export class MyComponent { | ||||
| 				public constructor(protected translateService: TranslateService) { | ||||
| 					translateService.get('It works!'); | ||||
| 				} | ||||
| 		`; | ||||
| 		const keys = parser.extract(contents, componentFilename).keys(); | ||||
| 		expect(keys).to.deep.equal(['It works!']); | ||||
| 	}); | ||||
|  | ||||
| 	it('should support extracting binary expressions', () => { | ||||
| 		const contents = ` | ||||
| 			@Component({ }) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user