Refactor code. Add DirectiveParser (Thanks ocombe\!)
This commit is contained in:
parent
085f5fe700
commit
1ee298f737
@ -1,17 +1,17 @@
|
|||||||
import { Extractor } from './extractor';
|
import { Extractor } from './extractor';
|
||||||
import { JsonSerializer } from './serializers/json.serializer';
|
import { JsonSerializer } from './serializers/json.serializer';
|
||||||
|
|
||||||
const dir = '/path/to/extract/strings/from';
|
|
||||||
const dest = '/path/to/save/template/to/template.pot';
|
|
||||||
|
|
||||||
const serializer = new JsonSerializer();
|
const serializer = new JsonSerializer();
|
||||||
// Or const serializer = new PotSerializer();
|
// Or const serializer = new PotSerializer();
|
||||||
const extractor = new Extractor(serializer);
|
const extractor = new Extractor(serializer);
|
||||||
|
|
||||||
|
const src = '/your/project';
|
||||||
|
const dest = '/your/project/template.json';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const messages: string[] = extractor.extract(dir);
|
const messages: string[] = extractor.extract(src);
|
||||||
const output: string = extractor.save(dest);
|
const output: string = extractor.save(dest);
|
||||||
console.log('Done!');
|
console.log({ messages, output });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`Something went wrong: ${e.toString()}`);
|
console.log(`Something went wrong: ${e.toString()}`);
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,13 @@ export class Extractor {
|
|||||||
|
|
||||||
public parsers: ParserInterface[] = [
|
public parsers: ParserInterface[] = [
|
||||||
new PipeParser(),
|
new PipeParser(),
|
||||||
new ServiceParser(),
|
new DirectiveParser(),
|
||||||
new DirectiveParser()
|
new ServiceParser()
|
||||||
];
|
];
|
||||||
|
|
||||||
public globPatterns: string[] = [
|
public globPatterns: string[] = [
|
||||||
'/**/*.ts',
|
'/**/*.html',
|
||||||
'/**/*.html'
|
'/**/*.ts'
|
||||||
];
|
];
|
||||||
|
|
||||||
public messages: string[] = [];
|
public messages: string[] = [];
|
||||||
@ -30,15 +30,11 @@ export class Extractor {
|
|||||||
*/
|
*/
|
||||||
public extract(dir: string): string[] {
|
public extract(dir: string): string[] {
|
||||||
let messages = [];
|
let messages = [];
|
||||||
this.globPatterns.forEach(globPattern => {
|
|
||||||
const filePaths = glob.sync(dir + globPattern);
|
this._getFiles(dir).forEach(filePath => {
|
||||||
filePaths
|
|
||||||
.filter(filePath => fs.statSync(filePath).isFile())
|
|
||||||
.forEach(filePath => {
|
|
||||||
const result = this._extractMessages(filePath);
|
const result = this._extractMessages(filePath);
|
||||||
messages = [...messages, ...result];
|
messages = [...messages, ...result];
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return this.messages = lodash.uniq(messages);
|
return this.messages = lodash.uniq(messages);
|
||||||
}
|
}
|
||||||
@ -56,19 +52,35 @@ export class Extractor {
|
|||||||
public save(destination: string): string {
|
public save(destination: string): string {
|
||||||
const data = this.serialize();
|
const data = this.serialize();
|
||||||
fs.writeFileSync(destination, data);
|
fs.writeFileSync(destination, data);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract messages from file using specialized parser
|
* Get all files in dir that matches glob patterns
|
||||||
|
*/
|
||||||
|
protected _getFiles(dir: string): string[] {
|
||||||
|
let results: string[] = [];
|
||||||
|
|
||||||
|
this.globPatterns.forEach(globPattern => {
|
||||||
|
const files = glob
|
||||||
|
.sync(dir + globPattern)
|
||||||
|
.filter(filePath => fs.statSync(filePath).isFile());
|
||||||
|
|
||||||
|
results = [...results, ...files];
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract messages from file using parser
|
||||||
*/
|
*/
|
||||||
protected _extractMessages(filePath: string): string[] {
|
protected _extractMessages(filePath: string): string[] {
|
||||||
let results = [];
|
let results: string[] = [];
|
||||||
|
|
||||||
const contents: string = fs.readFileSync(filePath, 'utf-8');
|
const contents: string = fs.readFileSync(filePath, 'utf-8');
|
||||||
this.parsers.forEach((parser: ParserInterface) => {
|
this.parsers.forEach((parser: ParserInterface) => {
|
||||||
results = results.concat(parser.process(contents));
|
results = [...results, ...parser.process(filePath, contents)];
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
23
src/parsers/abstract-template.parser.ts
Normal file
23
src/parsers/abstract-template.parser.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export abstract class AbstractTemplateParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if file is of type javascript or typescript and
|
||||||
|
* makes the assumption that it is an Angular Component
|
||||||
|
*/
|
||||||
|
protected _isAngularComponent(filePath: string): boolean {
|
||||||
|
return new RegExp('\.(ts|js)$', 'i').test(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts inline template from components
|
||||||
|
*/
|
||||||
|
protected _extractInlineTemplate(contents: string): string {
|
||||||
|
const match = new RegExp('template\\s?:\\s?(("|\'|`)(.|[\\r\\n])+?[^\\\\]\\2)').exec(contents);
|
||||||
|
if (match !== null) {
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,66 +1,45 @@
|
|||||||
import {ParserInterface} from './parser.interface';
|
import { ParserInterface } from './parser.interface';
|
||||||
|
import { AbstractTemplateParser } from './abstract-template.parser';
|
||||||
import * as $ from 'cheerio';
|
import * as $ from 'cheerio';
|
||||||
|
|
||||||
export class DirectiveParser implements ParserInterface {
|
export class DirectiveParser extends AbstractTemplateParser implements ParserInterface {
|
||||||
|
|
||||||
public patterns = {
|
public process(filePath: string, contents: string): string[] {
|
||||||
template: `template:\\s?(("|'|\`)(.|[\\r\\n])+?[^\\\\]\\2)`
|
if (this._isAngularComponent(filePath)) {
|
||||||
};
|
contents = this._extractInlineTemplate(contents);
|
||||||
|
|
||||||
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) {
|
return this._parseTemplate(contents);
|
||||||
key = $this.text().replace(/\\n/gi, '').trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(key) {
|
protected _parseTemplate(template: string): string[] {
|
||||||
results.push(key);
|
let results: string[] = [];
|
||||||
|
|
||||||
|
template = this._normalizeTemplateAttributes(template);
|
||||||
|
|
||||||
|
$(template).find('[translate]')
|
||||||
|
.each((i: number, element: CheerioElement) => {
|
||||||
|
const $element = $(element);
|
||||||
|
const attr = $element.attr('translate');
|
||||||
|
const text = $element.text();
|
||||||
|
|
||||||
|
if (attr) {
|
||||||
|
results.push(attr);
|
||||||
|
} else if (text) {
|
||||||
|
results.push(text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public process(contents: string): string[] {
|
/**
|
||||||
const regExp = new RegExp(this.patterns.template, 'gi');
|
* Angular's `[attr]="'val'"` syntax is not valid HTML,
|
||||||
|
* so Cheerio is not able to parse it.
|
||||||
let results: string[] = [],
|
* This method replaces `[attr]="'val'""` with `attr="val"`
|
||||||
hasTemplate = false,
|
*/
|
||||||
matches;
|
protected _normalizeTemplateAttributes(template: string): string {
|
||||||
|
return template.replace(/\[([^\]]+)\]="'([^\"]*)'"/g, '$1="$2"');
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export interface ParserInterface {
|
export interface ParserInterface {
|
||||||
|
|
||||||
process(contents: string): string[];
|
process(filePath: string, contents: string): string[];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
import { ParserInterface } from './parser.interface';
|
import { ParserInterface } from './parser.interface';
|
||||||
|
import { AbstractTemplateParser } from './abstract-template.parser';
|
||||||
|
|
||||||
export class PipeParser implements ParserInterface {
|
export class PipeParser extends AbstractTemplateParser implements ParserInterface {
|
||||||
|
|
||||||
public patterns = {
|
public process(filePath: string, contents: string): string[] {
|
||||||
pipe: `(['"\`])([^\\1\\r\\n]*)\\1\\s+\\|\\s*translate(:.*?)?`
|
if (this._isAngularComponent(filePath)) {
|
||||||
};
|
contents = this._extractInlineTemplate(contents);
|
||||||
|
}
|
||||||
|
|
||||||
public process(contents: string): string[] {
|
return this._parseTemplate(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _parseTemplate(template: string): string[] {
|
||||||
let results: string[] = [];
|
let results: string[] = [];
|
||||||
|
|
||||||
for (let patternName in this.patterns) {
|
const regExp = new RegExp('([\'"`])([^\\1\\r\\n]*)\\1\\s+\\|\\s*translate(:.*?)?', 'g');
|
||||||
const regExp = new RegExp(this.patterns[patternName], 'g');
|
|
||||||
|
|
||||||
let matches;
|
let matches;
|
||||||
while (matches = regExp.exec(contents)) {
|
while (matches = regExp.exec(template)) {
|
||||||
results.push(matches[2]);
|
results.push(matches[2]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
@ -2,48 +2,25 @@ import { ParserInterface } from './parser.interface';
|
|||||||
|
|
||||||
export class ServiceParser implements ParserInterface {
|
export class ServiceParser implements ParserInterface {
|
||||||
|
|
||||||
public patterns = {
|
public process(filePath: string, contents: string): string[] {
|
||||||
translateServiceMethods: `{{TRANSLATE_SERVICE}}\.(?:get|instant)\\s*\\\(\\s*(['"\`])([^\\1\\r\\n]+)\\1`,
|
|
||||||
};
|
|
||||||
|
|
||||||
public process(contents: string): string[] {
|
|
||||||
let results: string[] = [];
|
let results: string[] = [];
|
||||||
|
|
||||||
const translateServiceVar = this._extractTranslateServiceVar(contents);
|
const translateServiceVar = this._extractTranslateServiceVar(contents);
|
||||||
if (!translateServiceVar) {
|
if (!translateServiceVar) {
|
||||||
return [];
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let patternName in this.patterns) {
|
const methodPattern: string = '(?:get|instant)\\s*\\\(\\s*([\'"`])([^\\1\\r\\n]+)\\1';
|
||||||
const regExp = this._createRegExp(patternName, {
|
const regExp: RegExp = new RegExp(`${translateServiceVar}\.${methodPattern}`, 'g');
|
||||||
'TRANSLATE_SERVICE': translateServiceVar
|
|
||||||
});
|
|
||||||
|
|
||||||
let matches;
|
let matches;
|
||||||
while (matches = regExp.exec(contents)) {
|
while (matches = regExp.exec(contents)) {
|
||||||
results.push(matches[2]);
|
results.push(matches[2]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create regular expression, replacing placeholders with real values
|
|
||||||
*/
|
|
||||||
protected _createRegExp(patternName: string, replaceVars: {} = {}): RegExp {
|
|
||||||
if (!this.patterns.hasOwnProperty(patternName)) {
|
|
||||||
throw new Error('Invalid pattern name');
|
|
||||||
}
|
|
||||||
|
|
||||||
let pattern = this.patterns[patternName];
|
|
||||||
Object.keys(replaceVars).forEach(key => {
|
|
||||||
pattern = pattern.replace('{{' + key + '}}', replaceVars[key]);
|
|
||||||
});
|
|
||||||
|
|
||||||
return new RegExp(pattern, 'g');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract name of TranslateService variable for use in patterns
|
* Extract name of TranslateService variable for use in patterns
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user