/* * Copyright (C) 2025 Comunicaciones Productivas Castro y Asociados, S. de R. L. de C.V. - All Rights Reserved * * This source code is protected under international copyright law. * All rights reserved and protected by the copyright holders. * This file is confidential and only available to authorized individuals with the permission of the copyright holders. * If you encounter this file and do not have permission, please contact the copyright holders and delete this file. */ import {fileURLToPath} from 'node:url'; import path from 'path'; import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import ChunksWebpackPlugin from 'chunks-webpack-plugin'; import webpack from 'webpack'; import autoprefixer from "autoprefixer"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Groups node_modules into one cacheable chunk per top-level package, so that // bumping one dependency does not invalidate the others' [contenthash]. const vendorChunkName = (module) => { const matched = module.context?.match(/[\\/]node_modules[\\/]((?:@[^\\/]+[\\/])?[^\\/]+)/); if (!matched) return 'vendor'; return `vendor-${matched[1].replace(/@/g, '').replace(/[\\/]/g, '-')}`; }; export default(_env, argv) => { const isProduction = argv.mode === 'production'; return { entry: { monitor: './src/main/resources/js/monitor.js', 'login-page': './src/main/resources/js/login-page.js', download: './src/main/resources/js/download.js', otp: './src/main/resources/js/otp.js', enroll: './src/main/resources/js/enroll.js', welcome: './src/main/resources/js/welcome.js', done: './src/main/resources/js/done.js', }, output: { path: path.resolve(__dirname, './build/resources/main/static/dist'), publicPath: '/dist/', // thymepack resolves assets through chunks-manifest.json, so hashed // filenames are transparent to the templates. filename: isProduction ? '[name].[contenthash].js' : '[name].js', chunkFilename: isProduction ? '[name].[contenthash].js' : '[name].js', assetModuleFilename: isProduction ? '[name].[contenthash][ext]' : '[name][ext]', clean: true }, // Emit source maps as separate assets so browser DevTools can request // them from /dist/ instead of relying on inline eval maps. devtool: isProduction ? false : 'source-map', mode: argv.mode, cache: { type: 'filesystem', buildDependencies: { config: [__filename] } }, optimization: { minimize: isProduction, // '...' keeps webpack's default JS minimizer (Terser) with its default options. minimizer: [ '...', new CssMinimizerPlugin({ minimizerOptions: { preset: ['default', { // Bootstrap uses relative-color calc() expressions that // postcss-calc cannot parse yet. Preserve them for browsers. calc: false }] } }) ], splitChunks: { chunks: 'all', cacheGroups: { defaultVendors: { test: /[\\/]node_modules[\\/]/, name: vendorChunkName, reuseExistingChunk: true } } } }, performance: { // preline's single 388 KiB module cannot be split; warning on every // build adds no information. Revisit if the payload is trimmed. hints: false }, devServer: { port: 8081, compress: false, hot: false, liveReload: true, static: { directory: path.resolve(__dirname, './src/main/resources/static'), publicPath: '/', watch: true, }, devMiddleware: { writeToDisk: true, }, watchFiles: [ './src/main/resources/templates/*.html', './src/main/resources/templates/**/*.html', './src/main/resources/messages.properties', './src/main/resources/messages_*.properties', './src/main/resources/js/*.js', './src/main/resources/js/**/*.js', './src/main/resources/styles/**/*', './src/main/resources/static/**/*', ], proxy: [ { context: (pathname) => ![ '/dist/', '/img/', '/favicon/', '/.well-known/', '/site.webmanifest', ].some((path) => pathname.startsWith(path)), target: "http://localhost:8080", secure: false, prependPath: false, } ], headers: { 'Cache-Control': 'no-store' } }, module: { rules: [ { // Consume Bootstrap's shipped maps before webpack creates // its own bundle map. This prevents DevTools from trying // to load Bootstrap's relative *.js.map files at /dist/. test: /\.js$/, enforce: 'pre', include: path.resolve(__dirname, 'node_modules/bootstrap/js/dist'), use: ['source-map-loader'] }, { // Bootstrap test: /\.scss$/, use: [ isProduction ? MiniCssExtractPlugin.loader : 'style-loader', { loader: 'css-loader' }, { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ autoprefixer, // ...(isProduction ? [purgeUnusedCss] : []) ] } } }, { loader: 'sass-loader' } ] }, { test: /\.css$/, use: [ isProduction ? MiniCssExtractPlugin.loader : 'style-loader', { loader: 'css-loader', options: { importLoaders: 1, sourceMap: !isProduction } }, { // @tailwindcss/postcss resolves @import/@source/@plugin and // handles vendor prefixing through Lightning CSS. loader: 'postcss-loader', options: { postcssOptions: { plugins: [ autoprefixer ] }, sourceMap: !isProduction } }, { loader: 'sass-loader' } ] }, { test: /\.woff2?$/, type: "asset/resource", }, ] }, resolve: { // @qr-platform/qr-code.js includes a Node-only dynamic Buffer import. // Its browser runtime never reaches that path, but Webpack still resolves it. fallback: { buffer: false, }, modules: [ path.resolve(__dirname, 'src/main/resources'), 'node_modules' ], extensions: [ '...' ], }, plugins: [ new webpack.ProvidePlugin({ // _: 'lodash', }), new ChunksWebpackPlugin({ generateChunksManifest: true, generateChunksFiles: false }), ...(isProduction ? [new MiniCssExtractPlugin({ filename: '[name].[contenthash].css', chunkFilename: '[name].[contenthash].css' })] : []), ], }; };