webpack.config.js 8.5 KB
Newer Older
1 2
'use strict';

P
Phil Hughes 已提交
3
var fs = require('fs');
4
var path = require('path');
5
var execSync = require('child_process').execSync;
6 7
var webpack = require('webpack');
var StatsPlugin = require('stats-webpack-plugin');
M
Mike Greiling 已提交
8
var CompressionPlugin = require('compression-webpack-plugin');
9
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
10
var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
11

12
var ROOT_PATH = path.resolve(__dirname, '..');
13
var IS_PRODUCTION = process.env.NODE_ENV === 'production';
14
var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1;
15
var DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
16
var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
17
var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
18
var WEBPACK_REPORT = process.env.WEBPACK_REPORT;
19 20

var config = {
21 22 23 24
  // because sqljs requires fs.
  node: {
    fs: "empty"
  },
25
  context: path.join(ROOT_PATH, 'app/assets/javascripts'),
26
  entry: {
27 28
    blob:                 './blob_edit/blob_bundle.js',
    boards:               './boards/boards_bundle.js',
29
    common:               './commons/index.js',
30
    common_vue:           ['vue', './vue_shared/common_vue.js'],
31
    common_d3:            ['d3'],
32
    cycle_analytics:      './cycle_analytics/cycle_analytics_bundle.js',
F
Filipa Lacerda 已提交
33
    commit_pipelines:     './commit/pipelines/pipelines_bundle.js',
34
    deploy_keys:          './deploy_keys/index.js',
35 36
    diff_notes:           './diff_notes/diff_notes_bundle.js',
    environments:         './environments/environments_bundle.js',
F
Filipa Lacerda 已提交
37
    environments_folder:  './environments/folder/environments_folder_bundle.js',
38
    filtered_search:      './filtered_search/filtered_search_bundle.js',
39
    graphs:               './graphs/graphs_bundle.js',
40
    group:                './group.js',
F
Filipa Lacerda 已提交
41
    groups_list:          './groups_list.js',
42
    issue_show:           './issue_show/index.js',
P
Phil Hughes 已提交
43
    locale:               './locale/index.js',
44
    main:                 './main.js',
45
    merge_conflicts:      './merge_conflicts/merge_conflicts_bundle.js',
46
    monitoring:           './monitoring/monitoring_bundle.js',
47
    network:              './network/network_bundle.js',
P
Phil Hughes 已提交
48
    notebook_viewer:      './blob/notebook_viewer.js',
S
Sam Rose 已提交
49
    pdf_viewer:           './blob/pdf_viewer.js',
50
    pipelines:            './pipelines/index.js',
J
Jacob Schatz 已提交
51
    balsamiq_viewer:      './blob/balsamiq_viewer.js',
52
    pipelines_graph:      './pipelines/graph_bundle.js',
53 54
    profile:              './profile/profile_bundle.js',
    protected_branches:   './protected_branches/protected_branches_bundle.js',
55
    protected_tags:       './protected_tags',
56
    sidebar:              './sidebar/sidebar_bundle.js',
57 58
    schedule_form:        './pipeline_schedules/pipeline_schedule_form_bundle.js',
    schedules_index:      './pipeline_schedules/pipeline_schedules_index_bundle.js',
59
    snippet:              './snippet/snippet_bundle.js',
60
    sketch_viewer:        './blob/sketch_viewer.js',
P
Phil Hughes 已提交
61
    stl_viewer:           './blob/stl_viewer.js',
62
    terminal:             './terminal/terminal_bundle.js',
M
Mike Greiling 已提交
63
    u2f:                  ['vendor/u2f'],
64
    users:                './users/users_bundle.js',
65
    raven:                './raven/index.js',
F
Fatih Acet 已提交
66
    vue_merge_request_widget: './vue_merge_request_widget/index.js',
67
    test:                 './test.js',
68 69 70 71 72
  },

  output: {
    path: path.join(ROOT_PATH, 'public/assets/webpack'),
    publicPath: '/assets/webpack/',
73
    filename: IS_PRODUCTION ? '[name].[chunkhash].bundle.js' : '[name].bundle.js'
74 75
  },

76
  devtool: 'cheap-module-source-map',
77

M
Mike Greiling 已提交
78
  module: {
M
Mike Greiling 已提交
79
    rules: [
M
Mike Greiling 已提交
80
      {
81
        test: /\.js$/,
82
        exclude: /(node_modules|vendor\/assets)/,
M
Mike Greiling 已提交
83
        loader: 'babel-loader',
F
Filipa Lacerda 已提交
84
      },
85 86
      {
        test: /\.vue$/,
M
Mike Greiling 已提交
87
        loader: 'vue-loader',
88
      },
F
Filipa Lacerda 已提交
89 90
      {
        test: /\.svg$/,
M
Mike Greiling 已提交
91 92
        loader: 'raw-loader',
      },
S
Sam Rose 已提交
93
      {
94
        test: /\.(gif|png)$/,
S
Sam Rose 已提交
95
        loader: 'url-loader',
96
        options: { limit: 2048 },
S
Sam Rose 已提交
97
      },
M
Mike Greiling 已提交
98
      {
99
        test: /\.(worker\.js|pdf|bmpr)$/,
S
Sam Rose 已提交
100 101 102
        exclude: /node_modules/,
        loader: 'file-loader',
      },
103 104 105 106
      {
        test: /locale\/[a-z]+\/(.*)\.js$/,
        loader: 'exports-loader?locales',
      },
M
Mike Greiling 已提交
107 108 109
    ]
  },

110 111 112 113 114 115 116 117 118
  plugins: [
    // manifest filename must match config.webpack.manifest_filename
    // webpack-rails only needs assetsByChunkName to function properly
    new StatsPlugin('manifest.json', {
      chunkModules: false,
      source: false,
      chunks: false,
      modules: false,
      assets: true
P
Phil Hughes 已提交
119
    }),
M
Mike Greiling 已提交
120 121

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

124 125 126 127 128 129
    // fix legacy jQuery plugins which depend on globals
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
    }),

130 131 132 133
    // use deterministic module ids in all environments
    IS_PRODUCTION ?
      new webpack.HashedModuleIdsPlugin() :
      new webpack.NamedModulesPlugin(),
134

135 136 137 138 139 140 141
    // create cacheable common library bundle for all vue chunks
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common_vue',
      chunks: [
        'boards',
        'commit_pipelines',
        'cycle_analytics',
P
Phil Hughes 已提交
142
        'deploy_keys',
143 144 145
        'diff_notes',
        'environments',
        'environments_folder',
146
        'filtered_search',
147
        'issue_show',
148
        'merge_conflicts',
P
Phil Hughes 已提交
149
        'notebook_viewer',
S
Sam Rose 已提交
150
        'pdf_viewer',
151
        'pipelines',
152
        'pipelines_graph',
153 154 155
        'schedule_form',
        'schedules_index',
        'sidebar',
156
        'vue_merge_request_widget',
157
      ],
158 159 160
      minChunks: function(module, count) {
        return module.resource && (/vue_shared/).test(module.resource);
      },
161 162
    }),

163 164 165
    // create cacheable common library bundle for all d3 chunks
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common_d3',
166 167 168 169 170
      chunks: [
        'graphs',
        'users',
        'monitoring',
      ],
171 172
    }),

173
    // create cacheable common library bundles
174
    new webpack.optimize.CommonsChunkPlugin({
175
      names: ['main', 'common', 'runtime'],
176
    }),
177 178 179 180 181 182 183 184

    // locale common library
    new webpack.optimize.CommonsChunkPlugin({
      name: 'locale',
      chunks: [
        'cycle_analytics',
      ],
    }),
M
Mike Greiling 已提交
185 186 187
  ],

  resolve: {
188
    extensions: ['.js'],
189
    alias: {
190
      '~':              path.join(ROOT_PATH, 'app/assets/javascripts'),
191
      'emojis':         path.join(ROOT_PATH, 'fixtures/emojis'),
192
      'empty_states':   path.join(ROOT_PATH, 'app/views/shared/empty_states'),
193
      'icons':          path.join(ROOT_PATH, 'app/views/shared/icons'),
194
      'images':         path.join(ROOT_PATH, 'app/assets/images'),
195
      'vendor':         path.join(ROOT_PATH, 'vendor/assets/javascripts'),
196
      'vue$':           'vue/dist/vue.esm.js',
197
    }
M
Mike Greiling 已提交
198
  }
199 200
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
function getHeadCommitSHA() {
  // Simple SHA validation.
  // Match 5-40 numbers or lowercase letters between a and f.
  const SHA_REGEX = /^\b[0-9a-f]{5,40}\b$/;
  let stdout;

  try {
    stdout = execSync('git rev-parse HEAD');
  } catch (error) {
    throw error;
  }

  const headCommitSHA = stdout.trim();
  if (!SHA_REGEX.test(headCommitSHA)) {
    throw new Error(`\`git rev-parse HEAD\` output is not a valid SHA1: ${headCommitSHA}`);
  }

  return headCommitSHA;
}

221
if (IS_PRODUCTION) {
222 223 224 225 226 227
  const processEnv = {
    NODE_ENV: JSON.stringify('production'),
  };

  processEnv.HEAD_COMMIT_SHA = getHeadCommitSHA();

M
Mike Greiling 已提交
228
  config.devtool = 'source-map';
229
  config.plugins.push(
230
    new webpack.NoEmitOnErrorsPlugin(),
M
Mike Greiling 已提交
231 232 233 234
    new webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false
    }),
235
    new webpack.optimize.UglifyJsPlugin({
M
Mike Greiling 已提交
236
      sourceMap: true
237 238
    }),
    new webpack.DefinePlugin({
239
      'process.env': processEnv,
240 241 242
    }),
    new CompressionPlugin({
      asset: '[path].gz[query]',
M
Mike Greiling 已提交
243
    })
244
  );
245 246 247
}

if (IS_DEV_SERVER) {
248
  config.devtool = 'cheap-module-eval-source-map';
249
  config.devServer = {
250
    host: DEV_SERVER_HOST,
251
    port: DEV_SERVER_PORT,
252 253
    headers: { 'Access-Control-Allow-Origin': '*' },
    stats: 'errors-only',
254
    inline: DEV_SERVER_LIVERELOAD
255
  };
256
  config.output.publicPath = '//' + DEV_SERVER_HOST + ':' + DEV_SERVER_PORT + config.output.publicPath;
257 258 259 260
  config.plugins.push(
    // watch node_modules for changes if we encounter a missing module compile error
    new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
  );
261 262
}

263 264 265 266 267 268 269 270 271 272 273 274
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'),
    })
  );
}

275
module.exports = config;