Reworked the trgen project structure and remove unneeded functionality

master
WolverinDEV 2021-03-22 17:00:11 +01:00
parent 9d8c3600e1
commit 084dd4cb63
16 changed files with 65 additions and 424 deletions

View File

@ -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

View File

@ -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<ts.SourceFile> => {
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;
};
}

View File

@ -1,6 +0,0 @@
#!/bin/bash
BASEDIR=$(dirname "$0")
FILE="${BASEDIR}/../compiler.ts"
npm run dtsgen -- $@

View File

@ -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 = <T extends ts.Node>(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);

View File

@ -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 {

View File

@ -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;

View File

@ -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 = (<ts.SourceFile>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));
}

View File

@ -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 : ""};

View File

@ -1,66 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script class="jsrender-template" id="tmpl_connect" type="text/html">
<div style="margin-top: 5px;">
<div style="display: flex; flex-direction: row; width: 100%; justify-content: space-between">
<div style="width: 68%; margin-bottom: 5px">
<div>{{tr "Remote address and port:" /}}</div>
<input type="text" style="width: 100%" class="connect_address" value="unknown">
</div>
<div style="width: 20%">
<div>{{tr "Server password:" /}}</div>
<form name="server_password_form" onsubmit="return false;">
<input type="password" id="connect_server_password_{{rnd '0~13377331'/}}" autocomplete="off" style="width: 100%" class="connect_password">
</form>
</div>
</div>
<div>
<div>{{tr "Nickname:" /}}</div>
<input type="text" style="width: 100%" class="connect_nickname" value="">
</div>
<hr>
<div class="group_box">
<div style="display: flex; justify-content: space-between;">
<div style="text-align: right;">{{tr "Identity Settings" /}}</div>
<select class="identity_select">
<option name="identity_type" value="TEAFORO">{{tr "Forum Account" /}}</option>
<option name="identity_type" value="TEAMSPEAK">{{tr "TeamSpeak" /}}</option>
<option name="identity_type" value="NICKNAME">{{tr "Nickname (Debug purposes only!)" /}}</option> <!-- Only available on localhost for debug -->
</select>
</div>
<hr>
<div class="identity_config_TEAMSPEAK identity_config">
{{tr "Please enter your exported TS3 Identity string bellow or select your exported Identity"/}}<br>
<div style="width: 100%; display: flex; justify-content: stretch; flex-direction: row">
<input placeholder="Identity string" style="min-width: 60%; flex-shrink: 1; flex-grow: 2; margin: 5px;" class="identity_string">
<div style="max-width: 200px; flex-grow: 1; flex-shrink: 4; margin: 5px"><input style="display: flex; width: 100%;" class="identity_file" type="file"></div>
</div>
</div>
<div class="identity_config_TEAFORO identity_config">
<div class="connected">
{{tr "You're using your forum account as verification"/}}
</div>
<div class="disconnected">
<!-- TODO tr -->
You cant use your TeaSpeak forum account.<br>
You're not connected!<br>
Click {{if !client}}<a href="{{:forum_path}}login.php">here</a>{{else}}<a href="#" class="native-teaforo-login">here</a>{{/if}} to login via the TeaSpeak forum.
</div>
</div>
<div class="identity_config_NICKNAME identity_config">
{{tr "This is just for debug and uses the name as unique identifier" /}}
{{tr "This is just for debug and uses the name as unique identifier" + "Hello World :D" /}}
{{tr "This is just for debug and uses the name as unique identifier" + `Hello World :D 2` /}}
</div>
<div style="background-color: red; border-radius: 1px; display: none" class="error_message"></div>
</div> <!-- <a href="<?php echo authPath() . "login.php"; ?>">Login</a> via the TeaSpeak forum. -->
</div>
</script>
</body>
</html>

View File

@ -1,8 +0,0 @@
import {Translatable, VariadicTranslatable} from "../../../shared/js/ui/react-elements/i18n";
import * as React from "react";
function test() {
const element_0 = <Translatable>Hello World</Translatable>;
const element_1 = <Translatable>{"Hello World"}</Translatable>;
const element_2 = <VariadicTranslatable text={"XXX"}><>XXX</></VariadicTranslatable>;
}

View File

@ -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"
]
}

View File

@ -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({

View File

@ -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<ts.Node> {
return createTransformer(program, this.transformerConfig);
createTypeScriptTransformer(program: ts.Program) : ts.TransformerFactory<ts.SourceFile> {
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; }

View File

@ -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);
}
}

View File

@ -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<Config
env.package ? new ZipWebpackPlugin({
path: path.join(__dirname, "dist-package"),
filename: `TeaWeb-${isDevelopment ? "development" : "release"}-${localBuildInfo.gitVersion}.zip`,
filename: `${target === "web" ? "TeaWeb" : "TeaClient"}-${isDevelopment ? "development" : "release"}-${localBuildInfo.gitVersion}.zip`,
}) : undefined
].filter(e => !!e),
@ -263,7 +263,7 @@ export const config = async (env: any, target: "web" | "client"): Promise<Config
context: __dirname,
colors: true,
getCustomTransformers: program => ({
before: [translateablePlugin.createTypeScriptTransformer(program)]
before: [ translateablePlugin.createTypeScriptTransformer(program) ]
}),
transpileOnly: isDevelopment
}
@ -278,7 +278,7 @@ export const config = async (env: any, target: "web" | "client"): Promise<Config
},
{
test: /\.html$/i,
use: [translateablePlugin.createTemplateLoader()],
use: [ translateablePlugin.createTemplateLoader() ],
type: "asset/source",
},
{