Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Webpack Encore + Sass + MiniCSSExtractPlugin + PurgeCSS + OptimizeCss + Babel + Typescript
/* eslint-disable no-useless-escape */
const Encore = require('@symfony/webpack-encore');
const TerserPlugin = require('terser-webpack-plugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const HtmlCriticalWebpackPlugin = require('html-critical-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PurgeCssPlugin = require('purgecss-webpack-plugin');
const WebpackBar = require('webpackbar');
const path = require('path');
const glob = require('glob-all');
const cssWhitelist = require('./purge-css-whitelist');
const sassOptions = {
includePaths: ['node_modules', './resources/assets/scss/'],
};
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore.addPlugin(new WebpackBar());
if (!Encore.isProduction()) {
Encore.configureFilenames({
js: '[name].js',
css: '[name].css',
images: 'images/[name].[ext]',
fonts: 'fonts/[name].[ext]',
});
} else {
Encore.enablePostCssLoader();
}
if (!Encore.isProduction()) {
Encore.addPlugin(
new CircularDependencyPlugin({
// exclude detection of files based on a RegExp
exclude: /a\.js|node_modules/,
// add errors to webpack instead of warnings
failOnError: true,
// set the current working directory for displaying module paths
cwd: process.cwd(),
}),
);
}
// Uncomment only when generating static site for above the fold CSS (critical CSS).
// if (!Encore.isProduction()) {
// Encore.addPlugin(
// new HtmlCriticalWebpackPlugin({
// base: path.resolve(__dirname, 'public'),
// src: 'static_home.html',
// dest: 'optimized_home.html',
// inline: true,
// minify: true,
// extract: true,
// width: 375,
// height: 565,
// penthouse: {
// blockJSRequests: false,
// timeout: 99999,
// },
// }),
// );
// }
Encore.setOutputPath('public/assets/') // directory where compiled assets will be stored
.setPublicPath('/assets'); // public path used by the web server to access the output path
if (Encore.isProduction()) {
Encore.cleanupOutputBeforeBuild();
}
Encore.copyFiles({
from: './resources/assets/images',
to: 'images/[path][name].[ext]',
pattern: /\.(png|jpg|jpeg|gif|svg)/,
});
if (Encore.isProduction()) {
Encore.copyFiles({
from: './resources/assets/images',
to: 'images/[path][name]',
pattern: '/(logopng)/',
});
}
// Encore.enableSassLoader(options => {
// options.includePaths = sassOptions.includePaths;
//
// if (!Encore.isProduction()) {
// options.minimize = false;
// } else {
// options.minimize = true;
// }
// });
Encore.addLoader({
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
// by default it uses publicPath in webpackOptions.output
publicPath: '../',
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader',
'sass-loader',
],
});
Encore.addPlugin(
new MiniCssExtractPlugin({
filename: Encore.isProduction() ? '[name].[contenthash].css' : '[name].css',
}),
);
if (Encore.isProduction()) {
Encore.addPlugin(
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.(c|s[ac])ss$/,
cssProcessorPluginOptions: {
preset: [
'default',
{
discardComments: {
removeAll: true, // remove any comments?
},
},
],
},
canPrint: true,
}),
);
}
Encore.addPlugin(
new TerserPlugin({
terserOptions: {
sourceMap: !Encore.isProduction(),
cache: !Encore.isProduction(),
parallel: true,
output: {
// comments: false,
},
},
}),
);
if(Encore.isProduction()) {
Encore.addPlugin(
new PurgeCssPlugin({
// folders: ['resources/views/**/*', 'resources/assets/scss/'],
paths: glob.sync([path.join(__dirname, 'resources/views/**/*/*.blade.php')]),
whitelist: cssWhitelist,
whitelistPatterns: [
/icon$/,
/primary$/,
/info$/,
/success$/,
/danger$/,
/swiper\-w+/,
/slide$/,
/popover\-\w+/,
/tooltip\-\w+/,
/lb\-\w+/,
/ui\-\w+/,
],
}),
);
}
Encore.configureOptimizeCssPlugin().enableSourceMaps(!Encore.isProduction());
Encore.configureBabel(
babelConfig => {
// add additional presets (preset-env is added by default)
babelConfig.presets.push('@babel/preset-flow');
// IE11/Edge requires below plugins
babelConfig.plugins.push('@babel/plugin-transform-spread');
babelConfig.plugins.push('@babel/plugin-transform-exponentiation-operator');
// no plugins are added by default, but you can add some
// babelConfig.plugins.push('styled-jsx/babel');
// if (Encore.isProduction()) {
// babelConfig.plugins.push("transform-remove-console");
// }
},
{
// node_modules is not processed through Babel by default
// but you can whitelist specific modules to process
includeNodeModules: [
'jquery',
'jquery-ui',
'jquery-lazy',
'swiper',
'es-cookies',
'select2',
'mdb',
'bootstrap',
'urijs',
'dom7',
'cleave.js',
],
useBuiltIns: 'usage',
corejs: 3,
// or completely control the exclude rule (note that you
// can't use both "include_node_modules" and "exclude" at
// the same time)
// exclude: /bower_components/
},
);
/*
* ENTRY CONFIG
*
* Add 1 entry for each "page" of your app
* (including one that's included on every page - e.g. "app")
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if you JavaScript imports CSS.
*/
Encore.splitEntryChunks()
.addEntry('app', './resources/assets/js/entries/app.ts')
// .addEntry('errors', './resources/assets/js/entries/errors.ts')
.addEntry('home', './resources/assets/js/entries/home.ts')
.addEntry('profile', './resources/assets/js/entries/profile.ts')
.addEntry('checkout', './resources/assets/js/entries/checkout.ts')
.addEntry('product-list', './resources/assets/js/entries/product-list.ts')
.addEntry('product-view', './resources/assets/js/entries/product-view.ts')
.enableVersioning(Encore.isProduction())
.enableIntegrityHashes(Encore.isProduction())
.enableSingleRuntimeChunk()
.configureSplitChunks(() => ({
chunks: 'all',
}))
.autoProvidejQuery() // uncomment if you're having problems with a jQuery plugin
.configureFriendlyErrorsPlugin()
// uncomment if you use TypeScript
.enableTypeScriptLoader();
// .enableHandlebarsLoader()
// .enableForkedTypeScriptTypesChecking()
// .configureFilenames({
// images: '[path][name].[ext]',
// })
// Retrieve the config
const config = Encore.getWebpackConfig();
if (Encore.isProduction()) {
config.devtool = 'source-map';
} else {
Encore.addLoader({
test: [/\.ts$/, /\.scss/],
use: ['cache-loader', 'babel-loader'],
include: [path.resolve('resources/assets/**/*'), path.resolve('node_modules')],
});
// Change the kind of source map generated in development mode
config.devtool = 'inline-source-map';
// USE cheap for Debugging in Chrome
// config.devtool = 'cheap-module-eval-source-map';
//
config.optimization.minimize = false;
Encore.configureWatchOptions(function(watchOptions) {
watchOptions.poll = 1000;
watchOptions.aggregateTimeout = 1000;
watchOptions.ignored = /node_modules/;
});
//Use below options when you need to debug webpack build
config.stats = {
// assets: true,
// builtAt: true,
cachedAssets: true,
errors: true,
errorDetails: true,
// reasons: true,
timings: true,
// warnings: true,
};
config.devServer = {
host: 'web-app.local',
port: 9000,
historyApiFallback: true,
disableHostCheck: true,
public: 'web-app.local',
allowedHosts: ['app.local'],
contentBase: path.join(__dirname, 'public'),
proxy: {
'*': {
target: 'http://app.local:9000',
pathRewrite: {
'/[0-9]+': '',
},
},
},
};
}
// Export the config (be careful not to call
// getWebpackConfig() again)
module.exports = config;
@versedi

This comment has been minimized.

Copy link

@versedi versedi commented Jul 28, 2020

the devServer configuration is wrong, see source from the fork for updated rev

@toofff

This comment has been minimized.

Copy link
Owner Author

@toofff toofff commented Jul 28, 2020

Thank's @versedi ;)

@versedi

This comment has been minimized.

Copy link

@versedi versedi commented Jul 28, 2020

@toofff no problem, just saw the gist had several forks and figured folks should be aware it doesn't work for some things :D

I might update the config with couple of neat features I use daily now, watch for changes ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment