Improved the webpack script and using constants for build version detection
parent
b297147f98
commit
c460d05ee5
11
file.ts
11
file.ts
|
@ -37,8 +37,6 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
|
|||
"path": "./",
|
||||
"local-path": "./shared/html/"
|
||||
},
|
||||
|
||||
|
||||
{ /* javascript files as manifest.json */
|
||||
"type": "js",
|
||||
"search-pattern": /.*$/,
|
||||
|
@ -47,15 +45,6 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [
|
|||
"path": "js/",
|
||||
"local-path": "./dist/"
|
||||
},
|
||||
{ /* loader javascript file */
|
||||
"type": "js",
|
||||
"search-pattern": /.*$/,
|
||||
"build-target": "dev|rel",
|
||||
|
||||
"path": "js/",
|
||||
"local-path": "./loader/dist/"
|
||||
},
|
||||
|
||||
{ /* shared developer single css files */
|
||||
"type": "css",
|
||||
"search-pattern": /.*\.css$/,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import * as loader from "./targets/app";
|
||||
import * as loader_base from "./loader/loader";
|
||||
|
||||
export = loader_base;
|
||||
/* let the loader register himself at the window first */
|
||||
setTimeout(loader.run, 0);
|
||||
window["loader"] = loader_base;
|
||||
/* let the loader register himself at the window first */
|
||||
setTimeout(loader.run, 0);
|
||||
|
||||
export {};
|
|
@ -1,5 +1,3 @@
|
|||
import {AppVersion} from "tc-loader";
|
||||
import {LoadSyntaxError, script_name} from "./utils";
|
||||
import * as script_loader from "./script_loader";
|
||||
import * as style_loader from "./style_loader";
|
||||
import * as template_loader from "./template_loader";
|
||||
|
@ -75,38 +73,35 @@ const tasks: {[key:number]:Task[]} = {};
|
|||
|
||||
/* test if all files shall be load from cache or fetch again */
|
||||
function loader_cache_tag() {
|
||||
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 || version == "unknown" || version.replace(/0+/, "").length == 0)
|
||||
return undefined;
|
||||
|
||||
return version;
|
||||
})();
|
||||
if(config.verbose) console.log("Found current app version: %o", app_version);
|
||||
|
||||
if(!app_version) {
|
||||
/* TODO add warning */
|
||||
if(__build.mode === "debug") {
|
||||
cache_tag = "?_ts=" + Date.now();
|
||||
return;
|
||||
}
|
||||
|
||||
const cached_version = localStorage.getItem("cached_version");
|
||||
if(!cached_version || cached_version != app_version) {
|
||||
if(!cached_version || cached_version !== __build.version) {
|
||||
register_task(Stage.LOADED, {
|
||||
priority: 0,
|
||||
name: "cached version updater",
|
||||
function: async () => {
|
||||
localStorage.setItem("cached_version", app_version);
|
||||
localStorage.setItem("cached_version", __build.version);
|
||||
}
|
||||
});
|
||||
}
|
||||
cache_tag = "?_version=" + app_version;
|
||||
cache_tag = "?_version=" + __build.version;
|
||||
}
|
||||
|
||||
export type ModuleMapping = {
|
||||
application: string,
|
||||
modules: {
|
||||
"id": string,
|
||||
"context": string,
|
||||
"resource": string
|
||||
}[]
|
||||
};
|
||||
const module_mapping_: ModuleMapping[] = [];
|
||||
export function module_mapping() : ModuleMapping[] { return module_mapping_; }
|
||||
|
||||
export function get_cache_version() { return cache_tag; }
|
||||
|
||||
export function finished() {
|
||||
|
@ -259,12 +254,6 @@ export function hide_overlay() {
|
|||
});
|
||||
}
|
||||
|
||||
/* versions management */
|
||||
let version_: AppVersion;
|
||||
export function version() : AppVersion { return version_; }
|
||||
export function set_version(version: AppVersion) { version_ = version; }
|
||||
|
||||
|
||||
/* critical error handler */
|
||||
export type ErrorHandler = (message: string, detail: string) => void;
|
||||
let _callback_critical_error: ErrorHandler;
|
||||
|
|
|
@ -31,38 +31,20 @@ interface Manifest {
|
|||
version: number;
|
||||
|
||||
chunks: {[key: string]: {
|
||||
hash: string,
|
||||
file: string
|
||||
}[]};
|
||||
}
|
||||
|
||||
interface BuildDefinitions {
|
||||
development: boolean,
|
||||
version: string
|
||||
}
|
||||
declare global {
|
||||
const __build: BuildDefinitions;
|
||||
files: {
|
||||
hash: string,
|
||||
file: string
|
||||
}[],
|
||||
modules: {
|
||||
id: string,
|
||||
context: string,
|
||||
resource: string
|
||||
}[]
|
||||
}};
|
||||
}
|
||||
|
||||
/* all javascript loaders */
|
||||
const loader_javascript = {
|
||||
detect_type: async () => {
|
||||
if(window.native_client) {
|
||||
loader.set_version({
|
||||
backend: "-",
|
||||
ui: ui_version(),
|
||||
debug_mode: __build.development,
|
||||
type: "native"
|
||||
});
|
||||
} else {
|
||||
loader.set_version({
|
||||
backend: "-",
|
||||
ui: ui_version(),
|
||||
debug_mode: __build.development,
|
||||
type: "web"
|
||||
});
|
||||
}
|
||||
},
|
||||
load_scripts: async () => {
|
||||
if(!window.require) {
|
||||
await loader.scripts.load(["vendor/jquery/jquery.min.js"], { cache_tag: cache_tag() });
|
||||
|
@ -101,15 +83,19 @@ const loader_javascript = {
|
|||
loader.critical_error("Failed to load manifest.json", error);
|
||||
throw "failed to load manifest.json";
|
||||
}
|
||||
if(manifest.version !== 1)
|
||||
if(manifest.version !== 2)
|
||||
throw "invalid manifest version";
|
||||
|
||||
const chunk_name = loader.version().type === "web" ? "shared-app" : "client-app";
|
||||
if(!Array.isArray(manifest.chunks[chunk_name])) {
|
||||
const chunk_name = __build.entry_chunk_name;
|
||||
if(typeof manifest.chunks[chunk_name] !== "object") {
|
||||
loader.critical_error("Missing entry chunk in manifest.json", "Chunk " + chunk_name + " is missing.");
|
||||
throw "missing entry chunk";
|
||||
}
|
||||
await loader.scripts.load_multiple(manifest.chunks[chunk_name].map(e => "js/" + e.file), {
|
||||
loader.module_mapping().push({
|
||||
application: chunk_name,
|
||||
modules: manifest.chunks[chunk_name].modules
|
||||
});
|
||||
await loader.scripts.load_multiple(manifest.chunks[chunk_name].files.map(e => "js/" + e.file), {
|
||||
cache_tag: undefined,
|
||||
max_parallel_requests: -1
|
||||
});
|
||||
|
@ -154,7 +140,7 @@ const loader_style = {
|
|||
["vendor/highlight/styles/darcula.css", ""], /* empty string means not required */
|
||||
], options);
|
||||
|
||||
if(loader.version().debug_mode) {
|
||||
if(__build.mode === "debug") {
|
||||
await loader_style.load_style_debug();
|
||||
} else {
|
||||
await loader_style.load_style_release();
|
||||
|
@ -293,12 +279,6 @@ loader.register_task(loader.Stage.INITIALIZING, {
|
|||
priority: 20
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.INITIALIZING, {
|
||||
name: "app type test",
|
||||
function: loader_javascript.detect_type,
|
||||
priority: 20
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "javascript",
|
||||
function: loader_javascript.load_scripts,
|
||||
|
@ -398,9 +378,14 @@ loader.register_task(loader.Stage.SETUP, {
|
|||
});
|
||||
|
||||
export function run() {
|
||||
window["Module"] = (window["Module"] || {}) as any;
|
||||
window["Module"] = (window["Module"] || {}) as any; /* Why? */
|
||||
|
||||
/* TeaClient */
|
||||
if(node_require) {
|
||||
if(__build.target !== "client") {
|
||||
loader.critical_error("App seems not to be compiled for the client.", "This app has been compiled for " + __build.target);
|
||||
return;
|
||||
}
|
||||
window.native_client = true;
|
||||
|
||||
const path = node_require("path");
|
||||
|
@ -415,6 +400,11 @@ export function run() {
|
|||
priority: 40
|
||||
});
|
||||
} else {
|
||||
if(__build.target !== "web") {
|
||||
loader.critical_error("App seems not to be compiled for the web.", "This app has been compiled for " + __build.target);
|
||||
return;
|
||||
}
|
||||
|
||||
window.native_client = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,15 @@ export enum Stage {
|
|||
DONE
|
||||
}
|
||||
|
||||
export function version() : AppVersion;
|
||||
export type ModuleMapping = {
|
||||
application: string,
|
||||
modules: {
|
||||
"id": string,
|
||||
"context": string,
|
||||
"resource": string
|
||||
}[]
|
||||
};
|
||||
export function module_mapping() : ModuleMapping;
|
||||
|
||||
export function finished();
|
||||
export function running();
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
const webpack = require("webpack");
|
||||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
let isDevelopment = process.env.NODE_ENV === 'development';
|
||||
isDevelopment = true;
|
||||
module.exports = {
|
||||
entry: path.join(__dirname, "app/index.ts"),
|
||||
devtool: 'inline-source-map',
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: isDevelopment ? '[name].css' : '[name].[hash].css',
|
||||
chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
__build: {
|
||||
development: isDevelopment,
|
||||
version: '0000' //TODO!
|
||||
}
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.s[ac]ss$/,
|
||||
loader: [
|
||||
//isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: true,
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: /node_modules/,
|
||||
|
||||
loader: [
|
||||
{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js', ".scss"],
|
||||
alias: { }
|
||||
},
|
||||
output: {
|
||||
filename: 'loader.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
library: "loader",
|
||||
libraryTarget: "window" //"var" | "assign" | "this" | "window" | "self" | "global" | "commonjs" | "commonjs2" | "commonjs-module" | "amd" | "amd-require" | "umd" | "umd2" | "jsonp" | "system"
|
||||
},
|
||||
optimization: { }
|
||||
};
|
|
@ -21,9 +21,7 @@
|
|||
"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",
|
||||
"build-loader": "webpack --config loader/webpack.config.js",
|
||||
"watch-loader": "webpack --watch --config loader/webpack.config.js"
|
||||
"watch-client": "webpack --watch --config webpack-client.config.js"
|
||||
},
|
||||
"author": "TeaSpeak (WolverinDEV)",
|
||||
"license": "ISC",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as log from "tc-shared/log";
|
||||
import * as hex from "tc-shared/crypto/hex";
|
||||
import {LogCategory} from "tc-shared/log";
|
||||
import {ChannelEntry} from "tc-shared/ui/channel";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
|
|
|
@ -126,7 +126,7 @@ export namespace bbcode {
|
|||
},
|
||||
name: tr("Open URL in Browser"),
|
||||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
visible: loader.version().type === "native" && false // Currently not possible
|
||||
visible: __build.target === "client" && false // Currently not possible
|
||||
}, contextmenu.Entry.HR(), {
|
||||
callback: () => copy_to_clipboard(url),
|
||||
name: tr("Copy URL to clipboard"),
|
||||
|
|
|
@ -250,7 +250,7 @@ export class Group {
|
|||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "log enabled initialisation",
|
||||
function: async () => initialize(loader.version().debug_mode ? LogType.TRACE : LogType.INFO),
|
||||
function: async () => initialize(__build.mode === "debug" ? LogType.TRACE : LogType.INFO),
|
||||
priority: 150
|
||||
});
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ async function initialize_app() {
|
|||
try { //Initialize main template
|
||||
const main = $("#tmpl_main").renderTag({
|
||||
multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION),
|
||||
app_version: loader.version().ui
|
||||
app_version: __build.version
|
||||
}).dividerfy();
|
||||
|
||||
$("body").append(main);
|
||||
|
@ -589,7 +589,7 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
|||
try {
|
||||
await initialize();
|
||||
|
||||
if(loader.version().type == "web") {
|
||||
if(__build.target == "web") {
|
||||
loader.register_task(loader.Stage.LOADED, task_certificate_callback);
|
||||
} else {
|
||||
loader.register_task(loader.Stage.LOADED, task_teaweb_starter);
|
||||
|
|
|
@ -515,4 +515,4 @@ export class ServerSettings extends SettingsBase {
|
|||
}
|
||||
}
|
||||
|
||||
export let settings: Settings;
|
||||
export let settings: Settings = null;
|
|
@ -370,7 +370,7 @@ export function initialize() {
|
|||
return true;
|
||||
}};
|
||||
|
||||
if(loader.version().type !== "web") {
|
||||
if(__build.target !== "web") {
|
||||
menu.append_hr();
|
||||
|
||||
item = menu.append_item(tr("Quit"));
|
||||
|
@ -532,7 +532,7 @@ export function initialize() {
|
|||
{
|
||||
const menu = driver_.append_item(tr("Help"));
|
||||
|
||||
if(loader.version().type !== "web") {
|
||||
if(__build.target !== "web") {
|
||||
item = menu.append_item(tr("Check for updates"));
|
||||
item.click(() => native_actions.check_native_update());
|
||||
|
||||
|
@ -546,7 +546,7 @@ export function initialize() {
|
|||
item = menu.append_item(tr("Visit TeaSpeak forum"));
|
||||
item.click(() => window.open('https://forum.teaspeak.de/', '_blank'));
|
||||
|
||||
if(loader.version().type !== "web" && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) {
|
||||
if(__build.target !== "web" && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) {
|
||||
menu.append_hr();
|
||||
item = menu.append_item(tr("Open developer tools"));
|
||||
item.click(() => native_actions.open_dev_tools());
|
||||
|
@ -556,7 +556,7 @@ export function initialize() {
|
|||
}
|
||||
|
||||
menu.append_hr();
|
||||
item = menu.append_item(loader.version().type === "web" ? tr("About TeaWeb") : tr("About TeaClient"));
|
||||
item = menu.append_item(__build.target === "web" ? tr("About TeaWeb") : tr("About TeaClient"));
|
||||
item.click(() => spawnAbout())
|
||||
}
|
||||
|
||||
|
|
|
@ -27,9 +27,9 @@ export function spawnAbout() {
|
|||
header: tr("About"),
|
||||
body: () => {
|
||||
let tag = $("#tmpl_about").renderTag({
|
||||
client: loader.version().type !== "web",
|
||||
client: __build.target !== "web",
|
||||
|
||||
version_client: loader.version().type === "web" ? app_version || "in-dev" : "loading...",
|
||||
version_client: __build.target === "web" ? app_version || "in-dev" : "loading...",
|
||||
version_ui: app_version || "in-dev",
|
||||
|
||||
version_timestamp: !!app_version ? format_date(Date.now()) : "--"
|
||||
|
@ -43,7 +43,7 @@ export function spawnAbout() {
|
|||
connectModal.htmlTag.find(".modal-body").addClass("modal-about");
|
||||
connectModal.open();
|
||||
|
||||
if(loader.version().type !== "web") {
|
||||
if(__build.target !== "web") {
|
||||
(window as any).native.client_version().then(version => {
|
||||
connectModal.htmlTag.find(".version-client").text(version);
|
||||
}).catch(error => {
|
||||
|
|
|
@ -34,7 +34,7 @@ export function spawnKeySelect(callback: (key?: KeyEvent) => void) {
|
|||
|
||||
|
||||
button_save.on('click', () => {
|
||||
if(loader.version().type !== "web") {
|
||||
if(__build.version !== "web") {
|
||||
/* Because pressing the close button is also a mouse action */
|
||||
if(current_key_age + 1000 > Date.now() && current_key.key_code == "MOUSE2")
|
||||
current_key = last_key;
|
||||
|
|
|
@ -24,9 +24,9 @@ const last_step: {[key: string]:string} = (() => {
|
|||
|
||||
export function openModalNewcomer() : Modal {
|
||||
let modal = createModal({
|
||||
header: tra("Welcome to the {}", loader.version().type === "web" ? "TeaSpeak - Web client" : "TeaSpeak - Client"),
|
||||
header: tra("Welcome to the {}", __build.version === "web" ? "TeaSpeak - Web client" : "TeaSpeak - Client"),
|
||||
body: () => $("#tmpl_newcomer").renderTag({
|
||||
is_web: loader.version().type === "web"
|
||||
is_web: __build.version === "web"
|
||||
}).children(),
|
||||
footer: null,
|
||||
|
||||
|
|
|
@ -331,7 +331,7 @@ function settings_general_language(container: JQuery, modal: Modal) {
|
|||
}
|
||||
|
||||
container.find(".button-restart").on('click', () => {
|
||||
if(loader.version().type === "web") {
|
||||
if(__build.target === "web") {
|
||||
location.reload();
|
||||
} else {
|
||||
createErrorModal(tr("Not implemented"), tr("Client restart isn't implemented.<br>Please do it manually!")).open();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import * as sha1 from "../crypto/sha";
|
||||
|
||||
export function hashPassword(password: string) : Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
sha.sha1(password).then(result => {
|
||||
sha1.sha1(password).then(result => {
|
||||
resolve(btoa(String.fromCharCode.apply(null, new Uint8Array(result))));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@ function _generate(config: Configuration, node: ts.Node, result: TranslationEntr
|
|||
|
||||
node.forEachChild(n => _generate(config, n, result));
|
||||
}
|
||||
function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression, variables: { name: string, node: ts.Node }[]) : ts.Node[] {
|
||||
function create_unique_check(config: Configuration, source_file: ts.SourceFile, variable: ts.Expression, variables: { name: string, node: ts.Node }[]) : ts.Node[] {
|
||||
const nodes: ts.Node[] = [], blocked_nodes: ts.Statement[] = [];
|
||||
|
||||
const node_path = (node: ts.Node) => {
|
||||
|
@ -90,10 +90,10 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression
|
|||
|
||||
/* initialization */
|
||||
{
|
||||
const declarations = ts.createElementAccess(variable, ts.createLiteral("declared"));
|
||||
const declarations = ts.createElementAccess(variable, ts.createLiteral(config.variables.declarations));
|
||||
nodes.push(ts.createAssignment(declarations, ts.createBinary(declarations, SyntaxKind.BarBarToken, ts.createAssignment(declarations, ts.createObjectLiteral()))));
|
||||
|
||||
declarations_file = ts.createElementAccess(variable, ts.createLiteral("declared_files"));
|
||||
declarations_file = ts.createElementAccess(variable, ts.createLiteral(config.variables.declare_files));
|
||||
nodes.push(ts.createAssignment(declarations_file, ts.createBinary(declarations_file, SyntaxKind.BarBarToken, ts.createAssignment(declarations_file, ts.createObjectLiteral()))));
|
||||
|
||||
variable = declarations;
|
||||
|
@ -126,8 +126,8 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression
|
|||
const for_variable_name = ts.createLoopVariable();
|
||||
const for_variable_path = ts.createLoopVariable();
|
||||
const for_declaration = ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createObjectBindingPattern([
|
||||
ts.createBindingElement(undefined, "name", for_variable_name, undefined),
|
||||
ts.createBindingElement(undefined, "path", for_variable_path, undefined)])
|
||||
ts.createBindingElement(undefined, config.optimized ? "n": "name", for_variable_name, undefined),
|
||||
ts.createBindingElement(undefined, config.optimized ? "p": "path", for_variable_path, undefined)])
|
||||
, undefined, undefined)]);
|
||||
|
||||
let for_block: ts.Statement;
|
||||
|
@ -151,8 +151,8 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression
|
|||
let block = ts.createForOf(undefined,
|
||||
for_declaration, ts.createArrayLiteral(
|
||||
[...variables.map(e => ts.createObjectLiteral([
|
||||
ts.createPropertyAssignment("name", ts.createLiteral(e.name)),
|
||||
ts.createPropertyAssignment("path", ts.createLiteral(node_path(e.node)))
|
||||
ts.createPropertyAssignment(config.optimized ? "n": "name", ts.createLiteral(e.name)),
|
||||
ts.createPropertyAssignment(config.optimized ? "p": "path", ts.createLiteral(node_path(e.node)))
|
||||
]))
|
||||
])
|
||||
, for_block);
|
||||
|
@ -166,52 +166,69 @@ export function transform(config: Configuration, context: ts.TransformationConte
|
|||
const cache: VolatileTransformConfig = {} as any;
|
||||
cache.translations = [];
|
||||
|
||||
config.variables = (config.variables || {}) as any;
|
||||
config.variables.base = config.variables.base || (config.optimized ? "__tr" : "_translations");
|
||||
config.variables.declare_files = config.variables.declare_files || (config.optimized ? "f" : "declare_files");
|
||||
config.variables.declarations = config.variables.declarations || (config.optimized ? "d" : "definitions");
|
||||
|
||||
//Initialize nodes
|
||||
const extra_nodes: ts.Node[] = [];
|
||||
{
|
||||
cache.nodes = {} as any;
|
||||
if(config.use_window) {
|
||||
const window = ts.createIdentifier("window");
|
||||
let translation_map = ts.createPropertyAccess(window, ts.createIdentifier("_translations"));
|
||||
let translation_map = ts.createPropertyAccess(window, ts.createIdentifier(config.variables.base));
|
||||
const new_translations = ts.createAssignment(translation_map, ts.createObjectLiteral());
|
||||
|
||||
let translation_map_init: ts.Expression = ts.createBinary(translation_map, ts.SyntaxKind.BarBarToken, new_translations);
|
||||
translation_map_init = ts.createParen(translation_map_init);
|
||||
|
||||
extra_nodes.push(translation_map_init);
|
||||
cache.nodes = {
|
||||
translation_map: translation_map,
|
||||
translation_map_init: translation_map_init
|
||||
translation_map: translation_map
|
||||
};
|
||||
} else if(config.module) {
|
||||
cache.nodes = {
|
||||
translation_map: ts.createIdentifier(config.variables.base)
|
||||
};
|
||||
} else {
|
||||
const variable_name = "_translations";
|
||||
const variable_map = ts.createIdentifier(variable_name);
|
||||
|
||||
extra_nodes.push(ts.createVariableDeclarationList([
|
||||
ts.createVariableDeclaration(config.variables.base, undefined, ts.createObjectLiteral())
|
||||
], ts.NodeFlags.Const), ts.createToken(SyntaxKind.SemicolonToken));
|
||||
} else {
|
||||
const variable_map = ts.createIdentifier(config.variables.base);
|
||||
const inline_if = ts.createBinary(ts.createBinary(ts.createTypeOf(variable_map), SyntaxKind.ExclamationEqualsEqualsToken, ts.createLiteral("undefined")), ts.SyntaxKind.BarBarToken, ts.createAssignment(variable_map, ts.createObjectLiteral()));
|
||||
|
||||
cache.nodes = {
|
||||
translation_map: variable_map,
|
||||
translation_map_init: variable_map
|
||||
};
|
||||
|
||||
//ts.createVariableDeclarationList([ts.createVariableDeclaration(variable_name)], ts.NodeFlags.Let)
|
||||
extra_nodes.push(inline_if);
|
||||
}
|
||||
}
|
||||
|
||||
const used_names = [config.variables.declarations, config.variables.declare_files];
|
||||
const generated_names: { name: string, node: ts.Node }[] = [];
|
||||
let generator_base = 0;
|
||||
cache.name_generator = (config, node, message) => {
|
||||
const characters = "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
let name = "";
|
||||
while(name.length < 8) {
|
||||
const char = characters[Math.floor(Math.random() * characters.length)];
|
||||
name = name + char;
|
||||
if(name[0] >= '0' && name[0] <= '9')
|
||||
name = name.substr(1) || "";
|
||||
}
|
||||
let name;
|
||||
do {
|
||||
name = "";
|
||||
|
||||
if(config.module) {
|
||||
name = "_" + generator_base++;
|
||||
} else {
|
||||
/* Global namespace. We've to generate a random name so no duplicates happen */
|
||||
while(name.length < 8) {
|
||||
const char = characters[Math.floor(Math.random() * characters.length)];
|
||||
name = name + char;
|
||||
if(name[0] >= '0' && name[0] <= '9')
|
||||
name = name.substr(1) || "";
|
||||
}
|
||||
}
|
||||
} while(used_names.findIndex(e => e === name) !== -1);
|
||||
|
||||
//FIXME
|
||||
//if(generated_names.indexOf(name) != -1)
|
||||
// return cache.name_generator(config, node, message);
|
||||
generated_names.push({name: name, node: node});
|
||||
return name;
|
||||
};
|
||||
|
@ -221,7 +238,10 @@ export function transform(config: Configuration, context: ts.TransformationConte
|
|||
return replace_processor(config, cache, node, source_file);
|
||||
}
|
||||
source_file = ts.visitNode(source_file, visit);
|
||||
extra_nodes.push(...create_unique_check(source_file, cache.nodes.translation_map_init, generated_names));
|
||||
if(!config.module) {
|
||||
/* we don't need a unique check because we're just in our scope */
|
||||
extra_nodes.push(...create_unique_check(config, source_file, cache.nodes.translation_map, generated_names));
|
||||
}
|
||||
|
||||
source_file = ts.updateSourceFileNode(source_file, [...(extra_nodes as any[]), ...source_file.statements], source_file.isDeclarationFile, source_file.referencedFiles, source_file.typeReferenceDirectives, source_file.hasNoDefaultLib, source_file.referencedFiles);
|
||||
|
||||
|
@ -262,7 +282,7 @@ export function replace_processor(config: Configuration, cache: VolatileTransfor
|
|||
console.log("Message: %o", object.text || object.getText(source_file));
|
||||
|
||||
const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText(source_file)));
|
||||
const variable_init = ts.createPropertyAccess(cache.nodes.translation_map_init, variable_name);
|
||||
const variable_init = ts.createPropertyAccess(cache.nodes.translation_map, variable_name);
|
||||
|
||||
const variable = ts.createPropertyAccess(cache.nodes.translation_map, variable_name);
|
||||
const new_variable = ts.createAssignment(variable, call);
|
||||
|
@ -284,6 +304,15 @@ export interface Configuration {
|
|||
use_window?: boolean;
|
||||
replace_cache?: boolean;
|
||||
verbose?: boolean;
|
||||
|
||||
optimized?: boolean;
|
||||
module?: boolean;
|
||||
|
||||
variables?: {
|
||||
base: string,
|
||||
declarations: string,
|
||||
declare_files: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TransformResult {
|
||||
|
@ -294,7 +323,6 @@ export interface TransformResult {
|
|||
interface VolatileTransformConfig {
|
||||
nodes: {
|
||||
translation_map: ts.Expression;
|
||||
translation_map_init: ts.Expression;
|
||||
};
|
||||
|
||||
name_generator: (config: Configuration, node: ts.Node, message: string) => string;
|
||||
|
|
|
@ -9,6 +9,7 @@ import {TranslationEntry} from "./generator";
|
|||
export interface Config {
|
||||
target_file?: string;
|
||||
verbose?: boolean;
|
||||
optimized?: boolean;
|
||||
}
|
||||
|
||||
let process_config: Config;
|
||||
|
@ -37,6 +38,7 @@ export default function(program: ts.Program, config?: Config) : (context: ts.Tra
|
|||
return ctx => transformer(ctx) as any;
|
||||
}
|
||||
|
||||
let processed = [];
|
||||
const translations: TranslationEntry[] = [];
|
||||
const transformer = (context: ts.TransformationContext) =>
|
||||
(rootNode: ts.Node) => {
|
||||
|
@ -51,10 +53,17 @@ const transformer = (context: ts.TransformationContext) =>
|
|||
} else if(rootNode.kind == ts.SyntaxKind.SourceFile) {
|
||||
const file = rootNode as ts.SourceFile;
|
||||
|
||||
if(processed.findIndex(e => e === file.fileName) !== -1) {
|
||||
console.log("Skipping %s (already processed)", file.fileName);
|
||||
return rootNode;
|
||||
}
|
||||
processed.push(file.fileName);
|
||||
console.log("Processing " + file.fileName);
|
||||
const result = ts_generator.transform({
|
||||
use_window: false,
|
||||
replace_cache: true
|
||||
replace_cache: true,
|
||||
module: true,
|
||||
optimized: process_config.optimized
|
||||
}, context, file);
|
||||
translations.push(...result.translations);
|
||||
return result.node;
|
||||
|
|
|
@ -1,28 +1,21 @@
|
|||
import * as path from "path";
|
||||
const config = require("./webpack.config");
|
||||
import * as config_base from "./webpack.config";
|
||||
|
||||
let isDevelopment = process.env.NODE_ENV === 'development';
|
||||
isDevelopment = true;
|
||||
|
||||
config.entry = {
|
||||
const config = config_base.config();
|
||||
Object.assign(config.entry, {
|
||||
"client-app": "./client/js/index.ts"
|
||||
};
|
||||
});
|
||||
|
||||
config.resolve.alias = {
|
||||
Object.assign(config.resolve.alias, {
|
||||
"tc-shared": path.resolve(__dirname, "shared/js"),
|
||||
/* backend hasn't declared but its available via "require()" */
|
||||
"tc-backend": path.resolve(__dirname, "shared/backend.d"),
|
||||
};
|
||||
});
|
||||
|
||||
config.externals = [
|
||||
{
|
||||
"tc-loader": "window loader"
|
||||
},
|
||||
(context, request: string, callback) => {
|
||||
if (request.startsWith("tc-backend/"))
|
||||
return callback(null, `window["backend-loader"].require("${request}")`);
|
||||
callback();
|
||||
}
|
||||
];
|
||||
config.externals.push((context, request: string, callback) => {
|
||||
if (request.startsWith("tc-backend/"))
|
||||
return callback(null, `window["backend-loader"].require("${request}")`);
|
||||
callback();
|
||||
});
|
||||
|
||||
export = config;
|
|
@ -1,115 +1,16 @@
|
|||
import * as ts from "typescript";
|
||||
import trtransformer, {Config} from "./tools/trgen/ts_transformer";
|
||||
import * as path from "path";
|
||||
import * as config_base from "./webpack.config";
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require("webpack");
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
const ManifestGenerator = require("./webpack/ManifestPlugin");
|
||||
const WorkerPlugin = require('worker-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const config = config_base.config();
|
||||
Object.assign(config.entry, {
|
||||
"shared-app": "./web/js/index.ts"
|
||||
});
|
||||
|
||||
let isDevelopment = process.env.NODE_ENV === 'development';
|
||||
isDevelopment = true;
|
||||
module.exports = {
|
||||
entry: {
|
||||
"shared-app": "./web/js/index.ts"
|
||||
},
|
||||
devtool: isDevelopment ? "inline-source-map" : undefined,
|
||||
mode: isDevelopment ? "development" : "production",
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: isDevelopment ? '[name].css' : '[name].[hash].css',
|
||||
chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
|
||||
}),
|
||||
new ManifestGenerator({
|
||||
file: path.join(__dirname, "dist/manifest.json")
|
||||
}),
|
||||
new WorkerPlugin(),
|
||||
//new BundleAnalyzerPlugin()
|
||||
/*
|
||||
new CircularDependencyPlugin({
|
||||
//exclude: /a\.js|node_modules/,
|
||||
failOnError: true,
|
||||
allowAsyncCycles: false,
|
||||
cwd: process.cwd(),
|
||||
})
|
||||
*/
|
||||
new webpack.optimize.AggressiveSplittingPlugin({
|
||||
minSize: 1024 * 8,
|
||||
maxSize: 1024 * 128
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.s[ac]ss$/,
|
||||
loader: [
|
||||
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: true,
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: /node_modules/,
|
||||
Object.assign(config.resolve.alias, {
|
||||
"tc-shared": path.resolve(__dirname, "shared/js"),
|
||||
"tc-backend/web": path.resolve(__dirname, "web/js"),
|
||||
"tc-backend": path.resolve(__dirname, "web/js"),
|
||||
"tc-generated/codec/opus": path.resolve(__dirname, "asm/generated/TeaWeb-Worker-Codec-Opus.js"),
|
||||
});
|
||||
|
||||
loader: [
|
||||
{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true,
|
||||
getCustomTransformers: (prog: ts.Program) => {
|
||||
return {
|
||||
before: [trtransformer(prog, {})]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.was?t$/,
|
||||
loader: [
|
||||
"./webpack/WatLoader.js"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js', ".scss"],
|
||||
alias: {
|
||||
"tc-shared": path.resolve(__dirname, "shared/js"),
|
||||
"tc-backend/web": path.resolve(__dirname, "web/js"),
|
||||
"tc-backend": path.resolve(__dirname, "web/js"),
|
||||
"tc-generated/codec/opus": path.resolve(__dirname, "asm/generated/TeaWeb-Worker-Codec-Opus.js"),
|
||||
//"tc-backend": path.resolve(__dirname, "shared/backend.d"),
|
||||
},
|
||||
},
|
||||
externals: {
|
||||
"tc-loader": "window loader"
|
||||
},
|
||||
output: {
|
||||
filename: '[contenthash].js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
publicPath: "js/"
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: { },
|
||||
minimize: !isDevelopment,
|
||||
minimizer: [new TerserPlugin()]
|
||||
}
|
||||
};
|
||||
export = config;
|
|
@ -1,5 +1,6 @@
|
|||
import * as ts from "typescript";
|
||||
import trtransformer, {Config} from "./tools/trgen/ts_transformer";
|
||||
import * as fs from "fs";
|
||||
import trtransformer from "./tools/trgen/ts_transformer";
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require("webpack");
|
||||
|
@ -10,10 +11,32 @@ const WorkerPlugin = require('worker-plugin');
|
|||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
let isDevelopment = process.env.NODE_ENV === 'development';
|
||||
export let isDevelopment = process.env.NODE_ENV === 'development';
|
||||
isDevelopment = true;
|
||||
export = {
|
||||
entry: {}, /* will be individually set */
|
||||
|
||||
const generate_definitions = () => {
|
||||
const git_rev = fs.readFileSync(path.join(__dirname, ".git", "HEAD")).toString();
|
||||
let version;
|
||||
if(git_rev.indexOf("/") === -1)
|
||||
version = git_rev;
|
||||
else
|
||||
version = fs.readFileSync(path.join(__dirname, ".git", git_rev.substr(5).trim())).toString().substr(0, 7);
|
||||
|
||||
return {
|
||||
"__build": {
|
||||
target: JSON.stringify("web"),
|
||||
mode: JSON.stringify(isDevelopment ? "debug" : "release"),
|
||||
version: JSON.stringify(version),
|
||||
timestamp: Date.now(),
|
||||
entry_chunk_name: JSON.stringify("shared-app")
|
||||
} as BuildDefinitions
|
||||
} as any;
|
||||
};
|
||||
|
||||
export const config = () => { return {
|
||||
entry: {
|
||||
"loader": "./loader/app/index.ts"
|
||||
},
|
||||
|
||||
devtool: isDevelopment ? "inline-source-map" : undefined,
|
||||
mode: isDevelopment ? "development" : "production",
|
||||
|
@ -24,7 +47,8 @@ export = {
|
|||
chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
|
||||
}),
|
||||
new ManifestGenerator({
|
||||
file: path.join(__dirname, "dist/manifest.json")
|
||||
file: path.join(__dirname, "dist/manifest.json"),
|
||||
base: __dirname
|
||||
}),
|
||||
new WorkerPlugin(),
|
||||
//new BundleAnalyzerPlugin()
|
||||
|
@ -39,7 +63,8 @@ export = {
|
|||
isDevelopment ? undefined : new webpack.optimize.AggressiveSplittingPlugin({
|
||||
minSize: 1024 * 8,
|
||||
maxSize: 1024 * 128
|
||||
})
|
||||
}),
|
||||
new webpack.DefinePlugin(generate_definitions())
|
||||
].filter(e => !!e),
|
||||
module: {
|
||||
rules: [
|
||||
|
@ -73,7 +98,9 @@ export = {
|
|||
transpileOnly: true,
|
||||
getCustomTransformers: (prog: ts.Program) => {
|
||||
return {
|
||||
before: [trtransformer(prog, {})]
|
||||
before: [trtransformer(prog, {
|
||||
optimized: true
|
||||
})]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -90,19 +117,21 @@ export = {
|
|||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js', ".scss"],
|
||||
alias: { }, /* will be individually set */
|
||||
},
|
||||
externals: {
|
||||
"tc-loader": "window loader"
|
||||
alias: { },
|
||||
},
|
||||
externals: [
|
||||
{"tc-loader": "window loader"}
|
||||
] as any[],
|
||||
output: {
|
||||
filename: isDevelopment ? '[name].js' : '[contenthash].js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
publicPath: "js/"
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: { },
|
||||
splitChunks: {
|
||||
|
||||
},
|
||||
minimize: !isDevelopment,
|
||||
minimizer: [new TerserPlugin()]
|
||||
}
|
||||
};
|
||||
}};
|
|
@ -1,8 +1,10 @@
|
|||
import * as webpack from "webpack";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
interface Options {
|
||||
file?: string;
|
||||
base: string;
|
||||
}
|
||||
|
||||
class ManifestGenerator {
|
||||
|
@ -10,32 +12,51 @@ class ManifestGenerator {
|
|||
|
||||
readonly options: Options;
|
||||
constructor(options: Options) {
|
||||
this.options = options || {};
|
||||
this.options = options || { base: __dirname };
|
||||
}
|
||||
|
||||
apply(compiler: webpack.Compiler) {
|
||||
compiler.hooks.afterCompile.tap(this.constructor.name, compilation => {
|
||||
const chunks_data = {};
|
||||
for(const chunk_group of compilation.chunkGroups) {
|
||||
console.log(chunk_group.options.name);
|
||||
const js_files = [];
|
||||
const modules = [];
|
||||
|
||||
for(const chunk of chunk_group.chunks) {
|
||||
if(chunk.files.length !== 1) throw "expected only one file per chunk";
|
||||
|
||||
const file = chunk.files[0];
|
||||
console.log("Chunk: %s - %s - %s", chunk.id, chunk.hash, file);
|
||||
//console.log(chunk);
|
||||
//console.log(" - %s - %o", chunk.id, chunk);
|
||||
js_files.push({
|
||||
hash: chunk.hash,
|
||||
file: file
|
||||
})
|
||||
file: chunk.files[0]
|
||||
});
|
||||
|
||||
|
||||
for(const module of chunk._modules) {
|
||||
if(!module.type.startsWith("javascript/"))
|
||||
continue;
|
||||
|
||||
if(!module.resource || !module.context)
|
||||
continue;
|
||||
|
||||
if(module.context !== path.dirname(module.resource))
|
||||
throw "invalid context/resource relation";
|
||||
|
||||
modules.push({
|
||||
id: module.id,
|
||||
context: path.relative(this.options.base, module.context).replace(/\\/g, "/"),
|
||||
resource: path.basename(module.resource)
|
||||
});
|
||||
}
|
||||
}
|
||||
chunks_data[chunk_group.options.name] = js_files;
|
||||
|
||||
chunks_data[chunk_group.options.name] = {
|
||||
files: js_files,
|
||||
modules: modules
|
||||
};
|
||||
}
|
||||
|
||||
this.manifest_content = {
|
||||
version: 1,
|
||||
version: 2,
|
||||
chunks: chunks_data
|
||||
};
|
||||
});
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
declare global {
|
||||
interface BuildDefinitions {
|
||||
target: "web" | "client";
|
||||
mode: "release" | "debug";
|
||||
|
||||
/* chunk for the loader to load initially */
|
||||
entry_chunk_name: string;
|
||||
|
||||
version: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
const __build: BuildDefinitions;
|
||||
}
|
||||
|
||||
export {};
|
Loading…
Reference in New Issue