123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- const { resolve } = require('path');
- const { VueLoaderPlugin } = require('vue-loader');
- const HtmlWebpackPlugin = require('html-webpack-plugin');
- const MiniCssExtractPlugin = require('mini-css-extract-plugin');
- const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
- const TerserPlugin = require('terser-webpack-plugin');
- const deepmerge = require('deepmerge');
- const { alias, extensions, isProd } = require('./common');
- const defaultHtmlOptions = {
- minify: isProd && {
- collapseWhitespace: true,
- removeAttributeQuotes: true,
- removeComments: true,
- removeOptionalTags: true,
- removeRedundantAttributes: true,
- removeScriptTypeAttributes: true,
- removeStyleLinkTypeAttributes: true,
- },
- meta: { viewport: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0' },
- css: [],
- js: [],
- };
- const MIN_OPTS = {
- extractComments: false,
- parallel: true,
- terserOptions: {
- compress: {
- // `terser` often inlines big one-time functions inside a small "hot" function
- reduce_funcs: false,
- },
- output: {
- ascii_only: true,
- comments: false,
- wrap_func_args: false, // disabling a premature optimization designed for old browsers
- },
- },
- };
- const MIN_OPTS_PUBLIC = isProd && {
- include: 'public/',
- ...MIN_OPTS,
- };
- const MIN_OPTS_MAIN = isProd && deepmerge.all([{}, MIN_OPTS, {
- exclude: 'public/',
- terserOptions: {
- compress: {
- ecma: 8, // ES2017 Object.entries and so on
- passes: 2, // necessary now since we removed plaid's minimizer
- unsafe_arrows: true, // it's 'safe' since we don't rely on function prototypes
- },
- },
- }]);
- const nodeModules = resolve('node_modules');
- const pages = [
- 'background',
- 'confirm',
- 'options',
- 'popup',
- ];
- const createHtmlPage = key => new HtmlWebpackPlugin({
- ...defaultHtmlOptions,
- filename: `${key}/index.html`,
- chunks: [`${key}/index`],
- title: 'Violentmonkey',
- inject: 'body',
- scriptLoading: 'blocking', // we don't need `defer` and it breaks in some browsers, see #1632
- });
- const splitVendor = prefix => ({
- [prefix]: {
- test: new RegExp(`node_modules[/\\\\]${prefix}`),
- name: `public/lib/${prefix}`,
- chunks: 'all',
- priority: 100,
- },
- });
- function styleLoader(options) {
- const {
- extract,
- loaders = [],
- fallback = 'style-loader',
- modules = false,
- } = options || {};
- const cssLoader = {
- loader: 'css-loader',
- options: {
- modules,
- importLoaders: 1,
- sourceMap: false,
- },
- };
- return [
- extract ? MiniCssExtractPlugin.loader : fallback,
- cssLoader,
- ...loaders,
- ];
- }
- function styleRule(options, rule) {
- return {
- test: /\.css$/,
- use: styleLoader(options),
- ...rule,
- };
- }
- const styleOptions = {
- extract: isProd,
- };
- const postcssLoader = {
- loader: 'postcss-loader',
- };
- const getBaseConfig = () => ({
- mode: isProd ? 'production' : 'development',
- target: 'web', // required by live reloading
- devtool: isProd ? false : 'inline-source-map',
- output: {
- path: resolve('dist'),
- publicPath: '/',
- filename: '[name].js',
- hashFunction: 'xxhash64',
- },
- node: {
- global: false,
- },
- performance: {
- maxEntrypointSize: 1e6,
- maxAssetSize: 0.5e6,
- },
- resolve: {
- alias,
- extensions,
- },
- module: {
- rules: [
- // JS/TS
- {
- test: /\.m?[jt]sx?$/,
- use: 'babel-loader',
- exclude: file => /node_modules/.test(file) && !/vueleton|@vue[/\\]shared/.test(file),
- },
- // CSS
- {
- oneOf: [
- // library CSS files: node_modules/**/*.css
- styleRule(styleOptions, {
- include: [nodeModules],
- }),
- // CSS modules: src/**/*.module.css
- styleRule({
- ...styleOptions,
- loaders: [postcssLoader],
- modules: {},
- }, {
- test: /\.module\.css$/,
- }),
- // normal CSS files: src/**/*.css
- styleRule({
- ...styleOptions,
- loaders: [postcssLoader],
- }),
- ],
- },
- // SVG
- {
- test: /\.svg$/,
- use: [{
- loader: 'svg-sprite-loader',
- options: {
- // extract: extractSVG,
- },
- }],
- include: [resolve('src/resources/svg')],
- },
- // Vue
- {
- test: /\.vue$/,
- loader: 'vue-loader',
- options: {
- babelParserPlugins: ['functionBind'],
- compilerOptions: {
- whitespace: 'condense',
- },
- },
- },
- ],
- },
- optimization: {
- runtimeChunk: false,
- splitChunks: {
- cacheGroups: {
- 'common-ui': {
- name: 'common-ui',
- test: new RegExp([
- /\bsvg/,
- // don't extract CSS as it'll change the relative order of rules which breaks appearance
- 'src/common/(?!zip|.*\\.css$)',
- 'node_modules/@violentmonkey/shortcut',
- 'node_modules/@?vue',
- ].map(re => re.source || re).join('|').replace(/\\?\//g, '[/\\\\]')),
- chunks: c => ![
- 'background/index', // only 4kB of common code
- 'injected',
- 'injected-web',
- ].includes(c.name),
- },
- ...splitVendor('codemirror'),
- },
- },
- minimizer: isProd ? [
- new CssMinimizerPlugin(),
- new TerserPlugin(MIN_OPTS_PUBLIC),
- new TerserPlugin(MIN_OPTS_MAIN),
- ] : [],
- },
- plugins: [
- new VueLoaderPlugin(),
- ...styleOptions.extract ? [new MiniCssExtractPlugin({
- filename: '[name].css',
- })] : [],
- ],
- });
- const getPageConfig = () => {
- const config = getBaseConfig();
- config.entry = Object.fromEntries(pages.map(name => [`${name}/index`, `./src/${name}`]));
- config.plugins = [
- ...config.plugins,
- ...pages.filter(key => key !== 'background').map(createHtmlPage),
- ];
- return config;
- };
- exports.isProd = isProd;
- exports.getBaseConfig = getBaseConfig;
- exports.getPageConfig = getPageConfig;
|