Skip to content

Instantly share code, notes, and snippets.

@studentIvan
Last active May 18, 2020 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save studentIvan/042812b946cf074a5b1208c9eafe8e33 to your computer and use it in GitHub Desktop.
Save studentIvan/042812b946cf074a5b1208c9eafe8e33 to your computer and use it in GitHub Desktop.
Preact 8 with CLI configuration example. (the default configuration passed with --config is a web.preact.config.js)
/* eslint-disable */
import path from 'path';
import fs from 'fs';
import webpack from 'webpack';
import HtmlWebpackInlineSVGPlugin from 'html-webpack-inline-svg-plugin';
import SVGSpritemapPlugin from 'svg-spritemap-webpack-plugin';
// import { WebpackBundleSizeAnalyzerPlugin } from 'webpack-bundle-size-analyzer';
import packageData from '../../package.json';
import pathsResolveConfig from './paths.resolve.config';
import appPackageData from '../../../mobile-app/package.json';
import config from '../index';
const appSettings = config;
/**
* Function that mutates original webpack config.
* Supports asynchronous changes when promise is returned.
*
* @param {object} config - original webpack config.
* @param {object} env - options passed to CLI.
* @param {WebpackConfigHelpers} helpers - object with useful helpers when working with config.
*/
export default function (config, env, helpers) {
const PRODUCTION_MODE = process.env.NODE_ENV === 'production';
/** setup app version and gtm settings */
const htmlWebpackPlugin = helpers
.getPluginsByName(config, 'HtmlWebpackPlugin')[0];
htmlWebpackPlugin.plugin.options.appVersion = packageData.version;
htmlWebpackPlugin.plugin.options.fbqInitId = appSettings.fbqInitId.ridestore;
htmlWebpackPlugin.plugin.options.gtmId = appSettings.gtmId.ridestore;
htmlWebpackPlugin.plugin.options.analyticsId = appSettings.analyticsId;
htmlWebpackPlugin.plugin.options.buildEnv = PRODUCTION_MODE ? '1' : '0';
htmlWebpackPlugin.plugin.options.appBundleVersion = appPackageData.version;
/** configure css modules */
const cssLoaderOptions = helpers
.getLoadersByName(config, 'css-loader')[0].rule.loader[2].options;
const cssLoader2Options = helpers
.getLoadersByName(config, 'css-loader')[1].rule.loader[2].options;
cssLoaderOptions.modules = true;
cssLoaderOptions.localIdentName = '[local]';
cssLoaderOptions.importLoaders = 1;
cssLoaderOptions.sourceMap = false;
cssLoader2Options.modules = true;
cssLoader2Options.localIdentName = '[local]';
cssLoader2Options.importLoaders = 1;
cssLoader2Options.sourceMap = false;
const mobileEntry = path.join(__dirname, '../../src/package', 'mobile.package.js');
/** entry point */
config.resolve.alias['preact-cli-entrypoint'] = mobileEntry;
/** import paths */
config.resolve.alias = Object.assign(config.resolve.alias, pathsResolveConfig);
config.output.publicPath = './'; // public path is ./ not
const htmlPlugin = helpers
.getPluginsByName(config, 'HtmlWebpackPlugin')[0];
htmlPlugin.plugin.options.minify = false; // don't minify html
const possibleSize = PRODUCTION_MODE ? 8e5 : 8e6;
config.performance.maxAssetSize = possibleSize;
config.performance.maxEntrypointSize = possibleSize;
const commonChunkPlugin = helpers
.getPluginsByName(config, 'CommonsChunkPlugin')[0];
config.plugins.splice(commonChunkPlugin.index, 1);
config.plugins.push(new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, // Must be greater than or equal to one
minChunkSize: 1000
}));
config.plugins = (config.plugins || []).concat(
new SVGSpritemapPlugin({
src: path.join(__dirname, '../../src/assets/svg/*.svg'),
svgo: true,
})
);
config.plugins.push(
new webpack.NormalModuleReplacementPlugin(
/RSIconSvg\.component/,
path.join(__dirname, '../../src/lib/components/includes/ui-components/svg/RSIconSvg/RSIconSvg.mobile.component.js')
)
);
// config.plugins.push(new WebpackBundleSizeAnalyzerPlugin('./plain-report.txt'));
config.plugins.push(new HtmlWebpackInlineSVGPlugin());
/** ignore desktop modules */
config.plugins.push(new webpack.IgnorePlugin(/\.dt/));
/** using preact-compat with webpack */
Object.assign(config.resolve.alias, {
'react': 'preact-compat',
'react-dom': 'preact-compat',
// Not necessary unless you consume a module using `createClass`
'create-react-class': 'preact-compat/lib/create-react-class',
});
const uglifyJSPluginData = helpers
.getPluginsByName(config, 'UglifyJsPlugin')[0];
// if (PRODUCTION_MODE) {
if (uglifyJSPluginData) {
/** turn the uglify to sourcemap reduce mode if it presents */
uglifyJSPluginData.plugin.options.sourceMap = false;
}
/** reduce the bundle size without source maps */
config.devtool = 'none';
// }
// else if (uglifyJSPluginData) {
// /** better debugging */
// uglifyJSPluginData.plugin.options.output.beautify = true;
// uglifyJSPluginData.plugin.options.output.comments = true;
// uglifyJSPluginData.plugin.options.mangle = false;
// uglifyJSPluginData.plugin.options.compress = false;
// }
}
{
"scripts": {
//... very few examples ....
"start": "if-env NODE_ENV=production && yarn start:prod || yarn start:dev",
"start:dev": "NODE_ENV=development BROWSERSLIST=\"> 0.8%, not IE 11\" BABEL_ENV=normal preact watch --template src/templates/web.template.html --config config/preact/web.preact.config.js",
"start:prod": "BROWSERSLIST=\"> 0.8%, not IE 11\" NODE_ENV=production BABEL_ENV=server babel-node server/server.js",
"start:prod:node": "node server/dist/server.prebuilt.js",
"build:mobile": "BROWSERSLIST=defaults yarn build:mobile:only && (cd ../mobile-app && yarn start)",
"build:mobile:only": "NODE_ENV=production BABEL_ENV=normal preact build --production --template src/templates/mobile.template.html --no-prerender --dest ../mobile-app/www --service-worker false --config config/preact/mobile.preact.config.js && rm ../mobile-app/www/sw.js && rm ../mobile-app/www/push-manifest.json",
}
}
/* eslint-disable */
const path = require('path');
module.exports = {
'@services': path.join(__dirname, '../../src/lib/services'),
'@rest': path.join(__dirname, '../../src/lib/rest/'),
'@hocs': path.join(__dirname, '../../src/lib/hocs'),
'@test': path.join(__dirname, '../../src/lib/test'),
'@style': path.join(__dirname, '../../src/lib/style'),
'@classes': path.join(__dirname, '../../src/lib/classes'),
'@common': path.join(__dirname, '../../src/lib/common'),
'@services/language/no-chunk-version': path.join(__dirname, '../../src/lib/services/language/no-chunk-version'),
'@ui-components': path.join(__dirname, '../../src/lib/components/includes/ui-components'),
'@router': path.join(__dirname, '../../src/lib/components/includes/router'),
'@app': path.join(__dirname, '../../src/lib/components/general/app'),
'@pages': path.join(__dirname, '../../src/lib/components/general/pages/'),
'@config': path.join(__dirname, '../'),
'@util': path.join(__dirname, '../../util'),
}
/* eslint-disable */
import path from 'path';
import fs from 'fs';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import packageData from '../../package.json';
import pathsResolveConfig from './paths.resolve.config';
import webpack from 'webpack';
import SVGSpritemapPlugin from 'svg-spritemap-webpack-plugin';
import SWPrecacheWebpackPlugin from 'sw-precache-webpack-plugin';
import classNamesFn from './classnames.function';
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const v8 = require('v8');
v8.setFlagsFromString('--max-old-space-size=2048');
const totalHeapSize = v8.getHeapStatistics().total_available_size;
let totalHeapSizaInMB = (totalHeapSize / 1024 / 1024).toFixed(2);
console.log('NodeJS version', process.version);
console.log('V8 Total Heap Size', totalHeapSizaInMB, 'MB');
/**
* Function that mutates original webpack config.
* Supports asynchronous changes when promise is returned.
*
* @param {object} config - original webpack config.
* @param {object} env - options passed to CLI.
* @param {WebpackConfigHelpers} helpers - object with useful helpers when working with config.
*/
export default function (config, env, helpers) {
const PRODUCTION_MODE = process.env.NODE_ENV === 'production';
const BOOST_MODE = process.env.BOOST_BUILD == '1';
const appSettings = require('../index');
const webEntry = path.join(__dirname, '../../src/package', 'web.entry.js');
const webPackage = path.join(__dirname, '../../src/package', 'web.package.js');
const isESMBuild = process.env.BABEL_ENV === 'esm';
/** replace preact cli entry to web entry */
if (config.entry.bundle) {
config.entry.bundle = [webEntry];
}
if (config.entry.polyfills) {
config.entry.polyfills = [config.entry.polyfills];
}
/** setup app version and gtm settings */
const htmlWebpackPlugin = helpers
.getPluginsByName(config, 'HtmlWebpackPlugin')[0];
if (htmlWebpackPlugin) {
htmlWebpackPlugin.plugin.options.appVersion = packageData.version;
htmlWebpackPlugin.plugin.options.fbqInitId = appSettings.fbqInitId.ridestore;
htmlWebpackPlugin.plugin.options.gtmId = appSettings.gtmId.ridestore;
htmlWebpackPlugin.plugin.options.analyticsId = appSettings.analyticsId;
htmlWebpackPlugin.plugin.options.buildEnv = PRODUCTION_MODE ? '1' : '0';
htmlWebpackPlugin.plugin.options.codeVersionTag = appSettings.codeVersionTag;
}
const extractTextPluginData = helpers
.getPluginsByName(config, 'ExtractTextPlugin')[0];
if (extractTextPluginData) {
extractTextPluginData.plugin.filename = 'style.' + appSettings.codeVersionTag + '.css';
}
/** configure css modules */
const firstStyleRule = helpers.getLoadersByName(config, 'css-loader')[0].rule;
const secondStyleRule = helpers.getLoadersByName(config, 'css-loader')[1].rule;
const firstCSSLoaderRules = firstStyleRule.loader;
const secondCSSLoaderRules = secondStyleRule.loader;
const cssLoaderOptions1 = firstCSSLoaderRules.filter(x => x.loader === 'css-loader')[0].options;
const cssLoaderOptions2 = secondCSSLoaderRules.filter(x => x.loader === 'css-loader')[0].options;
/** post css turned off */
firstCSSLoaderRules.splice(firstCSSLoaderRules.length - 1, 1);
secondCSSLoaderRules.splice(secondCSSLoaderRules.length - 1, 1);
cssLoaderOptions1.modules = true;
cssLoaderOptions1.getLocalIdent = classNamesFn;
cssLoaderOptions1.importLoaders = 1;
cssLoaderOptions1.sourceMap = !PRODUCTION_MODE;
cssLoaderOptions2.modules = cssLoaderOptions1.modules;
cssLoaderOptions2.getLocalIdent = cssLoaderOptions1.getLocalIdent;
cssLoaderOptions2.importLoaders = cssLoaderOptions1.importLoaders;
cssLoaderOptions2.sourceMap = cssLoaderOptions1.sourceMap;
config.resolve.extensions = ['.js', '.jsx', '.ts', '.tsx', '.json', '.scss', '.css'];
const babelLoader = helpers.getLoadersByName(config, 'babel-loader')[0];
babelLoader.rule.options.cacheDirectory = true;
/** entry point */
config.resolve.alias['preact-cli-entrypoint'] = webPackage;
/** import paths */
config.resolve.alias = Object.assign(config.resolve.alias, pathsResolveConfig);
if (config.entry['ssr-bundle']) {
config.entry['ssr-bundle'] = [webPackage];
}
config.output.filename = !env.ssr ? '[name].' + appSettings.codeVersionTag + '.js' : '[name].js';
config.output.chunkFilename = PRODUCTION_MODE ? '[name].[chunkhash].chunk.js' : '[name].chunk.js';
config.performance = {
maxEntrypointSize: 400e3,
maxAssetSize: 200e3,
};
if (!env.ssr) {
/** BundleAnalyzerPlugin */
if (appSettings.generateBundleAnalyze) {
config.plugins.push(
new BundleAnalyzerPlugin(!env.production
? { analyzerPort: 8089, openAnalyzer: false }
: { openAnalyzer: false, analyzerMode: 'static' }
)
);
}
}
else if (env.ssr) {
config.performance = {
maxEntrypointSize: 2500e3,
maxAssetSize: 2500e3,
};
global.isServerSideApp = true;
const commonChunkPlugin = helpers
.getPluginsByName(config, 'CommonsChunkPlugin')[0];
config.plugins.splice(commonChunkPlugin.index, 1);
config.plugins.push(new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, // Must be greater than or equal to one
minChunkSize: 1000
}));
}
/** using preact-compat with webpack */
Object.assign(config.resolve.alias, {
'react': 'preact-compat',
'react-dom': 'preact-compat',
// Not necessary unless you consume a module using `createClass`
'create-react-class': 'preact-compat/lib/create-react-class'
});
config.plugins = (config.plugins || []).concat(
new SVGSpritemapPlugin({
src: path.join(__dirname, '../../src/assets/svg/*.svg'),
svgo: true,
})
);
config.plugins.push(new ManifestPlugin({
fileName: 'chunk-manifest.json',
}));
if (PRODUCTION_MODE && (env.production || env.ssr)) {
const swPrecachePluginData = helpers
.getPluginsByName(config, 'SWPrecacheWebpackPlugin')[0];
if (swPrecachePluginData) {
config.plugins.splice(swPrecachePluginData.index, 1);
config.plugins.push(new SWPrecacheWebpackPlugin({
cacheId: 'rs-pwa',
dontCacheBustUrlsMatching: /(\.\w{8}\.|^\/(v|cdntest|api|order|track|config|index\.php|admin|login|skin|js\/|media\/|translations\/|mag|ajaxwishlist|rsrest|ordermanagement|smsmanager|dynamicshipping|klarna|reviewemail|paymentaddon|connector|monkey|storecredit|paypal|xmlconnect|DragDrop|reclaim))/,
filename: 'sw.js',
handleFetch: false,
minify: true,
runtimeCaching: [
{ urlPattern: /\/(v|cdntest)$/, handler: 'networkOnly' },
{ urlPattern: /^\/(api\/|order|track|config|index\.php|admin|login|skin|js\/|media\/|translations\/|mag|ajaxwishlist|rsrest|ordermanagement|smsmanager|dynamicshipping|klarna|reviewemail|paymentaddon|connector|monkey|storecredit|paypal|xmlconnect|DragDrop|reclaim)/, handler: 'networkOnly' },
{ urlPattern: /^\/cache-management\/menu\/flush/, handler: 'networkOnly' },
],
// navigateFallback: '/index.html',
navigateFallback: false,
directoryIndex: '/index.html',
mergeStaticsConfig: true,
staticFileGlobsIgnorePatterns: [
/polyfills(\..*)?\.js$/,
/\.map$/,
/push-manifest\.json$/,
/([a-z0-9]+)\.(ttf|eot)$/,
/\.(DS_Store|png\.js|jpg\.js)$/,
/circularfont/,
/muli/,
/playfair/,
/safari-pinned-tab/,
/mstile-150x150/,
/\/assets\/favicon\.ico$/,
/\/assets\/images\/selector-arrow\.png$/,
/\/assets\/svg\/([a-z0-9-]+)\.svg$/,
/\/style\.([a-z0-9]+)\.([a-z0-9]+)\.css$/,
],
}));
const pushManifestPlugin = helpers.getPluginsByName(config, 'PushManifestPlugin')[0];
config.plugins.splice(pushManifestPlugin.index, 1);
}
const uglifyJSPluginData = helpers
.getPluginsByName(config, 'UglifyJsPlugin')[0];
if (uglifyJSPluginData) {
const opts = uglifyJSPluginData.plugin.options;
if (isESMBuild || BOOST_MODE) {
config.plugins.splice(uglifyJSPluginData.index, 1);
}
else {
/** turn the uglify to sourcemap reduce mode if it presents */
opts.sourceMap = false;
opts.ie8 = false;
opts.parallel = BOOST_MODE || appSettings.parallelBuild;
opts.warningsFilter = (warning, source) => {
if (/Dropping unreachable code/i.test(warning)) {
return true;
}
if (/filename\.js/i.test(source)) {
return true;
}
return false;
};
const ujsp = new UglifyJsPlugin(Object.assign({}, opts));
config.plugins[uglifyJSPluginData.index] = ujsp;
}
}
/** reduce the bundle size without source maps */
config.devtool = 'none';
}
}
const paths = require('./paths.resolve.config');
// webpack.config.js for eslint
module.exports = {
resolve: {
extensions: ['', '/index.js', '/index.jsx', '.jsx', '.js', '.json'],
alias: paths,
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment