terra-toolkit
Version:
Utilities to help when developing terra modules.
265 lines (250 loc) • 7.97 kB
JavaScript
const Autoprefixer = require('autoprefixer');
const PostCSSAssetsPlugin = require('postcss-assets-webpack-plugin');
const PostCSSCustomProperties = require('postcss-custom-properties');
const path = require('path');
const rtl = require('postcss-rtl');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const webpack = require('webpack');
const merge = require('webpack-merge');
const DuplicatePackageCheckerPlugin = require('@cerner/duplicate-package-checker-webpack-plugin');
const aggregateTranslations = require('terra-aggregate-translations');
const ThemeAggregator = require('../../scripts/aggregate-themes/theme-aggregator');
const getThemeWebpackPromise = require('./getThemeWebpackPromise');
const ThemePlugin = require('./postcss/ThemePlugin');
const getThemeConfig = require('./postcss/_getThemeConfig');
const webpackConfig = (options, env, argv) => {
const { disableCSSCustomProperties } = env;
const {
rootPath,
resolveModules,
staticOptions,
aggregatedLocales,
themeFile,
themeConfig,
} = options;
const production = argv.p;
const fileNameStategy = production ? '[name]-[chunkhash]' : '[name]';
const chunkFilename = argv['output-chunk-filename'] || fileNameStategy;
const filename = argv['output-filename'] || fileNameStategy;
const outputPath = argv['output-path'] || path.join(rootPath, 'build');
const publicPath = argv['output-public-path'] || '';
const sourceMap = env.generateLoaderSourceMaps || !production;
const devConfig = {
mode: 'development',
entry: {
raf: 'raf/polyfill',
'core-js': 'core-js/stable',
'regenerator-runtime': 'regenerator-runtime/runtime',
...themeFile && { theme: themeFile },
},
module: {
rules: [
{
test: /\.(jsx|js)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
rootMode: 'upward', // needed to correctly resolve babel's config root in mono-repos
},
},
},
{
test: /\.(scss|css)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: !production, // only enable hot module reloading in development
},
},
{
loader: 'css-loader',
options: {
modules: {
mode: 'global',
localIdentName: '[name]__[local]___[hash:base64:5]',
},
sourceMap,
importLoaders: 2,
},
},
{
loader: 'postcss-loader',
options: {
// Add unique ident to prevent the loader from searching for a postcss.config file. See: https://github.com/postcss/postcss-loader#plugins
ident: 'postcss',
sourceMap,
plugins: [
ThemePlugin(themeConfig),
rtl(),
Autoprefixer(),
],
},
},
{
loader: 'sass-loader',
options: {
sourceMap,
},
},
],
},
{
test: /\.md$/,
use: 'raw-loader',
},
{
test: /\.(png|svg|jpg|gif|otf|eot|ttf|svg|woff|woff2)$/,
use: 'file-loader',
}],
},
plugins: [
new MiniCssExtractPlugin({
filename: `${filename}.css`,
chunkFilename: `${chunkFilename}.css`,
ignoreOrder: true,
}),
new PostCSSAssetsPlugin({
test: /\.css$/,
log: false,
plugins: [
PostCSSCustomProperties({
preserve: !disableCSSCustomProperties,
// If we have a theme file, use the webpack promise to webpack it. This promise will resolve to
// an object with themeable variables and values. This will then be used to update the end state CSS
// so that they are populated with values if variables aren't supported (e.g. IE10). This dance is
// necessary when code splitting to ensure the variables and values are applied across all code split
// css files
...themeFile && { importFrom: [getThemeWebpackPromise(rootPath, themeFile, themeConfig)] },
}),
],
}),
new DuplicatePackageCheckerPlugin({
showHelp: false,
alwaysEmitErrorsFor: [
'react',
'react-dom',
'react-intl',
'react-on-rails',
'terra-application',
'terra-breakpoints',
'terra-disclosure-manager',
'terra-navigation-prompt',
'terra-theme-context',
],
}),
new webpack.DefinePlugin({
CERNER_BUILD_TIMESTAMP: JSON.stringify(new Date(Date.now()).toISOString()),
TERRA_AGGREGATED_LOCALES: JSON.stringify(aggregatedLocales),
TERRA_THEME_CONFIG: JSON.stringify(themeConfig),
}),
],
resolve: {
extensions: ['.js', '.jsx'],
modules: resolveModules,
mainFields: ['browser', 'main'],
},
output: {
filename: `${filename}.js`,
chunkFilename: `${chunkFilename}.js`,
path: outputPath,
publicPath,
},
devServer: {
...staticOptions,
host: '0.0.0.0',
publicPath,
stats: {
colors: true,
children: false,
},
},
devtool: 'eval-source-map',
resolveLoader: {
modules: [path.resolve(path.join(rootPath, 'node_modules'))],
},
stats: { children: false },
};
if (!production) {
return devConfig;
}
return merge.strategy({
devtool: 'replace',
})(devConfig, {
mode: 'production',
devtool: false,
plugins: [
new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['**/*', '!stats.json'] }),
],
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap,
terserOptions: {
compress: {
typeofs: false,
},
},
}),
],
},
});
};
const defaultWebpackConfig = (env = {}, argv = {}) => {
const {
disableAggregateTranslations,
disableHotReloading,
disableAggregateThemes,
themeConfig: envThemeConfig,
} = env;
const staticOptions = {
...disableHotReloading && {
hot: false,
inline: false,
},
};
const processPath = process.cwd();
/* Get the root path of a mono-repo process call */
const rootPath = processPath.includes('packages') ? processPath.split('packages')[0] : processPath;
const resolveModules = ['node_modules'];
let aggregatedLocales;
if (!disableAggregateTranslations) {
aggregatedLocales = aggregateTranslations({ baseDir: rootPath, ...env.aggregateOptions });
resolveModules.unshift(path.resolve(rootPath, 'aggregated-translations'));
}
const defaultTheme = process.env.THEME; // Flexes root theme for theme visual regression testing.
let themeConfig = {};
if (defaultTheme) {
themeConfig = { theme: defaultTheme };
} else if (envThemeConfig) {
themeConfig = envThemeConfig;
} else {
themeConfig = getThemeConfig();
}
let themeFile;
if (!disableAggregateThemes) {
// Set the default theme and disable scoped theme aggregation.
themeFile = ThemeAggregator.aggregate(
null, // Set the default theme via the config.
{
config: themeConfig,
aggregateDefaultThemeAsScopedTheme: true,
},
);
}
const options = {
rootPath,
resolveModules,
staticOptions,
aggregatedLocales,
themeFile,
themeConfig,
};
return webpackConfig(options, env, argv);
};
module.exports = defaultWebpackConfig;