terra-toolkit
Version:
Utilities to help when developing terra modules.
141 lines (134 loc) • 4.69 kB
JavaScript
const webpack = require('webpack');
const MemoryFS = require('memory-fs');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const rtl = require('postcss-rtl');
const Autoprefixer = require('autoprefixer');
const PostCSSAssetsPlugin = require('postcss-assets-webpack-plugin');
const PostCSSCustomProperties = require('postcss-custom-properties');
const path = require('path');
const Logger = require('../../scripts/utils/logger');
const ThemePlugin = require('./postcss/ThemePlugin');
/**
* This is a basic configuration to webpack just the theme file and grab the resulting css. Thus,
* there are no JS, raw, etc. loaders. The resulting themeable variables and values will be populated in
* the cachedObject. Note this will not support hot reloading. This is a very trimmed down config from
* what we normally do.
* @param {String} rootPath the root path from where the webpack is being run
* @param {String} themeFile the file to be webpacked
* @param {Object} themeConfig configuration to pass to the post css theme plugin
* @param {Object} cachedObject this object will be populated with the resulting themeable variables and values
*/
const webpackThemeConfig = (rootPath, themeFile, themeConfig, cachedObject) => (
{
mode: 'production',
entry: {
theme: themeFile,
},
module: {
rules: [
{
test: /\.(scss|css)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
modules: {
mode: 'global',
localIdentName: '[name]__[local]___[hash:base64:5]',
},
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',
plugins: [
ThemePlugin(themeConfig),
rtl(),
Autoprefixer(),
],
},
},
{
loader: 'sass-loader',
},
],
}],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'temp-themes.css',
ignoreOrder: true,
}),
new PostCSSAssetsPlugin({
test: /\.css$/,
log: false,
plugins: [
PostCSSCustomProperties({
preserve: true,
// Here is where the cachedObject is populated
exportTo: [
cachedObject,
],
}),
],
}),
],
resolve: {
extensions: ['.js'],
},
resolveLoader: {
modules: [path.resolve(path.join(rootPath, 'node_modules'))],
},
}
);
/**
* Gets a promise that performs webpack on the theme file. The promise resolves with an object that contains the
* themeable variables and values.
* @param {String} rootPath the root path from where the webpack is being run
* @param {String} themeFile the file to be webpacked
* @param {Object} themeConfig configuration to pass to the post css theme plugin
*/
module.exports = (rootPath, themeFile, themeConfig) => {
const cachedObject = {
// Provide our own toJSON as the default inserts commas in between things like inset values which is invalid CSS
toJSON: customProperties => (
Object.keys(customProperties).reduce((customPropertiesJSON, key) => {
const aggregatedCustomPropertiesJSON = customPropertiesJSON;
aggregatedCustomPropertiesJSON[key] = customProperties[key].map(customPropertyComponent => String(customPropertyComponent)).join(' ');
return aggregatedCustomPropertiesJSON;
}, {})
),
customProperties: {},
};
return new Promise((resolve, reject) => {
const compiler = webpack(webpackThemeConfig(rootPath, themeFile, themeConfig, cachedObject));
// Set the output file system to MemoryFS so that this all runs in memory
compiler.outputFileSystem = new MemoryFS();
compiler.run((error, stats) => {
if (error) {
Logger.error(error.stack || error);
if (error.details) {
Logger.error(error.details);
}
reject(error);
return;
}
const info = stats.toJson();
if (stats.hasErrors()) {
Logger.error(info.errors);
reject(info.errors);
return;
}
if (stats.hasWarnings()) {
Logger.warn(info.warnings);
}
resolve(cachedObject);
});
});
};