343 lines
12 KiB
TypeScript
343 lines
12 KiB
TypeScript
import * as fs from "fs";
|
|
import * as util from "util";
|
|
import * as path from "path";
|
|
import * as child_process from "child_process";
|
|
|
|
import {GeneratedAssetPlugin} from "./webpack/GeneratedAssetPlugin";
|
|
|
|
import { DefinePlugin, Configuration, } from "webpack";
|
|
|
|
import { Plugin as SvgSpriteGenerator } from "webpack-svg-sprite-generator";
|
|
import ManifestGenerator from "./webpack/ManifestPlugin";
|
|
import HtmlWebpackInlineSourcePlugin from "./webpack/HtmlWebpackInlineSource";
|
|
import TranslateableWebpackPlugin from "./tools/trgen/WebpackPlugin";
|
|
|
|
import ZipWebpackPlugin from "zip-webpack-plugin";
|
|
import HtmlWebpackPlugin from "html-webpack-plugin";
|
|
import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
|
import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
|
|
import TerserPlugin from "terser-webpack-plugin";
|
|
import CopyWebpackPlugin from "copy-webpack-plugin";
|
|
|
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
|
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
|
|
|
|
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");
|
|
|
|
interface LocalBuildInfo {
|
|
target: "client" | "web",
|
|
mode: "debug" | "release",
|
|
|
|
gitVersion: string,
|
|
gitTimestamp: number,
|
|
|
|
unixTimestamp: number,
|
|
localTimestamp: string
|
|
}
|
|
|
|
let localBuildInfo: LocalBuildInfo;
|
|
const generateLocalBuildInfo = async (target: string): Promise<LocalBuildInfo> => {
|
|
let info: LocalBuildInfo = {} as any;
|
|
|
|
info.target = target as any;
|
|
info.mode = isDevelopment ? "debug" : "release";
|
|
|
|
{
|
|
const gitRevision = fs.readFileSync(path.join(__dirname, ".git", "HEAD")).toString();
|
|
if(gitRevision.indexOf("/") === -1) {
|
|
info.gitVersion = (gitRevision || "0000000").substr(0, 7);
|
|
} else {
|
|
info.gitVersion = fs.readFileSync(path.join(__dirname, ".git", gitRevision.substr(5).trim())).toString().substr(0, 7);
|
|
}
|
|
|
|
try {
|
|
const { stdout } = await util.promisify(child_process.exec)("git show -s --format=%ct");
|
|
info.gitTimestamp = parseInt(stdout.toString());
|
|
if(isNaN(info.gitTimestamp)) {
|
|
throw "failed to parse timestamp '" + stdout.toString() + "'";
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to get commit timestamp: %o", error);
|
|
throw "failed to get commit timestamp";
|
|
}
|
|
}
|
|
|
|
info.unixTimestamp = Date.now();
|
|
info.localTimestamp = new Date().toString();
|
|
|
|
return info;
|
|
};
|
|
|
|
const generateDefinitions = async (target: string) => {
|
|
return {
|
|
"__build": {
|
|
target: JSON.stringify(target),
|
|
mode: JSON.stringify(isDevelopment ? "debug" : "release"),
|
|
version: JSON.stringify(localBuildInfo.gitVersion),
|
|
timestamp: localBuildInfo.gitTimestamp,
|
|
entry_chunk_name: JSON.stringify(target === "web" ? "shared-app" : "client-app")
|
|
} as BuildDefinitions
|
|
} as any;
|
|
};
|
|
|
|
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 (env: any, target: "web" | "client"): Promise<Configuration & { devServer: any }> => {
|
|
localBuildInfo = await generateLocalBuildInfo(target);
|
|
|
|
const translateablePlugin = new TranslateableWebpackPlugin({ assetName: "translations.json" });
|
|
|
|
return {
|
|
entry: {
|
|
"loader": "./loader/app/index.ts",
|
|
"modal-external": "./shared/js/ui/react-elements/external-modal/PopoutEntrypoint.ts",
|
|
//"devel-main": "./shared/js/devel_main.ts"
|
|
},
|
|
|
|
devtool: isDevelopment ? "inline-source-map" : "source-map",
|
|
mode: isDevelopment ? "development" : "production",
|
|
plugins: [
|
|
new CleanWebpackPlugin(),
|
|
|
|
new DefinePlugin(await generateDefinitions(target)),
|
|
new GeneratedAssetPlugin({
|
|
customFiles: [
|
|
{
|
|
assetName: "buildInfo.json",
|
|
content: JSON.stringify(localBuildInfo)
|
|
}
|
|
]
|
|
}),
|
|
new ManifestGenerator({
|
|
outputFileName: "manifest.json",
|
|
context: __dirname
|
|
}),
|
|
|
|
new CopyWebpackPlugin({
|
|
patterns: [
|
|
{
|
|
from: path.join(__dirname, "shared", "img"),
|
|
to: 'img',
|
|
globOptions: {
|
|
ignore: [
|
|
'**/client-icons/**',
|
|
//'**/style/**',
|
|
]
|
|
}
|
|
},
|
|
target === "web" ? { from: path.join(__dirname, "shared", "i18n"), to: 'i18n' } : undefined,
|
|
{ from: path.join(__dirname, "shared", "audio"), to: 'audio' }
|
|
].filter(e => !!e)
|
|
}),
|
|
|
|
new MiniCssExtractPlugin({
|
|
filename: isDevelopment ? "css/[name].[contenthash].css" : "css/[contenthash].css",
|
|
chunkFilename: isDevelopment ? "css/[name].[contenthash].css" : "css/[contenthash].css",
|
|
ignoreOrder: true,
|
|
|
|
}),
|
|
new SvgSpriteGenerator({
|
|
dtsOutputFolder: path.join(__dirname, "shared", "svg-sprites"),
|
|
publicPath: "/",
|
|
configurations: {
|
|
"client-icons": {
|
|
folder: path.join(__dirname, "shared", "img", "client-icons"),
|
|
cssClassPrefix: "client-",
|
|
cssOptions: [
|
|
{
|
|
scale: 1,
|
|
selector: ".icon",
|
|
unit: "px"
|
|
},
|
|
{
|
|
scale: 1.5,
|
|
selector: ".icon_x24",
|
|
unit: "px"
|
|
},
|
|
{
|
|
scale: 2,
|
|
selector: ".icon_x32",
|
|
unit: "px"
|
|
},
|
|
{
|
|
scale: 1,
|
|
selector: ".icon_em",
|
|
unit: "em"
|
|
}
|
|
],
|
|
dtsOptions: {
|
|
enumName: "ClientIcon",
|
|
classUnionName: "ClientIconClass",
|
|
module: false
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
|
|
generateIndexPlugin(target),
|
|
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
|
|
|
|
translateablePlugin,
|
|
//new BundleAnalyzerPlugin(),
|
|
|
|
env.package ? new ZipWebpackPlugin({
|
|
path: path.join(__dirname, "dist-package"),
|
|
filename: `TeaWeb-${isDevelopment ? "development" : "release"}-${localBuildInfo.gitVersion}.zip`,
|
|
}) : undefined
|
|
].filter(e => !!e),
|
|
|
|
module: {
|
|
rules: [
|
|
{
|
|
test: /\.(s[ac]|c)ss$/,
|
|
use: [
|
|
//'style-loader',
|
|
{
|
|
loader: MiniCssExtractPlugin.loader,
|
|
options: {
|
|
esModule: false,
|
|
}
|
|
},
|
|
{
|
|
loader: 'css-loader',
|
|
options: {
|
|
modules: {
|
|
mode: "local",
|
|
localIdentName: isDevelopment ? "[path][name]__[local]--[hash:base64:5]" : "[hash:base64]",
|
|
},
|
|
sourceMap: isDevelopment
|
|
}
|
|
},
|
|
{
|
|
loader: "postcss-loader"
|
|
},
|
|
{
|
|
loader: "sass-loader",
|
|
options: {
|
|
sourceMap: isDevelopment
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
test: /\.tsx?$/,
|
|
exclude: /node_modules/,
|
|
|
|
use: [
|
|
{
|
|
loader: "babel-loader",
|
|
options: {
|
|
presets: ["@babel/preset-env"]
|
|
}
|
|
},
|
|
{
|
|
loader: "ts-loader",
|
|
options: {
|
|
context: __dirname,
|
|
colors: true,
|
|
getCustomTransformers: program => ({
|
|
before: [translateablePlugin.createTypeScriptTransformer(program)]
|
|
}),
|
|
transpileOnly: isDevelopment
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
test: /\.was?t$/,
|
|
use: [
|
|
"./webpack/WatLoader.js"
|
|
]
|
|
},
|
|
{
|
|
test: /\.html$/i,
|
|
use: [translateablePlugin.createTemplateLoader()],
|
|
type: "asset/source",
|
|
},
|
|
{
|
|
test: /ChangeLog\.md$/i,
|
|
type: "asset/source",
|
|
},
|
|
{
|
|
test: /\.svg$/,
|
|
use: 'svg-inline-loader'
|
|
},
|
|
{
|
|
test: /\.(png|jpg|jpeg|gif)?$/,
|
|
type: "asset/resource",
|
|
generator: {
|
|
filename: 'img/[hash][ext][query]'
|
|
}
|
|
},
|
|
]
|
|
} as any,
|
|
resolve: {
|
|
extensions: ['.tsx', '.ts', '.js', ".scss"],
|
|
alias: {
|
|
"vendor/xbbcode": path.resolve(__dirname, "vendor/xbbcode/src"),
|
|
"tc-events": path.resolve(__dirname, "vendor/TeaEventBus/src/index.ts"),
|
|
"tc-services": path.resolve(__dirname, "vendor/TeaClientServices/src/index.ts"),
|
|
},
|
|
},
|
|
externals: [
|
|
{"tc-loader": "window loader"}
|
|
],
|
|
output: {
|
|
filename: isDevelopment ? "js/[name].[contenthash].js" : "js/[contenthash].js",
|
|
chunkFilename: isDevelopment ? "js/[name].[contenthash].js" : "js/[contenthash].js",
|
|
path: path.resolve(__dirname, "dist"),
|
|
publicPath: "/"
|
|
},
|
|
performance: {
|
|
hints: false
|
|
},
|
|
optimization: {
|
|
splitChunks: {
|
|
chunks: "all",
|
|
maxSize: 512 * 1024
|
|
},
|
|
minimize: !isDevelopment,
|
|
minimizer: [
|
|
new TerserPlugin(),
|
|
new CssMinimizerPlugin()
|
|
]
|
|
},
|
|
devServer: {
|
|
publicPath: "/",
|
|
contentBase: path.join(__dirname, 'dist'),
|
|
writeToDisk: true,
|
|
compress: true,
|
|
hot: false,
|
|
inline: false,
|
|
liveReload: false,
|
|
},
|
|
};
|
|
}; |