Adding a directive parser, and refactoring code a bit

This commit is contained in:
Olivier Combe 2016-12-06 17:56:45 +01:00 committed by Kim Biesbjerg
parent 63208952ac
commit 14972efec2
7 changed files with 90 additions and 29 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# IDE # IDE
.vscode .vscode
.idea
# Logs and other files # Logs and other files
npm-debug.log* npm-debug.log*

View File

@ -45,6 +45,7 @@
"dependencies": { "dependencies": {
"@types/glob": "^5.0.30", "@types/glob": "^5.0.30",
"@types/lodash": "^4.14.41", "@types/lodash": "^4.14.41",
"cheerio": "~0.22.0",
"cli": "^1.0.1", "cli": "^1.0.1",
"fs": "0.0.1-security", "fs": "0.0.1-security",
"glob": "^7.1.1", "glob": "^7.1.1",

View File

@ -1,6 +1,5 @@
import { Extractor } from './extractor'; import { Extractor } from './extractor';
import { JsonSerializer } from './serializers/json.serializer'; import { JsonSerializer } from './serializers/json.serializer';
import { PotSerializer } from './serializers/pot.serializer';
const dir = '/path/to/extract/strings/from'; const dir = '/path/to/extract/strings/from';
const dest = '/path/to/save/template/to/template.pot'; const dest = '/path/to/save/template/to/template.pot';

View File

@ -1,23 +1,20 @@
import { ParserInterface } from './parsers/parser.interface'; import { ParserInterface } from './parsers/parser.interface';
import { HtmlParser } from './parsers/html.parser'; import { PipeParser } from './parsers/pipe.parser';
import { TypescriptParser } from './parsers/typescript.parser'; import { DirectiveParser } from "./parsers/directive.parser";
import { ServiceParser } from './parsers/service.parser';
import { SerializerInterface } from './serializers/serializer.interface'; import { SerializerInterface } from './serializers/serializer.interface';
import { PotSerializer } from './serializers/pot.serializer';
import * as lodash from 'lodash'; import * as lodash from 'lodash';
import * as glob from 'glob'; import * as glob from 'glob';
import * as fs from 'fs'; import * as fs from 'fs';
export interface TypeParserMap {
[ext: string]: ParserInterface
}
export class Extractor { export class Extractor {
public parsers: TypeParserMap = { public parsers: ParserInterface[] = [
html: new HtmlParser(), new PipeParser(),
ts: new TypescriptParser() new ServiceParser(),
}; new DirectiveParser()
];
public globPatterns: string[] = [ public globPatterns: string[] = [
'/**/*.ts', '/**/*.ts',
@ -67,15 +64,14 @@ export class Extractor {
* Extract messages from file using specialized parser * Extract messages from file using specialized parser
*/ */
protected _extractMessages(filePath: string): string[] { protected _extractMessages(filePath: string): string[] {
const ext: string = filePath.split('.').pop(); let results = [];
if (!this.parsers.hasOwnProperty(ext)) {
return [];
}
const contents: string = fs.readFileSync(filePath, 'utf-8'); const contents: string = fs.readFileSync(filePath, 'utf-8');
const parser: ParserInterface = this.parsers[ext]; this.parsers.forEach((parser: ParserInterface) => {
results = results.concat(parser.process(contents));
});
return parser.process(contents); return results;
} }
} }

View File

@ -0,0 +1,66 @@
import {ParserInterface} from './parser.interface';
import * as $ from 'cheerio';
export class DirectiveParser implements ParserInterface {
public patterns = {
template: `template:\\s?(("|'|\`)(.|[\\r\\n])+?[^\\\\]\\2)`
};
protected _parseTemplate(content) {
let results: string[] = [],
template = content.trim()
// hack for cheerio that doesn't support wrapped attributes
.replace('[translate]=', '__translate__=');
$(template).find('[translate],[__translate__]').contents().filter(function() {
return this.nodeType === 3; // node type 3 = text node
}).each(function() {
let key,
$this = $(this),
element = $(this).parent(),
wrappedAttr = element.attr('__translate__'), // previously [translate]=
attr = element.attr('translate'); // translate=
// only support string values for now
if(wrappedAttr && wrappedAttr.match(/^['"].*['"]$/)) {
key = wrappedAttr.substr(1, wrappedAttr.length - 2);
} else if(attr) {
key = attr;
}
if(!key) {
key = $this.text().replace(/\\n/gi, '').trim();
}
if(key) {
results.push(key);
}
});
return results;
}
public process(contents: string): string[] {
const regExp = new RegExp(this.patterns.template, 'gi');
let results: string[] = [],
hasTemplate = false,
matches;
while(matches = regExp.exec(contents)) {
let content = matches[1]
.substr(1, matches[1].length - 2);
hasTemplate = true;
results = results.concat(this._parseTemplate(content));
}
if(!hasTemplate) {
this._parseTemplate(contents);
}
return results;
}
}

View File

@ -1,11 +1,10 @@
import { ParserInterface } from './parser.interface'; import { ParserInterface } from './parser.interface';
export class HtmlParser implements ParserInterface { export class PipeParser implements ParserInterface {
public patterns = { public patterns = {
PipeSingleQuote: '\'((?:\\\\.|[^\'\\\\])*)\'\\s*\\|\\s*translate(:.*?)?', pipe: `(['"\`])([^\\1\\r\\n]*)\\1\\s+\\|\\s*translate(:.*?)?`
PipeDoubleQuote: '"((?:\\\\.|[^"\\\\])*)"\\s*\\|\\s*translate(:.*?)?' };
}
public process(contents: string): string[] { public process(contents: string): string[] {
let results: string[] = []; let results: string[] = [];
@ -15,7 +14,7 @@ export class HtmlParser implements ParserInterface {
let matches; let matches;
while (matches = regExp.exec(contents)) { while (matches = regExp.exec(contents)) {
results.push(matches[1]); results.push(matches[2]);
} }
} }

View File

@ -1,11 +1,10 @@
import { ParserInterface } from './parser.interface'; import { ParserInterface } from './parser.interface';
export class TypescriptParser implements ParserInterface { export class ServiceParser implements ParserInterface {
public patterns = { public patterns = {
TranslateServiceMethodsSingleQuote: '{{TRANSLATE_SERVICE}}\.(?:get|instant)\\s*\\\(\\s*\'((?:\\\\.|[^\'\\\\])*)\\s*\'', translateServiceMethods: `{{TRANSLATE_SERVICE}}\.(?:get|instant)\\s*\\\(\\s*(['"\`])([^\\1\\r\\n]+)\\1`,
TranslateServiceMethodsDoubleQuote: '{{TRANSLATE_SERVICE}}\.(?:get|instant)\\s*\\\(\\s*"((?:\\\\.|[^"\\\\])*)\\s*"', };
}
public process(contents: string): string[] { public process(contents: string): string[] {
let results: string[] = []; let results: string[] = [];
@ -22,7 +21,7 @@ export class TypescriptParser implements ParserInterface {
let matches; let matches;
while (matches = regExp.exec(contents)) { while (matches = regExp.exec(contents)) {
results.push(matches[1]); results.push(matches[2]);
} }
} }
@ -49,7 +48,7 @@ export class TypescriptParser implements ParserInterface {
* Extract name of TranslateService variable for use in patterns * Extract name of TranslateService variable for use in patterns
*/ */
protected _extractTranslateServiceVar(contents: string): string { protected _extractTranslateServiceVar(contents: string): string {
const matches = contents.match(/([a-z0-9_]+)\s*:\s*TranslateService/i) const matches = contents.match(/([a-z0-9_]+)\s*:\s*TranslateService/i);
if (matches === null) { if (matches === null) {
return ''; return '';
} }