debian-mirror-gitlab/config/webpack.config.js

352 lines
11 KiB
JavaScript
Raw Normal View History

2018-03-27 19:54:05 +05:30
const path = require('path');
const glob = require('glob');
const webpack = require('webpack');
2018-11-08 19:23:39 +05:30
const VueLoaderPlugin = require('vue-loader/lib/plugin');
2018-03-27 19:54:05 +05:30
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
const CompressionPlugin = require('compression-webpack-plugin');
2018-11-08 19:23:39 +05:30
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
2018-03-27 19:54:05 +05:30
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ROOT_PATH = path.resolve(__dirname, '..');
2018-12-13 13:39:08 +05:30
const CACHE_PATH = process.env.WEBPACK_CACHE_PATH || path.join(ROOT_PATH, 'tmp/cache');
2018-03-27 19:54:05 +05:30
const IS_PRODUCTION = process.env.NODE_ENV === 'production';
const IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
2019-07-07 11:18:12 +05:30
const IS_EE = require('./helpers/is_ee_env');
2018-03-27 19:54:05 +05:30
const DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
const DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
2018-11-08 19:23:39 +05:30
const DEV_SERVER_LIVERELOAD = IS_DEV_SERVER && process.env.DEV_SERVER_LIVERELOAD !== 'false';
2018-03-27 19:54:05 +05:30
const WEBPACK_REPORT = process.env.WEBPACK_REPORT;
const NO_COMPRESSION = process.env.NO_COMPRESSION;
2018-11-08 19:23:39 +05:30
const NO_SOURCEMAPS = process.env.NO_SOURCEMAPS;
const VUE_VERSION = require('vue/package.json').version;
const VUE_LOADER_VERSION = require('vue-loader/package.json').version;
const devtool = IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map';
2018-03-27 19:54:05 +05:30
let autoEntriesCount = 0;
let watchAutoEntries = [];
2018-10-15 14:42:47 +05:30
const defaultEntries = ['./main'];
2018-03-27 19:54:05 +05:30
function generateEntries() {
// generate automatic entry points
const autoEntries = {};
2018-10-15 14:42:47 +05:30
const autoEntriesMap = {};
2018-05-09 12:01:36 +05:30
const pageEntries = glob.sync('pages/**/index.js', {
cwd: path.join(ROOT_PATH, 'app/assets/javascripts'),
});
watchAutoEntries = [path.join(ROOT_PATH, 'app/assets/javascripts/pages/')];
2018-03-27 19:54:05 +05:30
function generateAutoEntries(path, prefix = '.') {
const chunkPath = path.replace(/\/index\.js$/, '');
const chunkName = chunkPath.replace(/\//g, '.');
2018-10-15 14:42:47 +05:30
autoEntriesMap[chunkName] = `${prefix}/${path}`;
2018-03-17 18:26:18 +05:30
}
2018-05-09 12:01:36 +05:30
pageEntries.forEach(path => generateAutoEntries(path));
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
if (IS_EE) {
const eePageEntries = glob.sync('pages/**/index.js', {
cwd: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
});
eePageEntries.forEach(path => generateAutoEntries(path, 'ee'));
watchAutoEntries.push(path.join(ROOT_PATH, 'ee/app/assets/javascripts/pages/'));
}
2018-10-15 14:42:47 +05:30
const autoEntryKeys = Object.keys(autoEntriesMap);
autoEntriesCount = autoEntryKeys.length;
// import ancestor entrypoints within their children
autoEntryKeys.forEach(entry => {
const entryPaths = [autoEntriesMap[entry]];
const segments = entry.split('.');
while (segments.pop()) {
const ancestor = segments.join('.');
if (autoEntryKeys.includes(ancestor)) {
entryPaths.unshift(autoEntriesMap[ancestor]);
}
}
autoEntries[entry] = defaultEntries.concat(entryPaths);
});
2018-03-27 19:54:05 +05:30
const manualEntries = {
2018-10-15 14:42:47 +05:30
default: defaultEntries,
2018-05-09 12:01:36 +05:30
raven: './raven/index.js',
2018-03-27 19:54:05 +05:30
};
return Object.assign(manualEntries, autoEntries);
}
2019-07-07 11:18:12 +05:30
const 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',
spec: path.join(ROOT_PATH, 'spec/javascripts'),
// the following resolves files which are different between CE and EE
ee_else_ce: path.join(ROOT_PATH, 'app/assets/javascripts'),
};
if (IS_EE) {
Object.assign(alias, {
ee: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
ee_empty_states: path.join(ROOT_PATH, 'ee/app/views/shared/empty_states'),
ee_icons: path.join(ROOT_PATH, 'ee/app/views/shared/icons'),
ee_images: path.join(ROOT_PATH, 'ee/app/assets/images'),
ee_spec: path.join(ROOT_PATH, 'ee/spec/javascripts'),
ee_else_ce: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
});
}
2018-11-08 19:23:39 +05:30
module.exports = {
2018-10-15 14:42:47 +05:30
mode: IS_PRODUCTION ? 'production' : 'development',
2018-03-27 19:54:05 +05:30
context: path.join(ROOT_PATH, 'app/assets/javascripts'),
entry: generateEntries,
2017-08-17 22:00:37 +05:30
output: {
path: path.join(ROOT_PATH, 'public/assets/webpack'),
publicPath: '/assets/webpack/',
2018-10-15 14:42:47 +05:30
filename: IS_PRODUCTION ? '[name].[chunkhash:8].bundle.js' : '[name].bundle.js',
chunkFilename: IS_PRODUCTION ? '[name].[chunkhash:8].chunk.js' : '[name].chunk.js',
globalObject: 'this', // allow HMR and web workers to play nice
},
2018-11-08 19:23:39 +05:30
resolve: {
2019-02-15 15:39:39 +05:30
extensions: ['.js', '.gql', '.graphql'],
2019-07-07 11:18:12 +05:30
alias,
2017-08-17 22:00:37 +05:30
},
module: {
2018-11-08 19:23:39 +05:30
strictExportPresence: true,
2017-08-17 22:00:37 +05:30
rules: [
2019-02-15 15:39:39 +05:30
{
type: 'javascript/auto',
test: /\.mjs$/,
use: [],
},
2017-08-17 22:00:37 +05:30
{
test: /\.js$/,
2018-11-08 19:23:39 +05:30
exclude: path => /node_modules|vendor[\\/]assets/.test(path) && !/\.vue\.js/.test(path),
2017-08-17 22:00:37 +05:30
loader: 'babel-loader',
2018-10-15 14:42:47 +05:30
options: {
2018-11-08 19:23:39 +05:30
cacheDirectory: path.join(CACHE_PATH, 'babel-loader'),
2018-10-15 14:42:47 +05:30
},
2017-08-17 22:00:37 +05:30
},
{
test: /\.vue$/,
loader: 'vue-loader',
2018-11-08 19:23:39 +05:30
options: {
cacheDirectory: path.join(CACHE_PATH, 'vue-loader'),
cacheIdentifier: [
process.env.NODE_ENV || 'development',
webpack.version,
VUE_VERSION,
VUE_LOADER_VERSION,
].join('|'),
},
2017-08-17 22:00:37 +05:30
},
2019-02-15 15:39:39 +05:30
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader',
},
2017-08-17 22:00:37 +05:30
{
test: /\.svg$/,
loader: 'raw-loader',
},
{
2017-09-10 17:25:29 +05:30
test: /\.(gif|png)$/,
2017-08-17 22:00:37 +05:30
loader: 'url-loader',
2017-09-10 17:25:29 +05:30
options: { limit: 2048 },
2017-08-17 22:00:37 +05:30
},
2018-03-17 18:26:18 +05:30
{
test: /\_worker\.js$/,
use: [
{
loader: 'worker-loader',
options: {
2018-10-15 14:42:47 +05:30
name: '[name].[hash:8].worker.js',
2019-07-07 11:18:12 +05:30
inline: IS_DEV_SERVER,
2018-05-09 12:01:36 +05:30
},
2018-03-17 18:26:18 +05:30
},
2018-10-15 14:42:47 +05:30
'babel-loader',
2018-03-17 18:26:18 +05:30
],
},
2017-08-17 22:00:37 +05:30
{
2017-09-10 17:25:29 +05:30
test: /\.(worker(\.min)?\.js|pdf|bmpr)$/,
2017-08-17 22:00:37 +05:30
exclude: /node_modules/,
loader: 'file-loader',
2017-09-10 17:25:29 +05:30
options: {
2018-10-15 14:42:47 +05:30
name: '[name].[hash:8].[ext]',
2018-05-09 12:01:36 +05:30
},
2017-08-17 22:00:37 +05:30
},
{
2018-11-08 19:23:39 +05:30
test: /.css$/,
2018-03-17 18:26:18 +05:30
use: [
2018-11-08 19:23:39 +05:30
'vue-style-loader',
2018-03-27 19:54:05 +05:30
{
2018-03-17 18:26:18 +05:30
loader: 'css-loader',
options: {
2018-10-15 14:42:47 +05:30
name: '[name].[hash:8].[ext]',
2018-05-09 12:01:36 +05:30
},
2018-03-17 18:26:18 +05:30
},
],
},
{
test: /\.(eot|ttf|woff|woff2)$/,
include: /node_modules\/katex\/dist\/fonts/,
loader: 'file-loader',
options: {
2018-10-15 14:42:47 +05:30
name: '[name].[hash:8].[ext]',
2018-05-09 12:01:36 +05:30
},
2017-08-17 22:00:37 +05:30
},
2017-09-10 17:25:29 +05:30
],
2018-11-08 19:23:39 +05:30
},
2017-09-10 17:25:29 +05:30
2018-11-08 19:23:39 +05:30
optimization: {
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,
},
},
},
2017-08-17 22:00:37 +05:30
},
plugins: [
// manifest filename must match config.webpack.manifest_filename
// webpack-rails only needs assetsByChunkName to function properly
2017-09-10 17:25:29 +05:30
new StatsWriterPlugin({
filename: 'manifest.json',
transform: function(data, opts) {
2018-03-27 19:54:05 +05:30
const stats = opts.compiler.getStats().toJson({
2017-09-10 17:25:29 +05:30
chunkModules: false,
source: false,
chunks: false,
modules: false,
2018-05-09 12:01:36 +05:30
assets: true,
2017-09-10 17:25:29 +05:30
});
return JSON.stringify(stats, null, 2);
2018-05-09 12:01:36 +05:30
},
2017-08-17 22:00:37 +05:30
}),
2018-11-08 19:23:39 +05:30
// enable vue-loader to use existing loader rules for other module types
new VueLoaderPlugin(),
// automatically configure monaco editor web workers
new MonacoWebpackPlugin(),
2017-08-17 22:00:37 +05:30
// prevent pikaday from including moment.js
new webpack.IgnorePlugin(/moment/, /pikaday/),
// fix legacy jQuery plugins which depend on globals
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
2019-07-07 11:18:12 +05:30
new webpack.NormalModuleReplacementPlugin(/^ee_component\/(.*)\.vue/, function(resource) {
if (Object.keys(module.exports.resolve.alias).indexOf('ee') >= 0) {
resource.request = resource.request.replace(/^ee_component/, 'ee');
} else {
resource.request = path.join(
ROOT_PATH,
'app/assets/javascripts/vue_shared/components/empty_component.js',
);
}
}),
2018-11-08 19:23:39 +05:30
// compression can require a lot of compute time and is disabled in CI
IS_PRODUCTION && !NO_COMPRESSION && new CompressionPlugin(),
2017-08-17 22:00:37 +05:30
2018-11-08 19:23:39 +05:30
// WatchForChangesPlugin
// TODO: publish this as a separate plugin
IS_DEV_SERVER && {
apply(compiler) {
compiler.hooks.emit.tapAsync('WatchForChangesPlugin', (compilation, callback) => {
const missingDeps = Array.from(compilation.missingDependencies);
const nodeModulesPath = path.join(ROOT_PATH, 'node_modules');
const hasMissingNodeModules = missingDeps.some(
2019-07-07 11:18:12 +05:30
file => file.indexOf(nodeModulesPath) !== -1,
2018-11-08 19:23:39 +05:30
);
2017-08-17 22:00:37 +05:30
2018-11-08 19:23:39 +05:30
// watch for changes to missing node_modules
if (hasMissingNodeModules) compilation.contextDependencies.add(nodeModulesPath);
2018-03-17 18:26:18 +05:30
2018-11-08 19:23:39 +05:30
// watch for changes to automatic entrypoints
watchAutoEntries.forEach(watchPath => compilation.contextDependencies.add(watchPath));
2017-09-10 17:25:29 +05:30
2018-11-08 19:23:39 +05:30
// report our auto-generated bundle count
console.log(
2019-07-07 11:18:12 +05:30
`${autoEntriesCount} entries from '/pages' automatically added to webpack output.`,
2018-11-08 19:23:39 +05:30
);
2017-08-17 22:00:37 +05:30
2018-11-08 19:23:39 +05:30
callback();
});
},
},
// enable HMR only in webpack-dev-server
DEV_SERVER_LIVERELOAD && new webpack.HotModuleReplacementPlugin(),
// optionally generate webpack bundle analysis
WEBPACK_REPORT &&
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'),
}),
2019-07-07 11:18:12 +05:30
new webpack.DefinePlugin({
2019-09-30 21:07:59 +05:30
// This one is used to define window.gon.ee and other things properly in tests:
2019-07-07 11:18:12 +05:30
'process.env.IS_GITLAB_EE': JSON.stringify(IS_EE),
2019-09-30 21:07:59 +05:30
// This one is used to check against "EE" properly in application code
IS_EE: IS_EE ? 'window.gon && window.gon.ee' : JSON.stringify(false),
2019-07-07 11:18:12 +05:30
}),
2018-11-08 19:23:39 +05:30
].filter(Boolean),
devServer: {
2017-08-17 22:00:37 +05:30
host: DEV_SERVER_HOST,
port: DEV_SERVER_PORT,
2017-09-10 17:25:29 +05:30
disableHostCheck: true,
2018-11-08 19:23:39 +05:30
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
},
2017-08-17 22:00:37 +05:30
stats: 'errors-only',
2017-09-10 17:25:29 +05:30
hot: DEV_SERVER_LIVERELOAD,
2018-05-09 12:01:36 +05:30
inline: DEV_SERVER_LIVERELOAD,
2018-11-08 19:23:39 +05:30
},
2017-08-17 22:00:37 +05:30
2018-11-08 19:23:39 +05:30
devtool: NO_SOURCEMAPS ? false : devtool,
2017-08-17 22:00:37 +05:30
2019-09-30 21:07:59 +05:30
node: {
fs: 'empty', // sqljs requires fs
setImmediate: false,
},
2018-11-08 19:23:39 +05:30
};