//------------------------------------------------------------------------------ // This app is configured to use typescript. // See https://webpack.js.org/concepts/ on how to configure this file. //------------------------------------------------------------------------------ "use-strict"; //------------------------------------------------------------------------------ const path = require("path"); const webpack = require("webpack"); // Configure envirement (NOTE: This must be done as early as possible) require("dotenv").config(); //------------------------------------------------------------------------------ const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const ESLintPlugin = require("eslint-webpack-plugin"); const CheckPortPlugin = require("./plugins/check-port"); const BuildFolderWiper = require("./plugins/build-wiper"); const rootDir = path.resolve(__dirname, "../"); const nodeModulesDir = path.join(rootDir, "node_modules"); const publicDir = path.join(rootDir, "src/assets/public"); const sourcesDir = path.join(rootDir, "src"); const buildDir = path.join(rootDir, "build"); const indexHtmlPath = path.join(rootDir, "index.html"); const entryPointFile = path.join(rootDir, "src/index.tsx"); //------------------------------------------------------------------------------ const checkRequiredFiles = require("react-dev-utils/checkRequiredFiles"); const eslintFormatter = require("react-dev-utils/eslintFormatter"); //------------------------------------------------------------------------------ const requiredFiles = [indexHtmlPath, entryPointFile]; if (!checkRequiredFiles(requiredFiles)) { process.exit(1); } //------------------------------------------------------------------------------ const mainTsRegex = /\.(ts|tsx)$/; const mainCssRegex = /\.(css)$/; const assetsRegex = /\.(jpg|jpeg|png|gif|mp3|mp4|svg)$/; //------------------------------------------------------------------------------ module.exports = function (_, argv) { const isDevelopment = argv.mode === "development"; const isServed = argv.env.WEBPACK_SERVE; const isProduction = argv.mode === "production"; const port = process.env.PORT || 8080; const getStyleLoaders = () => [ isDevelopment ? "style-loader" : MiniCssExtractPlugin.loader, { loader: "css-loader", options: { sourceMap: isDevelopment, }, }, ]; return { target: "web", entry: entryPointFile, output: { path: buildDir, filename: isDevelopment ? "static/js/[name].js" : isProduction && "static/js/[name].[contenthash:8].js", chunkFilename: isDevelopment ? "static/js/[name].chunk.js" : isProduction && "static/js/[name].[contenthash:8].chunk.js", publicPath: "/", assetModuleFilename: "assets/[hash][ext]", }, devtool: isDevelopment ? "source-map" : false, devServer: { client: { logging: "none", overlay: { // Show only errors on the browser errors: true, warnings: false, }, }, headers: [ // Security headers { key: "Strict-Transport-Security", value: "max-age=63072000; preload", }, { key: "X-XSS-Protection", value: "1; mode=block", }, { key: "X-Frame-Options", value: "deny", }, { key: "X-Content-Type-Options", value: "nosniff", }, { key: "Referrer-Policy", value: "origin-when-cross-origin", }, ], historyApiFallback: true, // Nessessary for react router to work host: "0.0.0.0", // Allow local network access to the server hot: true, port, }, module: { rules: [ { test: mainTsRegex, exclude: /node_modules/, use: ["ts-loader"], }, { test: mainCssRegex, exclude: /node_modules/, use: getStyleLoaders(), }, { test: assetsRegex, exclude: [ /node_modules/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/, ], use: [ { loader: "file-loader", options: { outputPath: "static/media", name: "[name].[contenthash:8].[ext]", }, }, ], }, { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: "asset/resource", }, ], }, resolve: { extensions: ["*", ".js", ".ts", ".tsx"], modules: [sourcesDir, nodeModulesDir], fallback: { contentBase: buildDir, events: false, url: false, }, }, plugins: [ new ESLintPlugin({ formatter: eslintFormatter }), new HtmlWebpackPlugin({ template: indexHtmlPath, }), isProduction && new MiniCssExtractPlugin({ filename: "static/css/[name].[contenthash:8].css", chunkFilename: "static/css/[name].[contenthash:8].chunk.css", }), new WebpackManifestPlugin({ fileName: "asset-manifest.json", publicPath: "/", generate: (seed, files, entrypoints) => { const manifestFiles = files.reduce((manifest, file) => { manifest[file.name] = file.path; return manifest; }, seed); const entrypointFiles = entrypoints.main.filter((fileName) => { return !fileName.endsWith(".map"); }); return { files: manifestFiles, entrypoints: entrypointFiles, }; }, }), new CopyWebpackPlugin({ patterns: [ { from: publicDir, to: ".", }, ], }), isServed && new CheckPortPlugin(port), isProduction && new BuildFolderWiper(buildDir), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(argv.mode), }), new webpack.EnvironmentPlugin([ // Pass all public env variables here ]), ].filter(Boolean), }; };