Improved the initial page load

canary
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 query show state
- 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**
- Implemented the new music bot playlist song list
- 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 */
"type": "js",
"search-pattern": /.*$/,
"search-pattern": /.*\.(js|json)$/,
"build-target": "dev|rel",
"path": "js/",
"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 */
"type": "css",
"search-pattern": /.*\.css$/,

View File

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

View File

@ -309,85 +309,78 @@ export const templates = template_loader;
/* Hello World message */
{
const hello_world = () => {
const clog = console.log;
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 clog = console.log;
const print_security = () => {
{
const css = [
"display: block",
"text-align: center",
"font-size: 72px",
"font-size: 42px",
"font-weight: bold",
"-webkit-text-stroke: 2px black",
"color: #18BC9C"
"color: red"
].join(";");
clog("%cHey, hold on!", css);
clog("%c ", "font-size: 100px;");
clog("%cSecurity warning:", css);
}
{
const css = [
"display: block",
"text-align: center",
"font-size: 26px",
"font-size: 18px",
"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);
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;");
}
};
try { /* lets try to print it as VM code :)*/
let hello_world_code = hello_world.toString();
hello_world_code = hello_world_code.substr(hello_world_code.indexOf('() => {') + 8);
hello_world_code = hello_world_code.substring(0, hello_world_code.lastIndexOf("}"));
//Look aheads are not possible with firefox
//hello_world_code = hello_world_code.replace(/(?<!const|let)(?<=^([^"'/]|"[^"]*"|'[^']*'|`[^`]*`|\/[^/]*\/)*) /gm, ""); /* replace all spaces */
hello_world_code = hello_world_code.replace(/[\n\r]/g, ""); /* replace as new lines */
eval(hello_world_code);
} catch(e) {
console.error(e);
hello_world();
/* print the hello world */
{
const css = [
"display: block",
"text-align: center",
"font-size: 72px",
"font-weight: bold",
"-webkit-text-stroke: 2px black",
"color: #18BC9C"
].join(";");
clog("%cHey, hold on!", css);
}
}
{
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==",
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.0.1.tgz",
@ -161,6 +170,12 @@
"@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": {
"version": "1.39.2",
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.2.tgz",
@ -201,6 +216,17 @@
"@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": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz",
@ -274,6 +300,12 @@
"@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": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@types/sha256/-/sha256-0.2.0.tgz",
@ -2385,9 +2417,9 @@
"dev": true
},
"ejs": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz",
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.2.tgz",
"integrity": "sha512-IncmUpn1yN84hy2shb0POJ80FWrfGNY0cxO9f4v+/sG7qcBvAtVWUA1IdzY/8EYUmOVhoKJVdJjNd3AZcnxOjA==",
"dev": true
},
"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": {
"version": "5.0.5",
"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==",
"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": {
"version": "0.1.2",
"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==",
"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": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@ -10882,6 +10981,12 @@
"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": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

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

View File

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

View File

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

View File

@ -1,13 +1,10 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
<%
/* given build compile */
var build_target;
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>
<html lang="en">
<head>
@ -25,39 +22,22 @@
<!-- TODO Needs some fix -->
<link rel="manifest" href="manifest.json">
<?php
if(!$WEB_CLIENT) {
echo "\t\t<title>TeaClient</title>" . PHP_EOL;
echo "\t\t<meta name='og:title' content='TeaClient'>" . PHP_EOL;
} else {
echo "\t\t<title>TeaSpeak-Web</title>" . PHP_EOL;
echo "\t\t<meta name='og:title' content='TeaSpeak-Web'>" . PHP_EOL;
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">
}
?>
<% if(build_target === "client") { %>
<title>TeaClient</title>
<meta name='og:title' content='TeaClient'>
<% } else { %>
<title>TeaSpeak-Web</title>
<meta name='og:title' content='TeaSpeak-Web'>
<link rel='shortcut icon' href='img/favicon/teacup.png' type='image/x-icon'>
<!-- <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">
<?php
function spawn_property($name, $value, $element_id = null)
{
if(isset($value))
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");
?>
<!--
We don't need to put any properties down here.
But this tag is here to not brick the settings class.
But it will be removed quite soonly as soon this class has been fixed
-->
</x-properties>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
@ -124,6 +104,8 @@
display: block;
}
</style>
<%- initial_css %>
</head>
<body>
<!-- No javascript error -->
@ -139,18 +121,8 @@
</noscript>
<!-- loader setup -->
<div id="style">
<link rel="stylesheet" href="css/loader/loader.css">
</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>
<div id="style"></div>
<div id="scripts"></div>
<!-- Loading screen -->
<div class="loader" id="loader-overlay">
@ -172,13 +144,15 @@
<!-- Critical load error -->
<div class="fulloverlay" id="critical-load">
<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>
<h3 class="detail" style="margin-top: .5em"></h3>
</div>
</div>
<!-- debugging close -->
<%# <!-- Old debgging stuff back for the days where we designed the client -->
<?php if($localhost && false) { ?>
<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"> -->
@ -241,6 +215,7 @@
};
setTimeout(() => init($), 1000);
</script>
<?php } ?>
%>
<%- initial_script %>
</body>
</html>

View File

@ -1,5 +1,4 @@
import {createModal} from "tc-shared/ui/elements/Modal";
import * as loader from "tc-loader";
import {LogCategory} from "tc-shared/log";
import * as log from "tc-shared/log";
@ -10,29 +9,16 @@ function format_date(date: number) {
}
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({
header: tr("About"),
body: () => {
let tag = $("#tmpl_about").renderTag({
client: __build.target !== "web",
version_client: __build.target === "web" ? app_version || "in-dev" : "loading...",
version_ui: app_version || "in-dev",
version_client: __build.target === "web" ? __build.version || "in-dev" : "loading...",
version_ui: __build.version || "in-dev",
version_timestamp: !!app_version ? format_date(Date.now()) : "--"
version_timestamp: format_date(__build.timestamp)
});
return tag;
},

View File

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

View File

@ -3,6 +3,7 @@ import * as fs from "fs";
import trtransformer from "./tools/trgen/ts_transformer";
import {exec} from "child_process";
import * as util from "util";
import EJSGenerator = require("./webpack/EJSGenerator");
const path = require('path');
const webpack = require("webpack");
@ -74,7 +75,24 @@ export const config = async (target: "web" | "client") => { return {
minSize: 1024 * 8,
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),
module: {
rules: [
@ -142,13 +160,8 @@ export const config = async (target: "web" | "client") => { return {
{"tc-loader": "window loader"}
] as any[],
output: {
filename: (chunkData) => {
if(chunkData.chunk.name === "loader")
return "loader.js";
return '[name].js';
},
chunkFilename: "[name].js",
filename: isDevelopment ? "[name].[contenthash].js" : "[contenthash].js",
chunkFilename: isDevelopment ? "[name].[contenthash].js" : "[contenthash].js",
path: path.resolve(__dirname, 'dist'),
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 = [];
for(const chunk of chunk_group.chunks) {
if(!chunk.files.length) continue;
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));
throw "expected only one file per chunk";
}