TeaWeb/webpack.config.ts

289 lines
10 KiB
TypeScript
Raw Normal View History

import * as fs from "fs";
import * as util from "util";
2021-03-17 13:00:35 +00:00
import * as path from "path";
import * as child_process from "child_process";
2020-08-08 22:08:56 +00:00
import { DefinePlugin, Configuration, } from "webpack";
import { Plugin as SvgSpriteGenerator } from "webpack-svg-sprite-generator";
2020-03-30 23:27:59 +00:00
const ManifestGenerator = require("./webpack/ManifestPlugin");
const HtmlWebpackInlineSourcePlugin = require("./webpack/HtmlWebpackInlineSource");
import HtmlWebpackPlugin from "html-webpack-plugin";
import {TranslateableWebpackPlugin} from "./tools/trgen/WebpackPlugin";
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
2020-03-31 13:19:53 +00:00
const TerserPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
2020-03-27 22:36:14 +00:00
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");
2021-03-16 15:40:17 +00:00
2021-03-16 14:59:22 +00:00
const generateDefinitions = async (target: string) => {
2021-03-16 15:40:17 +00:00
const gitRevision = fs.readFileSync(path.join(__dirname, ".git", "HEAD")).toString();
let version;
2021-03-16 15:40:17 +00:00
if(gitRevision.indexOf("/") === -1) {
version = (gitRevision || "0000000").substr(0, 7);
} else {
version = fs.readFileSync(path.join(__dirname, ".git", gitRevision.substr(5).trim())).toString().substr(0, 7);
}
let timestamp;
try {
const { stdout } = await util.promisify(child_process.exec)("git show -s --format=%ct");
timestamp = parseInt(stdout.toString());
2021-03-16 15:40:17 +00:00
if(isNaN(timestamp)) {
throw "failed to parse timestamp '" + stdout.toString() + "'";
}
} catch (error) {
console.error("Failed to get commit timestamp: %o", error);
throw "failed to get commit timestamp";
}
2021-03-16 15:40:17 +00:00
return {
"__build": {
target: JSON.stringify(target),
mode: JSON.stringify(isDevelopment ? "debug" : "release"),
version: JSON.stringify(version),
timestamp: timestamp,
entry_chunk_name: JSON.stringify(target === "web" ? "shared-app" : "client-app")
} as BuildDefinitions
2021-03-17 13:00:35 +00:00
} 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 (target: "web" | "client"): Promise<Configuration & { devServer: any }> => {
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"
},
2021-03-17 13:00:35 +00:00
devtool: isDevelopment ? "inline-source-map" : "source-map",
mode: isDevelopment ? "development" : "production",
plugins: [
new CleanWebpackPlugin(),
new DefinePlugin(await generateDefinitions(target)),
2021-03-17 13:00:35 +00:00
new CopyWebpackPlugin({
patterns: [
{ from: path.join(__dirname, "shared", "img"), to: 'img' },
{ from: path.join(__dirname, "shared", "i18n"), to: 'i18n' },
{ from: path.join(__dirname, "shared", "audio"), to: 'audio' }
]
}),
new MiniCssExtractPlugin({
filename: isDevelopment ? "[name].[contenthash].css" : "[contenthash].css",
chunkFilename: isDevelopment ? "[name].[contenthash].css" : "[contenthash].css",
2021-03-17 17:32:03 +00:00
ignoreOrder: true,
}),
2021-03-17 13:00:35 +00:00
new ManifestGenerator({
outputFileName: "manifest.json",
context: __dirname
}),
new SvgSpriteGenerator({
dtsOutputFolder: path.join(__dirname, "shared", "svg-sprites"),
publicPath: "js/",
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
2020-08-08 22:08:56 +00:00
}
}
}
}),
2021-03-17 13:00:35 +00:00
generateIndexPlugin(target),
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
2021-03-17 13:00:35 +00:00
translateablePlugin,
//new BundleAnalyzerPlugin(),
].filter(e => !!e),
module: {
rules: [
{
test: /\.(s[ac]|c)ss$/,
use: [
//'style-loader',
{
loader: MiniCssExtractPlugin.loader,
options: {
2021-03-17 17:32:03 +00:00
esModule: false,
}
},
{
loader: 'css-loader',
options: {
modules: {
mode: "local",
localIdentName: isDevelopment ? "[path][name]__[local]--[hash:base64:5]" : "[hash:base64]",
},
sourceMap: isDevelopment
}
},
{
2021-03-17 17:32:03 +00:00
loader: "postcss-loader"
},
{
loader: "sass-loader",
options: {
sourceMap: isDevelopment
}
2020-03-27 22:36:14 +00:00
}
]
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
2020-03-27 22:36:57 +00:00
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
}
2020-03-27 22:36:57 +00:00
}
]
},
{
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"
},
]
} 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 ? "[name].[contenthash].js" : "[contenthash].js",
chunkFilename: isDevelopment ? "[name].[contenthash].js" : "[contenthash].js",
path: path.resolve(__dirname, 'dist'),
publicPath: "/js/"
},
performance: {
hints: false
},
optimization: {
splitChunks: {
chunks: "all",
maxSize: 512 * 1024
},
minimize: !isDevelopment,
minimizer: [
new TerserPlugin(),
new CssMinimizerPlugin()
]
2020-07-17 21:56:20 +00:00
},
devServer: {
publicPath: "/js/",
contentBase: path.join(__dirname, 'dist'),
writeToDisk: true,
compress: true,
hot: false,
inline: false,
liveReload: false,
},
};
};