code.gitea.io/gitea@v1.21.7/webpack.config.js (about)

     1  import fastGlob from 'fast-glob';
     2  import wrapAnsi from 'wrap-ansi';
     3  import AddAssetPlugin from 'add-asset-webpack-plugin';
     4  import LicenseCheckerWebpackPlugin from 'license-checker-webpack-plugin';
     5  import MiniCssExtractPlugin from 'mini-css-extract-plugin';
     6  import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
     7  import {VueLoaderPlugin} from 'vue-loader';
     8  import EsBuildLoader from 'esbuild-loader';
     9  import {parse, dirname} from 'node:path';
    10  import webpack from 'webpack';
    11  import {fileURLToPath} from 'node:url';
    12  import {readFileSync} from 'node:fs';
    13  import {env} from 'node:process';
    14  
    15  const {EsbuildPlugin} = EsBuildLoader;
    16  const {SourceMapDevToolPlugin, DefinePlugin} = webpack;
    17  const formatLicenseText = (licenseText) => wrapAnsi(licenseText || '', 80).trim();
    18  
    19  const glob = (pattern) => fastGlob.sync(pattern, {
    20    cwd: dirname(fileURLToPath(new URL(import.meta.url))),
    21    absolute: true,
    22  });
    23  
    24  const themes = {};
    25  for (const path of glob('web_src/css/themes/*.css')) {
    26    themes[parse(path).name] = [path];
    27  }
    28  
    29  const isProduction = env.NODE_ENV !== 'development';
    30  
    31  // ENABLE_SOURCEMAP accepts the following values:
    32  // true - all enabled, the default in development
    33  // reduced - minimal sourcemaps, the default in production
    34  // false - all disabled
    35  let sourceMaps;
    36  if ('ENABLE_SOURCEMAP' in env) {
    37    sourceMaps = ['true', 'false'].includes(env.ENABLE_SOURCEMAP) ? env.ENABLE_SOURCEMAP : 'reduced';
    38  } else {
    39    sourceMaps = isProduction ? 'reduced' : 'true';
    40  }
    41  
    42  const filterCssImport = (url, ...args) => {
    43    const cssFile = args[1] || args[0]; // resourcePath is 2nd argument for url and 3rd for import
    44    const importedFile = url.replace(/[?#].+/, '').toLowerCase();
    45  
    46    if (cssFile.includes('fomantic')) {
    47      if (/brand-icons/.test(importedFile)) return false;
    48      if (/(eot|ttf|otf|woff|svg)$/i.test(importedFile)) return false;
    49    }
    50  
    51    if (cssFile.includes('katex') && /(ttf|woff)$/i.test(importedFile)) {
    52      return false;
    53    }
    54  
    55    return true;
    56  };
    57  
    58  // in case lightningcss fails to load, fall back to esbuild for css minify
    59  let LightningCssMinifyPlugin;
    60  try {
    61    ({LightningCssMinifyPlugin} = await import('lightningcss-loader'));
    62  } catch {}
    63  
    64  /** @type {import("webpack").Configuration} */
    65  export default {
    66    mode: isProduction ? 'production' : 'development',
    67    entry: {
    68      index: [
    69        fileURLToPath(new URL('web_src/js/jquery.js', import.meta.url)),
    70        fileURLToPath(new URL('web_src/fomantic/build/semantic.js', import.meta.url)),
    71        fileURLToPath(new URL('web_src/js/index.js', import.meta.url)),
    72        fileURLToPath(new URL('node_modules/easymde/dist/easymde.min.css', import.meta.url)),
    73        fileURLToPath(new URL('web_src/fomantic/build/semantic.css', import.meta.url)),
    74        fileURLToPath(new URL('web_src/css/index.css', import.meta.url)),
    75      ],
    76      webcomponents: [
    77        fileURLToPath(new URL('web_src/js/webcomponents/webcomponents.js', import.meta.url)),
    78      ],
    79      swagger: [
    80        fileURLToPath(new URL('web_src/js/standalone/swagger.js', import.meta.url)),
    81        fileURLToPath(new URL('web_src/css/standalone/swagger.css', import.meta.url)),
    82      ],
    83      'eventsource.sharedworker': [
    84        fileURLToPath(new URL('web_src/js/features/eventsource.sharedworker.js', import.meta.url)),
    85      ],
    86      ...(!isProduction && {
    87        devtest: [
    88          fileURLToPath(new URL('web_src/js/standalone/devtest.js', import.meta.url)),
    89          fileURLToPath(new URL('web_src/css/standalone/devtest.css', import.meta.url)),
    90        ],
    91      }),
    92      ...themes,
    93    },
    94    devtool: false,
    95    output: {
    96      path: fileURLToPath(new URL('public/assets', import.meta.url)),
    97      filename: () => 'js/[name].js',
    98      chunkFilename: ({chunk}) => {
    99        const language = (/monaco.*languages?_.+?_(.+?)_/.exec(chunk.id) || [])[1];
   100        return `js/${language ? `monaco-language-${language.toLowerCase()}` : `[name]`}.[contenthash:8].js`;
   101      },
   102    },
   103    optimization: {
   104      minimize: isProduction,
   105      minimizer: [
   106        new EsbuildPlugin({
   107          target: 'es2015',
   108          minify: true,
   109          css: !LightningCssMinifyPlugin,
   110          legalComments: 'none',
   111        }),
   112        LightningCssMinifyPlugin && new LightningCssMinifyPlugin({
   113          sourceMap: sourceMaps === 'true',
   114        }),
   115      ],
   116      splitChunks: {
   117        chunks: 'async',
   118        name: (_, chunks) => chunks.map((item) => item.name).join('-'),
   119      },
   120      moduleIds: 'named',
   121      chunkIds: 'named',
   122    },
   123    module: {
   124      rules: [
   125        {
   126          test: /\.vue$/i,
   127          exclude: /node_modules/,
   128          loader: 'vue-loader',
   129        },
   130        {
   131          test: /\.js$/i,
   132          exclude: /node_modules/,
   133          use: [
   134            {
   135              loader: 'esbuild-loader',
   136              options: {
   137                loader: 'js',
   138                target: 'es2015',
   139              },
   140            },
   141          ],
   142        },
   143        {
   144          test: /\.css$/i,
   145          use: [
   146            {
   147              loader: MiniCssExtractPlugin.loader,
   148            },
   149            {
   150              loader: 'css-loader',
   151              options: {
   152                sourceMap: sourceMaps === 'true',
   153                url: {filter: filterCssImport},
   154                import: {filter: filterCssImport},
   155              },
   156            },
   157          ],
   158        },
   159        {
   160          test: /\.svg$/i,
   161          include: fileURLToPath(new URL('public/assets/img/svg', import.meta.url)),
   162          type: 'asset/source',
   163        },
   164        {
   165          test: /\.(ttf|woff2?)$/i,
   166          type: 'asset/resource',
   167          generator: {
   168            filename: 'fonts/[name].[contenthash:8][ext]',
   169          }
   170        },
   171        {
   172          test: /\.png$/i,
   173          type: 'asset/resource',
   174          generator: {
   175            filename: 'img/webpack/[name].[contenthash:8][ext]',
   176          }
   177        },
   178      ],
   179    },
   180    plugins: [
   181      new DefinePlugin({
   182        __VUE_OPTIONS_API__: true, // at the moment, many Vue components still use the Vue Options API
   183        __VUE_PROD_DEVTOOLS__: false, // do not enable devtools support in production
   184      }),
   185      new VueLoaderPlugin(),
   186      new MiniCssExtractPlugin({
   187        filename: 'css/[name].css',
   188        chunkFilename: 'css/[name].[contenthash:8].css',
   189      }),
   190      sourceMaps !== 'false' && new SourceMapDevToolPlugin({
   191        filename: '[file].[contenthash:8].map',
   192        ...(sourceMaps === 'reduced' && {include: /^js\/index\.js$/}),
   193      }),
   194      new MonacoWebpackPlugin({
   195        filename: 'js/monaco-[name].[contenthash:8].worker.js',
   196      }),
   197      isProduction ? new LicenseCheckerWebpackPlugin({
   198        outputFilename: 'licenses.txt',
   199        outputWriter: ({dependencies}) => {
   200          const line = '-'.repeat(80);
   201          const goJson = readFileSync('assets/go-licenses.json', 'utf8');
   202          const goModules = JSON.parse(goJson).map(({name, licenseText}) => {
   203            return {name, body: formatLicenseText(licenseText)};
   204          });
   205          const jsModules = dependencies.map(({name, version, licenseName, licenseText}) => {
   206            return {name, version, licenseName, body: formatLicenseText(licenseText)};
   207          });
   208  
   209          const modules = [...goModules, ...jsModules].sort((a, b) => a.name.localeCompare(b.name));
   210          return modules.map(({name, version, licenseName, body}) => {
   211            const title = licenseName ? `${name}@${version} - ${licenseName}` : name;
   212            return `${line}\n${title}\n${line}\n${body}`;
   213          }).join('\n');
   214        },
   215        override: {
   216          'khroma@*': {licenseName: 'MIT'}, // https://github.com/fabiospampinato/khroma/pull/33
   217        },
   218        emitError: true,
   219        allow: '(Apache-2.0 OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)',
   220      }) : new AddAssetPlugin('licenses.txt', `Licenses are disabled during development`),
   221    ],
   222    performance: {
   223      hints: false,
   224      maxEntrypointSize: Infinity,
   225      maxAssetSize: Infinity,
   226    },
   227    resolve: {
   228      symlinks: false,
   229    },
   230    watchOptions: {
   231      ignored: [
   232        'node_modules/**',
   233      ],
   234    },
   235    stats: {
   236      assetsSort: 'name',
   237      assetsSpace: Infinity,
   238      cached: false,
   239      cachedModules: false,
   240      children: false,
   241      chunkModules: false,
   242      chunkOrigins: false,
   243      chunksSort: 'name',
   244      colors: true,
   245      entrypoints: false,
   246      excludeAssets: [
   247        /^js\/monaco-language-.+\.js$/,
   248        !isProduction && /^licenses.txt$/,
   249      ].filter(Boolean),
   250      groupAssetsByChunk: false,
   251      groupAssetsByEmitStatus: false,
   252      groupAssetsByInfo: false,
   253      groupModulesByAttributes: false,
   254      modules: false,
   255      reasons: false,
   256      runtimeModules: false,
   257    },
   258  };