UNPKG

webpack-angular-translate

Version:

Webpack plugin that extracts the translation-ids with the default texts.

174 lines (142 loc) 4.32 kB
import * as path from "path"; import { AngularExpressionMatch } from "./ng-filters"; import TranslateLoaderContext from "../translate-loader-context"; export interface Attribute { name: string; value: string; expressions: AngularExpressionMatch[]; startPosition: number; } export interface Text { startPosition: number; text: string; raw: string; expressions: AngularExpressionMatch[]; } export abstract class HtmlParseContext { /** * The text contents of the element */ readonly texts: Text[] = []; public abstract get suppressDynamicTranslationErrors(): boolean; public abstract set suppressDynamicTranslationErrors(value: boolean); enter( elementName: string, attributes: Attribute[], startPosition: number ): HtmlParseContext { return new ElementContext(this, elementName, attributes, startPosition); } abstract leave(): HtmlParseContext; addText(text: Text): void { this.texts.push(text); } abstract emitError(message: string, position: number): void; emitSuppressableError(message: string, position: number): void { if (this.suppressDynamicTranslationErrors) { return; } this.emitError(message, position); } abstract asHtml(): string; abstract loc(position: number): { line: number; column: number }; } export class DocumentContext extends HtmlParseContext { public suppressDynamicTranslationErrors = false; constructor( private readonly loader: TranslateLoaderContext, private readonly html: string ) { super(); } leave(): never { throw new Error(`Cannot leave the root context.`); } emitError(message: string, position: number) { const loc = this.loc(position); const relativePath = path.relative( this.loader.context, this.loader.resourcePath ); message = `Failed to extract the angular-translate translations from '${relativePath}':${ loc.line }:${loc.column}: ${message}`; this.loader.emitError(new Error(message)); } asHtml(): string { return this.texts.reduce((memo, text) => memo + text.raw, ""); } loc(position: number): { line: number; column: number } { let line = 1; let column = 0; for (let i = 0; i < position; ++i) { if (this.html[i] === "\n") { ++line; column = 0; } else { ++column; } } return { line, column }; } } /** * Context for an html element. * * The context stores the state about the current (html) element and is used by the parser. * The parser calls `enter` for each new element. This will create a child context of the current context. * The child context inherits some attributes, like if translation-errors should be suppressed. */ export default class ElementContext extends HtmlParseContext { /** * The html attributes of the current element */ readonly attributes: Attribute[]; /** * The position in the html file where the element has started. */ readonly elementStartPosition: number; readonly tagName: string; private _suppressDynamicTranslationErrorMessage = false; constructor( public readonly parent: HtmlParseContext, tagName: string, attributes: Attribute[], startPosition: number ) { super(); this.attributes = attributes || []; this.tagName = tagName; this.elementStartPosition = startPosition; } get suppressDynamicTranslationErrors(): boolean { return ( this._suppressDynamicTranslationErrorMessage || (this.parent && this.parent.suppressDynamicTranslationErrors) ); } set suppressDynamicTranslationErrors(suppress: boolean) { this._suppressDynamicTranslationErrorMessage = suppress; } emitError(message: string, position: number): void { return this.parent.emitError(message, position); } asHtml(): string { let result = `<${this.tagName}`; result = this.attributes.reduce( (memo, { name, value }) => memo + " " + name + "='" + value + "'", result ); const text = this.texts.length === 0 ? "..." : this.texts.reduce((memo, text) => memo + text.raw, ""); return `${result}>${text}</${this.tagName}>`; } leave(): HtmlParseContext { return this.parent; } loc(position: number) { return this.parent.loc(position); } }