Mostly using direct assets instead of stored files somewhere within the project.
parent
66021b125b
commit
61da22895f
52
file.ts
52
file.ts
|
@ -30,49 +30,26 @@ type ProjectResource = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
|
const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
|
||||||
|
{ /* javascript files as manifest.json */
|
||||||
|
"type": "js",
|
||||||
|
"search-pattern": /.*\.(js|json|svg|png|css|html)$/,
|
||||||
|
"build-target": "dev|rel",
|
||||||
|
|
||||||
|
"path": "js/",
|
||||||
|
"local-path": "./dist/"
|
||||||
|
},
|
||||||
|
|
||||||
{ /* shared html files */
|
{ /* shared html files */
|
||||||
"type": "html",
|
"type": "html",
|
||||||
"search-pattern": /^.*([a-zA-Z]+)\.(html|json)$/,
|
"search-pattern": /^.*([a-zA-Z]+)\.(html|json)$/,
|
||||||
"build-target": "dev|rel",
|
"build-target": "dev|rel",
|
||||||
|
|
||||||
"path": "./",
|
"path": "./",
|
||||||
"local-path": "./shared/html/"
|
|
||||||
},
|
|
||||||
{ /* javascript files as manifest.json */
|
|
||||||
"type": "js",
|
|
||||||
"search-pattern": /.*\.(js|json|svg|png)$/,
|
|
||||||
"build-target": "dev|rel",
|
|
||||||
|
|
||||||
"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 sound files */
|
{ /* shared sound files */
|
||||||
"type": "wav",
|
"type": "wav",
|
||||||
"search-pattern": /.*\.wav$/,
|
"search-pattern": /.*\.(wav|json)$/,
|
||||||
"build-target": "dev|rel",
|
|
||||||
|
|
||||||
"path": "audio/",
|
|
||||||
"local-path": "./shared/audio/"
|
|
||||||
},
|
|
||||||
{ /* shared data sound files */
|
|
||||||
"type": "json",
|
|
||||||
"search-pattern": /.*\.json/,
|
|
||||||
"build-target": "dev|rel",
|
"build-target": "dev|rel",
|
||||||
|
|
||||||
"path": "audio/",
|
"path": "audio/",
|
||||||
|
@ -87,15 +64,6 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
|
||||||
"path": "img/",
|
"path": "img/",
|
||||||
"local-path": "./shared/img/"
|
"local-path": "./shared/img/"
|
||||||
},
|
},
|
||||||
{ /* assembly files */
|
|
||||||
"web-only": true,
|
|
||||||
"type": "wasm",
|
|
||||||
"search-pattern": /.*\.(wasm)/,
|
|
||||||
"build-target": "dev|rel",
|
|
||||||
|
|
||||||
"path": "js/",
|
|
||||||
"local-path": "./dist/"
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const APP_FILE_LIST_SHARED_VENDORS: ProjectResource[] = [];
|
const APP_FILE_LIST_SHARED_VENDORS: ProjectResource[] = [];
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
import * as path from "path";
|
|
||||||
import EJSGenerator = require("../webpack/EJSGenerator");
|
|
||||||
|
|
||||||
class IndexGenerator extends EJSGenerator {
|
|
||||||
constructor(options: {
|
|
||||||
buildTarget: string;
|
|
||||||
output: string,
|
|
||||||
isDevelopment: boolean
|
|
||||||
}) {
|
|
||||||
super({
|
|
||||||
variables: {
|
|
||||||
build_target: options.buildTarget
|
|
||||||
},
|
|
||||||
output: options.output,
|
|
||||||
initialJSEntryChunk: "loader",
|
|
||||||
input: path.join(__dirname, "html/index.html.ejs"),
|
|
||||||
minify: !options.isDevelopment,
|
|
||||||
|
|
||||||
embedInitialJSEntryChunk: !options.isDevelopment,
|
|
||||||
embedInitialCSSFile: !options.isDevelopment,
|
|
||||||
|
|
||||||
initialCSSFile: {
|
|
||||||
localFile: path.join(__dirname, "css/index.css"),
|
|
||||||
publicFile: "css/index.css"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export = IndexGenerator;
|
|
|
@ -8,7 +8,4 @@ body {
|
||||||
*, :before, :after {
|
*, :before, :after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "loader";
|
|
||||||
@import "overlay";
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./index.scss";
|
||||||
|
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./loader.scss";
|
||||||
|
import "!style-loader!css-loader?url=false!sass-loader?sourceMap=true!./overlay.css";
|
|
@ -80,7 +80,7 @@ $loop-time-halloween: 25s / 24;
|
||||||
|
|
||||||
width: 249px;
|
width: 249px;
|
||||||
height: 125px;
|
height: 125px;
|
||||||
background: url("img/loader/steam.png") 0 0, url("../img/loader/steam.png") 0 0;
|
background: url("../images/steam.png") 0 0;
|
||||||
|
|
||||||
animation: sprite-steam 2.5s steps(50) forwards infinite;
|
animation: sprite-steam 2.5s steps(50) forwards infinite;
|
||||||
}
|
}
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 188 KiB |
|
@ -1,10 +1,14 @@
|
||||||
import "core-js/stable";
|
import "core-js/stable";
|
||||||
import "./polifill";
|
import "./polifill";
|
||||||
|
import "./css";
|
||||||
|
|
||||||
import * as loader from "./loader/loader";
|
import * as loader from "./loader/loader";
|
||||||
import {ApplicationLoader} from "./loader/loader";
|
import {ApplicationLoader} from "./loader/loader";
|
||||||
import {getUrlParameter} from "./loader/utils";
|
import {getUrlParameter} from "./loader/utils";
|
||||||
|
|
||||||
|
if(window["loader"]) {
|
||||||
|
throw "an loader instance has already been defined";
|
||||||
|
}
|
||||||
window["loader"] = loader;
|
window["loader"] = loader;
|
||||||
/* let the loader register himself at the window first */
|
/* let the loader register himself at the window first */
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as script_loader from "./script_loader";
|
import * as script_loader from "./script_loader";
|
||||||
import * as template_loader from "./template_loader";
|
import * as style_loader from "./style_loader";
|
||||||
import * as Animation from "../animation";
|
import * as Animation from "../animation";
|
||||||
import {getUrlParameter} from "./utils";
|
import {getUrlParameter} from "./utils";
|
||||||
|
|
||||||
|
@ -111,8 +111,6 @@ export type ModuleMapping = {
|
||||||
const module_mapping_: ModuleMapping[] = [];
|
const module_mapping_: ModuleMapping[] = [];
|
||||||
export function module_mapping() : ModuleMapping[] { return module_mapping_; }
|
export function module_mapping() : ModuleMapping[] { return module_mapping_; }
|
||||||
|
|
||||||
export function get_cache_version() { return cache_tag; }
|
|
||||||
|
|
||||||
export function finished() {
|
export function finished() {
|
||||||
return currentStage == Stage.DONE;
|
return currentStage == Stage.DONE;
|
||||||
}
|
}
|
||||||
|
@ -360,7 +358,7 @@ export type DependSource = {
|
||||||
export type SourcePath = string | DependSource | string[];
|
export type SourcePath = string | DependSource | string[];
|
||||||
|
|
||||||
export const scripts = script_loader;
|
export const scripts = script_loader;
|
||||||
export const templates = template_loader;
|
export const style = style_loader;
|
||||||
|
|
||||||
/* Hello World message */
|
/* Hello World message */
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
import {config, critical_error, SourcePath} from "./loader";
|
||||||
|
import {load_parallel, LoadCallback, LoadSyntaxError, ParallelOptions, script_name} from "./utils";
|
||||||
|
|
||||||
|
let _style_promises: {[key: string]: Promise<void>} = {};
|
||||||
|
|
||||||
|
function load_style_url(url: string) : Promise<void> {
|
||||||
|
if(typeof _style_promises[url] === "object")
|
||||||
|
return _style_promises[url];
|
||||||
|
|
||||||
|
return (_style_promises[url] = new Promise((resolve, reject) => {
|
||||||
|
const tag: HTMLLinkElement = document.createElement("link");
|
||||||
|
|
||||||
|
let error = false;
|
||||||
|
const error_handler = (event: ErrorEvent) => {
|
||||||
|
if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error);
|
||||||
|
if(event.filename == tag.href) { //FIXME!
|
||||||
|
window.removeEventListener('error', error_handler as any);
|
||||||
|
|
||||||
|
reject(new SyntaxError(event.error));
|
||||||
|
event.preventDefault();
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('error', error_handler as any);
|
||||||
|
|
||||||
|
tag.type = "text/css";
|
||||||
|
tag.rel = "stylesheet";
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
tag.onerror = undefined;
|
||||||
|
tag.onload = undefined;
|
||||||
|
|
||||||
|
clearTimeout(timeout_handle);
|
||||||
|
window.removeEventListener('error', error_handler as any);
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeout_handle = setTimeout(() => {
|
||||||
|
cleanup();
|
||||||
|
reject("timeout");
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
tag.onerror = error => {
|
||||||
|
cleanup();
|
||||||
|
tag.remove();
|
||||||
|
if(config.error)
|
||||||
|
console.error("File load error for file %s: %o", url, error);
|
||||||
|
reject("failed to load file " + url);
|
||||||
|
};
|
||||||
|
tag.onload = () => {
|
||||||
|
cleanup();
|
||||||
|
{
|
||||||
|
const css: CSSStyleSheet = tag.sheet as CSSStyleSheet;
|
||||||
|
const rules = css.cssRules;
|
||||||
|
const rules_remove: number[] = [];
|
||||||
|
const rules_add: string[] = [];
|
||||||
|
|
||||||
|
for(let index = 0; index < rules.length; index++) {
|
||||||
|
const rule = rules.item(index);
|
||||||
|
let rule_text = rule.cssText;
|
||||||
|
|
||||||
|
if(rule.cssText.indexOf("%%base_path%%") != -1) {
|
||||||
|
rules_remove.push(index);
|
||||||
|
rules_add.push(rule_text.replace("%%base_path%%", document.location.origin + document.location.pathname));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const index of rules_remove.sort((a, b) => b > a ? 1 : 0)) {
|
||||||
|
if(css.removeRule)
|
||||||
|
css.removeRule(index);
|
||||||
|
else
|
||||||
|
css.deleteRule(index);
|
||||||
|
}
|
||||||
|
for(const rule of rules_add)
|
||||||
|
css.insertRule(rule, rules_remove[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.verbose) console.debug("Style sheet %o loaded", url);
|
||||||
|
setTimeout(resolve, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById("style").appendChild(tag);
|
||||||
|
tag.href = config.baseUrl + url;
|
||||||
|
})).then(result => {
|
||||||
|
/* cleanup memory */
|
||||||
|
_style_promises[url] = Promise.resolve(); /* this promise does not holds the whole script tag and other memory */
|
||||||
|
return _style_promises[url];
|
||||||
|
}).catch(error => {
|
||||||
|
/* cleanup memory */
|
||||||
|
_style_promises[url] = Promise.reject(error); /* this promise does not holds the whole script tag and other memory */
|
||||||
|
return _style_promises[url];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
cache_tag?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load(path: SourcePath, options: Options) : Promise<void> {
|
||||||
|
if(Array.isArray(path)) { //We have fallback scripts
|
||||||
|
return load(path[0], options).catch(error => {
|
||||||
|
if(error instanceof LoadSyntaxError)
|
||||||
|
return Promise.reject(error);
|
||||||
|
|
||||||
|
if(path.length > 1)
|
||||||
|
return load(path.slice(1), options);
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const source = typeof(path) === "string" ? {url: path, depends: []} : path;
|
||||||
|
if(source.url.length == 0) return Promise.resolve();
|
||||||
|
|
||||||
|
/* await depends */
|
||||||
|
for(const depend of source.depends) {
|
||||||
|
if(!_style_promises[depend])
|
||||||
|
throw "Missing dependency " + depend;
|
||||||
|
await _style_promises[depend];
|
||||||
|
}
|
||||||
|
await load_style_url(source.url + (options.cache_tag || ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MultipleOptions = Options | ParallelOptions;
|
||||||
|
export async function load_multiple(paths: SourcePath[], options: MultipleOptions, callback?: LoadCallback<SourcePath>) : Promise<void> {
|
||||||
|
const result = await load_parallel<SourcePath>(paths, e => load(e, options), e => script_name(e, false), options, callback);
|
||||||
|
if(result.failed.length > 0) {
|
||||||
|
if(config.error) {
|
||||||
|
console.error("Failed to load the following style sheets:");
|
||||||
|
for(const style of result.failed) {
|
||||||
|
const sname = script_name(style.request, false);
|
||||||
|
if(style.error instanceof LoadSyntaxError) {
|
||||||
|
console.log(" - %s: %o", sname, style.error.source);
|
||||||
|
} else {
|
||||||
|
console.log(" - %s: %o", sname, style.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
critical_error("Failed to load style " + script_name(result.failed[0].request, true) + " <br>" + "View the browser console for more information!");
|
||||||
|
throw "failed to load style " + script_name(result.failed[0].request, false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
import {config, critical_error, SourcePath} from "./loader";
|
|
||||||
import {load_parallel, LoadCallback, LoadSyntaxError, ParallelOptions, script_name} from "./utils";
|
|
||||||
|
|
||||||
let _template_promises: {[key: string]: Promise<void>} = {};
|
|
||||||
|
|
||||||
function load_template_url(url: string) : Promise<void> {
|
|
||||||
if(typeof _template_promises[url] === "object")
|
|
||||||
return _template_promises[url];
|
|
||||||
|
|
||||||
return (_template_promises[url] = (async () => {
|
|
||||||
const response = await (await fetch(config.baseUrl + url)).text();
|
|
||||||
|
|
||||||
let node = document.createElement("html");
|
|
||||||
node.innerHTML = response;
|
|
||||||
let tags: HTMLCollection;
|
|
||||||
if(node.getElementsByTagName("body").length > 0)
|
|
||||||
tags = node.getElementsByTagName("body")[0].children;
|
|
||||||
else
|
|
||||||
tags = node.children;
|
|
||||||
|
|
||||||
let root = document.getElementById("templates");
|
|
||||||
if(!root) {
|
|
||||||
critical_error("Failed to find template tag!");
|
|
||||||
throw "Failed to find template tag";
|
|
||||||
}
|
|
||||||
while(tags.length > 0){
|
|
||||||
let tag = tags.item(0);
|
|
||||||
root.appendChild(tag);
|
|
||||||
|
|
||||||
}
|
|
||||||
})()).then(result => {
|
|
||||||
/* cleanup memory */
|
|
||||||
_template_promises[url] = Promise.resolve(); /* this promise does not holds the whole script tag and other memory */
|
|
||||||
return _template_promises[url];
|
|
||||||
}).catch(error => {
|
|
||||||
/* cleanup memory */
|
|
||||||
_template_promises[url] = Promise.reject(error); /* this promise does not holds the whole script tag and other memory */
|
|
||||||
return _template_promises[url];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Options {
|
|
||||||
cache_tag?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function load(path: SourcePath, options: Options) : Promise<void> {
|
|
||||||
if(Array.isArray(path)) { //We have fallback scripts
|
|
||||||
return load(path[0], options).catch(error => {
|
|
||||||
if(error instanceof LoadSyntaxError)
|
|
||||||
return Promise.reject(error);
|
|
||||||
|
|
||||||
if(path.length > 1)
|
|
||||||
return load(path.slice(1), options);
|
|
||||||
|
|
||||||
return Promise.reject(error);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const source = typeof(path) === "string" ? {url: path, depends: []} : path;
|
|
||||||
if(source.url.length == 0) return Promise.resolve();
|
|
||||||
|
|
||||||
/* await depends */
|
|
||||||
for(const depend of source.depends) {
|
|
||||||
if(!_template_promises[depend])
|
|
||||||
throw "Missing dependency " + depend;
|
|
||||||
await _template_promises[depend];
|
|
||||||
}
|
|
||||||
await load_template_url(source.url + (options.cache_tag || ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MultipleOptions = Options | ParallelOptions;
|
|
||||||
export async function load_multiple(paths: SourcePath[], options: MultipleOptions, callback?: LoadCallback<SourcePath>) : Promise<void> {
|
|
||||||
const result = await load_parallel<SourcePath>(paths, e => load(e, options), e => script_name(e, false), options, callback);
|
|
||||||
if(result.failed.length > 0) {
|
|
||||||
if(config.error) {
|
|
||||||
console.error("Failed to load the following template files:");
|
|
||||||
for(const style of result.failed) {
|
|
||||||
const sname = script_name(style.request, false);
|
|
||||||
if(style.error instanceof LoadSyntaxError) {
|
|
||||||
console.log(" - %s: %o", sname, style.error.source);
|
|
||||||
} else {
|
|
||||||
console.log(" - %s: %o", sname, style.error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
critical_error("Failed to load template file " + script_name(result.failed[0].request, true) + " <br>" + "View the browser console for more information!");
|
|
||||||
throw "failed to load template file " + script_name(result.failed[0].request, false);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,6 +11,10 @@ export interface TeaManifest {
|
||||||
hash: string,
|
hash: string,
|
||||||
file: string
|
file: string
|
||||||
}[],
|
}[],
|
||||||
|
css_files: {
|
||||||
|
hash: string,
|
||||||
|
file: string
|
||||||
|
}[],
|
||||||
modules: {
|
modules: {
|
||||||
id: string,
|
id: string,
|
||||||
context: string,
|
context: string,
|
||||||
|
@ -52,12 +56,24 @@ export async function loadManifestTarget(chunkName: string, taskId: number) {
|
||||||
modules: manifest.chunks[chunkName].modules
|
modules: manifest.chunks[chunkName].modules
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loader.style.load_multiple(manifest.chunks[chunkName].css_files.map(e => "js/" + e.file), {
|
||||||
|
cache_tag: undefined,
|
||||||
|
max_parallel_requests: 4
|
||||||
|
}, (entry, state) => {
|
||||||
|
if(state !== "loading") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loader.setCurrentTaskName(taskId, script_name(entry, false));
|
||||||
|
});
|
||||||
|
|
||||||
await loader.scripts.load_multiple(manifest.chunks[chunkName].files.map(e => "js/" + e.file), {
|
await loader.scripts.load_multiple(manifest.chunks[chunkName].files.map(e => "js/" + e.file), {
|
||||||
cache_tag: undefined,
|
cache_tag: undefined,
|
||||||
max_parallel_requests: 4
|
max_parallel_requests: 4
|
||||||
}, (script, state) => {
|
}, (script, state) => {
|
||||||
if(state !== "loading")
|
if(state !== "loading") {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
loader.setCurrentTaskName(taskId, script_name(script, false));
|
loader.setCurrentTaskName(taskId, script_name(script, false));
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* IE11 and safari */
|
/* IE11 and safari */
|
||||||
if(Element.prototype.remove === undefined)
|
if(Element.prototype.remove === undefined) {
|
||||||
Object.defineProperty(Element.prototype, "remove", {
|
Object.defineProperty(Element.prototype, "remove", {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: false,
|
configurable: false,
|
||||||
|
@ -8,6 +8,7 @@ if(Element.prototype.remove === undefined)
|
||||||
this.parentElement.removeChild(this);
|
this.parentElement.removeChild(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/* IE11 */
|
/* IE11 */
|
||||||
function ReplaceWithPolyfill() {
|
function ReplaceWithPolyfill() {
|
||||||
|
@ -28,14 +29,17 @@ function ReplaceWithPolyfill() {
|
||||||
parent.insertBefore(currentNode, this.nextSibling);
|
parent.insertBefore(currentNode, this.nextSibling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Element.prototype.replaceWith)
|
if (!Element.prototype.replaceWith) {
|
||||||
Element.prototype.replaceWith = ReplaceWithPolyfill;
|
Element.prototype.replaceWith = ReplaceWithPolyfill;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CharacterData.prototype.replaceWith)
|
if (!CharacterData.prototype.replaceWith) {
|
||||||
CharacterData.prototype.replaceWith = ReplaceWithPolyfill;
|
CharacterData.prototype.replaceWith = ReplaceWithPolyfill;
|
||||||
|
}
|
||||||
|
|
||||||
if (!DocumentType.prototype.replaceWith)
|
if (!DocumentType.prototype.replaceWith) {
|
||||||
DocumentType.prototype.replaceWith = ReplaceWithPolyfill;
|
DocumentType.prototype.replaceWith = ReplaceWithPolyfill;
|
||||||
|
}
|
||||||
|
|
||||||
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md
|
// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md
|
||||||
(function (arr) {
|
(function (arr) {
|
||||||
|
|
|
@ -1,26 +1,8 @@
|
||||||
import "./shared";
|
import "./shared";
|
||||||
import * as loader from "../loader/loader";
|
import * as loader from "../loader/loader";
|
||||||
import {ApplicationLoader, SourcePath} from "../loader/loader";
|
import {ApplicationLoader} from "../loader/loader";
|
||||||
import {script_name} from "../loader/utils";
|
|
||||||
import {loadManifest, loadManifestTarget} from "../maifest";
|
import {loadManifest, loadManifestTarget} from "../maifest";
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
native_client: boolean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCacheTag() {
|
|
||||||
return "?_ts=" + (__build.mode === "debug" ? Date.now() : __build.timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const LoaderTaskCallback = taskId => (script: SourcePath, state) => {
|
|
||||||
if(state !== "loading")
|
|
||||||
return;
|
|
||||||
|
|
||||||
loader.setCurrentTaskName(taskId, script_name(script, false));
|
|
||||||
};
|
|
||||||
|
|
||||||
/* all javascript loaders */
|
/* all javascript loaders */
|
||||||
const loader_javascript = {
|
const loader_javascript = {
|
||||||
load_scripts: async taskId => {
|
load_scripts: async taskId => {
|
||||||
|
@ -30,33 +12,12 @@ const loader_javascript = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loader_webassembly = {
|
|
||||||
test_webassembly: async () => {
|
|
||||||
/* We dont required WebAssembly anymore for fundamental functions, only for auto decoding
|
|
||||||
if(typeof (WebAssembly) === "undefined" || typeof (WebAssembly.compile) === "undefined") {
|
|
||||||
console.log(navigator.browserSpecs);
|
|
||||||
if (navigator.browserSpecs.name == 'Safari') {
|
|
||||||
if (parseInt(navigator.browserSpecs.version) < 11) {
|
|
||||||
displayCriticalError("You require Safari 11 or higher to use the web client!<br>Safari " + navigator.browserSpecs.version + " does not support WebAssambly!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Do something for all other browsers.
|
|
||||||
}
|
|
||||||
displayCriticalError("You require WebAssembly for TeaSpeak-Web!");
|
|
||||||
throw "Missing web assembly";
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loader.register_task(loader.Stage.INITIALIZING, {
|
loader.register_task(loader.Stage.INITIALIZING, {
|
||||||
name: "secure tester",
|
name: "secure tester",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
/* we need https or localhost to use some things like the storage API */
|
/* we need https or localhost to use some things like the storage API */
|
||||||
if(typeof isSecureContext === "undefined")
|
if(typeof isSecureContext === "undefined")
|
||||||
(<any>window)["isSecureContext"] = location.protocol !== 'https:' || location.hostname === 'localhost';
|
(window as any)["isSecureContext"] = location.protocol !== 'https:' || location.hostname === 'localhost';
|
||||||
|
|
||||||
if(!isSecureContext) {
|
if(!isSecureContext) {
|
||||||
loader.critical_error("TeaWeb cant run on unsecured sides.", "App requires to be loaded via HTTPS!");
|
loader.critical_error("TeaWeb cant run on unsecured sides.", "App requires to be loaded via HTTPS!");
|
||||||
|
@ -66,33 +27,12 @@ loader.register_task(loader.Stage.INITIALIZING, {
|
||||||
priority: 20
|
priority: 20
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.register_task(loader.Stage.INITIALIZING, {
|
|
||||||
name: "webassembly tester",
|
|
||||||
function: loader_webassembly.test_webassembly,
|
|
||||||
priority: 20
|
|
||||||
});
|
|
||||||
|
|
||||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||||
name: "scripts",
|
name: "scripts",
|
||||||
function: loader_javascript.load_scripts,
|
function: loader_javascript.load_scripts,
|
||||||
priority: 10
|
priority: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.register_task(loader.Stage.TEMPLATES, {
|
|
||||||
name: "templates",
|
|
||||||
function: async taskId => {
|
|
||||||
await loader.templates.load_multiple([
|
|
||||||
"templates.html",
|
|
||||||
"templates/modal/musicmanage.html",
|
|
||||||
"templates/modal/newcomer.html",
|
|
||||||
], {
|
|
||||||
cache_tag: getCacheTag(),
|
|
||||||
max_parallel_requests: -1
|
|
||||||
}, LoaderTaskCallback(taskId));
|
|
||||||
},
|
|
||||||
priority: 10
|
|
||||||
});
|
|
||||||
|
|
||||||
loader.register_task(loader.Stage.SETUP, {
|
loader.register_task(loader.Stage.SETUP, {
|
||||||
name: "page setup",
|
name: "page setup",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
|
|
|
@ -45,19 +45,6 @@ export default class implements ApplicationLoader {
|
||||||
priority: 10
|
priority: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.register_task(loader.Stage.TEMPLATES, {
|
|
||||||
name: "templates",
|
|
||||||
function: async () => {
|
|
||||||
await loader.templates.load_multiple([
|
|
||||||
"templates.html"
|
|
||||||
], {
|
|
||||||
cache_tag: "?22",
|
|
||||||
max_parallel_requests: -1
|
|
||||||
});
|
|
||||||
},
|
|
||||||
priority: 10
|
|
||||||
});
|
|
||||||
|
|
||||||
loader.execute_managed(false);
|
loader.execute_managed(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,14 +13,11 @@ loader.register_task(Stage.SETUP, {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__native_client_init_hook();
|
window.__native_client_init_hook();
|
||||||
window.native_client = true;
|
|
||||||
} else {
|
} else {
|
||||||
if(__build.target !== "web") {
|
if(__build.target !== "web") {
|
||||||
loader.critical_error("App seems not to be compiled for the web.", "This app has been compiled for " + __build.target);
|
loader.critical_error("App seems not to be compiled for the web.", "This app has been compiled for " + __build.target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.native_client = false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
priority: 1000
|
priority: 1000
|
||||||
|
@ -50,7 +47,6 @@ loader.register_task(Stage.SETUP, {
|
||||||
case "ie":
|
case "ie":
|
||||||
loader.critical_error("Browser not supported", "We're sorry, but your browser isn't supported.");
|
loader.critical_error("Browser not supported", "We're sorry, but your browser isn't supported.");
|
||||||
throw "unsupported browser";
|
throw "unsupported browser";
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
priority: 50
|
priority: 50
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
**/*.css
|
|
||||||
**/*.css.map
|
|
|
@ -1,10 +1,3 @@
|
||||||
<%
|
|
||||||
/* given on compile time */
|
|
||||||
var build_target;
|
|
||||||
var initial_script;
|
|
||||||
var initial_css;
|
|
||||||
%>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -16,20 +9,20 @@ var initial_css;
|
||||||
|
|
||||||
<meta name="og:description" content="The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak." />
|
<meta name="og:description" content="The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak." />
|
||||||
<meta name="og:url" content="https://web.teaspeak.de/">
|
<meta name="og:url" content="https://web.teaspeak.de/">
|
||||||
<%# TODO: Put in an appropirate image <meta name="og:image" content="https://www.whatsapp.com/img/whatsapp-promo.png"> %>
|
<% /* TODO: Put in an appropriate image <meta name="og:image" content="https://www.whatsapp.com/img/whatsapp-promo.png"> */ %>
|
||||||
|
|
||||||
<%# Using an absolute path here since the manifest.json works only with such. %>
|
<% /* Using an absolute path here since the manifest.json works only with such. */ %>
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
|
||||||
<% if(build_target === "client") { %>
|
<% if(buildTarget === "client") { %>
|
||||||
<title>TeaClient</title>
|
<title>TeaClient</title>
|
||||||
<meta name='og:title' content='TeaClient'>
|
<meta name='og:title' content='TeaClient'>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<title>TeaSpeak-Web</title>
|
<title>TeaSpeak-Web</title>
|
||||||
<meta name='og:title' content='TeaSpeak-Web'>
|
<meta name='og:title' content='TeaSpeak-Web'>
|
||||||
<link rel='shortcut icon' href='img/favicon/teacup.png' type='image/x-icon' id="favicon">
|
<link rel='shortcut icon' href='img/favicon/teacup.png' type='image/x-icon' id="favicon">
|
||||||
<%# <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"> */ %>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="format-detection" content="telephone=no">
|
<meta name="format-detection" content="telephone=no">
|
||||||
|
@ -49,10 +42,10 @@ var initial_css;
|
||||||
|
|
||||||
<link rel="preload" as="image" href="img/loader/initial-sequence.gif">
|
<link rel="preload" as="image" href="img/loader/initial-sequence.gif">
|
||||||
<link rel="preload" as="image" href="img/loader/bowl.png">
|
<link rel="preload" as="image" href="img/loader/bowl.png">
|
||||||
<%# We don't preload the bowl since it's only a div background %>
|
<% /* We don't preload the bowl since it's only a div background */ %>
|
||||||
<link rel="preload" as="image" href="img/loader/text.png">
|
<link rel="preload" as="image" href="img/loader/text.png">
|
||||||
|
|
||||||
<%- initial_css %>
|
<%= htmlWebpackPlugin.tags.headTags %>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- No javascript error -->
|
<!-- No javascript error -->
|
||||||
|
@ -101,6 +94,6 @@ var initial_css;
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%- initial_script %>
|
<%= htmlWebpackPlugin.tags.bodyTags %>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
|
@ -12,7 +12,8 @@
|
||||||
"build-client": "webpack --config webpack-client.config.js",
|
"build-client": "webpack --config webpack-client.config.js",
|
||||||
"webpack-web": "webpack --config webpack-web.config.js",
|
"webpack-web": "webpack --config webpack-web.config.js",
|
||||||
"webpack-client": "webpack --config webpack-client.config.js",
|
"webpack-client": "webpack --config webpack-client.config.js",
|
||||||
"generate-i18n-gtranslate": "node shared/generate_i18n_gtranslate.js"
|
"generate-i18n-gtranslate": "node shared/generate_i18n_gtranslate.js",
|
||||||
|
"dev-server": "webpack serve --config webpack-web.config.js"
|
||||||
},
|
},
|
||||||
"author": "TeaSpeak (WolverinDEV)",
|
"author": "TeaSpeak (WolverinDEV)",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -29,12 +30,13 @@
|
||||||
"@types/html-minifier": "^3.5.3",
|
"@types/html-minifier": "^3.5.3",
|
||||||
"@types/jquery": "^3.3.34",
|
"@types/jquery": "^3.3.34",
|
||||||
"@types/jsrender": "^1.0.5",
|
"@types/jsrender": "^1.0.5",
|
||||||
"@types/loader-utils": "^1.1.3",
|
|
||||||
"@types/lodash": "^4.14.149",
|
"@types/lodash": "^4.14.149",
|
||||||
"@types/moment": "^2.13.0",
|
"@types/moment": "^2.13.0",
|
||||||
"@types/node": "^12.7.2",
|
"@types/node": "^12.7.2",
|
||||||
"@types/react-color": "^3.0.4",
|
"@types/react-color": "^3.0.4",
|
||||||
"@types/react-dom": "^16.9.5",
|
"@types/react-dom": "^16.9.5",
|
||||||
|
"@types/react-grid-layout": "^1.1.1",
|
||||||
|
"@types/react-transition-group": "^4.4.0",
|
||||||
"@types/remarkable": "^1.7.4",
|
"@types/remarkable": "^1.7.4",
|
||||||
"@types/sdp-transform": "^2.4.4",
|
"@types/sdp-transform": "^2.4.4",
|
||||||
"@types/sha256": "^0.2.0",
|
"@types/sha256": "^0.2.0",
|
||||||
|
@ -43,13 +45,11 @@
|
||||||
"@types/xml-parser": "^1.2.29",
|
"@types/xml-parser": "^1.2.29",
|
||||||
"@wasm-tool/wasm-pack-plugin": "^1.3.1",
|
"@wasm-tool/wasm-pack-plugin": "^1.3.1",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"chunk-manifest-webpack-plugin": "^1.1.2",
|
|
||||||
"circular-dependency-plugin": "^5.2.0",
|
"circular-dependency-plugin": "^5.2.0",
|
||||||
"clean-css": "^4.2.1",
|
"clean-css": "^4.2.1",
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
"css-loader": "^3.6.0",
|
"css-loader": "^3.6.0",
|
||||||
"csso-cli": "^3.0.0",
|
"css-minimizer-webpack-plugin": "^1.3.0",
|
||||||
"ejs": "^3.0.2",
|
|
||||||
"exports-loader": "^0.7.0",
|
"exports-loader": "^0.7.0",
|
||||||
"fast-xml-parser": "^3.17.4",
|
"fast-xml-parser": "^3.17.4",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
|
@ -57,13 +57,16 @@
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"html-loader": "^1.0.0",
|
"html-loader": "^1.0.0",
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier": "^4.0.0",
|
||||||
"html-webpack-plugin": "^4.0.3",
|
"html-webpack-inline-source-plugin": "0.0.10",
|
||||||
|
"html-webpack-plugin": "^5.3.1",
|
||||||
|
"inline-chunks-html-webpack-plugin": "^1.3.1",
|
||||||
"mime-types": "^2.1.24",
|
"mime-types": "^2.1.24",
|
||||||
"mini-css-extract-plugin": "^0.9.0",
|
"mini-css-extract-plugin": "^1.3.9",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"potpack": "^1.0.1",
|
"potpack": "^1.0.1",
|
||||||
"raw-loader": "^4.0.0",
|
"raw-loader": "^4.0.0",
|
||||||
|
"react-dev-utils": "^11.0.4",
|
||||||
"sass": "1.22.10",
|
"sass": "1.22.10",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"sha256": "^0.2.0",
|
"sha256": "^0.2.0",
|
||||||
|
@ -76,12 +79,11 @@
|
||||||
"typescript": "^3.7.0",
|
"typescript": "^3.7.0",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"wabt": "^1.0.13",
|
"wabt": "^1.0.13",
|
||||||
"webpack": "^4.42.1",
|
"webpack": "^5.26.1",
|
||||||
"webpack-bundle-analyzer": "^3.6.1",
|
"webpack-bundle-analyzer": "^3.6.1",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^4.5.0",
|
||||||
"webpack-svg-sprite-generator": "^1.0.17",
|
"webpack-dev-server": "^3.11.2",
|
||||||
"worker-plugin": "^4.0.3",
|
"webpack-svg-sprite-generator": "^5.0.2"
|
||||||
"xml-parser": "^1.2.1"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -92,8 +94,6 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://www.teaspeak.de",
|
"homepage": "https://www.teaspeak.de",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react-grid-layout": "^1.1.1",
|
|
||||||
"@types/react-transition-group": "^4.4.0",
|
|
||||||
"broadcastchannel-polyfill": "^1.0.1",
|
"broadcastchannel-polyfill": "^1.0.1",
|
||||||
"detect-browser": "^5.2.0",
|
"detect-browser": "^5.2.0",
|
||||||
"dompurify": "^2.0.8",
|
"dompurify": "^2.0.8",
|
||||||
|
|
|
@ -43,6 +43,8 @@ if [[ "$1" == "full" ]]; then
|
||||||
echo "Full cleanup. Deleting generated javascript and css files"
|
echo "Full cleanup. Deleting generated javascript and css files"
|
||||||
cleanup_files "shared/js" "*.js" "JavaScript"
|
cleanup_files "shared/js" "*.js" "JavaScript"
|
||||||
cleanup_files "shared/js" "*.js.map" "JavaScript-Mapping"
|
cleanup_files "shared/js" "*.js.map" "JavaScript-Mapping"
|
||||||
|
cleanup_files "shared/js" "*.css" "JavaScript - CSS"
|
||||||
|
cleanup_files "shared/js" "*.css.map" "JavaScript - CSS - Mapping"
|
||||||
cleanup_files "shared/css/static/" "*.css" "CSS" # We only use SCSS, not CSS
|
cleanup_files "shared/css/static/" "*.css" "CSS" # We only use SCSS, not CSS
|
||||||
cleanup_files "shared/css/static/" "*.css.map" "CSS-Mapping"
|
cleanup_files "shared/css/static/" "*.css.map" "CSS-Mapping"
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cd "$(dirname "$0")" || exit 1
|
|
||||||
#find css/static/ -name '*.css' -exec cat {} \; | npm run csso -- --output `pwd`/generated/static/base.css
|
|
||||||
|
|
||||||
|
|
||||||
#File order
|
|
||||||
files=(
|
|
||||||
"css/static/properties.css"
|
|
||||||
"css/static/main-layout.css"
|
|
||||||
"css/static/general.css"
|
|
||||||
"css/static/channel-tree.css"
|
|
||||||
"css/static/connection_handlers.css"
|
|
||||||
"css/static/context_menu.css"
|
|
||||||
"css/static/frame-chat.css"
|
|
||||||
"css/static/server-log.css"
|
|
||||||
"css/static/scroll.css"
|
|
||||||
"css/static/hostbanner.css"
|
|
||||||
"css/static/htmltags.css"
|
|
||||||
"css/static/menu-bar.css"
|
|
||||||
"css/static/mixin.css"
|
|
||||||
"css/static/modal.css"
|
|
||||||
"css/static/modals.css"
|
|
||||||
"css/static/modal-about.css"
|
|
||||||
"css/static/modal-avatar.css"
|
|
||||||
"css/static/modal-banclient.css"
|
|
||||||
"css/static/modal-banlist.css"
|
|
||||||
"css/static/modal-bookmarks.css"
|
|
||||||
"css/static/modal-channel.css"
|
|
||||||
"css/static/modal-channelinfo.css"
|
|
||||||
"css/static/modal-clientinfo.css"
|
|
||||||
"css/static/modal-connect.css"
|
|
||||||
"css/static/modal-group-assignment.css"
|
|
||||||
"css/static/modal-icons.css"
|
|
||||||
"css/static/modal-identity.css"
|
|
||||||
"css/static/modal-newcomer.css"
|
|
||||||
"css/static/modal-invite.css"
|
|
||||||
"css/static/modal-keyselect.css"
|
|
||||||
"css/static/modal-poke.css"
|
|
||||||
"css/static/modal-query.css"
|
|
||||||
"css/static/modal-server.css"
|
|
||||||
"css/static/modal-musicmanage.css"
|
|
||||||
"css/static/modal-serverinfobandwidth.css"
|
|
||||||
"css/static/modal-serverinfo.css"
|
|
||||||
"css/static/modal-settings.css"
|
|
||||||
"css/static/overlay-image-preview.css"
|
|
||||||
|
|
||||||
"css/static/ts/tab.css"
|
|
||||||
"css/static/ts/chat.css"
|
|
||||||
"css/static/ts/icons.css"
|
|
||||||
"css/static/ts/icons_em.css"
|
|
||||||
"css/static/ts/country.css"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_file=`pwd`/../generated/static/base.css
|
|
||||||
|
|
||||||
if [[ ! -d $(dirname ${target_file}) ]]; then
|
|
||||||
echo "Creating target path ($(dirname "${target_file}"))"
|
|
||||||
mkdir -p $(dirname "${target_file}")
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
echo "Failed to create target path!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "/* Auto generated merged CSS file */" > "${target_file}"
|
|
||||||
for file in "${files[@]}"; do
|
|
||||||
if [[ ${file} =~ css/* ]]; then
|
|
||||||
file="./${file:4}"
|
|
||||||
fi
|
|
||||||
cat "${file}" >> "${target_file}"
|
|
||||||
done
|
|
||||||
|
|
||||||
cat "${target_file}" | npm run csso -- --output "$(pwd)/../generated/static/base.css"
|
|
|
@ -1,4 +1,9 @@
|
||||||
declare module "*.png" {
|
declare module "*.png" {
|
||||||
const value: any;
|
const value: any;
|
||||||
export = value;
|
export = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.html" {
|
||||||
|
const value: any;
|
||||||
|
export = value;
|
||||||
}
|
}
|
|
@ -583,4 +583,4 @@ loader.register_task(Stage.JAVASCRIPT_INITIALIZING,{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
priority: 100
|
priority: 100
|
||||||
});
|
});
|
|
@ -294,7 +294,8 @@ class IdentityPOWWorker {
|
||||||
private _initialized = false;
|
private _initialized = false;
|
||||||
|
|
||||||
async initialize(key: string) {
|
async initialize(key: string) {
|
||||||
this._worker = new Worker("tc-shared/workers/pow", { type: "module" });
|
// @ts-ignore
|
||||||
|
this._worker = new Worker(new URL("tc-shared/workers/pow", import.meta.url));
|
||||||
|
|
||||||
/* initialize */
|
/* initialize */
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
|
|
@ -228,21 +228,17 @@ if(typeof ($) !== "undefined") {
|
||||||
if(!$.fn.renderTag) {
|
if(!$.fn.renderTag) {
|
||||||
$.fn.renderTag = function (this: JQuery, values?: any) : JQuery {
|
$.fn.renderTag = function (this: JQuery, values?: any) : JQuery {
|
||||||
let result;
|
let result;
|
||||||
if(this.render) {
|
const template = $.views.templates[this.attr("id")];
|
||||||
result = $(this.render(values));
|
if(!template) {
|
||||||
} else {
|
console.error("Tried to render template %o, but template is not available!", this.attr("id"));
|
||||||
const template = jsrender.render[this.attr("id")];
|
throw "missing template " + this.attr("id");
|
||||||
if(!template) {
|
|
||||||
console.error("Tried to render template %o, but template is not available!", this.attr("id"));
|
|
||||||
throw "missing template " + this.attr("id");
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
result = window.jsrender.templates("tmpl_permission_entry", $("#tmpl_permission_entry").html());
|
|
||||||
result = window.jsrender.templates("xxx", this.html());
|
|
||||||
*/
|
|
||||||
result = template(values);
|
|
||||||
result = $(result);
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
result = window.jsrender.templates("tmpl_permission_entry", $("#tmpl_permission_entry").html());
|
||||||
|
result = window.jsrender.templates("xxx", this.html());
|
||||||
|
*/
|
||||||
|
result = template(values);
|
||||||
|
result = $(result);
|
||||||
result.find("node").each((index, element) => {
|
result.find("node").each((index, element) => {
|
||||||
$(element).replaceWith(values[$(element).attr("key")] || (values[0] || [])[$(element).attr("key")]);
|
$(element).replaceWith(values[$(element).attr("key")] || (values[0] || [])[$(element).attr("key")]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {ChannelTreeRenderer} from "tc-shared/ui/tree/Renderer";
|
||||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||||
|
|
||||||
const cssStyle = require("./AppRenderer.scss");
|
const cssStyle = require("./AppRenderer.scss");
|
||||||
|
|
||||||
const VideoFrame = React.memo((props: { events: Registry<AppUiEvents> }) => {
|
const VideoFrame = React.memo((props: { events: Registry<AppUiEvents> }) => {
|
||||||
const refElement = React.useRef<HTMLDivElement>();
|
const refElement = React.useRef<HTMLDivElement>();
|
||||||
const [ container, setContainer ] = useState<HTMLDivElement | undefined>(() => {
|
const [ container, setContainer ] = useState<HTMLDivElement | undefined>(() => {
|
||||||
|
|
|
@ -2,6 +2,26 @@ import * as loader from "tc-loader";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {LogCategory, logError, logTrace} from "../log";
|
import {LogCategory, logError, logTrace} from "../log";
|
||||||
import {tr} from "tc-shared/i18n/localize";
|
import {tr} from "tc-shared/i18n/localize";
|
||||||
|
import TemplateFile from "../../html/templates.html";
|
||||||
|
import TemplateMusicManage from "../../html/templates/modal/musicmanage.html";
|
||||||
|
import TemplateNewComer from "../../html/templates/modal/newcomer.html";
|
||||||
|
|
||||||
|
function initializeHtml(html: string) {
|
||||||
|
const hangingPoint = document.getElementById("templates");
|
||||||
|
|
||||||
|
const node = document.createElement("html");
|
||||||
|
node.innerHTML = html;
|
||||||
|
for(const element of node.getElementsByClassName("jsrender-template")) {
|
||||||
|
if(!$.templates(element.id, element.innerHTML)) {
|
||||||
|
logError(LogCategory.GENERAL, tr("Failed to setup cache for js renderer template %s!"), element.id);
|
||||||
|
} else {
|
||||||
|
logTrace(LogCategory.GENERAL, tr("Successfully loaded jsrender template %s"), element.id);
|
||||||
|
|
||||||
|
const elem = document.createElement("div");
|
||||||
|
elem.id = element.id;
|
||||||
|
hangingPoint?.appendChild(elem); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function setupJSRender() : boolean {
|
export function setupJSRender() : boolean {
|
||||||
if(!$.views) {
|
if(!$.views) {
|
||||||
|
@ -25,11 +45,8 @@ export function setupJSRender() : boolean {
|
||||||
return /* @tr-ignore */ tr(args[0]);
|
return /* @tr-ignore */ tr(args[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".jsrender-template").each((idx, _entry) => {
|
initializeHtml(TemplateFile);
|
||||||
if(!$.templates(_entry.id, _entry.innerHTML)) {
|
initializeHtml(TemplateMusicManage);
|
||||||
logError(LogCategory.GENERAL, tr("Failed to setup cache for js renderer template %s!"), _entry.id);
|
initializeHtml(TemplateNewComer);
|
||||||
} else
|
|
||||||
logTrace(LogCategory.GENERAL, tr("Successfully loaded jsrender template %s"), _entry.id);
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
|
@ -14,12 +14,9 @@
|
||||||
"webpack-web.config.ts",
|
"webpack-web.config.ts",
|
||||||
|
|
||||||
"webpack/build-definitions.d.ts",
|
"webpack/build-definitions.d.ts",
|
||||||
"webpack/ManifestPlugin.ts",
|
"webpack/HtmlWebpackInlineSource.ts",
|
||||||
"webpack/EJSGenerator.ts",
|
|
||||||
"webpack/WatLoader.ts",
|
"webpack/WatLoader.ts",
|
||||||
"webpack/DevelBlocks.ts",
|
"webpack/ManifestPlugin.ts",
|
||||||
|
|
||||||
"loader/IndexGenerator.ts",
|
|
||||||
|
|
||||||
"babel.config.ts",
|
"babel.config.ts",
|
||||||
"file.ts"
|
"file.ts"
|
||||||
|
|
|
@ -25,6 +25,7 @@ async function initializeFaviconRenderer() {
|
||||||
|
|
||||||
iconImage = new Image();
|
iconImage = new Image();
|
||||||
iconImage.src = kClientSpriteUrl;
|
iconImage.src = kClientSpriteUrl;
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
iconImage.onload = resolve;
|
iconImage.onload = resolve;
|
||||||
iconImage.onerror = () => reject("failed to load client icon sprite");
|
iconImage.onerror = () => reject("failed to load client icon sprite");
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as config_base from "./webpack.config";
|
||||||
|
|
||||||
export = () => config_base.config("client").then(config => {
|
export = () => config_base.config("client").then(config => {
|
||||||
Object.assign(config.entry, {
|
Object.assign(config.entry, {
|
||||||
"client-app": "./client/app/index.ts"
|
"client-app": ["./client/app/index.ts"]
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(config.resolve.alias, {
|
Object.assign(config.resolve.alias, {
|
||||||
|
@ -15,7 +15,7 @@ export = () => config_base.config("client").then(config => {
|
||||||
if(!Array.isArray(config.externals))
|
if(!Array.isArray(config.externals))
|
||||||
throw "invalid config";
|
throw "invalid config";
|
||||||
|
|
||||||
config.externals.push((context, request, callback) => {
|
config.externals.push(({ context, request }, callback) => {
|
||||||
if (request.startsWith("tc-backend/")) {
|
if (request.startsWith("tc-backend/")) {
|
||||||
return callback(null, `window["backend-loader"].require("${request}")`);
|
return callback(null, `window["backend-loader"].require("${request}")`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ import * as config_base from "./webpack.config";
|
||||||
|
|
||||||
export = () => config_base.config("web").then(config => {
|
export = () => config_base.config("web").then(config => {
|
||||||
Object.assign(config.entry, {
|
Object.assign(config.entry, {
|
||||||
"shared-app": "./web/app/index.ts",
|
"shared-app": ["./web/app/index.ts"],
|
||||||
"modal-external": "./web/app/index-external.ts"
|
"modal-external": ["./web/app/index-external.ts"]
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(config.resolve.alias, {
|
Object.assign(config.resolve.alias, {
|
||||||
|
@ -13,7 +13,5 @@ export = () => config_base.config("web").then(config => {
|
||||||
"tc-backend": path.resolve(__dirname, "web/app"),
|
"tc-backend": path.resolve(__dirname, "web/app"),
|
||||||
});
|
});
|
||||||
|
|
||||||
config.node = config.node || {};
|
|
||||||
config.node["fs"] = "empty";
|
|
||||||
return Promise.resolve(config);
|
return Promise.resolve(config);
|
||||||
});
|
});
|
|
@ -3,20 +3,23 @@ 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 { Plugin as SvgSpriteGenerator } from "webpack-svg-sprite-generator";
|
|
||||||
|
|
||||||
import LoaderIndexGenerator from "./loader/IndexGenerator";
|
|
||||||
import {Configuration} from "webpack";
|
import {Configuration} from "webpack";
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
||||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
import { Plugin as SvgSpriteGenerator } from "webpack-svg-sprite-generator";
|
||||||
const ManifestGenerator = require("./webpack/ManifestPlugin");
|
const ManifestGenerator = require("./webpack/ManifestPlugin");
|
||||||
const WorkerPlugin = require('worker-plugin');
|
const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
|
||||||
|
|
||||||
|
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
|
||||||
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
|
||||||
|
|
||||||
export let isDevelopment = process.env.NODE_ENV === 'development';
|
export let isDevelopment = process.env.NODE_ENV === 'development';
|
||||||
console.log("Webpacking for %s (%s)", isDevelopment ? "development" : "production", process.env.NODE_ENV || "NODE_ENV not specified");
|
console.log("Webpacking for %s (%s)", isDevelopment ? "development" : "production", process.env.NODE_ENV || "NODE_ENV not specified");
|
||||||
|
@ -63,34 +66,60 @@ const isLoaderFile = (file: string) => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const config = async (target: "web" | "client"): Promise<Configuration> => ({
|
const generateIndexPlugin = (target: "web" | "client"): HtmlWebpackPlugin => {
|
||||||
|
const options: HtmlWebpackPlugin.Options & { inlineSource?: RegExp | string } = {};
|
||||||
|
|
||||||
|
options.cache = true;
|
||||||
|
options.chunks = ["loader"];
|
||||||
|
options.inject = false;
|
||||||
|
options.template = path.join(__dirname, "loader", "index.ejs");
|
||||||
|
options.templateParameters = { buildTarget: target };
|
||||||
|
options.scriptLoading = "defer";
|
||||||
|
|
||||||
|
if(!isDevelopment) {
|
||||||
|
options.minify = {
|
||||||
|
html5: true,
|
||||||
|
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeComments: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
removeScriptTypeAttributes: true,
|
||||||
|
removeTagWhitespace: true,
|
||||||
|
minifyCSS: true,
|
||||||
|
minifyJS: true,
|
||||||
|
minifyURLs: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
options.inlineSource = /\.(js|css)$/;
|
||||||
|
}
|
||||||
|
return new HtmlWebpackPlugin(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = async (target: "web" | "client"): Promise<Configuration & { devServer: any }> => ({
|
||||||
entry: {
|
entry: {
|
||||||
"loader": "./loader/app/index.ts",
|
"loader": ["./loader/app/index.ts"],
|
||||||
"modal-external": "./shared/js/ui/react-elements/external-modal/PopoutEntrypoint.ts",
|
"modal-external": ["./shared/js/ui/react-elements/external-modal/PopoutEntrypoint.ts"],
|
||||||
//"devel-main": "./shared/js/devel_main.ts"
|
//"devel-main": "./shared/js/devel_main.ts"
|
||||||
},
|
},
|
||||||
|
|
||||||
devtool: isDevelopment ? "inline-source-map" : undefined,
|
devtool: isDevelopment ? "inline-source-map" : undefined,
|
||||||
mode: isDevelopment ? "development" : "production",
|
mode: isDevelopment ? "development" : "production",
|
||||||
plugins: [
|
plugins: [
|
||||||
//new CleanWebpackPlugin(),
|
new CleanWebpackPlugin(),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: isDevelopment ? '[name].css' : '[name].[hash].css',
|
filename: isDevelopment ? '[name].css' : '[name].[contenthash].css',
|
||||||
chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
|
chunkFilename: isDevelopment ? '[id].css' : '[id].[contenthash].css',
|
||||||
|
ignoreOrder: true
|
||||||
}),
|
}),
|
||||||
new ManifestGenerator({
|
new ManifestGenerator({
|
||||||
file: path.join(__dirname, "dist/manifest.json"),
|
outputFileName: "manifest.json",
|
||||||
base: __dirname
|
context: __dirname
|
||||||
}),
|
}),
|
||||||
new WorkerPlugin(),
|
|
||||||
//new BundleAnalyzerPlugin(),
|
//new BundleAnalyzerPlugin(),
|
||||||
isDevelopment ? undefined : new webpack.optimize.AggressiveSplittingPlugin({
|
|
||||||
minSize: 1024 * 8,
|
|
||||||
maxSize: 1024 * 128
|
|
||||||
}),
|
|
||||||
new webpack.DefinePlugin(await generateDefinitions(target)),
|
new webpack.DefinePlugin(await generateDefinitions(target)),
|
||||||
new SvgSpriteGenerator({
|
new SvgSpriteGenerator({
|
||||||
dtsOutputFolder: path.join(__dirname, "shared", "svg-sprites"),
|
dtsOutputFolder: path.join(__dirname, "shared", "svg-sprites"),
|
||||||
|
publicPath: "js/",
|
||||||
configurations: {
|
configurations: {
|
||||||
"client-icons": {
|
"client-icons": {
|
||||||
folder: path.join(__dirname, "shared", "img", "client-icons"),
|
folder: path.join(__dirname, "shared", "img", "client-icons"),
|
||||||
|
@ -125,20 +154,22 @@ export const config = async (target: "web" | "client"): Promise<Configuration> =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new LoaderIndexGenerator({
|
generateIndexPlugin(target),
|
||||||
buildTarget: target,
|
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/.*/]),
|
||||||
output: path.join(__dirname, "dist/index.html"),
|
|
||||||
isDevelopment: isDevelopment
|
|
||||||
})
|
|
||||||
].filter(e => !!e),
|
].filter(e => !!e),
|
||||||
|
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.(s[ac]|c)ss$/,
|
test: /\.(s[ac]|c)ss$/,
|
||||||
loader: [
|
use: [
|
||||||
'style-loader',
|
//'style-loader',
|
||||||
//MiniCssExtractPlugin.loader,
|
{
|
||||||
|
loader: MiniCssExtractPlugin.loader,
|
||||||
|
options: {
|
||||||
|
esModule: false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
|
@ -161,7 +192,7 @@ export const config = async (target: "web" | "client"): Promise<Configuration> =
|
||||||
test: (module: string) => module.match(/\.tsx?$/) && !isLoaderFile(module),
|
test: (module: string) => module.match(/\.tsx?$/) && !isLoaderFile(module),
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
|
|
||||||
loader: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'ts-loader',
|
loader: 'ts-loader',
|
||||||
options: {
|
options: {
|
||||||
|
@ -185,7 +216,7 @@ export const config = async (target: "web" | "client"): Promise<Configuration> =
|
||||||
test: (module: string) => module.match(/\.tsx?$/) && isLoaderFile(module),
|
test: (module: string) => module.match(/\.tsx?$/) && isLoaderFile(module),
|
||||||
exclude: /(node_modules|bower_components)/,
|
exclude: /(node_modules|bower_components)/,
|
||||||
|
|
||||||
loader: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: "babel-loader",
|
loader: "babel-loader",
|
||||||
options: {
|
options: {
|
||||||
|
@ -202,24 +233,26 @@ export const config = async (target: "web" | "client"): Promise<Configuration> =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.was?t$/,
|
test: /\.was?t$/,
|
||||||
loader: [
|
use: [
|
||||||
"./webpack/WatLoader.js"
|
"./webpack/WatLoader.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.svg$/,
|
test: /\.svg$/,
|
||||||
loader: 'svg-inline-loader'
|
use: 'svg-inline-loader'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /ChangeLog\.md$/i,
|
test: /ChangeLog\.md$|\.html$/i,
|
||||||
loader: "raw-loader",
|
use: {
|
||||||
options: {
|
loader: "raw-loader",
|
||||||
esModule: false
|
options: {
|
||||||
}
|
esModule: false
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|jpg|jpeg|gif)?$/,
|
test: /\.(png|jpg|jpeg|gif)?$/,
|
||||||
loader: 'file-loader',
|
use: 'file-loader',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
} as any,
|
} as any,
|
||||||
|
@ -245,9 +278,19 @@ export const config = async (target: "web" | "client"): Promise<Configuration> =
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
chunks: "all"
|
chunks: "all",
|
||||||
|
maxSize: 512 * 1024
|
||||||
},
|
},
|
||||||
minimize: !isDevelopment,
|
minimize: !isDevelopment,
|
||||||
minimizer: [new TerserPlugin()]
|
minimizer: [
|
||||||
}
|
new TerserPlugin(),
|
||||||
|
new CssMinimizerPlugin()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
publicPath: "/",
|
||||||
|
contentBase: path.join(__dirname, 'dist'),
|
||||||
|
writeToDisk: true,
|
||||||
|
compress: true
|
||||||
|
},
|
||||||
});
|
});
|
|
@ -1,107 +0,0 @@
|
||||||
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 = webpack.compilation.Compilation;
|
|
||||||
|
|
||||||
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 generateEntryJsTag(compilation: Compilation) {
|
|
||||||
const entry_group = compilation.chunkGroups.find(e => e.options.name === this.options.initialJSEntryChunk);
|
|
||||||
if(!entry_group) return; /* not the correct compilation */
|
|
||||||
|
|
||||||
const tags = entry_group.chunks.map(chunk => {
|
|
||||||
if(chunk.files.length !== 1)
|
|
||||||
throw "invalid chunk file count";
|
|
||||||
|
|
||||||
const file = chunk.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 = fs.readFileSync(path.join(compilation.compiler.outputPath, file));
|
|
||||||
return `<script type="application/javascript">${script}</script>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return tags.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async generateEntryCssTag() {
|
|
||||||
if(this.options.embedInitialCSSFile) {
|
|
||||||
const style = await util.promisify(fs.readFile)(this.options.initialCSSFile.localFile);
|
|
||||||
return `<style>${style}</style>`
|
|
||||||
} else {
|
|
||||||
return `<link 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.generateEntryJsTag(compilation);
|
|
||||||
variables["initial_css"] = await this.generateEntryCssTag();
|
|
||||||
|
|
||||||
let generated = await ejs.render(input.toString(), variables, {
|
|
||||||
beautify: false, /* uglify is a bit dump and does not understands ES6 */
|
|
||||||
context: this
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
compiler.hooks.afterCompile.tapPromise(this.constructor.name, async compilation => {
|
|
||||||
const file = path.resolve(this.options.input);
|
|
||||||
if(compilation.fileDependencies.has(file))
|
|
||||||
return;
|
|
||||||
|
|
||||||
console.log("Adding additional watch to %s", file);
|
|
||||||
compilation.fileDependencies.add(file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export = EJSGenerator;
|
|
|
@ -1,65 +1,64 @@
|
||||||
import * as webpack from "webpack";
|
import * as webpack from "webpack";
|
||||||
import * as fs from "fs-extra";
|
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
file?: string;
|
outputFileName?: string;
|
||||||
base: string;
|
context: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ManifestGenerator {
|
class ManifestGenerator {
|
||||||
private manifest_content;
|
private readonly options: Options;
|
||||||
|
|
||||||
readonly options: Options;
|
|
||||||
constructor(options: Options) {
|
constructor(options: Options) {
|
||||||
this.options = options || { base: __dirname };
|
this.options = options || { context: __dirname };
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(compiler: webpack.Compiler) {
|
apply(compiler: webpack.Compiler) {
|
||||||
compiler.hooks.afterCompile.tap(this.constructor.name, compilation => {
|
compiler.hooks.emit.tap(this.constructor.name, compilation => {
|
||||||
const chunks_data = {};
|
const chunkData = {};
|
||||||
for(const chunkGroup of compilation.chunkGroups) {
|
for(const chunkGroup of compilation.chunkGroups) {
|
||||||
const fileJs = [];
|
const fileJs = [];
|
||||||
const filesCss = [];
|
const filesCss = [];
|
||||||
const modules = [];
|
const modules = [];
|
||||||
|
|
||||||
for(const chunk of chunkGroup.chunks) {
|
for(const chunk of chunkGroup.chunks) {
|
||||||
if(!chunk.files.length)
|
if(!chunk.files.size) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
|
||||||
if(chunk.files.length !== 1) {
|
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
for(const file of chunk.files) {
|
for(const file of chunk.files) {
|
||||||
const extension = path.extname(file);
|
const extension = path.extname(file);
|
||||||
if(extension === ".js") {
|
switch (extension) {
|
||||||
fileJs.push({
|
case ".js":
|
||||||
hash: chunk.hash,
|
fileJs.push({
|
||||||
file: file
|
hash: chunk.hash,
|
||||||
});
|
file: file
|
||||||
} else if(extension === ".css") {
|
});
|
||||||
filesCss.push({
|
break;
|
||||||
hash: chunk.hash,
|
|
||||||
file: file
|
case ".css":
|
||||||
});
|
filesCss.push({
|
||||||
} else if(extension === ".wasm") {
|
hash: chunk.hash,
|
||||||
/* do nothing */
|
file: file
|
||||||
} else {
|
});
|
||||||
throw "Unknown chunk file with extension " + extension;
|
break;
|
||||||
|
|
||||||
|
case ".wasm":
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw "Unknown chunk file with extension " + extension;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const module of chunk.getModules()) {
|
for(const module of chunk.getModules() as any[]) {
|
||||||
if(!module.type.startsWith("javascript/"))
|
if(!module.type.startsWith("javascript/")) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(!module.context)
|
if(!module.context) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(module.context.startsWith("svg-sprites/")) {
|
if(module.context.startsWith("svg-sprites/")) {
|
||||||
/* custom svg sprite handler */
|
/* custom svg sprite handler */
|
||||||
|
@ -71,37 +70,39 @@ class ManifestGenerator {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!module.resource)
|
if(!module.resource) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(module.context !== path.dirname(module.resource))
|
if(module.context !== path.dirname(module.resource)) {
|
||||||
throw "invalid context/resource relation";
|
throw "invalid context/resource relation";
|
||||||
|
}
|
||||||
|
|
||||||
modules.push({
|
modules.push({
|
||||||
id: module.id,
|
id: module.id,
|
||||||
context: path.relative(this.options.base, module.context).replace(/\\/g, "/"),
|
context: path.relative(this.options.context, module.context).replace(/\\/g, "/"),
|
||||||
resource: path.basename(module.resource)
|
resource: path.basename(module.resource)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chunks_data[chunkGroup.options.name] = {
|
chunkData[chunkGroup.options.name] = {
|
||||||
files: fileJs,
|
files: fileJs,
|
||||||
css_files: filesCss,
|
css_files: filesCss,
|
||||||
modules: modules
|
modules: modules
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.manifest_content = {
|
const payload = JSON.stringify({
|
||||||
version: 2,
|
version: 2,
|
||||||
chunks: chunks_data
|
chunks: chunkData
|
||||||
};
|
});
|
||||||
});
|
|
||||||
|
|
||||||
compiler.hooks.done.tap(this.constructor.name, () => {
|
const fileName = this.options.outputFileName || "manifest.json";
|
||||||
const file = this.options.file || "manifest.json";
|
compilation.assets[fileName] = {
|
||||||
fs.mkdirpSync(path.dirname(file));
|
size() { return payload.length; },
|
||||||
fs.writeFileSync(this.options.file || "manifest.json", JSON.stringify(this.manifest_content));
|
source() { return payload; }
|
||||||
|
} as any;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,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";
|
const filename = "module.wast";
|
||||||
|
|
||||||
export default function loader(this: LoaderContext, source: string | Buffer, sourceMap?: RawSourceMap): string | Buffer | void | undefined {
|
export default function loader(source: string | Buffer): string | Buffer | void | undefined {
|
||||||
this.cacheable();
|
this.cacheable();
|
||||||
|
|
||||||
const module = wabt.parseWat(filename, source);
|
const module = wabt.parseWat(filename, source);
|
||||||
|
|
Loading…
Reference in New Issue