webpack.config.js 8.5 KB
Newer Older
1 2 3 4 5 6 7 8
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const webpack = require('webpack');
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const NameAllModulesPlugin = require('name-all-modules-plugin');
P
Phil Hughes 已提交
9
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
10 11 12 13
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');

const ROOT_PATH = path.resolve(__dirname, '..');
const IS_PRODUCTION = process.env.NODE_ENV === 'production';
P
Phil Hughes 已提交
14
const IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
15 16 17 18 19 20 21 22
const DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
const DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
const DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
const WEBPACK_REPORT = process.env.WEBPACK_REPORT;
const NO_COMPRESSION = process.env.NO_COMPRESSION;

let autoEntriesCount = 0;
let watchAutoEntries = [];
M
Mike Greiling 已提交
23
const defaultEntries = ['./webpack', './commons', './main'];
24 25 26

function generateEntries() {
  // generate automatic entry points
27
  const autoEntries = {};
M
Mike Greiling 已提交
28 29 30 31
  const pageEntries = glob.sync('pages/**/index.js', {
    cwd: path.join(ROOT_PATH, 'app/assets/javascripts'),
  });
  watchAutoEntries = [path.join(ROOT_PATH, 'app/assets/javascripts/pages/')];
32 33 34

  function generateAutoEntries(path, prefix = '.') {
    const chunkPath = path.replace(/\/index\.js$/, '');
35
    const chunkName = chunkPath.replace(/\//g, '.');
M
Mike Greiling 已提交
36
    autoEntries[chunkName] = defaultEntries.concat(`${prefix}/${path}`);
37
  }
38

M
Mike Greiling 已提交
39
  pageEntries.forEach(path => generateAutoEntries(path));
40

41
  autoEntriesCount = Object.keys(autoEntries).length;
42

43
  const manualEntries = {
M
Mike Greiling 已提交
44 45
    raven: './raven/index.js',
    ide: './ide/index.js',
46 47 48 49 50
  };

  return Object.assign(manualEntries, autoEntries);
}

51
const config = {
M
Mike Greiling 已提交
52 53
  mode: IS_PRODUCTION ? 'production' : 'development',

54 55 56
  context: path.join(ROOT_PATH, 'app/assets/javascripts'),

  entry: generateEntries,
57 58 59 60

  output: {
    path: path.join(ROOT_PATH, 'public/assets/webpack'),
    publicPath: '/assets/webpack/',
P
Phil Hughes 已提交
61 62
    filename: IS_PRODUCTION ? '[name].[chunkhash].bundle.js' : '[name].bundle.js',
    chunkFilename: IS_PRODUCTION ? '[name].[chunkhash].chunk.js' : '[name].chunk.js',
63 64
  },

M
Mike Greiling 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
  optimization: {
    nodeEnv: false,
    runtimeChunk: 'single',
    splitChunks: {
      maxInitialRequests: 4,
      cacheGroups: {
        default: false,
        common: () => ({
          priority: 20,
          name: 'main',
          chunks: 'initial',
          minChunks: autoEntriesCount * 0.9,
        }),
        vendors: {
          priority: 10,
          chunks: 'async',
          test: /[\\/](node_modules|vendor[\\/]assets[\\/]javascripts)[\\/]/,
        },
        commons: {
          chunks: 'all',
          minChunks: 2,
          reuseExistingChunk: true,
        },
      },
    },
  },

M
Mike Greiling 已提交
92
  module: {
M
Mike Greiling 已提交
93
    rules: [
M
Mike Greiling 已提交
94
      {
95
        test: /\.js$/,
96
        exclude: /(node_modules|vendor\/assets)/,
M
Mike Greiling 已提交
97
        loader: 'babel-loader',
98 99 100
        options: {
          cacheDirectory: path.join(ROOT_PATH, 'tmp/cache/babel-loader'),
        },
F
Filipa Lacerda 已提交
101
      },
102 103
      {
        test: /\.vue$/,
M
Mike Greiling 已提交
104
        loader: 'vue-loader',
105
      },
F
Filipa Lacerda 已提交
106 107
      {
        test: /\.svg$/,
M
Mike Greiling 已提交
108 109
        loader: 'raw-loader',
      },
S
Sam Rose 已提交
110
      {
111
        test: /\.(gif|png)$/,
S
Sam Rose 已提交
112
        loader: 'url-loader',
113
        options: { limit: 2048 },
S
Sam Rose 已提交
114
      },
P
Phil Hughes 已提交
115 116
      {
        test: /\_worker\.js$/,
P
Phil Hughes 已提交
117
        use: [
P
Phil Hughes 已提交
118
          {
T
Tim Zallmann 已提交
119
            loader: 'worker-loader',
P
Phil Hughes 已提交
120
            options: {
M
Mike Greiling 已提交
121 122
              inline: true,
            },
T
Tim Zallmann 已提交
123
          },
P
Phil Hughes 已提交
124 125
          { loader: 'babel-loader' },
        ],
P
Phil Hughes 已提交
126
      },
M
Mike Greiling 已提交
127
      {
128
        test: /\.(worker(\.min)?\.js|pdf|bmpr)$/,
S
Sam Rose 已提交
129 130
        exclude: /node_modules/,
        loader: 'file-loader',
131 132
        options: {
          name: '[name].[hash].[ext]',
M
Mike Greiling 已提交
133
        },
S
Sam Rose 已提交
134
      },
135
      {
C
Clement Ho 已提交
136
        test: /katex.min.css$/,
137 138 139
        include: /node_modules\/katex\/dist/,
        use: [
          { loader: 'style-loader' },
140
          {
141 142
            loader: 'css-loader',
            options: {
M
Mike Greiling 已提交
143 144
              name: '[name].[hash].[ext]',
            },
145 146 147 148 149 150 151 152 153
          },
        ],
      },
      {
        test: /\.(eot|ttf|woff|woff2)$/,
        include: /node_modules\/katex\/dist\/fonts/,
        loader: 'file-loader',
        options: {
          name: '[name].[hash].[ext]',
M
Mike Greiling 已提交
154
        },
155
      },
156 157 158 159
      {
        test: /monaco-editor\/\w+\/vs\/loader\.js$/,
        use: [
          { loader: 'exports-loader', options: 'l.global' },
P
Phil Hughes 已提交
160
          { loader: 'imports-loader', options: 'l=>{},this=>l,AMDLoader=>this,module=>undefined' },
161
        ],
M
Mike Greiling 已提交
162
      },
163 164 165
    ],

    noParse: [/monaco-editor\/\w+\/vs\//],
166
    strictExportPresence: true,
M
Mike Greiling 已提交
167 168
  },

169 170 171
  plugins: [
    // manifest filename must match config.webpack.manifest_filename
    // webpack-rails only needs assetsByChunkName to function properly
172 173 174
    new StatsWriterPlugin({
      filename: 'manifest.json',
      transform: function(data, opts) {
175
        const stats = opts.compiler.getStats().toJson({
176 177 178 179
          chunkModules: false,
          source: false,
          chunks: false,
          modules: false,
M
Mike Greiling 已提交
180
          assets: true,
181 182
        });
        return JSON.stringify(stats, null, 2);
M
Mike Greiling 已提交
183
      },
P
Phil Hughes 已提交
184
    }),
M
Mike Greiling 已提交
185 186

    // prevent pikaday from including moment.js
P
Phil Hughes 已提交
187
    new webpack.IgnorePlugin(/moment/, /pikaday/),
M
Mike Greiling 已提交
188

189 190 191 192 193 194
    // fix legacy jQuery plugins which depend on globals
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
    }),

195
    // copy pre-compiled vendor libraries verbatim
196 197
    new CopyWebpackPlugin([
      {
M
Mike Greiling 已提交
198 199 200 201
        from: path.join(
          ROOT_PATH,
          `node_modules/monaco-editor/${IS_PRODUCTION ? 'min' : 'dev'}/vs`
        ),
202 203
        to: 'monaco-editor/vs',
        transform: function(content, path) {
P
Phil Hughes 已提交
204
          if (/\.js$/.test(path) && !/worker/i.test(path) && !/typescript/i.test(path)) {
205 206 207
            return (
              '(function(){\n' +
              'var define = this.define, require = this.require;\n' +
208
              'window.define = define; window.require = require;\n' +
209 210 211 212 213
              content +
              '\n}.call(window.__monaco_context__ || (window.__monaco_context__ = {})));'
            );
          }
          return content;
M
Mike Greiling 已提交
214 215
        },
      },
216
    ]),
M
Mike Greiling 已提交
217 218 219
  ],

  resolve: {
220
    extensions: ['.js'],
221
    alias: {
M
Mike Greiling 已提交
222 223 224 225 226 227 228 229 230
      '~': path.join(ROOT_PATH, 'app/assets/javascripts'),
      emojis: path.join(ROOT_PATH, 'fixtures/emojis'),
      empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'),
      icons: path.join(ROOT_PATH, 'app/views/shared/icons'),
      images: path.join(ROOT_PATH, 'app/assets/images'),
      vendor: path.join(ROOT_PATH, 'vendor/assets/javascripts'),
      vue$: 'vue/dist/vue.esm.js',
      spec: path.join(ROOT_PATH, 'spec/javascripts'),
    },
231
  },
232

233 234 235 236 237
  // sqljs requires fs
  node: {
    fs: 'empty',
  },
};
238

239
if (IS_PRODUCTION) {
M
Mike Greiling 已提交
240
  config.devtool = 'source-map';
M
Mike Greiling 已提交
241

242
  // compression can require a lot of compute time and is disabled in CI
M
Mike Greiling 已提交
243
  if (!NO_COMPRESSION) {
244
    config.plugins.push(new CompressionPlugin());
M
Mike Greiling 已提交
245
  }
246 247 248
}

if (IS_DEV_SERVER) {
249
  config.devtool = 'cheap-module-eval-source-map';
250
  config.devServer = {
251
    host: DEV_SERVER_HOST,
252
    port: DEV_SERVER_PORT,
253
    disableHostCheck: true,
254 255
    headers: { 'Access-Control-Allow-Origin': '*' },
    stats: 'errors-only',
S
Simon Knox 已提交
256
    hot: DEV_SERVER_LIVERELOAD,
M
Mike Greiling 已提交
257
    inline: DEV_SERVER_LIVERELOAD,
258
  };
259 260
  config.plugins.push(
    // watch node_modules for changes if we encounter a missing module compile error
M
Mike Greiling 已提交
261
    // new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules')),
262 263 264 265 266 267 268 269 270 271 272

    // watch for changes to our automatic entry point modules
    {
      apply(compiler) {
        compiler.plugin('emit', (compilation, callback) => {
          compilation.contextDependencies = [
            ...compilation.contextDependencies,
            ...watchAutoEntries,
          ];

          // report our auto-generated bundle count
M
Mike Greiling 已提交
273 274 275
          console.log(
            `${autoEntriesCount} entries from '/pages' automatically added to webpack output.`
          );
276 277

          callback();
M
Mike Greiling 已提交
278
        });
279
      },
P
Phil Hughes 已提交
280
    }
281
  );
S
Simon Knox 已提交
282 283 284
  if (DEV_SERVER_LIVERELOAD) {
    config.plugins.push(new webpack.HotModuleReplacementPlugin());
  }
285 286
}

287 288 289 290 291 292 293 294
if (WEBPACK_REPORT) {
  config.plugins.push(
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      generateStatsFile: true,
      openAnalyzer: false,
      reportFilename: path.join(ROOT_PATH, 'webpack-report/index.html'),
      statsFilename: path.join(ROOT_PATH, 'webpack-report/stats.json'),
P
Phil Hughes 已提交
295
    })
296 297 298
  );
}

299
module.exports = config;