Add StringCollection to make it easier to work with strings
This commit is contained in:
		| @@ -1,17 +1,17 @@ | ||||
| import { Extractor } from './extractor'; | ||||
| import { JsonSerializer } from './serializers/json.serializer'; | ||||
| import { StringCollection } from './utils/string.collection'; | ||||
|  | ||||
| const serializer = new JsonSerializer(); | ||||
| // Or const serializer = new PotSerializer(); | ||||
| const extractor = new Extractor(serializer); | ||||
|  | ||||
| const src = '/your/project'; | ||||
| const dest = '/your/project/template.json'; | ||||
|  | ||||
| try { | ||||
| 	const messages: string[] = extractor.extract(src); | ||||
| 	const collection: StringCollection = extractor.process(src); | ||||
| 	const output: string = extractor.save(dest); | ||||
| 	console.log({ messages, output }); | ||||
| 	console.log({ strings: collection.keys(), output: output }); | ||||
| } catch (e) { | ||||
| 	console.log(`Something went wrong: ${e.toString()}`); | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,8 @@ import { PipeParser } from './parsers/pipe.parser'; | ||||
| import { DirectiveParser } from './parsers/directive.parser'; | ||||
| import { ServiceParser } from './parsers/service.parser'; | ||||
| import { SerializerInterface } from './serializers/serializer.interface'; | ||||
| import { StringCollection } from './utils/string.collection'; | ||||
|  | ||||
| import * as lodash from 'lodash'; | ||||
| import * as glob from 'glob'; | ||||
| import * as fs from 'fs'; | ||||
|  | ||||
| @@ -16,35 +16,35 @@ export class Extractor { | ||||
| 		new ServiceParser() | ||||
| 	]; | ||||
|  | ||||
| 	public globPatterns: string[] = [ | ||||
| 	public find: string[] = [ | ||||
| 		'/**/*.html', | ||||
| 		'/**/*.ts', | ||||
| 		'/**/*.js' | ||||
| 	]; | ||||
|  | ||||
| 	public messages: string[] = []; | ||||
| 	public collection: StringCollection = new StringCollection(); | ||||
|  | ||||
| 	public constructor(public serializer: SerializerInterface) { } | ||||
|  | ||||
| 	/** | ||||
| 	 * Extracts messages from paths | ||||
| 	 * Process dir | ||||
| 	 */ | ||||
| 	public extract(dir: string): string[] { | ||||
| 		let messages = []; | ||||
|  | ||||
| 		this._getFiles(dir).forEach(filePath => { | ||||
| 			const result = this._extractMessages(filePath); | ||||
| 			messages = [...messages, ...result]; | ||||
| 	public process(dir: string): StringCollection { | ||||
| 		this._getFiles(dir).forEach(path => { | ||||
| 			const contents: string = fs.readFileSync(path, 'utf-8'); | ||||
| 			this.parsers.forEach((parser: ParserInterface) => { | ||||
| 				this.collection.merge(parser.extract(contents, path)); | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		return this.messages = lodash.uniq(messages); | ||||
| 		return this.collection; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Serialize and return output | ||||
| 	 */ | ||||
| 	public serialize(): string { | ||||
| 		return this.serializer.serialize(this.messages); | ||||
| 		return this.serializer.serialize(this.collection); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -62,10 +62,10 @@ export class Extractor { | ||||
| 	protected _getFiles(dir: string): string[] { | ||||
| 		let results: string[] = []; | ||||
|  | ||||
| 		this.globPatterns.forEach(globPattern => { | ||||
| 		this.find.forEach(pattern => { | ||||
| 			const files = glob | ||||
| 				.sync(dir + globPattern) | ||||
| 				.filter(filePath => fs.statSync(filePath).isFile()); | ||||
| 				.sync(dir + pattern) | ||||
| 				.filter(path => fs.statSync(path).isFile()); | ||||
|  | ||||
| 			results = [...results, ...files]; | ||||
| 		}); | ||||
| @@ -73,18 +73,4 @@ export class Extractor { | ||||
| 		return results; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Extract messages from file using parser | ||||
| 	 */ | ||||
| 	protected _extractMessages(filePath: string): string[] { | ||||
| 		let results: string[] = []; | ||||
|  | ||||
| 		const contents: string = fs.readFileSync(filePath, 'utf-8'); | ||||
| 		this.parsers.forEach((parser: ParserInterface) => { | ||||
| 			results = [...results, ...parser.process(filePath, contents)]; | ||||
| 		}); | ||||
|  | ||||
| 		return results; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -4,8 +4,8 @@ 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); | ||||
| 	protected _isAngularComponent(path: string): boolean { | ||||
| 		return new RegExp(/\.(ts|js)$/, 'i').test(path); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
|   | ||||
| @@ -1,20 +1,21 @@ | ||||
| import { ParserInterface } from './parser.interface'; | ||||
| import { AbstractTemplateParser } from './abstract-template.parser'; | ||||
| import { StringCollection } from '../utils/string.collection'; | ||||
|  | ||||
| import * as $ from 'cheerio'; | ||||
|  | ||||
| export class DirectiveParser extends AbstractTemplateParser implements ParserInterface { | ||||
|  | ||||
| 	public process(filePath: string, contents: string): string[] { | ||||
| 		if (this._isAngularComponent(filePath)) { | ||||
| 	public extract(contents: string, path?: string): StringCollection { | ||||
| 		if (path && this._isAngularComponent(path)) { | ||||
| 			contents = this._extractInlineTemplate(contents); | ||||
| 		} | ||||
|  | ||||
| 		return this._parseTemplate(contents); | ||||
| 	} | ||||
|  | ||||
| 	protected _parseTemplate(template: string): string[] { | ||||
| 		let results: string[] = []; | ||||
| 	protected _parseTemplate(template: string): StringCollection { | ||||
| 		const collection = new StringCollection(); | ||||
|  | ||||
| 		template = this._normalizeTemplateAttributes(template); | ||||
| 		$(template) | ||||
| @@ -25,7 +26,7 @@ export class DirectiveParser extends AbstractTemplateParser implements ParserInt | ||||
| 				const attr = $element.attr('translate') || $element.attr('ng2-translate'); | ||||
|  | ||||
| 				if (attr) { | ||||
| 					results.push(attr); | ||||
| 					collection.add(attr); | ||||
| 				} else { | ||||
| 					$element | ||||
| 						.contents() | ||||
| @@ -33,11 +34,11 @@ export class DirectiveParser extends AbstractTemplateParser implements ParserInt | ||||
| 						.filter(textNode => textNode.type === 'text') | ||||
| 						.map(textNode => textNode.nodeValue.trim()) | ||||
| 						.filter(text => text.length > 0) | ||||
| 						.forEach(text => results.push(text)); | ||||
| 						.forEach(text => collection.add(text)); | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
| 		return results; | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| import { StringCollection } from '../utils/string.collection'; | ||||
|  | ||||
| export interface ParserInterface { | ||||
|  | ||||
| 	process(filePath: string, contents: string): string[]; | ||||
| 	extract(contents: string, path?: string): StringCollection; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,27 +1,28 @@ | ||||
| import { ParserInterface } from './parser.interface'; | ||||
| import { AbstractTemplateParser } from './abstract-template.parser'; | ||||
| import { StringCollection } from '../utils/string.collection'; | ||||
|  | ||||
| export class PipeParser extends AbstractTemplateParser implements ParserInterface { | ||||
|  | ||||
| 	public process(filePath: string, contents: string): string[] { | ||||
| 		if (this._isAngularComponent(filePath)) { | ||||
| 	public extract(contents: string, path?: string): StringCollection { | ||||
| 		if (path && this._isAngularComponent(path)) { | ||||
| 			contents = this._extractInlineTemplate(contents); | ||||
| 		} | ||||
|  | ||||
| 		return this._parseTemplate(contents); | ||||
| 	} | ||||
|  | ||||
| 	protected _parseTemplate(template: string): string[] { | ||||
| 		let results: string[] = []; | ||||
| 	protected _parseTemplate(template: string): StringCollection { | ||||
| 		const collection = new StringCollection(); | ||||
|  | ||||
| 		const regExp = new RegExp(/([\'"`])([^\1\r\n]*)\1\s+\|\s*translate(:.*?)?/, 'g'); | ||||
|  | ||||
| 		let matches; | ||||
| 		while (matches = regExp.exec(template)) { | ||||
| 			results.push(matches[2]); | ||||
| 			collection.add(matches[2]); | ||||
| 		} | ||||
|  | ||||
| 		return results; | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| import { ParserInterface } from './parser.interface'; | ||||
| import { StringCollection } from '../utils/string.collection'; | ||||
|  | ||||
| export class ServiceParser implements ParserInterface { | ||||
|  | ||||
| 	public process(filePath: string, contents: string): string[] { | ||||
| 		let results: string[] = []; | ||||
| 	public extract(contents: string, path?: string): StringCollection { | ||||
| 		const collection = new StringCollection(); | ||||
|  | ||||
| 		const translateServiceVar = this._extractTranslateServiceVar(contents); | ||||
| 		if (!translateServiceVar) { | ||||
| 			return results; | ||||
| 			return collection; | ||||
| 		} | ||||
|  | ||||
| 		const methodRegExp: RegExp = new RegExp(/(?:get|instant)\s*\(\s*(\[?([\'"`])([^\1\r\n]+)\2\]?)/); | ||||
| @@ -16,13 +17,13 @@ export class ServiceParser implements ParserInterface { | ||||
| 		let matches; | ||||
| 		while (matches = regExp.exec(contents)) { | ||||
| 			if (this._stringContainsArray(matches[1])) { | ||||
| 				results.push(...this._stringToArray(matches[1])); | ||||
| 				collection.add(this._stringToArray(matches[1])); | ||||
| 			} else { | ||||
| 				results.push(matches[3]); | ||||
| 				collection.add(matches[3]); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return results; | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
|   | ||||
| @@ -1,14 +1,10 @@ | ||||
| import { SerializerInterface } from './serializer.interface'; | ||||
| import { StringCollection } from '../utils/string.collection'; | ||||
|  | ||||
| export class JsonSerializer implements SerializerInterface { | ||||
|  | ||||
| 	public serialize(messages: string[]): string { | ||||
| 		let result = {}; | ||||
| 		messages.forEach(message => { | ||||
| 			result[message] = ''; | ||||
| 		}); | ||||
|  | ||||
| 		return JSON.stringify(result, null, '\t'); | ||||
| 	public serialize(collection: StringCollection): string { | ||||
| 		return JSON.stringify(collection.values, null, '\t'); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { SerializerInterface } from './serializer.interface'; | ||||
| import { StringCollection } from '../utils/string.collection'; | ||||
|  | ||||
| export class PotSerializer implements SerializerInterface { | ||||
|  | ||||
| @@ -9,10 +10,10 @@ export class PotSerializer implements SerializerInterface { | ||||
|  | ||||
| 	protected _buffer: string[] = []; | ||||
|  | ||||
| 	public serialize(messages: string[]): string { | ||||
| 	public serialize(collection: StringCollection): string { | ||||
| 		this._reset(); | ||||
| 		this._addHeader(this._headers); | ||||
| 		this._addMessages(messages); | ||||
| 		this._addMessages(collection); | ||||
|  | ||||
| 		return this._buffer.join('\n'); | ||||
| 	} | ||||
| @@ -25,10 +26,10 @@ export class PotSerializer implements SerializerInterface { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	protected _addMessages(messages: string[]): void { | ||||
| 		messages.forEach(message => { | ||||
| 			this._add('msgid', message); | ||||
| 			this._add('msgstr', ''); | ||||
| 	protected _addMessages(collection: StringCollection): void { | ||||
| 		Object.keys(collection.values).forEach(key => { | ||||
| 			this._add('msgid', key); | ||||
| 			this._add('msgstr', collection.get(key)); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| import { StringCollection } from '../utils/string.collection'; | ||||
|  | ||||
| export interface SerializerInterface { | ||||
|  | ||||
| 	serialize(messages: string[]): string; | ||||
| 	serialize(collection: StringCollection): string; | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										51
									
								
								src/utils/string.collection.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/utils/string.collection.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| export interface StringType { | ||||
| 	[key: string]: string | ||||
| }; | ||||
|  | ||||
| export class StringCollection { | ||||
|  | ||||
| 	public values: StringType = {}; | ||||
|  | ||||
| 	public static fromObject(values: StringType): StringCollection { | ||||
| 		const collection = new StringCollection(); | ||||
| 		Object.keys(values).forEach(key => collection.add(key, values[key])); | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| 	public static fromArray(keys: string[]): StringCollection { | ||||
| 		const collection = new StringCollection(); | ||||
| 		keys.forEach(key => collection.add(key)); | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| 	public add(keys: string | string[], val: string = ''): StringCollection { | ||||
| 		if (!Array.isArray(keys)) { | ||||
| 			keys = [keys]; | ||||
| 		} | ||||
| 		keys.forEach(key => this.values[key] = val); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	public remove(key: string): StringCollection { | ||||
| 		delete this.values[key]; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	public merge(collection: StringCollection): StringCollection { | ||||
| 		this.values = Object.assign({}, this.values, collection.values); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	public get(key: string): string { | ||||
| 		return this.values[key]; | ||||
| 	} | ||||
|  | ||||
| 	public keys(): string[] { | ||||
| 		return Object.keys(this.values); | ||||
| 	} | ||||
|  | ||||
| 	public count(): number { | ||||
| 		return Object.keys(this.values).length; | ||||
| 	} | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user