diff --git a/src/js/extractors/factories/htmlTemplate.ts b/src/js/extractors/factories/htmlTemplate.ts new file mode 100644 index 0000000..5f1bfa9 --- /dev/null +++ b/src/js/extractors/factories/htmlTemplate.ts @@ -0,0 +1,22 @@ +import * as ts from 'typescript'; + +import { IJsExtractorFunction } from '../../../js/parser'; +import { Validate } from '../../../utils/validate'; +import { HtmlParser } from '../../../html/parser'; + +export function htmlTemplateExtractor(htmlParser: HtmlParser): IJsExtractorFunction { + Validate.required.argument({ htmlParser }); + + return (node: ts.Node, sourceFile: ts.SourceFile, _, lineNumberStart = 1) => { + if (ts.isStringLiteralLike(node)) { + const source = node.getText(sourceFile); + const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)); + + htmlParser.parseString( + source, + sourceFile.fileName, + { lineNumberStart: lineNumberStart + location.line } + ); + } + }; +} diff --git a/src/js/extractors/index.ts b/src/js/extractors/index.ts index 2486c0d..4c8bc6e 100644 --- a/src/js/extractors/index.ts +++ b/src/js/extractors/index.ts @@ -1,5 +1,7 @@ import { callExpressionExtractor } from './factories/callExpression'; +import { htmlTemplateExtractor } from './factories/htmlTemplate'; export abstract class JsExtractors { public static callExpression: typeof callExpressionExtractor = callExpressionExtractor; + public static htmlTemplate: typeof htmlTemplateExtractor = htmlTemplateExtractor; } diff --git a/src/js/parser.ts b/src/js/parser.ts index 7b2ec3b..7af25cf 100644 --- a/src/js/parser.ts +++ b/src/js/parser.ts @@ -3,7 +3,7 @@ import * as ts from 'typescript'; import { Parser, IAddMessageCallback, IParseOptions } from '../parser'; import { IMessage } from '../builder'; -export type IJsExtractorFunction = (node: ts.Node, sourceFile: ts.SourceFile, addMessage: IAddMessageCallback) => void; +export type IJsExtractorFunction = (node: ts.Node, sourceFile: ts.SourceFile, addMessage: IAddMessageCallback, lineNumberStart: number) => void; export interface IJsParseOptions extends IParseOptions { scriptKind?: ts.ScriptKind; @@ -24,7 +24,7 @@ export class JsParser extends Parser { }); for (let extractor of this.extractors) { - extractor(node, sourceFile, addMessageCallback); + extractor(node, sourceFile, addMessageCallback, lineNumberStart); } ts.forEachChild(node, n => { diff --git a/tests/js/extractors/factories/htmlTemplate.test.ts b/tests/js/extractors/factories/htmlTemplate.test.ts new file mode 100644 index 0000000..8e2c655 --- /dev/null +++ b/tests/js/extractors/factories/htmlTemplate.test.ts @@ -0,0 +1,148 @@ +import { htmlTemplateExtractor } from '../../../../src/js/extractors/factories/htmlTemplate' +import { HtmlExtractors } from '../../../../src/html/extractors'; +import { JsParser } from '../../../../src/js/parser'; +import { HtmlParser } from '../../../../src/html/parser'; +import { CatalogBuilder, IMessage } from '../../../../src/builder'; + +describe('JS: HTML template extractor', () => { + describe('calling html parser ', () => { + let builder: CatalogBuilder; + let messages: IMessage[] + let jsParser: JsParser; + let htmlParser: HtmlParser; + + beforeEach(() => { + messages = []; + + builder = { + addMessage: jest.fn((message: IMessage) => { + messages.push(message); + }) + }; + + htmlParser = new HtmlParser(builder, [ + HtmlExtractors.elementContent('translate') + ]); + + jsParser = new JsParser(builder, [ + htmlTemplateExtractor(htmlParser) + ]); + }); + + test('single line (regular string)', () => { + jsParser.parseString('let itBe = "
test
"'); + expect(messages).toEqual([ + { + text: 'test' + } + ]) + }); + + test('single line (template string)', () => { + jsParser.parseString('let itBe = "
test
"'); + expect(messages).toEqual([ + { + text: 'test' + } + ]) + }); + + test('with lineNumberStart option (regular string)', () => { + jsParser.parseString( + 'let itBe = "
test
"', + 'test', + { lineNumberStart: 10 } + ); + + expect(messages).toEqual([ + { + text: 'test', + references: ['test:10'], + }, + ]) + }) + + test('with lineNumberStart option (template string)', () => { + jsParser.parseString( + 'let itBe = "
test
"', + 'test', + { lineNumberStart: 10 } + ); + + expect(messages).toEqual([ + { + text: 'test', + references: ['test:10'], + }, + ]) + }) + + test('HTML inside a template literal with the correct line numbers', () => { + jsParser.parseString(` + + + + + + + let tuce = \` +
+ First level +
+ Second level +
Third level
+
+
\` + `, 'test') + + expect(messages).toEqual([ + { + text: 'First level', + references: ['test:10'], + }, + { + text: 'Second level', + references: ['test:12'], + }, + { + text: 'Third level', + references: ['test:13'], + }, + ]) + }) + + test('HTML inside a template literal with correct line numbers and with lineNumberStart', () => { + jsParser.parseString(` + + + + + + + let tuce = \` +
+ First level +
+ Second level +
Third level
+
+
\` + `, 'test', { lineNumberStart: 10 }) + + expect(messages).toEqual([ + { + text: 'First level', + references: ['test:19'], + }, + { + text: 'Second level', + references: ['test:21'], + }, + { + text: 'Third level', + references: ['test:22'], + }, + ]) + }) + }) +}) \ No newline at end of file