|
/* eslint-disable no-var*/
|
|
'use strict';
|
|
|
|
var path = require('path');
|
|
var webpack = require('webpack');
|
|
const dotenv = require('dotenv');
|
|
dotenv.config();
|
|
var ForemanVendorPlugin = require('@theforeman/vendor')
|
|
.WebpackForemanVendorPlugin;
|
|
var StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
|
|
var vendorEntry = require('./webpack.vendor');
|
|
var fs = require('fs');
|
|
const { ModuleFederationPlugin } = require('webpack').container;
|
|
var pluginUtils = require('../script/plugin_webpack_directories');
|
|
var { generateExportsFile }= require('../webpack/assets/javascripts/exportAll');
|
|
var CompressionPlugin = require('compression-webpack-plugin');
|
|
|
|
class AddRuntimeRequirement {
|
|
// to avoid "webpackRequire.l is not a function" error
|
|
// enables use of webpack require inside promise new promise
|
|
apply(compiler) {
|
|
compiler.hooks.compilation.tap('AddRuntimeRequirement', compilation => {
|
|
const { RuntimeGlobals } = compiler.webpack;
|
|
compilation.hooks.additionalModuleRuntimeRequirements.tap(
|
|
'AddRuntimeRequirement',
|
|
(module, set) => {
|
|
set.add(RuntimeGlobals.loadScript);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
const supportedLocales = () => {
|
|
const localeDir = path.join(__dirname, '..', 'locale');
|
|
|
|
// Find all files in ./locale/*
|
|
const localesFiles = fs.readdirSync(localeDir);
|
|
|
|
// Return only folders
|
|
return localesFiles.filter(f =>
|
|
fs.statSync(path.join(localeDir, f)).isDirectory()
|
|
);
|
|
};
|
|
|
|
const supportedLanguages = () => {
|
|
// Extract extract languages from the language tags (strip off dialects)
|
|
return [...new Set(supportedLocales().map(d => d.split('_')[0]))];
|
|
};
|
|
|
|
const supportedLanguagesRE = new RegExp(
|
|
`/(${supportedLanguages().join('|')})$`
|
|
);
|
|
|
|
const commonConfig = function() {
|
|
var production =
|
|
process.env.RAILS_ENV === 'production' ||
|
|
process.env.NODE_ENV === 'production';
|
|
const mode = production ? 'production' : 'development';
|
|
const config = {};
|
|
if (production) {
|
|
config.devtool = 'source-map';
|
|
config.optimization = {
|
|
moduleIds: 'named',
|
|
splitChunks: false,
|
|
};
|
|
} else {
|
|
config.devtool = 'inline-source-map';
|
|
config.optimization = {
|
|
splitChunks: false,
|
|
};
|
|
}
|
|
return {
|
|
...config,
|
|
mode,
|
|
resolve: {
|
|
fallback: {
|
|
path: require.resolve('path-browserify'),
|
|
os: require.resolve('os-browserify'),
|
|
},
|
|
alias: {
|
|
foremanReact: path.join(
|
|
__dirname,
|
|
'../webpack/assets/javascripts/react_app'
|
|
),
|
|
},
|
|
},
|
|
resolveLoader: {
|
|
modules: [path.resolve(__dirname, '..', 'node_modules')],
|
|
},
|
|
module: {
|
|
rules: [
|
|
{
|
|
test: /\.js$/,
|
|
/* Include novnc, unidiff in webpack, transpiling is needed for phantomjs (which does not support ES6) to run tests
|
|
unidiff can be removed once https://github.com/mvoss9000/unidiff/pull/1 is merged */
|
|
exclude: /node_modules(?!\/(@novnc|unidiff))/,
|
|
loader: 'babel-loader',
|
|
options: {
|
|
presets: [require.resolve('@theforeman/builder/babel')],
|
|
},
|
|
},
|
|
{
|
|
test: /\.(png|gif|svg)$/,
|
|
type: 'asset',
|
|
parser: {
|
|
dataUrlCondition: {
|
|
maxSize: 32767,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
test: /\.(graphql|gql)$/,
|
|
exclude: /node_modules/,
|
|
loader: 'graphql-tag/loader',
|
|
},
|
|
],
|
|
},
|
|
plugins: [
|
|
new ForemanVendorPlugin({
|
|
mode,
|
|
}),
|
|
new webpack.DefinePlugin({
|
|
'process.env': {
|
|
NODE_ENV: JSON.stringify(mode),
|
|
NOTIFICATIONS_POLLING: process.env.NOTIFICATIONS_POLLING,
|
|
REDUX_LOGGER: process.env.REDUX_LOGGER,
|
|
},
|
|
}),
|
|
// limit locales from intl only to supported ones
|
|
new webpack.ContextReplacementPlugin(
|
|
/intl\/locale-data\/jsonp/,
|
|
supportedLanguagesRE
|
|
),
|
|
// limit locales from react-intl only to supported ones
|
|
new webpack.ContextReplacementPlugin(
|
|
/react-intl\/locale-data/,
|
|
supportedLanguagesRE
|
|
),
|
|
new AddRuntimeRequirement(),
|
|
new CompressionPlugin(),
|
|
],
|
|
stats: process.env.WEBPACK_STATS || 'normal',
|
|
};
|
|
};
|
|
|
|
const coreConfig = function() {
|
|
var config = commonConfig();
|
|
var manifestFilename = 'manifest.json';
|
|
var bundleEntry = path.join(
|
|
__dirname,
|
|
'..',
|
|
'webpack/assets/javascripts/bundle.js'
|
|
);
|
|
config.context = path.resolve(__dirname, '..');
|
|
config.entry = {
|
|
bundle: { import: bundleEntry, dependOn: ['vendor', 'reactExports'] },
|
|
vendor: vendorEntry,
|
|
reactExports: path.join(
|
|
__dirname,
|
|
'..',
|
|
'webpack/assets/javascripts/all_react_app_exports.js'
|
|
),
|
|
};
|
|
config.output = {
|
|
path: path.join(__dirname, '..', 'public', 'webpack'),
|
|
publicPath: '/webpack/',
|
|
library: {
|
|
name: ['TheForeman', '[name]'],
|
|
type: 'var',
|
|
},
|
|
};
|
|
var plugins = config.plugins;
|
|
|
|
plugins.push(
|
|
new ModuleFederationPlugin({
|
|
name: 'foremanReact',
|
|
})
|
|
);
|
|
plugins.push(
|
|
new StatsWriterPlugin({
|
|
filename: manifestFilename,
|
|
})
|
|
);
|
|
config.plugins = plugins;
|
|
var rules = config.module.rules;
|
|
rules.push({
|
|
test: /\.(sa|sc|c)ss$/,
|
|
use: [
|
|
{
|
|
loader: 'style-loader',
|
|
options: {
|
|
injectType: 'singletonStyleTag',
|
|
attributes: { id: 'foreman_core_css' },
|
|
},
|
|
},
|
|
'css-loader',
|
|
'sass-loader',
|
|
],
|
|
});
|
|
config.module.rules = rules;
|
|
return config;
|
|
};
|
|
|
|
const pluginConfig = function(plugin) {
|
|
const pluginRoot = plugin.root;
|
|
const pluginName = plugin.name.replace('-', '_'); // module federation doesnt like -
|
|
var config = commonConfig();
|
|
config.context = path.join(pluginRoot, 'webpack');
|
|
config.entry = {};
|
|
|
|
function convertImportStatement(importStatement) {
|
|
const importPath = importStatement;
|
|
const importPathParts = importPath.split('/');
|
|
const newImportName = importPathParts.slice(1).join('_');
|
|
return newImportName;
|
|
}
|
|
config.externals = function({ request }, callback) {
|
|
if (/^foremanReact(\/.*)?$/.test(request)) {
|
|
const prefix = 'var TheForeman.reactExports.';
|
|
const newPath = prefix + convertImportStatement(request.substring('foremanReact'.length));
|
|
return callback(null, newPath);
|
|
}
|
|
return callback();
|
|
};
|
|
var pluginEntries = {
|
|
'./index': path.resolve(pluginRoot, 'webpack', 'index'),
|
|
};
|
|
plugin.entries.filter(Boolean).forEach(entry => {
|
|
pluginEntries[`./${entry}_index`] = path.resolve(
|
|
pluginRoot,
|
|
'webpack',
|
|
`${entry}_index`
|
|
);
|
|
});
|
|
|
|
if (config.mode == 'production') {
|
|
var outputPath = path.join(pluginRoot, 'public', 'webpack', pluginName);
|
|
} else {
|
|
var outputPath = path.join(
|
|
__dirname,
|
|
'..',
|
|
'public',
|
|
'webpack',
|
|
pluginName
|
|
);
|
|
}
|
|
config.output = {
|
|
path: outputPath,
|
|
publicPath: '/webpack/' + pluginName + '/',
|
|
uniqueName: pluginName,
|
|
};
|
|
var configModules = config.resolve.modules || [];
|
|
// make webpack to resolve modules from core first
|
|
configModules.unshift(path.resolve(__dirname, '..', 'node_modules'));
|
|
// add plugin's node_modules to the reslver list
|
|
configModules.push(path.resolve(pluginRoot, 'node_modules'));
|
|
configModules.push('node_modules/');
|
|
config.resolve.modules = configModules;
|
|
|
|
//get the list of webpack plugins
|
|
var plugins = config.plugins;
|
|
|
|
plugins.push(
|
|
new ModuleFederationPlugin({
|
|
name: pluginName,
|
|
filename: pluginName + '_remoteEntry.js',
|
|
exposes: pluginEntries,
|
|
})
|
|
);
|
|
config.plugins = plugins;
|
|
var rules = config.module.rules;
|
|
rules.push({
|
|
test: /\.(sa|sc|c)ss$/,
|
|
use: [
|
|
{
|
|
loader: 'style-loader',
|
|
options: {
|
|
injectType: 'singletonStyleTag',
|
|
attributes: { id: `${pluginName}_css` },
|
|
},
|
|
},
|
|
'css-loader',
|
|
'sass-loader',
|
|
],
|
|
});
|
|
config.module.rules = rules;
|
|
|
|
return config;
|
|
};
|
|
|
|
module.exports = function(env, argv) {
|
|
generateExportsFile();
|
|
const { pluginName } = env;
|
|
var pluginsDirs = pluginUtils.getPluginDirs('pipe');
|
|
var pluginsInfo = {};
|
|
var pluginsConfigEnv = [];
|
|
var pluginDirKeys = Object.keys(pluginsDirs.plugins);
|
|
if (pluginName) {
|
|
pluginDirKeys = pluginDirKeys.filter(key => key.includes(pluginName));
|
|
}
|
|
pluginDirKeys.forEach(pluginDirKey => {
|
|
const parts = pluginDirKey.split(':');
|
|
const name = parts[0];
|
|
const entry = parts[1];
|
|
if (pluginsInfo[name]) {
|
|
pluginsInfo[name].entries.push(entry);
|
|
} else {
|
|
pluginsInfo[name] = {
|
|
name,
|
|
entries: [entry],
|
|
root: pluginsDirs.plugins[pluginDirKey].root,
|
|
};
|
|
}
|
|
if (!pluginDirKey.includes(':')) {
|
|
const keysWithExtras = pluginDirKeys.filter(key =>
|
|
key.includes(pluginDirKey + ':')
|
|
);
|
|
// for example: {global: true, routes: true}
|
|
const pluginExtras = keysWithExtras.map(key => ({
|
|
[key.split(':')[1]]: true,
|
|
}));
|
|
pluginsConfigEnv.push({
|
|
plugin: {
|
|
...pluginExtras,
|
|
name: pluginDirKey,
|
|
root: pluginsDirs.plugins[pluginDirKey].root,
|
|
},
|
|
});
|
|
}
|
|
});
|
|
let configs = [];
|
|
const pluginsInfoValues = Object.values(pluginsInfo);
|
|
if (pluginsInfoValues.length > 0) {
|
|
configs = pluginsInfoValues.map(plugin => pluginConfig(plugin));
|
|
}
|
|
if (pluginName) return configs;
|
|
|
|
return [coreConfig(env, argv), ...configs];
|
|
};
|