mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Instead of statically hardcoding every frontend asset, this uses a type-based approach to cache all js,css and manifest.json requests. This also fixes the issue that the service worker was interfering with EventSource because it was unconditionally handling all requests which this new implementation doesn't. Fixes: https://github.com/go-gitea/gitea/issues/11092 Fixes: https://github.com/go-gitea/gitea/issues/7372
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const cssnano = require('cssnano');
 | |
| const fastGlob = require('fast-glob');
 | |
| const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries');
 | |
| const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 | |
| const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
 | |
| const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
 | |
| const PostCSSPresetEnv = require('postcss-preset-env');
 | |
| const PostCSSSafeParser = require('postcss-safe-parser');
 | |
| const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
 | |
| const TerserPlugin = require('terser-webpack-plugin');
 | |
| const VueLoaderPlugin = require('vue-loader/lib/plugin');
 | |
| const {statSync} = require('fs');
 | |
| const {resolve, parse} = require('path');
 | |
| const {SourceMapDevToolPlugin} = require('webpack');
 | |
| 
 | |
| const glob = (pattern) => fastGlob.sync(pattern, {cwd: __dirname, absolute: true});
 | |
| 
 | |
| const themes = {};
 | |
| for (const path of glob('web_src/less/themes/*.less')) {
 | |
|   themes[parse(path).name] = [path];
 | |
| }
 | |
| 
 | |
| const isProduction = process.env.NODE_ENV !== 'development';
 | |
| 
 | |
| module.exports = {
 | |
|   mode: isProduction ? 'production' : 'development',
 | |
|   entry: {
 | |
|     index: [
 | |
|       resolve(__dirname, 'web_src/js/index.js'),
 | |
|       resolve(__dirname, 'web_src/less/index.less'),
 | |
|     ],
 | |
|     swagger: [
 | |
|       resolve(__dirname, 'web_src/js/standalone/swagger.js'),
 | |
|     ],
 | |
|     jquery: [
 | |
|       resolve(__dirname, 'web_src/js/jquery.js'),
 | |
|     ],
 | |
|     serviceworker: [
 | |
|       resolve(__dirname, 'web_src/js/serviceworker.js'),
 | |
|     ],
 | |
|     icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'),
 | |
|     ...themes,
 | |
|   },
 | |
|   devtool: false,
 | |
|   output: {
 | |
|     path: resolve(__dirname, 'public'),
 | |
|     filename: ({chunk}) => {
 | |
|       // serviceworker can only manage assets below it's script path so we
 | |
|       // have to put it in / instead of /js/
 | |
|       return chunk.id === 'serviceworker' ? '[name].js' : 'js/[name].js';
 | |
|     },
 | |
|     chunkFilename: 'js/[name].js',
 | |
|   },
 | |
|   optimization: {
 | |
|     minimize: isProduction,
 | |
|     minimizer: [
 | |
|       new TerserPlugin({
 | |
|         sourceMap: true,
 | |
|         extractComments: false,
 | |
|         terserOptions: {
 | |
|           keep_fnames: /^(HTML|SVG)/, // https://github.com/fgnass/domino/issues/144
 | |
|           output: {
 | |
|             comments: false,
 | |
|           },
 | |
|         },
 | |
|       }),
 | |
|       new OptimizeCSSAssetsPlugin({
 | |
|         cssProcessor: cssnano,
 | |
|         cssProcessorOptions: {
 | |
|           parser: PostCSSSafeParser,
 | |
|         },
 | |
|         cssProcessorPluginOptions: {
 | |
|           preset: [
 | |
|             'default',
 | |
|             {
 | |
|               discardComments: {
 | |
|                 removeAll: true,
 | |
|               },
 | |
|             },
 | |
|           ],
 | |
|         },
 | |
|       }),
 | |
|     ],
 | |
|     splitChunks: {
 | |
|       chunks: 'async',
 | |
|       name: (_, chunks) => chunks.map((item) => item.name).join('-'),
 | |
|       cacheGroups: {
 | |
|         // this bundles all monaco's languages into one file instead of emitting 1-65.js files
 | |
|         monaco: {
 | |
|           test: /monaco-editor/,
 | |
|           name: 'monaco',
 | |
|           chunks: 'async'
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   module: {
 | |
|     rules: [
 | |
|       {
 | |
|         test: /\.vue$/,
 | |
|         exclude: /node_modules/,
 | |
|         loader: 'vue-loader',
 | |
|       },
 | |
|       {
 | |
|         test: require.resolve('jquery-datetimepicker'),
 | |
|         use: 'imports-loader?define=>false,exports=>false',
 | |
|       },
 | |
|       {
 | |
|         test: /\.worker\.js$/,
 | |
|         exclude: /monaco/,
 | |
|         use: [
 | |
|           {
 | |
|             loader: 'worker-loader',
 | |
|             options: {
 | |
|               name: '[name].js',
 | |
|               inline: true,
 | |
|               fallback: false,
 | |
|             },
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|       {
 | |
|         test: /\.js$/,
 | |
|         exclude: /node_modules/,
 | |
|         use: [
 | |
|           {
 | |
|             loader: 'babel-loader',
 | |
|             options: {
 | |
|               cacheDirectory: true,
 | |
|               cacheCompression: false,
 | |
|               cacheIdentifier: [
 | |
|                 resolve(__dirname, 'package.json'),
 | |
|                 resolve(__dirname, 'package-lock.json'),
 | |
|                 resolve(__dirname, 'webpack.config.js'),
 | |
|               ].map((path) => statSync(path).mtime.getTime()).join(':'),
 | |
|               sourceMaps: true,
 | |
|               presets: [
 | |
|                 [
 | |
|                   '@babel/preset-env',
 | |
|                   {
 | |
|                     useBuiltIns: 'usage',
 | |
|                     corejs: 3,
 | |
|                   },
 | |
|                 ],
 | |
|               ],
 | |
|               plugins: [
 | |
|                 [
 | |
|                   '@babel/plugin-transform-runtime',
 | |
|                   {
 | |
|                     regenerator: true,
 | |
|                   }
 | |
|                 ],
 | |
|                 '@babel/plugin-proposal-object-rest-spread',
 | |
|               ],
 | |
|             },
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|       {
 | |
|         test: /\.(less|css)$/i,
 | |
|         use: [
 | |
|           {
 | |
|             loader: MiniCssExtractPlugin.loader,
 | |
|           },
 | |
|           {
 | |
|             loader: 'css-loader',
 | |
|             options: {
 | |
|               importLoaders: 2,
 | |
|               url: (_url, resourcePath) => {
 | |
|                 // only resolve URLs for dependencies
 | |
|                 return resourcePath.includes('node_modules');
 | |
|               },
 | |
|             }
 | |
|           },
 | |
|           {
 | |
|             loader: 'postcss-loader',
 | |
|             options: {
 | |
|               plugins: () => [
 | |
|                 PostCSSPresetEnv(),
 | |
|               ],
 | |
|             },
 | |
|           },
 | |
|           {
 | |
|             loader: 'less-loader',
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|       {
 | |
|         test: /\.svg$/,
 | |
|         use: [
 | |
|           {
 | |
|             loader: 'svg-sprite-loader',
 | |
|             options: {
 | |
|               extract: true,
 | |
|               spriteFilename: 'img/svg/icons.svg',
 | |
|               symbolId: (path) => {
 | |
|                 const {name} = parse(path);
 | |
|                 if (/@primer[/\\]octicons/.test(path)) {
 | |
|                   return `octicon-${name}`;
 | |
|                 }
 | |
|                 return name;
 | |
|               },
 | |
|             },
 | |
|           },
 | |
|           {
 | |
|             loader: 'svgo-loader',
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|       {
 | |
|         test: /\.(ttf|woff2?)$/,
 | |
|         use: [
 | |
|           {
 | |
|             loader: 'file-loader',
 | |
|             options: {
 | |
|               name: '[name].[ext]',
 | |
|               outputPath: 'fonts/',
 | |
|               publicPath: (url) => `../fonts/${url}`, // seems required for monaco's font
 | |
|             },
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|     ],
 | |
|   },
 | |
|   plugins: [
 | |
|     new VueLoaderPlugin(),
 | |
|     // avoid generating useless js output files for css- and svg-only chunks
 | |
|     new FixStyleOnlyEntriesPlugin({
 | |
|       extensions: ['less', 'scss', 'css', 'svg'],
 | |
|       silent: true,
 | |
|     }),
 | |
|     new MiniCssExtractPlugin({
 | |
|       filename: 'css/[name].css',
 | |
|       chunkFilename: 'css/[name].css',
 | |
|     }),
 | |
|     new SourceMapDevToolPlugin({
 | |
|       filename: 'js/[name].js.map',
 | |
|       include: [
 | |
|         'js/index.js',
 | |
|       ],
 | |
|     }),
 | |
|     new SpriteLoaderPlugin({
 | |
|       plainSprite: true,
 | |
|     }),
 | |
|     new MonacoWebpackPlugin({
 | |
|       filename: 'js/monaco-[name].worker.js',
 | |
|     }),
 | |
|   ],
 | |
|   performance: {
 | |
|     hints: false,
 | |
|     maxEntrypointSize: Infinity,
 | |
|     maxAssetSize: Infinity,
 | |
|   },
 | |
|   resolve: {
 | |
|     symlinks: false,
 | |
|     alias: {
 | |
|       vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only
 | |
|     },
 | |
|   },
 | |
|   watchOptions: {
 | |
|     ignored: [
 | |
|       'node_modules/**',
 | |
|     ],
 | |
|   },
 | |
|   stats: {
 | |
|     children: false,
 | |
|   },
 | |
| };
 |