// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const childProcess = require('child_process');
const path = require('path');
const url = require('url');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const WebpackPwaManifest = require('webpack-pwa-manifest');
const NPM_TARGET = process.env.npm_lifecycle_event; //eslint-disable-line no-process-env
var DEV = false;
var TEST = false;
if (NPM_TARGET === 'run') {
DEV = true;
}
if (NPM_TARGET === 'test') {
DEV = false;
TEST = true;
}
if (NPM_TARGET === 'stats') {
DEV = true;
TEST = false;
}
const STANDARD_EXCLUDE = [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'non_npm_dependencies'),
];
var MYSTATS = {
// Add asset Information
assets: false,
// Sort assets by a field
assetsSort: '',
// Add information about cached (not built) modules
cached: true,
// Show cached assets (setting this to `false` only shows emitted files)
cachedAssets: true,
// Add children information
children: true,
// Add chunk information (setting this to `false` allows for a less verbose output)
chunks: true,
// Add built modules information to chunk information
chunkModules: true,
// Add the origins of chunks and chunk merging info
chunkOrigins: true,
// Sort the chunks by a field
chunksSort: '',
// `webpack --colors` equivalent
colors: true,
// Display the distance from the entry point for each module
depth: true,
// Display the entry points with the corresponding bundles
entrypoints: true,
// Add errors
errors: true,
// Add details to errors (like resolving log)
errorDetails: true,
// Exclude modules which match one of the given strings or regular expressions
exclude: [],
// Add the hash of the compilation
hash: true,
// Set the maximum number of modules to be shown
maxModules: 0,
// Add built modules information
modules: false,
// Sort the modules by a field
modulesSort: '!size',
// Show performance hint when file size exceeds `performance.maxAssetSize`
performance: true,
// Show the exports of the modules
providedExports: true,
// Add public path information
publicPath: true,
// Add information about the reasons why modules are included
reasons: true,
// Add the source code of modules
source: true,
// Add timing information
timings: true,
// Show which exports of a module are used
usedExports: true,
// Add webpack version information
version: true,
// Add warnings
warnings: true,
// Filter warnings to be shown (since webpack 2.4.0),
// can be a String, Regexp, a function getting the warning and returning a boolean
// or an Array of a combination of the above. First match wins.
warningsFilter: '',
};
let publicPath = '/static/';
// Allow overriding the publicPath in dev from the exported SiteURL.
if (DEV) {
const siteURL = process.env.MM_SERVICESETTINGS_SITEURL || ''; //eslint-disable-line no-process-env
if (siteURL) {
publicPath = path.join(new url.URL(siteURL).pathname, 'static') + '/';
}
}
var config = {
entry: ['@babel/polyfill', 'whatwg-fetch', 'url-search-params-polyfill', './root.jsx', 'root.html'],
output: {
path: path.join(__dirname, 'dist'),
publicPath,
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js',
},
module: {
rules: [
{
test: /\.(js|jsx)?$/,
exclude: STANDARD_EXCLUDE,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// Babel configuration is in .babelrc because jest requires it to be there.
},
},
},
{
type: 'javascript/auto',
test: /\.json$/,
include: [
path.resolve(__dirname, 'i18n'),
],
exclude: [/en\.json$/],
use: [
{
loader: 'file-loader?name=i18n/[name].[hash].[ext]',
},
],
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
options: {
includePaths: ['node_modules/compass-mixins/lib', 'sass'],
},
},
],
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
},
],
},
{
test: /\.(png|eot|tiff|svg|woff2|woff|ttf|gif|mp3|jpg)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'files/[hash].[ext]',
},
},
{
loader: 'image-webpack-loader',
options: {},
},
],
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: {
attrs: 'link:href',
},
},
],
},
],
},
resolve: {
modules: [
'node_modules',
'non_npm_dependencies',
path.resolve(__dirname),
],
alias: {
jquery: 'jquery/src/jquery',
superagent: 'node_modules/superagent/lib/client',
},
extensions: ['.js', '.jsx'],
},
performance: {
hints: 'warning',
},
target: 'web',
plugins: [
new webpack.ProvidePlugin({
'window.jQuery': 'jquery',
$: 'jquery',
jQuery: 'jquery',
}),
new webpack.DefinePlugin({
COMMIT_HASH: JSON.stringify(childProcess.execSync('git rev-parse HEAD || echo dev').toString()),
}),
new MiniCssExtractPlugin({
filename: '[name].[contentHash].css',
chunkFilename: '[name].[contentHash].css',
}),
new HtmlWebpackPlugin({
filename: 'root.html',
inject: 'head',
template: 'root.html',
}),
new CopyWebpackPlugin([
{from: 'images/emoji', to: 'emoji'},
{from: 'images/img_trans.gif', to: 'images'},
{from: 'images/logo-email.png', to: 'images'},
{from: 'images/circles.png', to: 'images'},
{from: 'images/favicon', to: 'images/favicon'},
{from: 'images/appIcons.png', to: 'images'},
{from: 'images/warning.png', to: 'images'},
]),
// Generate manifest.json, honouring any configured publicPath. This also handles injecting
// and tags into root.html.
new WebpackPwaManifest({
name: 'Mattermost',
short_name: 'Mattermost',
start_url: '..',
description: 'Mattermost is an open source, self-hosted Slack-alternative',
background_color: '#ffffff',
inject: true,
ios: true,
fingerprints: false,
orientation: 'any',
filename: 'manifest.json',
icons: [{
src: path.resolve('images/favicon/android-chrome-192x192.png'),
type: 'image/png',
sizes: '192x192',
}, {
src: path.resolve('images/favicon/apple-touch-icon-120x120.png'),
type: 'image/png',
sizes: '120x120',
ios: true,
}, {
src: path.resolve('images/favicon/apple-touch-icon-144x144.png'),
type: 'image/png',
sizes: '144x144',
ios: true,
}, {
src: path.resolve('images/favicon/apple-touch-icon-152x152.png'),
type: 'image/png',
sizes: '152x152',
ios: true,
}, {
src: path.resolve('images/favicon/apple-touch-icon-57x57.png'),
type: 'image/png',
sizes: '57x57',
ios: true,
}, {
src: path.resolve('images/favicon/apple-touch-icon-60x60.png'),
type: 'image/png',
sizes: '60x60',
ios: true,
}, {
src: path.resolve('images/favicon/apple-touch-icon-72x72.png'),
type: 'image/png',
sizes: '72x72',
ios: true,
}, {
src: path.resolve('images/favicon/apple-touch-icon-76x76.png'),
type: 'image/png',
sizes: '76x76',
ios: true,
}, {
src: path.resolve('images/favicon/favicon-16x16.png'),
type: 'image/png',
sizes: '16x16',
}, {
src: path.resolve('images/favicon/favicon-32x32.png'),
type: 'image/png',
sizes: '32x32',
}, {
src: path.resolve('images/favicon/favicon-96x96.png'),
type: 'image/png',
sizes: '96x96',
}],
}),
],
};
if (NPM_TARGET !== 'stats') {
config.stats = MYSTATS;
}
// Development mode configuration
if (DEV) {
config.mode = 'development';
config.devtool = 'source-map';
}
// Production mode configuration
if (!DEV) {
config.mode = 'production';
config.devtool = 'source-map';
}
const env = {};
if (DEV) {
env.PUBLIC_PATH = JSON.stringify(publicPath);
} else {
env.NODE_ENV = JSON.stringify('production');
}
config.plugins.push(new webpack.DefinePlugin({
'process.env': env,
}));
// Test mode configuration
if (TEST) {
config.entry = ['@babel/polyfill', './root.jsx'];
config.target = 'node';
config.externals = [nodeExternals()];
}
// Export PRODUCTION_PERF_DEBUG=1 when running webpack to enable support for the react profiler
// even while generating production code. (Performance testing development code is typically
// not helpful.)
// See https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html and
// https://gist.github.com/bvaughn/25e6233aeb1b4f0cdb8d8366e54a3977
if (process.env.PRODUCTION_PERF_DEBUG) { //eslint-disable-line no-process-env
console.log('Enabling production performance debug settings'); //eslint-disable-line no-console
config.resolve.alias['react-dom'] = 'react-dom/profiling';
config.resolve.alias['schedule/tracing'] = 'schedule/tracing-profiling';
config.optimization = {
// Skip minification to make the profiled data more useful.
minimize: false,
};
}
module.exports = config;