diff --git a/tools/build_trgen.sh b/tools/build_trgen.sh index b6ebc528..db61baf0 100644 --- a/tools/build_trgen.sh +++ b/tools/build_trgen.sh @@ -1,13 +1,8 @@ #!/usr/bin/env bash -BASEDIR=$(dirname "$0") -source "${BASEDIR}/../scripts/resolve_commands.sh" -cd "$BASEDIR/trgen" - -execute_tsc -p tsconfig.json +npm run compile-tr-gen if [ $? -ne 0 ]; then echo "Failed to build typescript translation generator" exit 1 fi - exit 0 \ No newline at end of file diff --git a/tools/trgen/generator.ts b/tools/trgen/Definitions.ts similarity index 100% rename from tools/trgen/generator.ts rename to tools/trgen/Definitions.ts diff --git a/tools/trgen/TsTransformer.ts b/tools/trgen/TsTransformer.ts deleted file mode 100644 index 0411fca8..00000000 --- a/tools/trgen/TsTransformer.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as ts from "typescript"; -import * as ts_generator from "./TsGenerator"; -import {TranslationEntry} from "./generator"; - -export interface TransformerConfig { - verbose: boolean; - optimized: boolean; - - /** - * Output array for all gathered translations. - */ - translations?: TranslationEntry[]; -} - -export const deltaTranslations = (result: TranslationEntry[], fileName: string, processSpeed: number, newTranslations: TranslationEntry[]) => { - let deletedTranslations = 0; - for(let index = 0; index < result.length; index++) { - if(result[index].filename === fileName) { - result.splice(index, 1); - index--; - deletedTranslations++; - } - } - - result.push(...newTranslations); - if(deletedTranslations === 0 && newTranslations.length === 0) { - console.log("Processed %s (%dms). No translations found.", fileName, processSpeed); - } else if(deletedTranslations === 0) { - console.log("Processed %s (%dms). Found %d translations", fileName, processSpeed, newTranslations.length); - } else if(newTranslations.length === 0) { - console.log("Processed %s (%dms). %d translations deleted.", fileName, processSpeed, deletedTranslations); - } else { - console.log("Processed %s (%dms). Old translation count: %d New translation count: %d", fileName, processSpeed, deletedTranslations, newTranslations.length); - } -} - -export const createTransformer = (program: ts.Program, config: TransformerConfig) : ts.TransformerFactory => { - return ctx => sourceFile => { - const timestampBegin = Date.now(); - const result = ts_generator.transform({ - module: true, - useWindow: false, - /* Note: Even though caching might cause less method calls but if the tr method is performant developed it's faster than having the cache lookup */ - cacheTranslations: false, - optimized: config.optimized, - }, ctx, sourceFile); - const timestampEnd = Date.now(); - - deltaTranslations(config.translations || [], sourceFile.fileName, timestampEnd - timestampBegin, result.translations); - return result.node; - }; -} \ No newline at end of file diff --git a/tools/trgen/bin/tsc.sh b/tools/trgen/bin/tsc.sh deleted file mode 100644 index 64142f3c..00000000 --- a/tools/trgen/bin/tsc.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -BASEDIR=$(dirname "$0") -FILE="${BASEDIR}/../compiler.ts" - -npm run dtsgen -- $@ \ No newline at end of file diff --git a/tools/trgen/compiler.ts b/tools/trgen/compiler.ts deleted file mode 100644 index 012b32b2..00000000 --- a/tools/trgen/compiler.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as ts from "typescript"; -import * as generator from "./TsGenerator"; - -import {readFileSync} from "fs"; -import * as glob from "glob"; -import * as path from "path"; - -const transformer = (context: ts.TransformationContext) => (rootNode: T) => { - return generator.transform({ - useWindow: false, - cacheTranslations: true, - verbose: true - }, context, rootNode as any).node; -}; - - -function compile(fileNames: string[], options: ts.CompilerOptions): void { - const program: ts.Program = ts.createProgram(fileNames, options); - - let emitResult = program.emit(undefined, undefined, undefined, undefined, { - before: [ transformer ] - }); - - let allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); - - allDiagnostics.forEach(diagnostic => { - if (diagnostic.file) { - let { line, character } = diagnostic.file.getLineAndCharacterOfPosition( - diagnostic.start! - ); - let message = ts.flattenDiagnosticMessageText( - diagnostic.messageText, - "\n" - ); - console.log( - `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}` - ); - } else { - console.log( - `${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}` - ); - } - }); - - let exitCode = emitResult.emitSkipped ? 1 : 0; - console.log(`Process exiting with code '${exitCode}'.`); - process.exit(exitCode); -} - -const config = ts.parseCommandLine(process.argv.slice(2), file => readFileSync(file).toString()); -if(config.errors && config.errors.length > 0) { - for(const error of config.errors) - console.log(error.messageText); - process.exit(1); -} - -if(config.options.project) { - const project = ts.readConfigFile(config.options.project, file => readFileSync(file).toString()).config; - const base_path = path.dirname(config.options.project) + "/"; - console.dir(project); - - - const negate_files: string[] = [].concat.apply([], (project.exclude || []).map(file => glob.sync(base_path + file))).map(file => path.normalize(file)); - project.include.forEach(file => { - glob.sync(base_path + file).forEach(_file => { - _file = path.normalize(_file); - for(const n_file of negate_files) { - if(n_file == _file) { - console.log("Skipping %s", _file); - return; - } - } - - config.fileNames.push(_file); - }); - }); - - Object.assign(config.options, project.compilerOptions); - console.log(config.options); -} - -compile(config.fileNames, config.options); \ No newline at end of file diff --git a/tools/trgen/JsRendererGenerator.ts b/tools/trgen/generator/JsRenderer.ts similarity index 93% rename from tools/trgen/JsRendererGenerator.ts rename to tools/trgen/generator/JsRenderer.ts index 18381ed1..d4363277 100644 --- a/tools/trgen/JsRendererGenerator.ts +++ b/tools/trgen/generator/JsRenderer.ts @@ -1,4 +1,4 @@ -import {TranslationEntry} from "./generator"; +import {TranslationEntry} from "../Definitions"; export interface File { content: string; @@ -17,7 +17,6 @@ export function extractJsRendererTranslations(file: File) : TranslationEntry[] { while(match = regex.exec(file.content.substr(baseIndex))) { let expression = (match.groups || {})["message_expression"] || match[1]; - //expression = expression.replace(/\n/g, "\\n"); let message; try { diff --git a/tools/trgen/TsGenerator.ts b/tools/trgen/generator/TypeScript.ts similarity index 99% rename from tools/trgen/TsGenerator.ts rename to tools/trgen/generator/TypeScript.ts index 028cc3d3..2ce03bb1 100644 --- a/tools/trgen/TsGenerator.ts +++ b/tools/trgen/generator/TypeScript.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import sha256 from "sha256"; import {SyntaxKind} from "typescript"; -import {TranslationEntry} from "./generator"; +import {TranslationEntry} from "../Definitions"; const getSourceLocation = (node: ts.Node) => { const sf = node.getSourceFile(); @@ -384,6 +384,7 @@ export function visitNode(config: Configuration, cache: VolatileTransformConfig, return node; } + export interface Configuration { useWindow?: boolean; cacheTranslations?: boolean; diff --git a/tools/trgen/index.ts b/tools/trgen/index.ts deleted file mode 100644 index b6f8bf8b..00000000 --- a/tools/trgen/index.ts +++ /dev/null @@ -1,150 +0,0 @@ -import * as ts from "typescript"; -import * as ts_generator from "./TsGenerator"; -import * as jsrender_generator from "./JsRendererGenerator"; -import {readFileSync, writeFileSync} from "fs"; -import * as path from "path"; -import * as glob from "glob"; -import {isArray} from "util"; -import * as mkdirp from "mkdirp"; -import {TranslationEntry} from "./generator"; - -/* -const files = ["/home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/build/trgen/test/test_01.ts"]; -files.forEach(file => { - let source = ts.createSourceFile( - file, - readFileSync(file).toString(), - ts.ScriptTarget.ES2016, - true - ); - - generator.generate(source); -}); -*/ - -interface Config { - source_files?: string[]; - excluded_files?: string[]; - target_file?: string; - verbose?: boolean; - - base_bath?: string; - file_config?: string; -} - -let config: Config = {}; - -let args = process.argv.slice(2); -while(args.length > 0) { - if(args[0] == "-f" || args[0] == "--file") { - (config.source_files || (config.source_files = [])).push(args[1]); - args = args.slice(2); - } else if(args[0] == "-e" || args[0] == "--exclude") { - (config.excluded_files || (config.excluded_files = [])).push(args[1]); - args = args.slice(2); - } else if(args[0] == "-d" || args[0] == "--destination") { - config.target_file = args[1]; - args = args.slice(2); - } else if(args[0] == "-v" || args[0] == "--verbose") { - config.verbose = true; - args = args.slice(1); - } else if(args[0] == "-c" || args[0] == "--config") { - config.file_config = args[1]; - config.base_bath = path.normalize(path.dirname(config.file_config)) + "/"; - args = args.slice(2); - } else { - console.error("Invalid command line option \"%s\"", args[0]); - process.exit(1); - } -} - -config.base_bath = config.base_bath || ""; - - -if(config.verbose) { - console.log("Base path: " + config.base_bath); - console.log("Input files:"); - for(const file of config.source_files) - console.log(" - " + file); - console.log("Target file: " + config.target_file); -} - -const negate_files: string[] = [].concat.apply([], (config.excluded_files || []).map(file => glob.sync(config.base_bath + file))).map(file => path.normalize(file)); - -let result = ""; - -function print(nodes: ts.Node[] | ts.SourceFile) : string { - if(!isArray(nodes) && nodes.kind == ts.SyntaxKind.SourceFile) - nodes = (nodes).getChildren(); - const dummy_file = ts.createSourceFile( - "dummy_file", - "", - ts.ScriptTarget.ES2016, - false, - ts.ScriptKind.TS - ); - - const printer = ts.createPrinter({ - newLine: ts.NewLineKind.LineFeed - }); - - return printer.printList( - ts.ListFormat.SpaceBetweenBraces | ts.ListFormat.MultiLine | ts.ListFormat.PreferNewLine, - nodes as any, - dummy_file - ); -} - -const translations: TranslationEntry[] = []; -config.source_files.forEach(file => { - if(config.verbose) - console.log("iterating over %s (%s)", file, path.resolve(path.normalize(config.base_bath + file))); - - glob.sync(config.base_bath + file).forEach(_file => { - _file = path.normalize(_file); - for(const n_file of negate_files) { - if(n_file == _file) { - console.log("Skipping %s", _file); - return; - } - } - - const file_type = path.extname(_file); - if(file_type == ".ts" || file_type == ".tsx") { - let source = ts.createSourceFile( - _file, - readFileSync(_file).toString(), - ts.ScriptTarget.ES2016, - true - ); - console.log("Compile " + _file); - - throw "not supported"; - //const messages = ts_generator.generate(source, {}); - //translations.push(...messages); - } else if(file_type == ".html") { - const messages = jsrender_generator.extractJsRendererTranslations({ - content: readFileSync(_file).toString(), - name: _file - }); - translations.push(...messages); - /* - messages.forEach(message => { - console.log(message); - }); - */ - } else { - console.log("Unknown file type \"%s\". Skipping file %s", file_type, _file); - } - }); -}); - -if(config.target_file) { - mkdirp(path.normalize(path.dirname(config.base_bath + config.target_file)), error => { - if(error) - throw error; - writeFileSync(config.base_bath + config.target_file, JSON.stringify(translations, null, 2)); - }); -} else { - console.log(JSON.stringify(translations, null, 2)); -} \ No newline at end of file diff --git a/tools/trgen/test/test_01.ts b/tools/trgen/test/test_01.ts deleted file mode 100644 index e174e87f..00000000 --- a/tools/trgen/test/test_01.ts +++ /dev/null @@ -1,27 +0,0 @@ -function tr(message: string) : string { - console.log("Message: " + message); - return message; -} - -const x = tr("yyy"); -function y() { - const y = tr(tr("yyy")); -} - -console.log("XXX: " + tr("XXX")); -console.log("YYY: " + tr("YYY")); - -var z = 1 + 2 + 3; - -debugger; -debugger; -debugger; -debugger; -const zzz = true ? "yyy" : "bbb"; - -const zy = ""; -debugger; -debugger; -debugger; -debugger; -const { a } = {a : ""}; \ No newline at end of file diff --git a/tools/trgen/test/test_02.html b/tools/trgen/test/test_02.html deleted file mode 100644 index 5f91ace4..00000000 --- a/tools/trgen/test/test_02.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - Title - - - - - \ No newline at end of file diff --git a/tools/trgen/test/test_03.tsx b/tools/trgen/test/test_03.tsx deleted file mode 100644 index fbd08155..00000000 --- a/tools/trgen/test/test_03.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import {Translatable, VariadicTranslatable} from "../../../shared/js/ui/react-elements/i18n"; -import * as React from "react"; - -function test() { - const element_0 = Hello World; - const element_1 = {"Hello World"}; - const element_2 = <>XXX; -} \ No newline at end of file diff --git a/tools/trgen/tsconfig.json b/tools/trgen/tsconfig.json index eb3b1f72..d358e626 100644 --- a/tools/trgen/tsconfig.json +++ b/tools/trgen/tsconfig.json @@ -11,11 +11,11 @@ "esModuleInterop": true }, "files": [ - "generator.ts", - "index.ts", - "compiler.ts", - "JsRendererGenerator.ts", - "TsGenerator.ts", - "JsRendererTranslationLoader.ts" + "./Definitions.ts", + "./generator/JsRenderer.ts", + "./generator/TypeScript.ts", + "./webpack/Loader.ts", + "./webpack/Plugin.ts", + "./webpack/Utils.ts" ] } \ No newline at end of file diff --git a/tools/trgen/JsRendererTranslationLoader.ts b/tools/trgen/webpack/Loader.ts similarity index 80% rename from tools/trgen/JsRendererTranslationLoader.ts rename to tools/trgen/webpack/Loader.ts index 32d97850..9ecabf3f 100644 --- a/tools/trgen/JsRendererTranslationLoader.ts +++ b/tools/trgen/webpack/Loader.ts @@ -1,5 +1,5 @@ -import {extractJsRendererTranslations} from "./JsRendererGenerator"; -import {deltaTranslations} from "./TsTransformer"; +import {extractJsRendererTranslations} from "../generator/JsRenderer"; +import {deltaTranslations} from "./Utils"; export default function (source) { const options = this.getOptions({ diff --git a/tools/trgen/WebpackPlugin.ts b/tools/trgen/webpack/Plugin.ts similarity index 51% rename from tools/trgen/WebpackPlugin.ts rename to tools/trgen/webpack/Plugin.ts index f2d64142..c4042bed 100644 --- a/tools/trgen/WebpackPlugin.ts +++ b/tools/trgen/webpack/Plugin.ts @@ -1,7 +1,9 @@ import * as ts from "typescript"; -import {createTransformer, TransformerConfig} from "./TsTransformer"; import * as webpack from "webpack"; import * as path from "path"; +import {transform} from "../generator/TypeScript"; +import {deltaTranslations} from "./Utils"; +import {TranslationEntry} from "../Definitions"; export interface TranslateableWebpackPluginConfig { assetName: string, @@ -9,33 +11,45 @@ export interface TranslateableWebpackPluginConfig { export default class TranslateableWebpackPlugin { private readonly config: TranslateableWebpackPluginConfig; - private readonly transformerConfig: TransformerConfig; + private readonly translations: TranslationEntry[]; constructor(config: TranslateableWebpackPluginConfig) { this.config = config; - this.transformerConfig = { - optimized: true, - translations: [], - verbose: true - }; + this.translations = []; } - createTypeScriptTransformer(program: ts.Program) : ts.TransformerFactory { - return createTransformer(program, this.transformerConfig); + createTypeScriptTransformer(program: ts.Program) : ts.TransformerFactory { + return ctx => sourceFile => { + const timestampBegin = Date.now(); + const result = transform({ + module: true, + useWindow: false, + /* + * Note: Even though caching might cause less method calls but if the tr method is performant developed + * it's faster than having the cache lookup. + */ + cacheTranslations: false, + optimized: true, + }, ctx, sourceFile); + const timestampEnd = Date.now(); + + deltaTranslations(this.translations, sourceFile.fileName, timestampEnd - timestampBegin, result.translations); + return result.node; + }; } createTemplateLoader() { return { - loader: path.join(__dirname, "JsRendererTranslationLoader.js"), + loader: path.join(__dirname, "Loader.js"), options: { - translations: this.transformerConfig.translations + translations: this.translations } }; } apply(compiler: webpack.Compiler) { compiler.hooks.emit.tap("TranslateableWebpackPlugin", compilation => { - const payload = JSON.stringify(this.transformerConfig.translations); + const payload = JSON.stringify(this.translations); compilation.assets[this.config.assetName] = { size() { return payload.length; }, source() { return payload; } diff --git a/tools/trgen/webpack/Utils.ts b/tools/trgen/webpack/Utils.ts new file mode 100644 index 00000000..51d24b9b --- /dev/null +++ b/tools/trgen/webpack/Utils.ts @@ -0,0 +1,23 @@ +import {TranslationEntry} from "./Definitions"; + +export const deltaTranslations = (result: TranslationEntry[], fileName: string, processSpeed: number, newTranslations: TranslationEntry[]) => { + let deletedTranslations = 0; + for(let index = 0; index < result.length; index++) { + if(result[index].filename === fileName) { + result.splice(index, 1); + index--; + deletedTranslations++; + } + } + + result.push(...newTranslations); + if(deletedTranslations === 0 && newTranslations.length === 0) { + console.log("Processed %s (%dms). No translations found.", fileName, processSpeed); + } else if(deletedTranslations === 0) { + console.log("Processed %s (%dms). Found %d translations", fileName, processSpeed, newTranslations.length); + } else if(newTranslations.length === 0) { + console.log("Processed %s (%dms). %d translations deleted.", fileName, processSpeed, deletedTranslations); + } else { + console.log("Processed %s (%dms). Old translation count: %d New translation count: %d", fileName, processSpeed, deletedTranslations, newTranslations.length); + } +} \ No newline at end of file diff --git a/webpack.config.ts b/webpack.config.ts index be9b04ef..c2f91345 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -10,7 +10,7 @@ import { DefinePlugin, Configuration, } from "webpack"; import { Plugin as SvgSpriteGenerator } from "webpack-svg-sprite-generator"; import ManifestGenerator from "./webpack/ManifestPlugin"; import HtmlWebpackInlineSourcePlugin from "./webpack/HtmlWebpackInlineSource"; -import TranslateableWebpackPlugin from "./tools/trgen/WebpackPlugin"; +import TranslateableWebpackPlugin from "./tools/trgen/webpack/Plugin"; import ZipWebpackPlugin from "zip-webpack-plugin"; import HtmlWebpackPlugin from "html-webpack-plugin"; @@ -209,7 +209,7 @@ export const config = async (env: any, target: "web" | "client"): Promise !!e), @@ -263,7 +263,7 @@ export const config = async (env: any, target: "web" | "client"): Promise ({ - before: [translateablePlugin.createTypeScriptTransformer(program)] + before: [ translateablePlugin.createTypeScriptTransformer(program) ] }), transpileOnly: isDevelopment } @@ -278,7 +278,7 @@ export const config = async (env: any, target: "web" | "client"): Promise