Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'config/webpack.config.js')
-rw-r--r--config/webpack.config.js363
1 files changed, 213 insertions, 150 deletions
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 2e8c94655c1..b74d9dde494 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -1,77 +1,60 @@
-'use strict';
-
-var fs = require('fs');
-var path = require('path');
-var webpack = require('webpack');
-var StatsPlugin = require('stats-webpack-plugin');
-var CompressionPlugin = require('compression-webpack-plugin');
-var NameAllModulesPlugin = require('name-all-modules-plugin');
-var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
-var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
-
-var ROOT_PATH = path.resolve(__dirname, '..');
-var IS_PRODUCTION = process.env.NODE_ENV === 'production';
-var IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
-var DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
-var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
-var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
-var WEBPACK_REPORT = process.env.WEBPACK_REPORT;
-var NO_COMPRESSION = process.env.NO_COMPRESSION;
-
-var config = {
- // because sqljs requires fs.
- node: {
- fs: "empty"
- },
+const crypto = require('crypto');
+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');
+const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
+
+const ROOT_PATH = path.resolve(__dirname, '..');
+const IS_PRODUCTION = process.env.NODE_ENV === 'production';
+const IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
+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 = [];
+
+function generateEntries() {
+ // generate automatic entry points
+ const autoEntries = {};
+ const pageEntries = glob.sync('pages/**/index.js', {
+ cwd: path.join(ROOT_PATH, 'app/assets/javascripts'),
+ });
+ watchAutoEntries = [path.join(ROOT_PATH, 'app/assets/javascripts/pages/')];
+
+ function generateAutoEntries(path, prefix = '.') {
+ const chunkPath = path.replace(/\/index\.js$/, '');
+ const chunkName = chunkPath.replace(/\//g, '.');
+ autoEntries[chunkName] = `${prefix}/${path}`;
+ }
+
+ pageEntries.forEach(path => generateAutoEntries(path));
+
+ autoEntriesCount = Object.keys(autoEntries).length;
+
+ const manualEntries = {
+ common: './commons/index.js',
+ main: './main.js',
+ raven: './raven/index.js',
+ webpack_runtime: './webpack.js',
+ ide: './ide/index.js',
+ };
+
+ return Object.assign(manualEntries, autoEntries);
+}
+
+const config = {
context: path.join(ROOT_PATH, 'app/assets/javascripts'),
- entry: {
- balsamiq_viewer: './blob/balsamiq_viewer.js',
- blob: './blob_edit/blob_bundle.js',
- boards: './boards/boards_bundle.js',
- common: './commons/index.js',
- common_vue: ['vue', './vue_shared/common_vue.js'],
- common_d3: ['d3'],
- cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js',
- commit_pipelines: './commit/pipelines/pipelines_bundle.js',
- deploy_keys: './deploy_keys/index.js',
- diff_notes: './diff_notes/diff_notes_bundle.js',
- environments: './environments/environments_bundle.js',
- environments_folder: './environments/folder/environments_folder_bundle.js',
- filtered_search: './filtered_search/filtered_search_bundle.js',
- graphs: './graphs/graphs_bundle.js',
- group: './group.js',
- groups: './groups/index.js',
- groups_list: './groups_list.js',
- issue_show: './issue_show/index.js',
- integrations: './integrations',
- job_details: './jobs/job_details_bundle.js',
- locale: './locale/index.js',
- main: './main.js',
- merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js',
- monitoring: './monitoring/monitoring_bundle.js',
- network: './network/network_bundle.js',
- notebook_viewer: './blob/notebook_viewer.js',
- pdf_viewer: './blob/pdf_viewer.js',
- pipelines: './pipelines/pipelines_bundle.js',
- pipelines_details: './pipelines/pipeline_details_bundle.js',
- profile: './profile/profile_bundle.js',
- prometheus_metrics: './prometheus_metrics',
- protected_branches: './protected_branches/protected_branches_bundle.js',
- protected_tags: './protected_tags',
- sidebar: './sidebar/sidebar_bundle.js',
- schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js',
- schedules_index: './pipeline_schedules/pipeline_schedules_index_bundle.js',
- snippet: './snippet/snippet_bundle.js',
- sketch_viewer: './blob/sketch_viewer.js',
- stl_viewer: './blob/stl_viewer.js',
- terminal: './terminal/terminal_bundle.js',
- u2f: ['vendor/u2f'],
- users: './users/users_bundle.js',
- raven: './raven/index.js',
- vue_merge_request_widget: './vue_merge_request_widget/index.js',
- test: './test.js',
- peek: './peek.js',
- },
+
+ entry: generateEntries,
output: {
path: path.join(ROOT_PATH, 'public/assets/webpack'),
@@ -101,26 +84,74 @@ var config = {
options: { limit: 2048 },
},
{
- test: /\.(worker\.js|pdf|bmpr)$/,
+ test: /\_worker\.js$/,
+ use: [
+ {
+ loader: 'worker-loader',
+ options: {
+ inline: true,
+ },
+ },
+ { loader: 'babel-loader' },
+ ],
+ },
+ {
+ test: /\.(worker(\.min)?\.js|pdf|bmpr)$/,
exclude: /node_modules/,
loader: 'file-loader',
+ options: {
+ name: '[name].[hash].[ext]',
+ },
},
{
- test: /locale\/\w+\/(.*)\.js$/,
- loader: 'exports-loader?locales',
+ test: /katex.css$/,
+ include: /node_modules\/katex\/dist/,
+ use: [
+ { loader: 'style-loader' },
+ {
+ loader: 'css-loader',
+ options: {
+ name: '[name].[hash].[ext]',
+ },
+ },
+ ],
},
- ]
+ {
+ test: /\.(eot|ttf|woff|woff2)$/,
+ include: /node_modules\/katex\/dist\/fonts/,
+ loader: 'file-loader',
+ options: {
+ name: '[name].[hash].[ext]',
+ },
+ },
+ {
+ test: /monaco-editor\/\w+\/vs\/loader\.js$/,
+ use: [
+ { loader: 'exports-loader', options: 'l.global' },
+ { loader: 'imports-loader', options: 'l=>{},this=>l,AMDLoader=>this,module=>undefined' },
+ ],
+ },
+ ],
+
+ noParse: [/monaco-editor\/\w+\/vs\//],
+ strictExportPresence: true,
},
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
+ new StatsWriterPlugin({
+ filename: 'manifest.json',
+ transform: function(data, opts) {
+ const stats = opts.compiler.getStats().toJson({
+ chunkModules: false,
+ source: false,
+ chunks: false,
+ modules: false,
+ assets: true,
+ });
+ return JSON.stringify(stats, null, 2);
+ },
}),
// prevent pikaday from including moment.js
@@ -137,75 +168,92 @@ var config = {
new NameAllModulesPlugin(),
// assign deterministic chunk ids
- new webpack.NamedChunksPlugin((chunk) => {
+ new webpack.NamedChunksPlugin(chunk => {
if (chunk.name) {
return chunk.name;
}
- return chunk.modules.map((m) => {
- var chunkPath = m.request.split('!').pop();
- return path.relative(m.context, chunkPath);
- }).join('_');
- }),
- // create cacheable common library bundle for all vue chunks
- new webpack.optimize.CommonsChunkPlugin({
- name: 'common_vue',
- chunks: [
- 'boards',
- 'commit_pipelines',
- 'cycle_analytics',
- 'deploy_keys',
- 'diff_notes',
- 'environments',
- 'environments_folder',
- 'filtered_search',
- 'groups',
- 'issue_show',
- 'job_details',
- 'merge_conflicts',
- 'notebook_viewer',
- 'pdf_viewer',
- 'pipelines',
- 'pipelines_details',
- 'schedule_form',
- 'schedules_index',
- 'sidebar',
- 'vue_merge_request_widget',
- ],
- minChunks: function(module, count) {
- return module.resource && (/vue_shared/).test(module.resource);
- },
- }),
+ const moduleNames = [];
- // create cacheable common library bundle for all d3 chunks
- new webpack.optimize.CommonsChunkPlugin({
- name: 'common_d3',
- chunks: [
- 'graphs',
- 'users',
- 'monitoring',
- ],
+ function collectModuleNames(m) {
+ // handle ConcatenatedModule which does not have resource nor context set
+ if (m.modules) {
+ m.modules.forEach(collectModuleNames);
+ return;
+ }
+
+ const pagesBase = path.join(ROOT_PATH, 'app/assets/javascripts/pages');
+
+ if (m.resource.indexOf(pagesBase) === 0) {
+ moduleNames.push(
+ path
+ .relative(pagesBase, m.resource)
+ .replace(/\/index\.[a-z]+$/, '')
+ .replace(/\//g, '__')
+ );
+ } else {
+ moduleNames.push(path.relative(m.context, m.resource));
+ }
+ }
+
+ chunk.forEachModule(collectModuleNames);
+
+ const hash = crypto
+ .createHash('sha256')
+ .update(moduleNames.join('_'))
+ .digest('hex');
+
+ return `${moduleNames[0]}-${hash.substr(0, 6)}`;
}),
// create cacheable common library bundles
new webpack.optimize.CommonsChunkPlugin({
- names: ['main', 'locale', 'common', 'runtime'],
+ names: ['main', 'common', 'webpack_runtime'],
}),
+
+ // copy pre-compiled vendor libraries verbatim
+ new CopyWebpackPlugin([
+ {
+ from: path.join(
+ ROOT_PATH,
+ `node_modules/monaco-editor/${IS_PRODUCTION ? 'min' : 'dev'}/vs`
+ ),
+ to: 'monaco-editor/vs',
+ transform: function(content, path) {
+ if (/\.js$/.test(path) && !/worker/i.test(path) && !/typescript/i.test(path)) {
+ return (
+ '(function(){\n' +
+ 'var define = this.define, require = this.require;\n' +
+ 'window.define = define; window.require = require;\n' +
+ content +
+ '\n}.call(window.__monaco_context__ || (window.__monaco_context__ = {})));'
+ );
+ }
+ return content;
+ },
+ },
+ ]),
],
resolve: {
extensions: ['.js'],
alias: {
- '~': 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',
- }
- }
-}
+ '~': 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'),
+ },
+ },
+
+ // sqljs requires fs
+ node: {
+ fs: 'empty',
+ },
+};
if (IS_PRODUCTION) {
config.devtool = 'source-map';
@@ -213,24 +261,20 @@ if (IS_PRODUCTION) {
new webpack.NoEmitOnErrorsPlugin(),
new webpack.LoaderOptionsPlugin({
minimize: true,
- debug: false
+ debug: false,
}),
+ new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.UglifyJsPlugin({
- sourceMap: true
+ sourceMap: true,
}),
new webpack.DefinePlugin({
- 'process.env': { NODE_ENV: JSON.stringify('production') }
+ 'process.env': { NODE_ENV: JSON.stringify('production') },
})
);
- // zopfli requires a lot of compute time and is disabled in CI
+ // compression can require a lot of compute time and is disabled in CI
if (!NO_COMPRESSION) {
- // gracefully fall back to gzip if `node-zopfli` is unavailable (e.g. in CentOS 6)
- try {
- config.plugins.push(new CompressionPlugin({ algorithm: 'zopfli' }));
- } catch(err) {
- config.plugins.push(new CompressionPlugin({ algorithm: 'gzip' }));
- }
+ config.plugins.push(new CompressionPlugin());
}
}
@@ -239,15 +283,34 @@ if (IS_DEV_SERVER) {
config.devServer = {
host: DEV_SERVER_HOST,
port: DEV_SERVER_PORT,
+ disableHostCheck: true,
headers: { 'Access-Control-Allow-Origin': '*' },
stats: 'errors-only',
hot: DEV_SERVER_LIVERELOAD,
- inline: DEV_SERVER_LIVERELOAD
+ inline: DEV_SERVER_LIVERELOAD,
};
- config.output.publicPath = '//' + DEV_SERVER_HOST + ':' + DEV_SERVER_PORT + config.output.publicPath;
config.plugins.push(
// watch node_modules for changes if we encounter a missing module compile error
- new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
+ new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules')),
+
+ // 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
+ console.log(
+ `${autoEntriesCount} entries from '/pages' automatically added to webpack output.`
+ );
+
+ callback();
+ });
+ },
+ }
);
if (DEV_SERVER_LIVERELOAD) {
config.plugins.push(new webpack.HotModuleReplacementPlugin());