Improved the initial page load

This commit is contained in:
WolverinDEV 2020-04-09 23:09:59 +02:00
parent 479168c26e
commit 702f0bae43
16 changed files with 343 additions and 158 deletions

View file

@ -4,7 +4,9 @@
- Saving last away state and message - Saving last away state and message
- Saving last query show state - Saving last query show state
- Removing the hostbutton when we're disconnected from the server - Removing the hostbutton when we're disconnected from the server
- Improved main pages loader speed as well inlining the initial js/css sources
This ensures that the error & loading animation loads properly regardless of any errors
* **04.03.20** * **04.03.20**
- Implemented the new music bot playlist song list - Implemented the new music bot playlist song list
- Implemented the missing server log message builders - Implemented the missing server log message builders

18
file.ts
View file

@ -40,12 +40,28 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
}, },
{ /* javascript files as manifest.json */ { /* javascript files as manifest.json */
"type": "js", "type": "js",
"search-pattern": /.*$/, "search-pattern": /.*\.(js|json)$/,
"build-target": "dev|rel", "build-target": "dev|rel",
"path": "js/", "path": "js/",
"local-path": "./dist/" "local-path": "./dist/"
}, },
{ /* javascript files as manifest.json */
"type": "html",
"search-pattern": /.*\.html$/,
"build-target": "dev|rel",
"path": "./",
"local-path": "./dist/"
},
{ /* Loader css file (only required in dev mode. In release it gets inlined) */
"type": "css",
"search-pattern": /.*\.css$/,
"build-target": "dev",
"path": "css/",
"local-path": "./loader/css/"
},
{ /* shared developer single css files */ { /* shared developer single css files */
"type": "css", "type": "css",
"search-pattern": /.*\.css$/, "search-pattern": /.*\.css$/,

View file

@ -1,6 +1,5 @@
import * as loader from "./targets/app"; import * as loader from "./targets/app";
import * as loader_base from "./loader/loader"; import * as loader_base from "./loader/loader";
window["loader"] = loader_base; window["loader"] = loader_base;
/* let the loader register himself at the window first */ /* let the loader register himself at the window first */
setTimeout(loader.run, 0); setTimeout(loader.run, 0);

View file

@ -309,85 +309,78 @@ export const templates = template_loader;
/* Hello World message */ /* Hello World message */
{ {
const hello_world = () => { const clog = console.log;
const clog = console.log; const print_security = () => {
const print_security = () => {
{
const css = [
"display: block",
"text-align: center",
"font-size: 42px",
"font-weight: bold",
"-webkit-text-stroke: 2px black",
"color: red"
].join(";");
clog("%c ", "font-size: 100px;");
clog("%cSecurity warning:", css);
}
{
const css = [
"display: block",
"text-align: center",
"font-size: 18px",
"font-weight: bold"
].join(";");
clog("%cPasting anything in here could give attackers access to your data.", css);
clog("%cUnless you understand exactly what you are doing, close this window and stay safe.", css);
clog("%c ", "font-size: 100px;");
}
};
/* print the hello world */
{ {
const css = [ const css = [
"display: block", "display: block",
"text-align: center", "text-align: center",
"font-size: 72px", "font-size: 42px",
"font-weight: bold", "font-weight: bold",
"-webkit-text-stroke: 2px black", "-webkit-text-stroke: 2px black",
"color: #18BC9C" "color: red"
].join(";"); ].join(";");
clog("%cHey, hold on!", css); clog("%c ", "font-size: 100px;");
clog("%cSecurity warning:", css);
} }
{ {
const css = [ const css = [
"display: block", "display: block",
"text-align: center", "text-align: center",
"font-size: 26px", "font-size: 18px",
"font-weight: bold" "font-weight: bold"
].join(";"); ].join(";");
const css_2 = [ clog("%cPasting anything in here could give attackers access to your data.", css);
"display: block", clog("%cUnless you understand exactly what you are doing, close this window and stay safe.", css);
"text-align: center", clog("%c ", "font-size: 100px;");
"font-size: 26px",
"font-weight: bold",
"color: blue"
].join(";");
const display_detect = /./;
display_detect.toString = function() { print_security(); return ""; };
clog("%cLovely to see you using and debugging the TeaSpeak-Web client.", css);
clog("%cIf you have some good ideas or already done some incredible changes,", css);
clog("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2);
clog("%c ", display_detect);
} }
}; };
try { /* lets try to print it as VM code :)*/ /* print the hello world */
let hello_world_code = hello_world.toString(); {
hello_world_code = hello_world_code.substr(hello_world_code.indexOf('() => {') + 8); const css = [
hello_world_code = hello_world_code.substring(0, hello_world_code.lastIndexOf("}")); "display: block",
"text-align: center",
//Look aheads are not possible with firefox "font-size: 72px",
//hello_world_code = hello_world_code.replace(/(?<!const|let)(?<=^([^"'/]|"[^"]*"|'[^']*'|`[^`]*`|\/[^/]*\/)*) /gm, ""); /* replace all spaces */ "font-weight: bold",
hello_world_code = hello_world_code.replace(/[\n\r]/g, ""); /* replace as new lines */ "-webkit-text-stroke: 2px black",
"color: #18BC9C"
eval(hello_world_code); ].join(";");
} catch(e) { clog("%cHey, hold on!", css);
console.error(e);
hello_world();
} }
} {
const css = [
"display: block",
"text-align: center",
"font-size: 26px",
"font-weight: bold"
].join(";");
const css_2 = [
"display: block",
"text-align: center",
"font-size: 26px",
"font-weight: bold",
"color: blue"
].join(";");
const display_detect = /./;
display_detect.toString = function() { print_security(); return ""; };
clog("%cLovely to see you using and debugging the TeaSpeak-Web client.", css);
clog("%cIf you have some good ideas or already done some incredible changes,", css);
clog("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2);
clog("%c ", display_detect);
}
}
/* Loading error image (async) */
const init_error_image = () => {
const node = document.getElementById("load-error-image");
const image = document.createElement("img");
image.src = node.getAttribute("x-src");
image.style.height = "12em";
node.replaceWith(image);
};
setTimeout(init_error_image, 100);

111
package-lock.json generated
View file

@ -152,6 +152,15 @@
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
"dev": true "dev": true
}, },
"@types/clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.1.tgz",
"integrity": "sha512-A1HQhQ0hkvqqByJMgg+Wiv9p9XdoYEzuwm11SVo1mX2/4PSdhjcrUlilJQoqLscIheC51t1D5g+EFWCXZ2VTQQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/dompurify": { "@types/dompurify": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.0.1.tgz",
@ -161,6 +170,12 @@
"@types/trusted-types": "*" "@types/trusted-types": "*"
} }
}, },
"@types/ejs": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.0.2.tgz",
"integrity": "sha512-+nriFZYDz+C+6SWzJp0jHrGvyzL3Sg63u1vRlH1AfViyaT4oTod+MtfZ0YMNYXzO7ybFkdx4ZaBwBtLwdYkReQ==",
"dev": true
},
"@types/emscripten": { "@types/emscripten": {
"version": "1.39.2", "version": "1.39.2",
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.2.tgz", "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.2.tgz",
@ -201,6 +216,17 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/html-minifier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@types/html-minifier/-/html-minifier-3.5.3.tgz",
"integrity": "sha512-j1P/4PcWVVCPEy5lofcHnQ6BtXz9tHGiFPWzqm7TtGuWZEfCHEP446HlkSNc9fQgNJaJZ6ewPtp2aaFla/Uerg==",
"dev": true,
"requires": {
"@types/clean-css": "*",
"@types/relateurl": "*",
"@types/uglify-js": "*"
}
},
"@types/html-minifier-terser": { "@types/html-minifier-terser": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz",
@ -274,6 +300,12 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/relateurl": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz",
"integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y=",
"dev": true
},
"@types/sha256": { "@types/sha256": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@types/sha256/-/sha256-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/sha256/-/sha256-0.2.0.tgz",
@ -2385,9 +2417,9 @@
"dev": true "dev": true
}, },
"ejs": { "ejs": {
"version": "2.7.4", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.2.tgz",
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", "integrity": "sha512-IncmUpn1yN84hy2shb0POJ80FWrfGNY0cxO9f4v+/sG7qcBvAtVWUA1IdzY/8EYUmOVhoKJVdJjNd3AZcnxOjA==",
"dev": true "dev": true
}, },
"elliptic": { "elliptic": {
@ -4940,6 +4972,57 @@
} }
} }
}, },
"html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"dev": true,
"requires": {
"camel-case": "^3.0.0",
"clean-css": "^4.2.1",
"commander": "^2.19.0",
"he": "^1.2.0",
"param-case": "^2.1.1",
"relateurl": "^0.2.7",
"uglify-js": "^3.5.1"
},
"dependencies": {
"camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
"dev": true,
"requires": {
"no-case": "^2.2.0",
"upper-case": "^1.1.1"
}
},
"lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
"dev": true
},
"no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
"dev": true,
"requires": {
"lower-case": "^1.1.1"
}
},
"param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
"dev": true,
"requires": {
"no-case": "^2.2.0"
}
}
}
},
"html-minifier-terser": { "html-minifier-terser": {
"version": "5.0.5", "version": "5.0.5",
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.0.5.tgz", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.0.5.tgz",
@ -9676,6 +9759,16 @@
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
"dev": true "dev": true
}, },
"uglify-js": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz",
"integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==",
"dev": true,
"requires": {
"commander": "~2.20.3",
"source-map": "~0.6.1"
}
},
"unc-path-regex": { "unc-path-regex": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@ -9815,6 +9908,12 @@
"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
"dev": true "dev": true
}, },
"upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
"dev": true
},
"uri-js": { "uri-js": {
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@ -10882,6 +10981,12 @@
"supports-color": "^5.3.0" "supports-color": "^5.3.0"
} }
}, },
"ejs": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz",
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==",
"dev": true
},
"supports-color": { "supports-color": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View file

@ -5,21 +5,17 @@
"main": "main.js", "main": "main.js",
"directories": {}, "directories": {},
"scripts": { "scripts": {
"compile-sass": "sass --update shared/css/:shared/css/ web/css/:web/css/ client/css/:client/css/ vendor/:vendor/", "compile-sass": "sass --update shared/css/:shared/css/ loader/css/:loader/css/ web/css/:web/css/ client/css/:client/css/ vendor/:vendor/",
"compile-project-base": "tsc -p tsbaseconfig.json", "compile-project-base": "tsc -p tsbaseconfig.json",
"dtsgen": "node tools/dtsgen/index.js", "dtsgen": "node tools/dtsgen/index.js",
"trgen": "node tools/trgen/index.js", "trgen": "node tools/trgen/index.js",
"sass": "sass", "sass": "sass",
"tsc": "tsc", "tsc": "tsc",
"start": "npm run compile-project-base && node file.js ndevelop", "start": "npm run compile-project-base && node file.js ndevelop",
"build-web": "webpack --config webpack-web.config.js", "build-web": "webpack --config webpack-web.config.js",
"watch-web": "webpack --watch --config webpack-web.config.js", "watch-web": "webpack --watch --config webpack-web.config.js",
"build-client": "webpack --config webpack-client.config.js", "build-client": "webpack --config webpack-client.config.js",
"watch-client": "webpack --watch --config webpack-client.config.js", "watch-client": "webpack --watch --config webpack-client.config.js",
"generate-i18n-gtranslate": "node shared/generate_i18n_gtranslate.js" "generate-i18n-gtranslate": "node shared/generate_i18n_gtranslate.js"
}, },
"author": "TeaSpeak (WolverinDEV)", "author": "TeaSpeak (WolverinDEV)",
@ -27,8 +23,10 @@
"devDependencies": { "devDependencies": {
"@google-cloud/translate": "^5.3.0", "@google-cloud/translate": "^5.3.0",
"@types/dompurify": "^2.0.1", "@types/dompurify": "^2.0.1",
"@types/ejs": "^3.0.2",
"@types/emscripten": "^1.38.0", "@types/emscripten": "^1.38.0",
"@types/fs-extra": "^8.0.1", "@types/fs-extra": "^8.0.1",
"@types/html-minifier": "^3.5.3",
"@types/jquery": "^3.3.34", "@types/jquery": "^3.3.34",
"@types/lodash": "^4.14.149", "@types/lodash": "^4.14.149",
"@types/moment": "^2.13.0", "@types/moment": "^2.13.0",
@ -42,10 +40,12 @@
"clean-webpack-plugin": "^3.0.0", "clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.4.2", "css-loader": "^3.4.2",
"csso-cli": "^2.0.2", "csso-cli": "^2.0.2",
"ejs": "^3.0.2",
"exports-loader": "^0.7.0", "exports-loader": "^0.7.0",
"fs-extra": "latest", "fs-extra": "latest",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"html-loader": "^1.0.0", "html-loader": "^1.0.0",
"html-minifier": "^4.0.0",
"html-webpack-plugin": "^4.0.3", "html-webpack-plugin": "^4.0.3",
"mime-types": "^2.1.24", "mime-types": "^2.1.24",
"mini-css-extract-plugin": "^0.9.0", "mini-css-extract-plugin": "^0.9.0",

View file

@ -1,5 +1,5 @@
@import "./mixin"; @import "mixin";
@import "./properties"; @import "properties";
//$color_client_normal: #bebebe; //$color_client_normal: #bebebe;
$color_client_normal: #cccccc; $color_client_normal: #cccccc;

View file

@ -1,4 +1,4 @@
@import "./mixin.scss"; @import "mixin";
.hostbanner { .hostbanner {
.container-hostbanner { .container-hostbanner {

View file

@ -1,13 +1,10 @@
<?php <%
ini_set('display_errors', 1); /* given build compile */
ini_set('display_startup_errors', 1); var build_target;
error_reporting(E_ALL); var initial_script;
var initial_css;
%>
$WEB_CLIENT = (!isset($CLIENT) || !$CLIENT) && http_response_code() !== false || (defined("TEA_SERVE") && TEA_SERVE);
$localhost = false;
if(gethostname() == "WolverinDEV")
$localhost = true;
?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -25,39 +22,22 @@
<!-- TODO Needs some fix --> <!-- TODO Needs some fix -->
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<?php <% if(build_target === "client") { %>
if(!$WEB_CLIENT) { <title>TeaClient</title>
echo "\t\t<title>TeaClient</title>" . PHP_EOL; <meta name='og:title' content='TeaClient'>
echo "\t\t<meta name='og:title' content='TeaClient'>" . PHP_EOL; <% } else { %>
} else { <title>TeaSpeak-Web</title>
echo "\t\t<title>TeaSpeak-Web</title>" . PHP_EOL; <meta name='og:title' content='TeaSpeak-Web'>
echo "\t\t<meta name='og:title' content='TeaSpeak-Web'>" . PHP_EOL; <link rel='shortcut icon' href='img/favicon/teacup.png' type='image/x-icon'>
echo "\t\t<link rel='shortcut icon' href='img/favicon/teacup.png' type='image/x-icon'>" . PHP_EOL; <!-- <link rel="apple-touch-icon" sizes="194x194" href="/apple-touch-icon.png" type="image/png"> -->
//<link rel="apple-touch-icon" sizes="194x194" href="/apple-touch-icon.png" type="image/png"> <% } %>
}
?>
<!-- PHP generated properties -->
<x-properties id="properties" style="display: none"> <x-properties id="properties" style="display: none">
<?php <!--
function spawn_property($name, $value, $element_id = null) We don't need to put any properties down here.
{ But this tag is here to not brick the settings class.
if(isset($value)) But it will be removed quite soonly as soon this class has been fixed
echo "\t\t\t<x-property key=\"" . $name . "\" " . (isset($element_id) ? "id=\"" . $element_id . "\" " : "") . "value=\"" . urlencode($value) . "\"></x-property>\r\n"; -->
}
spawn_property('connect_default_host', $localhost ? "localhost" : "ts.TeaSpeak.de");
spawn_property('localhost_debug', $localhost ? "true" : "false");
if(defined("TEA_SERVE") && TEA_SERVE) {
$version = "0000000";
} else {
$version = file_get_contents("./version");
if ($version === false)
$version = "unknown";
}
spawn_property("version", $version, "app_version");
?>
</x-properties> </x-properties>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
@ -124,6 +104,8 @@
display: block; display: block;
} }
</style> </style>
<%- initial_css %>
</head> </head>
<body> <body>
<!-- No javascript error --> <!-- No javascript error -->
@ -139,18 +121,8 @@
</noscript> </noscript>
<!-- loader setup --> <!-- loader setup -->
<div id="style"> <div id="style"></div>
<link rel="stylesheet" href="css/loader/loader.css"> <div id="scripts"></div>
</div>
<meta name="app-loader-target" content="app">
<div id="scripts">
<!--
<script type="application/javascript" src="loader/loader_app.min.js" async defer></script>
<script type="application/javascript" src="loader/loader_app.js" async defer></script>
-->
<script type="application/javascript" src="js/loader.js?_<?php echo time() ?>" async defer></script>
</div>
<!-- Loading screen --> <!-- Loading screen -->
<div class="loader" id="loader-overlay"> <div class="loader" id="loader-overlay">
@ -172,13 +144,15 @@
<!-- Critical load error --> <!-- Critical load error -->
<div class="fulloverlay" id="critical-load"> <div class="fulloverlay" id="critical-load">
<div class="container"> <div class="container">
<img src="img/loading_error_right.svg" style="height: 12em"> <!-- We're loading the error image via JS so improve first page content loaded speed -->
<div id="load-error-image" x-src="img/loading_error_right.svg"></div>
<h1 class="error" style="color: red; margin-bottom: 0"></h1> <h1 class="error" style="color: red; margin-bottom: 0"></h1>
<h3 class="detail" style="margin-top: .5em"></h3> <h3 class="detail" style="margin-top: .5em"></h3>
</div> </div>
</div> </div>
<!-- debugging close --> <%# <!-- Old debgging stuff back for the days where we designed the client -->
<?php if($localhost && false) { ?> <?php if($localhost && false) { ?>
<div id="spoiler-style" style="z-index: 1000000; position: absolute; display: block; background: white; right: 5px; left: 5px; top: 34px;"> <div id="spoiler-style" style="z-index: 1000000; position: absolute; display: block; background: white; right: 5px; left: 5px; top: 34px;">
<!-- <img src="https://www.chromatic-solutions.de/teaspeak/window/connect_opened.png"> --> <!-- <img src="https://www.chromatic-solutions.de/teaspeak/window/connect_opened.png"> -->
@ -241,6 +215,7 @@
}; };
setTimeout(() => init($), 1000); setTimeout(() => init($), 1000);
</script> </script>
<?php } ?> %>
<%- initial_script %>
</body> </body>
</html> </html>

View file

@ -1,5 +1,4 @@
import {createModal} from "tc-shared/ui/elements/Modal"; import {createModal} from "tc-shared/ui/elements/Modal";
import * as loader from "tc-loader";
import {LogCategory} from "tc-shared/log"; import {LogCategory} from "tc-shared/log";
import * as log from "tc-shared/log"; import * as log from "tc-shared/log";
@ -10,29 +9,16 @@ function format_date(date: number) {
} }
export function spawnAbout() { export function spawnAbout() {
const app_version = (() => {
const version_node = document.getElementById("app_version");
if(!version_node) return undefined;
const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined;
if(!version) return undefined;
if(version == "unknown" || version.replace(/0+/, "").length == 0)
return undefined;
return version;
})();
const connectModal = createModal({ const connectModal = createModal({
header: tr("About"), header: tr("About"),
body: () => { body: () => {
let tag = $("#tmpl_about").renderTag({ let tag = $("#tmpl_about").renderTag({
client: __build.target !== "web", client: __build.target !== "web",
version_client: __build.target === "web" ? app_version || "in-dev" : "loading...", version_client: __build.target === "web" ? __build.version || "in-dev" : "loading...",
version_ui: app_version || "in-dev", version_ui: __build.version || "in-dev",
version_timestamp: !!app_version ? format_date(Date.now()) : "--" version_timestamp: format_date(__build.timestamp)
}); });
return tag; return tag;
}, },

View file

@ -14,6 +14,7 @@
"webpack/build-definitions.d.ts", "webpack/build-definitions.d.ts",
"webpack/ManifestPlugin.ts", "webpack/ManifestPlugin.ts",
"webpack/EJSGenerator.ts",
"webpack/WatLoader.ts", "webpack/WatLoader.ts",
"file.ts" "file.ts"

View file

@ -3,6 +3,7 @@ import * as fs from "fs";
import trtransformer from "./tools/trgen/ts_transformer"; import trtransformer from "./tools/trgen/ts_transformer";
import {exec} from "child_process"; import {exec} from "child_process";
import * as util from "util"; import * as util from "util";
import EJSGenerator = require("./webpack/EJSGenerator");
const path = require('path'); const path = require('path');
const webpack = require("webpack"); const webpack = require("webpack");
@ -74,7 +75,24 @@ export const config = async (target: "web" | "client") => { return {
minSize: 1024 * 8, minSize: 1024 * 8,
maxSize: 1024 * 128 maxSize: 1024 * 128
}), }),
new webpack.DefinePlugin(await generate_definitions(target)) new webpack.DefinePlugin(await generate_definitions(target)),
new EJSGenerator({
variables: {
build_target: target
},
input: path.join(__dirname, "shared/html/index.html.ejs"),
output: path.join(__dirname, "dist/index.html"),
initialJSEntryChunk: "loader",
minify: !isDevelopment,
embedInitialJSEntryChunk: !isDevelopment,
embedInitialCSSFile: !isDevelopment,
initialCSSFile: {
localFile: path.join(__dirname, "loader/css/loader.css"),
publicFile: "css/loader.css"
}
})
].filter(e => !!e), ].filter(e => !!e),
module: { module: {
rules: [ rules: [
@ -142,13 +160,8 @@ export const config = async (target: "web" | "client") => { return {
{"tc-loader": "window loader"} {"tc-loader": "window loader"}
] as any[], ] as any[],
output: { output: {
filename: (chunkData) => { filename: isDevelopment ? "[name].[contenthash].js" : "[contenthash].js",
if(chunkData.chunk.name === "loader") chunkFilename: isDevelopment ? "[name].[contenthash].js" : "[contenthash].js",
return "loader.js";
return '[name].js';
},
chunkFilename: "[name].js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, 'dist'),
publicPath: "js/" publicPath: "js/"
}, },

94
webpack/EJSGenerator.ts Normal file
View file

@ -0,0 +1,94 @@
import * as webpack from "webpack";
import * as ejs from "ejs";
import * as fs from "fs";
import * as util from "util";
import * as minifier from "html-minifier";
import * as path from "path";
import {Compilation} from "webpack";
interface Options {
input: string,
output: string,
minify?: boolean,
initialJSEntryChunk: string,
embedInitialJSEntryChunk?: boolean,
initialCSSFile: {
localFile: string,
publicFile: string
},
embedInitialCSSFile?: boolean,
variables?: {[name: string]: any};
}
class EJSGenerator {
readonly options: Options;
constructor(options: Options) {
this.options = options;
}
private async generate_entry_js_tag(compilation: Compilation) {
const entry_group = compilation.chunkGroups.find(e => e.options.name === this.options.initialJSEntryChunk);
if(!entry_group) return; /* not the correct compilation */
if(entry_group.chunks.length !== 1) throw "Unsupported entry chunk size. We only support one at the moment.";
if(entry_group.chunks[0].files.length !== 1)
throw "Entry chunk has too many files. We only support to inline one!";
const file = entry_group.chunks[0].files[0];
if(path.extname(file) !== ".js")
throw "Entry chunk file has unknown extension";
if(!this.options.embedInitialJSEntryChunk) {
return '<script type="application/javascript" src=' + compilation.compiler.options.output.publicPath + file + ' async defer></script>';
} else {
const script = await util.promisify(fs.readFile)(path.join(compilation.compiler.outputPath, file));
return `<script type="application/javascript">${script}</script>`;
}
}
private async generate_entry_css_tag() {
if(this.options.embedInitialCSSFile) {
const style = await util.promisify(fs.readFile)(this.options.initialCSSFile.localFile);
return `<style>${style}</style>`
} else {
//<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">
return `<link rel="preload" as="style" onload="this.rel='stylesheet'" href="${this.options.initialCSSFile.publicFile}">`
}
}
apply(compiler: webpack.Compiler) {
compiler.hooks.afterEmit.tapPromise(this.constructor.name, async compilation => {
const input = await util.promisify(fs.readFile)(this.options.input);
const variables = Object.assign({}, this.options.variables);
variables["initial_script"] = await this.generate_entry_js_tag(compilation);
variables["initial_css"] = await this.generate_entry_css_tag();
let generated = await ejs.render(input.toString(), variables, {
beautify: false /* uglify is a bit dump and does not understands ES6 */
});
if(this.options.minify) {
generated = minifier.minify(generated, {
html5: true,
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeTagWhitespace: true,
minifyCSS: true,
minifyJS: true,
minifyURLs: true
});
}
await util.promisify(fs.writeFile)(this.options.output, generated);
});
}
}
export = EJSGenerator;

View file

@ -24,8 +24,9 @@ class ManifestGenerator {
const modules = []; const modules = [];
for(const chunk of chunk_group.chunks) { for(const chunk of chunk_group.chunks) {
if(!chunk.files.length) continue;
if(chunk.files.length !== 1) { if(chunk.files.length !== 1) {
console.error("Expected only on file per chunk but got " + chunk.files.length); console.error("Expected only one file per chunk but got " + chunk.files.length);
chunk.files.forEach(e => console.log(" - %s", e)); chunk.files.forEach(e => console.log(" - %s", e));
throw "expected only one file per chunk"; throw "expected only one file per chunk";
} }