Added tr caching to webpack

canary
WolverinDEV 2020-03-31 15:19:53 +02:00
parent 2d5006bf64
commit d5ed46150e
18 changed files with 160 additions and 137 deletions

6
package-lock.json generated
View File

@ -9240,6 +9240,12 @@
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
"dev": true
},
"wabt": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/wabt/-/wabt-1.0.13.tgz",
"integrity": "sha512-nkWPyUjYt+SqPox2mjJF5jrpWv30awdXKdG0OmryzfhtahHBrPz/BGSbakPgcJU2SFjC1s7Mb8MadRhQ6lmqUg==",
"dev": true
},
"watchpack": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",

View File

@ -26,8 +26,8 @@
"author": "TeaSpeak (WolverinDEV)",
"license": "ISC",
"devDependencies": {
"@types/fs-extra": "^8.0.1",
"@types/emscripten": "^1.38.0",
"@types/fs-extra": "^8.0.1",
"@types/jquery": "^3.3.34",
"@types/lodash": "^4.14.149",
"@types/moment": "^2.13.0",
@ -36,12 +36,15 @@
"@types/sha256": "^0.2.0",
"@types/websocket": "0.0.40",
"chunk-manifest-webpack-plugin": "^1.1.2",
"circular-dependency-plugin": "^5.2.0",
"clean-css": "^4.2.1",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.4.2",
"csso-cli": "^2.0.2",
"exports-loader": "^0.7.0",
"fs-extra": "latest",
"gulp": "^4.0.2",
"html-loader": "^1.0.0",
"html-webpack-plugin": "^4.0.3",
"mime-types": "^2.1.24",
"mini-css-extract-plugin": "^0.9.0",
@ -49,20 +52,19 @@
"node-sass": "^4.13.1",
"raw-loader": "^4.0.0",
"sass": "1.22.10",
"sass-loader": "^8.0.2",
"sha256": "^0.2.0",
"style-loader": "^1.1.3",
"terser": "^4.2.1",
"ts-loader": "^6.2.2",
"ttypescript": "^1.5.10",
"typescript": "3.6.5",
"wabt": "^1.0.13",
"webpack": "^4.42.1",
"webpack-bundle-analyzer": "^3.6.1",
"webpack-cli": "^3.3.11",
"worker-plugin": "^4.0.2",
"circular-dependency-plugin": "^5.2.0",
"css-loader": "^3.4.2",
"html-loader": "^1.0.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.1.3"
"terser-webpack-plugin": "latest"
},
"repository": {
"type": "git",

View File

@ -477,7 +477,7 @@ export class ConnectionHandler {
this._certificate_modal = createErrorModal(
tr("Could not connect"),
formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept())
formatMessage(/* @tr-ignore */ tr(error_message_format), this.generate_ssl_certificate_accept())
);
this._certificate_modal.close_listener.push(() => this._certificate_modal = undefined);
this._certificate_modal.open();

View File

@ -70,7 +70,7 @@ export function tr(message: string, key?: string) {
}
export function tra(message: string, ...args: any[]) {
message = tr(message);
message = /* @tr-ignore */ tr(message);
return formatMessage(message, ...args);
}

View File

@ -113,7 +113,7 @@ function setup_jsrender() : boolean {
});
js_render.views.tags("tr", (...args) => {
return tr(args[0]);
return /* @tr-ignore */ tr(args[0]);
});
$(".jsrender-template").each((idx, _entry) => {
@ -481,7 +481,7 @@ const task_connect_handler: loader.Task = {
"You could now close this page.";
createInfoModal(
tr("Connecting successfully within other instance"),
formatMessage(tr(message), connect_data.address),
formatMessage(/* @tr-ignore */ tr(message), connect_data.address),
{
closeable: false,
footer: undefined
@ -548,7 +548,7 @@ const task_certificate_callback: loader.Task = {
"This page will close in {0} seconds.";
createInfoModal(
tr("Certificate acccepted successfully"),
formatMessage(tr(message), seconds_tag),
formatMessage(/* @tr-ignore */ tr(message), seconds_tag),
{
closeable: false,
footer: undefined

View File

@ -10,7 +10,6 @@ import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler";
console.error(AbstractHandshakeIdentityHandler);
class NameHandshakeHandler extends AbstractHandshakeIdentityHandler {
readonly identity: NameIdentity;
handler: HandshakeCommandHandler<NameHandshakeHandler>;

View File

@ -163,7 +163,7 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
container.addClass("help-shown");
const text = tr(
const text = tr( /* @tr-ignore */
"After you've successfully set upped your microphone,\n" +
"lets setup some profiles and identities!\n" +
"\n" +
@ -202,7 +202,7 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
update_position();
container_help_text.off('resize').on('resize', update_position);
const text = tr(
const text = tr( /* @tr-ignore */
"You could have as many connect profiles as you want.\n" +
"All created profiles will be listed here.\n" +
"\n" +
@ -234,7 +234,7 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
bottom: "1em"
});
};
set_help_text(tr(
set_help_text(tr( /* @tr-ignore */
"In the upper left, you'll find the profile settings for the selected profile.\n" +
"You could give each profile an individual name. You could also specify the default connect nickname here.\n" +
"\n" +
@ -267,7 +267,7 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
bottom: "1em"
});
};
set_help_text(tr(
set_help_text(tr( /* @tr-ignore */
"When selecting an identify type, some corresponding will pop up in the highlighted area.\n" +
"\n" +
"But don't worry, we've already generated\n" +
@ -321,7 +321,7 @@ function initializeStepMicrophone(tag: JQuery, event_registry: Registry<emodal.n
is_first_show = false;
container.addClass("help-shown");
const text = tr(
const text = tr( /* @tr-ignore */
"Firstly we need to setup a microphone.\n" +
"Let me guide you thru the basic UI elements.\n" +
"\n" +
@ -356,7 +356,7 @@ function initializeStepMicrophone(tag: JQuery, event_registry: Registry<emodal.n
update_position();
container_help_text.off('resize').on('resize', update_position);
const text = tr(
const text = tr( /* @tr-ignore */
"All your available microphones are listed within this box.\n" +
"\n" +
"The currently selected microphone\n" +
@ -393,7 +393,7 @@ function initializeStepMicrophone(tag: JQuery, event_registry: Registry<emodal.n
$.spawn("ol").append(
$.spawn("li").addClass("vad-type").append(
$.spawn("a").addClass("title").text(tr("Push to Talk")),
$.spawn("a").addClass("description").html(tr(
$.spawn("a").addClass("description").html(tr( /* @tr-ignore */
"To transmit audio data you'll have to<br>" +
"press a key. The key could be selected " +
"via the button right to the radio button."
@ -401,7 +401,7 @@ function initializeStepMicrophone(tag: JQuery, event_registry: Registry<emodal.n
),
$.spawn("li").addClass("vad-type").append(
$.spawn("a").addClass("title").text(tr("Voice activity detection")),
$.spawn("a").addClass("description").html(tr(
$.spawn("a").addClass("description").html(tr( /* @tr-ignore */
"In this mode, TeaSpeak will continuously analyze your microphone input. " +
"If the audio level is grater than a certain threshold, " +
"the audio will be transmitted. " +
@ -410,7 +410,7 @@ function initializeStepMicrophone(tag: JQuery, event_registry: Registry<emodal.n
),
$.spawn("li").addClass("vad-type").append(
$.spawn("a").addClass("title").html(tr("Always active")),
$.spawn("a").addClass("description").text(tr(
$.spawn("a").addClass("description").text(tr( /* @tr-ignore */
"Continuously transmit any audio data.\n"
))
)

View File

@ -23,6 +23,7 @@ import {
senseless_client_permissions, senseless_server_group_permissions
} from "tc-shared/ui/modal/permission/SenselessPermissions";
import {AbstractPermissionEditor, PermissionEditorMode} from "tc-shared/ui/modal/permission/AbstractPermissionEditor";
import {tra} from "tc-shared/i18n/localize";
declare global {
interface JQuery<TElement = HTMLElement> {
@ -1358,7 +1359,7 @@ function apply_server_groups(connection: ConnectionHandler, editor: AbstractPerm
console.log(tr("Failed to add client %o to server group %o: %o"), dbid, current_group.id, error);
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Failed to add client"), tr("Failed to add client to server group\n" + error)).open();
createErrorModal(tr("Failed to add client"), tra("Failed to add client to server group{:br:}", error)).open();
});
}).open();
});

View File

@ -49,7 +49,6 @@ function compile(fileNames: string[], options: ts.CompilerOptions): void {
}
const config = ts.parseCommandLine(process.argv.slice(2), file => readFileSync(file).toString());
console.dir(config);
if(config.errors && config.errors.length > 0) {
for(const error of config.errors)
console.log(error.messageText);

View File

@ -1,4 +1,3 @@
export interface TranslationEntry {
filename: string;
line: number;

View File

@ -8,8 +8,6 @@ import {isArray} from "util";
import * as mkdirp from "mkdirp";
import {TranslationEntry} from "./generator";
console.log("TR GEN!");
/*
const files = ["/home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/build/trgen/test/test_01.ts"];
files.forEach(file => {

View File

@ -11,10 +11,14 @@ export function generate(file: ts.SourceFile, config: Configuration) : Translati
return result;
}
function report(node: ts.Node, message: string) {
const source_location = (node: ts.Node) => {
const sf = node.getSourceFile();
let { line, character } = sf ? sf.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
console.log(`${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1}): ${message}`);
return `${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1})`;
};
function report(node: ts.Node, message: string) {
console.log(`${source_location(node)}: ${message}`);
}
function _generate(config: Configuration, node: ts.Node, result: TranslationEntry[]) {
@ -27,7 +31,6 @@ function _generate(config: Configuration, node: ts.Node, result: TranslationEntr
if(call_name != "tr") break call_analize;
console.dir(call_name);
console.log("Parameters: %o", call.arguments.length);
if(call.arguments.length > 1) {
report(call, "Invalid argument count");
@ -159,7 +162,7 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression
return [...nodes, ts.createLabel(unique_check_label_name, ts.createBlock(blocked_nodes))];
}
export function transform(config: Configuration, context: ts.TransformationContext, node: ts.SourceFile) : TransformResult {
export function transform(config: Configuration, context: ts.TransformationContext, source_file: ts.SourceFile) : TransformResult {
const cache: VolatileTransformConfig = {} as any;
cache.translations = [];
@ -215,56 +218,59 @@ export function transform(config: Configuration, context: ts.TransformationConte
function visit(node: ts.Node): ts.Node {
node = ts.visitEachChild(node, visit, context);
return replace_processor(config, cache, node);
return replace_processor(config, cache, node, source_file);
}
node = ts.visitNode(node, visit);
extra_nodes.push(...create_unique_check(node, cache.nodes.translation_map_init, generated_names));
source_file = ts.visitNode(source_file, visit);
extra_nodes.push(...create_unique_check(source_file, cache.nodes.translation_map_init, generated_names));
node = ts.updateSourceFileNode(node, [...(extra_nodes as any[]), ...node.statements], node.isDeclarationFile, node.referencedFiles, node.typeReferenceDirectives, node.hasNoDefaultLib, node.referencedFiles);
source_file = ts.updateSourceFileNode(source_file, [...(extra_nodes as any[]), ...source_file.statements], source_file.isDeclarationFile, source_file.referencedFiles, source_file.typeReferenceDirectives, source_file.hasNoDefaultLib, source_file.referencedFiles);
const result: TransformResult = {} as any;
result.node = node;
result.node = source_file;
result.translations = cache.translations;
return result;
}
export function replace_processor(config: Configuration, cache: VolatileTransformConfig, node: ts.Node) : ts.Node {
export function replace_processor(config: Configuration, cache: VolatileTransformConfig, node: ts.Node, source_file: ts.SourceFile) : ts.Node {
if(config.verbose)
console.log("Process %s", SyntaxKind[node.kind]);
if(ts.isCallExpression(node)) {
const call = <ts.CallExpression>node;
const call_name = call.expression["escapedText"] as string;
if(call_name != "tr") return node;
if(!node.getSourceFile()) return node;
if(config.verbose) {
console.dir(call_name);
console.log("Parameters: %o", call.arguments.length);
}
if(call.arguments.length > 1) {
report(call, "Invalid argument count");
if(call.arguments.length > 1)
throw new Error(source_location(call) + ": tr(...) has been called with an invalid arguments (" + (call.arguments.length === 0 ? "too few" : "too many") + ")");
const fullText = call.getFullText(source_file);
if(fullText && fullText.indexOf("@tr-ignore") !== -1)
return node;
}
const object = <ts.StringLiteral>call.arguments[0];
if(object.kind != SyntaxKind.StringLiteral) {
report(call, "Invalid argument: " + SyntaxKind[object.kind]);
return node;
if(call.getSourceFile())
throw new Error(source_location(call) + ": Ignoring tr call because given argument isn't of type string literal. (" + SyntaxKind[object.kind] + ")");
report(call, "Ignoring tr call because given argument isn't of type string literal. (" + SyntaxKind[object.kind] + ")");
}
if(config.verbose)
console.log("Message: %o", object.text || object.getText());
console.log("Message: %o", object.text || object.getText(source_file));
const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText()));
const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText(source_file)));
const variable_init = ts.createPropertyAccess(cache.nodes.translation_map_init, variable_name);
const variable = ts.createPropertyAccess(cache.nodes.translation_map, variable_name);
const new_variable = ts.createAssignment(variable, call);
const source_file = node.getSourceFile();
let { line, character } = source_file ? source_file.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
let { line, character } = source_file.getLineAndCharacterOfPosition(node.getStart());
cache.translations.push({
message: object.text || object.getText(),
message: object.text || object.getText(source_file),
line: line,
character: character,
filename: (source_file || {fileName: "unknown"}).fileName

View File

@ -0,0 +1,67 @@
import * as ts from "typescript";
import * as ts_generator from "./ts_generator";
import * as path from "path";
import * as mkdirp from "mkdirp";
import {writeFileSync} from "fs";
import {TranslationEntry} from "./generator";
export interface Config {
target_file?: string;
verbose?: boolean;
}
let process_config: Config;
export default function(program: ts.Program, config?: Config) : (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
process_config = config as any || {};
const base_path = path.dirname(program.getCompilerOptions().project || program.getCurrentDirectory());
if(process_config.verbose) {
console.log("TRGen transformer called");
console.log("Base path: %s", base_path);
}
process.on('exit', function () {
if(!process_config.target_file) return;
const target = path.isAbsolute(process_config.target_file) ? process_config.target_file : path.join(base_path, process_config.target_file);
if(process_config.target_file) {
if(process_config.verbose)
console.log("Writing translation file to " + target);
mkdirp.sync(path.dirname(target));
writeFileSync(target, JSON.stringify(translations, null, 2));
}
});
return ctx => transformer(ctx) as any;
}
const translations: TranslationEntry[] = [];
const transformer = (context: ts.TransformationContext) =>
(rootNode: ts.Node) => {
const handler = (rootNode: ts.Node) => {
if(rootNode.kind == ts.SyntaxKind.Bundle) {
const bundle = rootNode as ts.Bundle;
const result = [];
for(const file of bundle.sourceFiles)
result.push(handler(file));
return ts.updateBundle(bundle, result as any, bundle.prepends as any);
} else if(rootNode.kind == ts.SyntaxKind.SourceFile) {
const file = rootNode as ts.SourceFile;
console.log("Processing " + file.fileName);
const result = ts_generator.transform({
use_window: false,
replace_cache: true
}, context, file);
translations.push(...result.translations);
return result.node;
} else {
console.warn("Invalid transform input: %s", ts.SyntaxKind[rootNode.kind]);
}
};
return handler(rootNode);
};

View File

@ -1,67 +1,9 @@
import * as ts from "typescript";
import * as ts_generator from "./ts_generator";
import * as path from "path";
import * as mkdirp from "mkdirp";
import transform, {Config} from "./ts_transformer";
import {PluginConfig} from "ttypescript/lib/PluginCreator";
import {writeFileSync} from "fs";
import {TranslationEntry} from "./generator";
import * as ts from "typescript";
interface Config {
target_file?: string;
verbose?: boolean;
}
//(program: ts.Program, config?: PluginConfig) => ts.TransformerFactory
let process_config: Config;
export default function(program: ts.Program, config?: PluginConfig) : (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
process_config = config as any || {};
const process_config: Config = config as any || {};
const base_path = path.dirname(program.getCompilerOptions().project || program.getCurrentDirectory());
if(process_config.verbose) {
console.log("TRGen transformer called");
console.log("Base path: %s", base_path);
}
process.on('exit', function () {
const target = path.isAbsolute(process_config.target_file) ? process_config.target_file : path.join(base_path, process_config.target_file);
if(process_config.target_file) {
if(process_config.verbose)
console.log("Writing translation file to " + target);
mkdirp.sync(path.dirname(target));
writeFileSync(target, JSON.stringify(translations, null, 2));
}
});
return ctx => transformer(ctx) as any;
return transform(program, process_config);
}
const translations: TranslationEntry[] = [];
const transformer = (context: ts.TransformationContext) =>
(rootNode: ts.Node) => {
const handler = (rootNode: ts.Node) => {
if(rootNode.kind == ts.SyntaxKind.Bundle) {
const bundle = rootNode as ts.Bundle;
const result = [];
for(const file of bundle.sourceFiles)
result.push(handler(file));
return ts.updateBundle(bundle, result as any, bundle.prepends as any);
} else if(rootNode.kind == ts.SyntaxKind.SourceFile) {
const file = rootNode as ts.SourceFile;
console.log("Processing " + file.fileName);
const result = ts_generator.transform({
use_window: false,
replace_cache: true
}, context, file);
translations.push(...result.translations);
return result.node;
} else {
console.warn("Invalid transform input: %s", ts.SyntaxKind[rootNode.kind]);
}
};
return handler(rootNode);
};

View File

@ -1,2 +0,0 @@
**/*.js
**/*.js.map

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Translation Manager</title>
</head>
<body>
<div>This needs some improvements</div>
</body>
</html>

View File

@ -1,19 +1,23 @@
import * as ts from "typescript";
import trtransformer, {Config} from "./tools/trgen/ts_transformer";
const path = require('path');
const webpack = require("webpack");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ManifestGenerator = require("./webpack/ManifestPlugin");
const WorkerPlugin = require('worker-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const isDevelopment = process.env.NODE_ENV === 'development';
let isDevelopment = process.env.NODE_ENV === 'development';
isDevelopment = true;
module.exports = {
entry: {
//"shared-app": "./shared/js/main.ts"
"shared-app": "./web/js/index.ts"
},
devtool: 'inline-source-map',
mode: "development",
devtool: isDevelopment ? "inline-source-map" : undefined,
mode: isDevelopment ? "development" : "production",
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
@ -34,8 +38,8 @@ module.exports = {
})
*/
new webpack.optimize.AggressiveSplittingPlugin({
minSize: 1024 * 128,
maxSize: 1024 * 1024
minSize: 1024 * 8,
maxSize: 1024 * 128
})
],
module: {
@ -43,8 +47,7 @@ module.exports = {
{
test: /\.s[ac]ss$/,
loader: [
//isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
'style-loader',
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
@ -68,8 +71,21 @@ module.exports = {
{
loader: 'ts-loader',
options: {
transpileOnly: true
transpileOnly: true,
getCustomTransformers: (prog: ts.Program) => {
return {
before: [trtransformer(prog, {})]
};
}
}
/*
{
"transform": "../../tools/trgen/ttsc_transformer.js",
"type": "program",
"target_file": "../generated/messages_script.json",
"verbose": true
}
*/
}
]
},
@ -100,6 +116,8 @@ module.exports = {
publicPath: "js/"
},
optimization: {
splitChunks: { }
splitChunks: { },
minimize: !isDevelopment,
minimizer: [new TerserPlugin()]
}
};

View File

@ -2,7 +2,7 @@ import * as webpack from "webpack";
import {RawSourceMap} from "source-map";
import LoaderContext = webpack.loader.LoaderContext;
const wabt = require("wabt");
const wabt = require("wabt")();
const filename = "module.wast";
@ -10,9 +10,7 @@ export default function loader(this: LoaderContext, source: string | Buffer, sou
this.cacheable();
const module = wabt.parseWat(filename, source);
const { buffer } = module.toBinary({ write_debug_names: true, relocatable: true, canonicalize_lebs: true, log: true });
const { buffer } = module.toBinary({ write_debug_names: false });
this.emitFile("test.wasm", buffer, null);
const result = `module.exports = new Uint8Array([${buffer.join(",")}]);`;
this.callback(null, result);
this.callback(null, `module.exports = new Uint8Array([${buffer.join(",")}]);`);
}