webpack.config.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. const path = require('path');
  2. const webpack = require('webpack');
  3. const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  4. const CleanWebpackPlugin = require('clean-webpack-plugin');
  5. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  6. const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
  7. const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
  8. const TerserPlugin = require('terser-webpack-plugin');
  9. const WebpackAssetsManifest = require('webpack-assets-manifest');
  10. // Parse command-line arguments
  11. const parsedArgs = require('minimist')(process.argv.slice(2));
  12. // input dir
  13. const APP_DIR = path.resolve(__dirname, './');
  14. // output dir
  15. const BUILD_DIR = path.resolve(__dirname, './dist');
  16. const {
  17. mode = 'development',
  18. devserverPort = 9000,
  19. supersetPort = 8088,
  20. measure = false,
  21. analyzeBundle = false,
  22. } = parsedArgs;
  23. const isDevMode = mode !== 'production';
  24. const plugins = [
  25. // creates a manifest.json mapping of name to hashed output used in template files
  26. new WebpackAssetsManifest({
  27. publicPath: true,
  28. // This enables us to include all relevant files for an entry
  29. entrypoints: true,
  30. // Also write to disk when using devServer
  31. // instead of only keeping manifest.json in memory
  32. // This is required to make devServer work with flask.
  33. writeToDisk: isDevMode,
  34. }),
  35. // create fresh dist/ upon build
  36. new CleanWebpackPlugin(['dist']),
  37. // expose mode variable to other modules
  38. new webpack.DefinePlugin({
  39. 'process.env.WEBPACK_MODE': JSON.stringify(mode),
  40. }),
  41. ];
  42. if (isDevMode) {
  43. // Enable hot module replacement
  44. plugins.push(new webpack.HotModuleReplacementPlugin());
  45. } else {
  46. // text loading (webpack 4+)
  47. plugins.push(new MiniCssExtractPlugin({
  48. filename: '[name].[chunkhash].entry.css',
  49. chunkFilename: '[name].[chunkhash].chunk.css',
  50. }));
  51. plugins.push(new OptimizeCSSAssetsPlugin());
  52. }
  53. const output = {
  54. path: BUILD_DIR,
  55. publicPath: '/static/assets/dist/', // necessary for lazy-loaded chunks
  56. };
  57. if (isDevMode) {
  58. output.filename = '[name].[hash:8].entry.js';
  59. output.chunkFilename = '[name].[hash:8].chunk.js';
  60. } else {
  61. output.filename = '[name].[chunkhash].entry.js';
  62. output.chunkFilename = '[name].[chunkhash].chunk.js';
  63. }
  64. const config = {
  65. node: {
  66. fs: 'empty',
  67. },
  68. entry: {
  69. theme: APP_DIR + '/src/theme.js',
  70. common: APP_DIR + '/src/common.js',
  71. addSlice: ['babel-polyfill', APP_DIR + '/src/addSlice/index.jsx'],
  72. explore: ['babel-polyfill', APP_DIR + '/src/explore/index.jsx'],
  73. dashboard: ['babel-polyfill', APP_DIR + '/src/dashboard/index.jsx'],
  74. sqllab: ['babel-polyfill', APP_DIR + '/src/SqlLab/index.jsx'],
  75. welcome: ['babel-polyfill', APP_DIR + '/src/welcome/index.jsx'],
  76. profile: ['babel-polyfill', APP_DIR + '/src/profile/index.jsx'],
  77. },
  78. output,
  79. optimization: {
  80. splitChunks: {
  81. chunks: 'all',
  82. automaticNameDelimiter: '-',
  83. minChunks: 2,
  84. cacheGroups: {
  85. default: false,
  86. major: {
  87. name: 'vendors-major',
  88. test: /[\\/]node_modules\/(brace|react[-]dom|core[-]js)[\\/]/,
  89. },
  90. },
  91. },
  92. },
  93. resolve: {
  94. extensions: ['.js', '.jsx'],
  95. },
  96. module: {
  97. // Uglifying mapbox-gl results in undefined errors, see
  98. // https://github.com/mapbox/mapbox-gl-js/issues/4359#issuecomment-288001933
  99. noParse: /(mapbox-gl)\.js$/,
  100. rules: [
  101. {
  102. test: /datatables\.net.*/,
  103. loader: 'imports-loader?define=>false',
  104. },
  105. {
  106. test: /\.jsx?$/,
  107. exclude: /node_modules/,
  108. loader: 'babel-loader',
  109. },
  110. {
  111. test: /\.css$/,
  112. include: APP_DIR,
  113. use: [
  114. isDevMode ? 'style-loader' : MiniCssExtractPlugin.loader,
  115. 'css-loader',
  116. ],
  117. },
  118. {
  119. test: /\.less$/,
  120. include: APP_DIR,
  121. use: [
  122. isDevMode ? 'style-loader' : MiniCssExtractPlugin.loader,
  123. 'css-loader',
  124. 'less-loader',
  125. ],
  126. },
  127. /* for css linking images */
  128. {
  129. test: /\.png$/,
  130. loader: 'url-loader',
  131. options: {
  132. limit: 10000,
  133. name: '[name].[hash:8].[ext]',
  134. },
  135. },
  136. {
  137. test: /\.(jpg|gif)$/,
  138. loader: 'file-loader',
  139. options: {
  140. name: '[name].[hash:8].[ext]',
  141. },
  142. },
  143. /* for font-awesome */
  144. {
  145. test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
  146. loader: 'url-loader?limit=10000&mimetype=application/font-woff',
  147. },
  148. {
  149. test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
  150. loader: 'file-loader',
  151. },
  152. ],
  153. },
  154. externals: {
  155. cheerio: 'window',
  156. 'react/lib/ExecutionEnvironment': true,
  157. 'react/lib/ReactContext': true,
  158. },
  159. plugins,
  160. devtool: isDevMode ? 'cheap-module-eval-source-map' : false,
  161. devServer: {
  162. historyApiFallback: true,
  163. hot: true,
  164. index: '', // This line is needed to enable root proxying
  165. inline: true,
  166. stats: { colors: true },
  167. overlay: true,
  168. port: devserverPort,
  169. // Only serves bundled files from webpack-dev-server
  170. // and proxy everything else to Superset backend
  171. proxy: {
  172. context: () => true,
  173. '/': `http://localhost:${supersetPort}`,
  174. target: `http://localhost:${supersetPort}`,
  175. },
  176. contentBase: path.join(process.cwd(), '../static/assets/dist'),
  177. },
  178. };
  179. if (!isDevMode) {
  180. config.optimization.minimizer = [
  181. new TerserPlugin({
  182. cache: true,
  183. parallel: true,
  184. extractComments: true,
  185. }),
  186. ];
  187. }
  188. // Bundle analyzer is disabled by default
  189. // Pass flag --analyzeBundle=true to enable
  190. // e.g. npm run build -- --analyzeBundle=true
  191. if (analyzeBundle) {
  192. config.plugins.push(new BundleAnalyzerPlugin());
  193. }
  194. // Speed measurement is disabled by default
  195. // Pass flag --measure=true to enable
  196. // e.g. npm run build -- --measure=true
  197. const smp = new SpeedMeasurePlugin({
  198. disable: !measure,
  199. });
  200. module.exports = smp.wrap(config);