You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/packages/ui-default/build/config/webpack.ts

286 lines
8.8 KiB
TypeScript

/* eslint-disable global-require */
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import { ESBuildMinifyPlugin } from 'esbuild-loader';
import { DuplicatesPlugin } from 'inspectpack/plugin';
import ExtractCssPlugin from 'mini-css-extract-plugin';
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
import { dirname } from 'path';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { WebpackManifestPlugin } from 'webpack-manifest-plugin';
import WebpackBar from 'webpackbar';
import root from '../utils/root';
export default function (env: { watch?: boolean, production?: boolean, measure?: boolean } = {}) {
function esbuildLoader() {
return {
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
sourcemap: true,
},
};
}
function cssLoader() {
return {
loader: 'css-loader',
options: { importLoaders: 1 },
};
}
function postcssLoader() {
return {
loader: 'postcss-loader',
options: { postcssOptions: { sourceMap: false, config: root('postcss.config.js') } },
};
}
function extractCssLoader() {
return {
loader: ExtractCssPlugin.loader,
// FIXME auto?
options: {
publicPath: '',
},
};
}
function stylusLoader() {
return {
loader: 'stylus-loader',
options: {
stylusOptions: {
preferPathResolver: 'webpack',
use: [require('rupture')()],
import: ['~vj/common/common.inc.styl'],
},
},
};
}
const config: import('webpack').Configuration = {
// bail: !env.production,
mode: (env.production || env.measure) ? 'production' : 'development',
profile: env.measure,
context: root(),
stats: {
preset: 'errors-warnings',
},
devtool: false,
entry: {
hydro: './entry.js',
polyfill: './polyfill.ts',
'default.theme': './theme/default.js',
'service-worker': './service-worker.ts',
'messages-shared-worker': './components/message/worker.ts',
},
cache: {
type: 'filesystem',
cacheDirectory: root('../../.cache'),
idleTimeout: 30000,
buildDependencies: {
config: [__filename],
},
},
output: {
path: root('public'),
publicPath: '/', // overwrite in entry.js
hashFunction: 'sha1',
hashDigest: 'hex',
hashDigestLength: 10,
filename: '[name].js?[contenthash:6]',
chunkFilename: '[name].[chunkhash:6].chunk.js',
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.cjs'],
alias: {
vj: root(),
},
},
module: {
rules: [
{
test: /\.svg$/i,
oneOf: [
{
issuer: /\.[jt]sx?$/,
resourceQuery: /react/,
use: { loader: '@svgr/webpack', options: { icon: true } },
},
{
type: 'asset/resource',
},
],
},
{
resourceQuery: /inline/,
type: 'asset/inline',
},
{
test: /\.(ttf|eot|woff|woff2|png|jpg|jpeg|gif)$/,
type: 'asset/resource',
generator: {
filename: (pathData) => {
const p = pathData.module.resource.replace(/\\/g, '/');
const filename = p.split('/').pop();
if (p.includes('node_modules')) {
const extra = p.split('node_modules/').pop();
const moduleName = extra.split('/')[0];
if (extra.includes('@fontsource')) {
return `fonts/${filename}?[hash:6]`;
}
if (['katex', 'monaco-editor'].includes(moduleName)) {
return `modules/${moduleName}/${filename}?[hash:6]`;
}
return `modules/${extra.substr(1)}?[hash:6]`;
}
if (p.includes('.iconfont')) return `${filename}?[hash:6]`;
return `${p.split('ui-default')[1].substring(1)}?[hash:6]`;
},
},
},
{
test: /\.ts$/,
include: /@types\//,
type: 'asset/inline',
generator: {
dataUrl: (buf) => buf.toString(),
},
},
{
test: /\.[mc]?[jt]sx?$/,
exclude: [/@types\//, /components\/message\//, /entry\.js/],
type: 'javascript/auto',
use: [esbuildLoader()],
},
{
test: /\.[mc]?[jt]sx?$/,
include: [/components\/message\//, /entry\.js/],
type: 'javascript/auto',
use: [{
loader: 'ts-loader',
options: {
transpileOnly: true,
},
}],
},
{
test: /\.styl$/,
use: [extractCssLoader(), cssLoader(), postcssLoader(), stylusLoader()],
},
{
test: /\.css$/,
use: [extractCssLoader(), cssLoader(), postcssLoader()],
},
],
},
experiments: {
asyncWebAssembly: true,
syncWebAssembly: true,
},
optimization: {
splitChunks: {
minSize: 256000,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '-',
cacheGroups: {
style: {
priority: 99,
name: 'theme',
type: 'css/mini-extract',
chunks: 'all',
enforce: true,
},
vendors: {
test: /[\\/]node_modules[\\/].+\.([jt]sx?|json|yaml)$/,
priority: -10,
name(module) {
const packageName = module.context.replace(/\\/g, '/').split('node_modules/').pop().split('/')[0];
if (packageName === 'monaco-editor-nls') {
return `i.monaco.${module.userRequest.split('/').pop().split('.')[0]}`;
}
return `n.${packageName.replace('@', '')}`;
},
reuseExistingChunk: true,
},
default: {
priority: -20,
reuseExistingChunk: true,
},
},
},
usedExports: true,
minimizer: [new ESBuildMinifyPlugin({
css: true,
minify: true,
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
treeShaking: true,
target: [
'chrome60',
],
exclude: [/mathmaps/, /\.min\.js$/],
})],
moduleIds: env.production ? 'deterministic' : 'named',
chunkIds: env.production ? 'deterministic' : 'named',
},
plugins: [
new CleanWebpackPlugin(),
new WebpackBar(),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
React: 'react',
monaco: 'monaco-editor/esm/vs/editor/editor.api',
}),
new ExtractCssPlugin({
filename: '[name].css?[fullhash:6]',
}),
new WebpackManifestPlugin({}),
new webpack.IgnorePlugin({ resourceRegExp: /(^\.\/locale$|mathjax|abcjs|vditor.+\.d\.ts)/ }),
new CopyWebpackPlugin({
patterns: [
{ from: root('static') },
{ from: root('components/navigation/nav-logo-small_dark.png'), to: 'components/navigation/nav-logo-small_dark.png' },
{ from: root(`${dirname(require.resolve('streamsaver/package.json'))}/mitm.html`), to: 'streamsaver/mitm.html' },
{ from: root(`${dirname(require.resolve('streamsaver/package.json'))}/sw.js`), to: 'streamsaver/sw.js' },
{ from: root(`${dirname(require.resolve('vditor/package.json'))}/dist`), to: 'vditor/dist' },
{ from: root(`${dirname(require.resolve('graphiql/package.json'))}/graphiql.min.css`), to: 'graphiql.min.css' },
{ from: `${dirname(require.resolve('monaco-themes/package.json'))}/themes`, to: 'monaco/themes/' },
],
}),
new webpack.DefinePlugin({
'process.env.VERSION': JSON.stringify(require('@hydrooj/ui-default/package.json').version),
}),
new webpack.optimize.MinChunkSizePlugin({
minChunkSize: 128000,
}),
new webpack.NormalModuleReplacementPlugin(/\/(vscode-)?nls\.js/, require.resolve('../../components/monaco/nls')),
new webpack.NormalModuleReplacementPlugin(/^prettier[$/]/, root('../../modules/nop.ts')),
new MonacoWebpackPlugin({
filename: '[name].[hash:6].worker.js',
customLanguages: [{
label: 'yaml',
entry: require.resolve('monaco-yaml/index.js'),
worker: {
id: 'vs/language/yaml/yamlWorker',
entry: require.resolve('monaco-yaml/yaml.worker.js'),
},
}],
}),
...env.measure ? [
new BundleAnalyzerPlugin({ analyzerPort: 'auto' }),
new DuplicatesPlugin(),
] : [],
],
};
return config;
}