const webpack = require('webpack'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // const CircularDependencyPlugin = require('circular-dependency-plugin') // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const MomentLocalesPlugin = require('moment-locales-webpack-plugin'); //TODO: replace Moment with date-fns ?? const path = require('path'); const fs = require('fs'); const alias = require('./path-alias'); const environments = require('./env'); const DIST_DIR = 'public'; const GLOBAL_STYLES_DIR = 'app/styles'; const cssEntrypoints = [ 'codemirror/lib/codemirror.css', 'codemirror/theme/yeti.css', 'codemirror/addon/lint/lint.css', 'react-daterange-picker/dist/css/react-calendar.css', 'react-datepicker/dist/react-datepicker.css', 'rc-time-picker/assets/index.css', ]; const babelLoader = { loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { // probably, use dynamic imports for polifills in future "targets": "> 4%, not dead", useBuiltIns: 'entry', corejs: 3 }], '@babel/preset-react', "@babel/preset-flow", //TODO: remove, use ts ], plugins: [ "@babel/plugin-syntax-bigint", [ '@babel/plugin-proposal-decorators', { legacy: true } ], [ '@babel/plugin-proposal-class-properties', { loose: true }], [ '@babel/plugin-proposal-private-methods', { loose: true }], // 'recharts' ] } }; const cssFiles = fs.readdirSync(GLOBAL_STYLES_DIR, { withFileTypes: true }); cssFiles.forEach(file => { if (/.css$/.test(file.name)) { const pathFullName = path.join(__dirname, GLOBAL_STYLES_DIR, file.name); cssEntrypoints.push(pathFullName); } }); function prepareEnv(env) { const pEnv = {}; Object.keys(env).forEach(key => { pEnv[ `window.ENV.${ key }` ] = typeof env[ key ] === 'function' ? env[ key ]() : JSON.stringify(env[ key ]); }); return pEnv; } module.exports = (envName = 'local') => { const env = environments[ envName ]; const cssFileLoader = { loader: MiniCssExtractPlugin.loader, options: { hmr: !env.PRODUCTION, }, } return { // Polyfill only for async (TODO) entry: [ './app/initialize.js' ].concat(cssEntrypoints), output: { path: path.join(__dirname, DIST_DIR), filename: 'app-[contenthash:7].js', publicPath: '/', }, plugins: [ new webpack.ProvidePlugin({ 'React': 'react' // back code compatability }), new MiniCssExtractPlugin({ path: path.join(__dirname, DIST_DIR), filename: 'app-[contenthash:7].css' }), new CopyWebpackPlugin([ 'app/assets' ]), new HtmlWebpackPlugin({ template: 'app/assets/index.html' }), new MomentLocalesPlugin(), new webpack.DefinePlugin(prepareEnv(env)), // new BundleAnalyzerPlugin({ analyzerMode: 'static'}), // new CircularDependencyPlugin({ // // exclude detection of files based on a RegExp // exclude: /node_modules/, // // add errors to webpack instead of warnings // failOnError: true, // // allow import cycles that include an asyncronous import, // // e.g. via import(/* webpackMode: "weak" */ './file.js') // allowAsyncCycles: false, // // set the current working directory for displaying module paths // cwd: process.cwd(), // }) ], module: { rules: [ // global and module css separation. TODO more beautyfull { test: /\.css$/, include: [ path.join(__dirname, "app/components"), path.join(__dirname, "app/player") ], use: [ cssFileLoader, { loader: 'css-loader', options: { importLoaders: 1, modules: { localIdentName: '[name]_[local]_[hash:base64:7]' }, } }, 'postcss-loader' ] }, { test: /\.css$/, include: [ path.join(__dirname, "node_modules"), path.join(__dirname, "app/styles") ], use: [ cssFileLoader, { loader: 'css-loader', options: { importLoaders: 1, } }, 'postcss-loader' ] }, { test: /\.svg$/, use: ['@svgr/webpack'], }, { test: /\.js$/, include: [ path.join(__dirname, "app"), path.join(__dirname, ".storybook") ], use: babelLoader, }, { test: /\.tsx?$/, include: path.join(__dirname, "app"), use: [ 'ts-loader' ] }, ] }, resolve: { alias, extensions: ['.js', '.json', '.ts', '.tsx' ], }, mode: env.PRODUCTION ? 'production' : 'development', optimization: { splitChunks: { chunks: 'all', }, }, devServer: { contentBase: path.join(__dirname, DIST_DIR), //compress: true, port: 3333, historyApiFallback: true, }, stats: 'errors-only', devtool: env.SOURCEMAP && 'source-map' }; }