feat(build) migrate to Webpack 5

This commit is contained in:
Saúl Ibarra Corretgé 2021-10-07 10:39:03 +02:00 committed by Saúl Ibarra Corretgé
parent 720d19ba95
commit a313f5cde2
4 changed files with 2088 additions and 3383 deletions

View File

@ -14,12 +14,12 @@ STYLES_BUNDLE = css/all.bundle.css
STYLES_DESTINATION = css/all.css
STYLES_MAIN = css/main.scss
WEBPACK = ./node_modules/.bin/webpack
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack-dev-server
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
all: compile deploy clean
compile: compile-load-test
$(WEBPACK) -p
$(WEBPACK)
compile-load-test:
${NPM} install --prefix resources/load-test && ${NPM} run build --prefix resources/load-test
@ -51,9 +51,11 @@ deploy-appbundle:
$(OUTPUT_DIR)/analytics-ga.js \
$(BUILD_DIR)/analytics-ga.min.js \
$(BUILD_DIR)/analytics-ga.min.map \
$(DEPLOY_DIR)
cp \
$(BUILD_DIR)/close3.min.js \
$(BUILD_DIR)/close3.min.map \
$(DEPLOY_DIR)
$(DEPLOY_DIR) || true
deploy-lib-jitsi-meet:
cp \
@ -100,7 +102,7 @@ deploy-local:
.NOTPARALLEL:
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm
$(WEBPACK_DEV_SERVER) --detect-circular-deps
$(WEBPACK_DEV_SERVER)
source-package:
mkdir -p source_package/jitsi-meet/css && \

4845
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -124,7 +124,7 @@
"@babel/preset-env": "7.1.0",
"@babel/preset-flow": "7.0.0",
"@babel/preset-react": "7.0.0",
"@babel/runtime": "7.9.0",
"@babel/runtime": "7.15.3",
"babel-eslint": "10.0.1",
"babel-loader": "8.0.4",
"babel-plugin-optional-require": "0.3.1",
@ -138,24 +138,25 @@
"eslint-plugin-jsdoc": "3.8.0",
"eslint-plugin-react": "7.11.1",
"eslint-plugin-react-native": "3.3.0",
"expose-loader": "0.7.5",
"expose-loader": "3.0.0",
"flow-bin": "0.104.0",
"imports-loader": "0.7.1",
"jetifier": "1.6.4",
"metro-react-native-babel-preset": "0.56.0",
"patch-package": "6.4.7",
"process": "0.11.10",
"sass": "1.26.8",
"string-replace-loader": "2.1.1",
"string-replace-loader": "3.0.3",
"style-loader": "0.19.0",
"traverse": "0.6.6",
"unorm": "1.6.0",
"webpack": "4.43.0",
"webpack-bundle-analyzer": "3.4.1",
"webpack-cli": "3.3.11",
"webpack-dev-server": "3.11.0"
"webpack": "5.57.1",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "4.9.0",
"webpack-dev-server": "4.3.1"
},
"engines": {
"node": ">=8.0.0",
"node": ">=12.0.0",
"npm": ">=6.0.0"
},
"license": "Apache-2.0",

View File

@ -1,6 +1,8 @@
/* global __dirname */
const CircularDependencyPlugin = require('circular-dependency-plugin');
const fs = require('fs');
const { join } = require('path');
const process = require('process');
const webpack = require('webpack');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
@ -12,18 +14,19 @@ const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const devServerProxyTarget
= process.env.WEBPACK_DEV_SERVER_PROXY_TARGET || 'https://alpha.jitsi.net';
const analyzeBundle = process.argv.indexOf('--analyze-bundle') !== -1;
const detectCircularDeps = process.argv.indexOf('--detect-circular-deps') !== -1;
const minimize
= process.argv.indexOf('-p') !== -1
|| process.argv.indexOf('--optimize-minimize') !== -1;
/**
* Build a Performance configuration object for the given size.
* See: https://webpack.js.org/configuration/performance/
*
* @param {Object} options - options for the bundles configuration.
* @param {boolean} options.analyzeBundle - whether the bundle needs to be analyzed for size.
* @param {boolean} options.minimize - whether the code should be minimized or not.
* @param {number} size - the size limit to apply.
* @returns {Object} a performance hints object.
*/
function getPerformanceHints(size) {
function getPerformanceHints(options, size) {
const { analyzeBundle, minimize } = options;
return {
hints: minimize && !analyzeBundle ? 'error' : false,
maxAssetSize: size,
@ -33,8 +36,12 @@ function getPerformanceHints(size) {
/**
* Build a BundleAnalyzerPlugin plugin instance for the given bundle name.
*
* @param {boolean} analyzeBundle - whether the bundle needs to be analyzed for size.
* @param {string} name - the name of the bundle.
* @returns {Array} a configured list of plugins.
*/
function getBundleAnalyzerPlugin(name) {
function getBundleAnalyzerPlugin(analyzeBundle, name) {
if (!analyzeBundle) {
return [];
}
@ -46,251 +53,6 @@ function getBundleAnalyzerPlugin(name) {
}) ];
}
// The base Webpack configuration to bundle the JavaScript artifacts of
// jitsi-meet such as app.bundle.js and external_api.js.
const config = {
devServer: {
https: true,
host: '127.0.0.1',
inline: true,
proxy: {
'/': {
bypass: devServerProxyBypass,
secure: false,
target: devServerProxyTarget,
headers: {
'Host': new URL(devServerProxyTarget).host
}
}
}
},
devtool: 'source-map',
mode: minimize ? 'production' : 'development',
module: {
rules: [ {
// Transpile ES2015 (aka ES6) to ES5. Accept the JSX syntax by React
// as well.
loader: 'babel-loader',
options: {
// Avoid loading babel.config.js, since we only use it for React Native.
configFile: false,
// XXX The require.resolve bellow solves failures to locate the
// presets when lib-jitsi-meet, for example, is npm linked in
// jitsi-meet.
plugins: [
require.resolve('@babel/plugin-transform-flow-strip-types'),
require.resolve('@babel/plugin-proposal-class-properties'),
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
require.resolve('@babel/plugin-proposal-optional-chaining')
],
presets: [
[
require.resolve('@babel/preset-env'),
// Tell babel to avoid compiling imports into CommonJS
// so that webpack may do tree shaking.
{
modules: false,
// Specify our target browsers so no transpiling is
// done unnecessarily. For browsers not specified
// here, the ES2015+ profile will be used.
targets: {
chrome: 58,
electron: 2,
firefox: 54,
safari: 11
}
}
],
require.resolve('@babel/preset-flow'),
require.resolve('@babel/preset-react')
]
},
test: /\.jsx?$/
}, {
// Expose jquery as the globals $ and jQuery because it is expected
// to be available in such a form by multiple jitsi-meet
// dependencies including lib-jitsi-meet.
loader: 'expose-loader?$!expose-loader?jQuery',
test: /[/\\]node_modules[/\\]jquery[/\\].*\.js$/
}, {
// Allow CSS to be imported into JavaScript.
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}, {
test: /\/node_modules\/@atlaskit\/modal-dialog\/.*\.js$/,
resolve: {
alias: {
'react-focus-lock': `${__dirname}/react/features/base/util/react-focus-lock-wrapper.js`,
'../styled/Modal': `${__dirname}/react/features/base/dialog/components/web/ThemedDialog.js`
}
}
}, {
test: /\/react\/features\/base\/util\/react-focus-lock-wrapper.js$/,
resolve: {
alias: {
'react-focus-lock': `${__dirname}/node_modules/react-focus-lock`
}
}
}, {
test: /\.svg$/,
use: [ {
loader: '@svgr/webpack',
options: {
dimensions: false,
expandProps: 'start'
}
} ]
} ]
},
node: {
// Allow the use of the real filename of the module being executed. By
// default Webpack does not leak path-related information and provides a
// value that is a mock (/index.js).
__filename: true,
// Provide some empty Node modules (required by olm).
crypto: 'empty',
fs: 'empty'
},
optimization: {
concatenateModules: minimize,
minimize
},
output: {
filename: `[name]${minimize ? '.min' : ''}.js`,
path: `${__dirname}/build`,
publicPath: '/libs/',
sourceMapFilename: `[name].${minimize ? 'min' : 'js'}.map`
},
plugins: [
detectCircularDeps
&& new CircularDependencyPlugin({
allowAsyncCycles: false,
exclude: /node_modules/,
failOnError: false
})
].filter(Boolean),
resolve: {
alias: {
'focus-visible': 'focus-visible/dist/focus-visible.min.js',
jquery: `jquery/dist/jquery${minimize ? '.min' : ''}.js`
},
aliasFields: [
'browser'
],
extensions: [
'.web.js',
// Webpack defaults:
'.js',
'.json'
]
}
};
module.exports = [
Object.assign({}, config, {
entry: {
'app.bundle': './app.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('app'),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
performance: getPerformanceHints(4 * 1024 * 1024)
}),
Object.assign({}, config, {
entry: {
'alwaysontop': './react/features/always-on-top/index.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('alwaysontop')
],
performance: getPerformanceHints(800 * 1024)
}),
Object.assign({}, config, {
entry: {
'dial_in_info_bundle': './react/features/invite/components/dial-in-info-page'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('dial_in_info'),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
performance: getPerformanceHints(500 * 1024)
}),
Object.assign({}, config, {
entry: {
'do_external_connect': './connection_optimization/do_external_connect.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('do_external_connect')
],
performance: getPerformanceHints(5 * 1024)
}),
Object.assign({}, config, {
entry: {
'flacEncodeWorker': './react/features/local-recording/recording/flac/flacEncodeWorker.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('flacEncodeWorker')
],
performance: getPerformanceHints(5 * 1024)
}),
Object.assign({}, config, {
entry: {
'analytics-ga': './react/features/analytics/handlers/GoogleAnalyticsHandler.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('analytics-ga')
],
performance: getPerformanceHints(5 * 1024)
}),
Object.assign({}, config, {
entry: {
'close3': './static/close3.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('close3')
],
performance: getPerformanceHints(128 * 1024)
}),
Object.assign({}, config, {
entry: {
'external_api': './modules/API/external/index.js'
},
output: Object.assign({}, config.output, {
library: 'JitsiMeetExternalAPI',
libraryTarget: 'umd'
}),
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin('external_api')
],
performance: getPerformanceHints(35 * 1024)
})
];
/**
* Determines whether a specific (HTTP) request is to bypass the proxy of
* webpack-dev-server (i.e. is to be handled by the proxy target) and, if not,
@ -301,7 +63,8 @@ module.exports = [
* target, undefined; otherwise, the path to the local file to be served.
*/
function devServerProxyBypass({ path }) {
if (path.startsWith('/css/') || path.startsWith('/doc/')
if (path.startsWith('/css/')
|| path.startsWith('/doc/')
|| path.startsWith('/fonts/')
|| path.startsWith('/images/')
|| path.startsWith('/lang/')
@ -312,33 +75,307 @@ function devServerProxyBypass({ path }) {
return path;
}
const configs = module.exports;
/* eslint-disable array-callback-return, indent */
if ((Array.isArray(configs) ? configs : Array(configs)).some(c => {
if (path.startsWith(c.output.publicPath)) {
if (!minimize) {
// Since webpack-dev-server is serving non-minimized
// artifacts, serve them even if the minimized ones are
// requested.
return Object.keys(c.entry).some(e => {
const name = `${e}.min.js`;
if (path.indexOf(name) !== -1) {
// eslint-disable-next-line no-param-reassign
path = path.replace(name, `${e}.js`);
return true;
}
});
}
}
})) {
return path;
}
if (path.startsWith('/libs/')) {
if (path.endsWith('.min.js') && !fs.existsSync(join(process.cwd(), path))) {
return path.replace('.min.js', '.js');
}
return path;
}
}
/**
* The base Webpack configuration to bundle the JavaScript artifacts of
* jitsi-meet such as app.bundle.js and external_api.js.
*
* @param {Object} options - options for the bundles configuration.
* @param {boolean} options.detectCircularDeps - whether to detect circular dependencies or not.
* @param {boolean} options.minimize - whether the code should be minimized or not.
* @returns {Object} the base config object.
*/
function getConfig(options = {}) {
const { detectCircularDeps, minimize } = options;
return {
devtool: 'source-map',
mode: minimize ? 'production' : 'development',
module: {
rules: [ {
// Transpile ES2015 (aka ES6) to ES5. Accept the JSX syntax by React
// as well.
loader: 'babel-loader',
options: {
// Avoid loading babel.config.js, since we only use it for React Native.
configFile: false,
// XXX The require.resolve bellow solves failures to locate the
// presets when lib-jitsi-meet, for example, is npm linked in
// jitsi-meet.
plugins: [
require.resolve('@babel/plugin-transform-flow-strip-types'),
require.resolve('@babel/plugin-proposal-class-properties'),
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
require.resolve('@babel/plugin-proposal-optional-chaining')
],
presets: [
[
require.resolve('@babel/preset-env'),
// Tell babel to avoid compiling imports into CommonJS
// so that webpack may do tree shaking.
{
modules: false,
// Specify our target browsers so no transpiling is
// done unnecessarily. For browsers not specified
// here, the ES2015+ profile will be used.
targets: {
chrome: 58,
electron: 2,
firefox: 54,
safari: 11
}
}
],
require.resolve('@babel/preset-flow'),
require.resolve('@babel/preset-react')
]
},
test: /\.jsx?$/
}, {
// TODO: get rid of this.
// Expose jquery as the globals $ and jQuery because it is expected
// to be available in such a form by lib-jitsi-meet.
loader: 'expose-loader',
options: {
exposes: [ '$', 'jQuery' ]
},
test: require.resolve('jquery')
}, {
// Allow CSS to be imported into JavaScript.
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}, {
test: /\/node_modules\/@atlaskit\/modal-dialog\/.*\.js$/,
resolve: {
alias: {
'react-focus-lock': `${__dirname}/react/features/base/util/react-focus-lock-wrapper.js`,
'../styled/Modal': `${__dirname}/react/features/base/dialog/components/web/ThemedDialog.js`
}
}
}, {
test: /\/react\/features\/base\/util\/react-focus-lock-wrapper.js$/,
resolve: {
alias: {
'react-focus-lock': `${__dirname}/node_modules/react-focus-lock`
}
}
}, {
test: /\.svg$/,
use: [ {
loader: '@svgr/webpack',
options: {
dimensions: false,
expandProps: 'start'
}
} ]
} ]
},
node: {
// Allow the use of the real filename of the module being executed. By
// default Webpack does not leak path-related information and provides a
// value that is a mock (/index.js).
__filename: true
},
optimization: {
concatenateModules: minimize,
minimize
},
output: {
filename: `[name]${minimize ? '.min' : ''}.js`,
path: `${__dirname}/build`,
publicPath: '/libs/',
sourceMapFilename: `[name].${minimize ? 'min' : 'js'}.map`
},
plugins: [
detectCircularDeps
&& new CircularDependencyPlugin({
allowAsyncCycles: false,
exclude: /node_modules/,
failOnError: false
})
].filter(Boolean),
resolve: {
alias: {
'focus-visible': 'focus-visible/dist/focus-visible.min.js'
},
aliasFields: [
'browser'
],
extensions: [
'.web.js',
// Webpack defaults:
'.js',
'.json'
],
fallback: {
// Provide some empty Node modules (required by AtlasKit, olm).
crypto: false,
fs: false,
path: false,
process: false
}
}
};
}
/**
* Helper function to build the dev server config. It's necessary to split it in
* Webpack 5 because only one devServer entry is supported, so we attach it to
* the main bundle.
*
* @returns {Object} the dev server configuration.
*/
function getDevServerConfig() {
return {
https: true,
host: '127.0.0.1',
proxy: {
'/': {
bypass: devServerProxyBypass,
secure: false,
target: devServerProxyTarget,
headers: {
'Host': new URL(devServerProxyTarget).host
}
}
},
static: {
directory: process.cwd()
}
};
}
module.exports = (_env, argv) => {
const analyzeBundle = Boolean(process.env.ANALYZE_BUNDLE);
const mode = typeof argv.mode === 'undefined' ? 'production' : argv.mode;
const isProduction = mode === 'production';
const configOptions = {
detectCircularDeps: Boolean(process.env.DETECT_CIRCULAR_DEPS) || !isProduction,
minimize: isProduction
};
const config = getConfig(configOptions);
const perfHintOptions = {
analyzeBundle,
minimize: isProduction
};
return [
Object.assign({}, config, {
entry: {
'app.bundle': './app.js'
},
devServer: isProduction ? {} : getDevServerConfig(),
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'app'),
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/
}),
new webpack.ProvidePlugin({
process: 'process/browser'
})
],
performance: getPerformanceHints(perfHintOptions, 4 * 1024 * 1024)
}),
Object.assign({}, config, {
entry: {
'alwaysontop': './react/features/always-on-top/index.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'alwaysontop')
],
performance: getPerformanceHints(perfHintOptions, 800 * 1024)
}),
Object.assign({}, config, {
entry: {
'dial_in_info_bundle': './react/features/invite/components/dial-in-info-page'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'dial_in_info'),
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/
})
],
performance: getPerformanceHints(perfHintOptions, 500 * 1024)
}),
Object.assign({}, config, {
entry: {
'do_external_connect': './connection_optimization/do_external_connect.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'do_external_connect')
],
performance: getPerformanceHints(perfHintOptions, 5 * 1024)
}),
Object.assign({}, config, {
entry: {
'flacEncodeWorker': './react/features/local-recording/recording/flac/flacEncodeWorker.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'flacEncodeWorker')
],
performance: getPerformanceHints(perfHintOptions, 5 * 1024)
}),
Object.assign({}, config, {
entry: {
'analytics-ga': './react/features/analytics/handlers/GoogleAnalyticsHandler.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'analytics-ga')
],
performance: getPerformanceHints(perfHintOptions, 5 * 1024)
}),
Object.assign({}, config, {
entry: {
'close3': './static/close3.js'
},
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'close3')
],
performance: getPerformanceHints(perfHintOptions, 128 * 1024)
}),
Object.assign({}, config, {
entry: {
'external_api': './modules/API/external/index.js'
},
output: Object.assign({}, config.output, {
library: 'JitsiMeetExternalAPI',
libraryTarget: 'umd'
}),
plugins: [
...config.plugins,
...getBundleAnalyzerPlugin(analyzeBundle, 'external_api')
],
performance: getPerformanceHints(perfHintOptions, 35 * 1024)
})
];
};