Загрузка данных
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no--requires */
const path = require('path');
require('dotenv').config({ path: '.env.local' });
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { ESBuildPlugin } = require('esbuild-loader');
const ForkTSCheckerPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ReactRefreshTypeScript = require('react-refresh-typescript');
const TerserPlugin = require('terser-webpack-plugin');
const { ids } = require('webpack');
const { HashedModuleIdsPlugin } = ids;
let standSwitcher;
try {
standSwitcher = require('./dev/stands/index.js');
} catch {}
const PORT = 3000;
const MAX_CYCLES = 84;
let numCyclesDetected = 0;
module.exports = (env) => {
const isDevelopment = env.type === 'start';
const currentStand = process.env.DEV_STAND || env.stand || standSwitcher?.getDefaultStand();
const localDevUrl = `${standSwitcher?.getLocalDevUrl(currentStand) || 'localhost'}:${PORT}`;
const proxy = {
target: '',
secure: false,
changeOrigin: true,
ws: true,
// Этот заголовок нужен для правильного редиректа обратно на локальный домен после авторизации
headers: {
'X-Dev-Redirect': localDevUrl,
},
};
return {
entry: './src/index.tsx',
devtool: isDevelopment ? 'source-map' : false,
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CircularDependencyPlugin({
// exclude detection of files based on a RegExp
exclude: /a\.js|node_modules/,
// include specific files based on a RegExp
// include: /dir/,
// 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(),
onStart({ compilation }) {
numCyclesDetected = 0;
},
onDetected({ module: webpackModuleRecord, paths, compilation }) {
numCyclesDetected += 1;
compilation.warnings.push(new Error(paths.join(' -> ')));
},
onEnd({ compilation }) {
if (numCyclesDetected > MAX_CYCLES) {
compilation.errors.push(
new Error(`Detected ${numCyclesDetected} cycles which exceeds configured limit of ${MAX_CYCLES}`),
);
}
},
}),
new HtmlWebpackPlugin({
template: 'src/index.html',
publicPath: env.staticContextPath || 'auto',
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
new CopyWebpackPlugin({
patterns: [
{ from: 'public' },
isDevelopment && { from: standSwitcher?.getPathForCopyPlugin(currentStand) || 'env' },
].filter(Boolean),
}),
isDevelopment && new ReactRefreshWebpackPlugin({ overlay: false }),
new HashedModuleIdsPlugin(),
new ForkTSCheckerPlugin({}),
].filter(Boolean),
resolve: {
extensions: ['.ts', '.tsx', '.js'],
extensionAlias: {
'.js': ['.js', '.ts'],
'.cjs': ['.cjs', '.cts'],
'.mjs': ['.mjs', '.mts'],
},
alias: {
'@modules': path.resolve(__dirname, 'src/modules'),
'@widgets': path.resolve(__dirname, 'src/widgets'),
'@core': path.resolve(__dirname, 'src/core'),
'@uikit': path.resolve(__dirname, 'src/uikit'),
'@pages': path.resolve(__dirname, 'src/pages'),
'@components': path.resolve(__dirname, 'src/components'),
'@styles': path.resolve(__dirname, 'src/styles'),
'@store': path.resolve(__dirname, 'src/store'),
types: path.resolve(__dirname, 'src/types'),
'@configs': path.resolve(__dirname, 'src/configs'),
'@hooks': path.resolve(__dirname, 'src/hooks'),
'@api': path.resolve(__dirname, 'src/api'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@localisation': path.resolve(__dirname, 'src/localisation'),
'@features': path.resolve(__dirname, 'src/features'),
'@terminal': path.resolve(__dirname, 'src/terminal'),
'@testing': path.resolve(__dirname, 'src/testing'),
},
},
optimization: isDevelopment
? {
minimizer: [
new TerserPlugin({
extractComments: false,
}),
],
}
: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// получает имя, то есть node_modules/packageName/not/this/part.js
// или node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// имена npm-пакетов можно, не опасаясь проблем, использовать
// в URL, но некоторые серверы не любят символы наподобие @
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
minimizer: [
new TerserPlugin({
extractComments: false,
}),
],
},
module: {
rules: [
{
test: /\.d\.ts$/,
use: 'null-loader',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'static/fonts/[name][ext]',
},
},
{
test: /\.[jt]sx?$/,
exclude: /(node_modules)/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true,
getCustomTransformers: () => ({
before: [isDevelopment && ReactRefreshTypeScript()].filter(Boolean),
}),
},
},
],
},
{
test: /\.s[ac]ss$/i,
use: [
env.production ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: isDevelopment
? {
modules: {
auto: true,
localIdentName: '[local]--[hash:base64:5]',
},
}
: {
modules: {
auto: true,
hashStrategy: 'minimal-subset',
localIdentHashDigestLength: 5,
},
},
},
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: ['./src/styles'],
},
},
},
],
},
{
test: /\.(png|jpe?g|gif|mp(3|4))$/i,
use: [
{
loader: 'file-loader',
},
],
},
{
test: /\.css$/i,
use: [env.production ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader'],
},
{
test: /\.mdx$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
},
},
{
loader: '@mdx-js/loader',
},
],
},
{
test: /\.svg$/,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack', 'url-loader'],
},
],
},
devServer: {
server: 'https',
allowedHosts: 'all',
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: PORT,
client: {
overlay: false,
progress: true,
},
historyApiFallback: true,
open: {
target: localDevUrl,
},
proxy: {
'/session': standSwitcher?.modifyProxy(currentStand, proxy) || proxy,
'/ntbagro': standSwitcher?.modifyProxy(currentStand, proxy) || proxy,
},
},
};
};