Skip to content

Instantly share code, notes, and snippets.

@martinherweg
Created June 13, 2018 17:08
Show Gist options
  • Save martinherweg/8a2eb674419a94c0c8db7bd9dd5aab58 to your computer and use it in GitHub Desktop.
Save martinherweg/8a2eb674419a94c0c8db7bd9dd5aab58 to your computer and use it in GitHub Desktop.
/**
* Webpack Config for Javascript and CSS Bundling
*
* @package generator-lilly
* @author Martin Herweg <info@martinherweg.de>
*/
import webpack from 'webpack';
import { getIfUtils, removeEmpty } from 'webpack-config-utils';
import path from 'path';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import WriteFilePlugin from 'write-file-webpack-plugin';
import CleanWebpackPlugin from 'clean-webpack-plugin';
import WebpackBar from 'webpackbar';
import StylelintPlugin from 'stylelint-webpack-plugin';
import ManifestPlugin from 'webpack-manifest-plugin';
import Stylish from 'webpack-stylish';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import FriendlyErrors from 'friendly-errors-webpack-plugin';
import { VueLoaderPlugin } from 'vue-loader';
import WebpackAlias from './webpack.alias';
const config = require('../package.json');
/*
|--------------------------------------------------------------------------
| Setting some paths for our Application
|--------------------------------------------------------------------------
*/
const BASE_PATH = path.join(path.resolve(__dirname, '../'));
const ASSETS_ROOT = path.resolve(BASE_PATH, config.distPaths.base, 'assets');
/*
|--------------------------------------------------------------------------
| Hot Middleware Client
|--------------------------------------------------------------------------
*/
const hot_client =
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true&overlay=true';
/*
|--------------------------------------------------------------------------
| Defining Entry Points, could be used to manually split Parts of the Application, for example
| Admin Javascript and FrontEnd JavaScript
|--------------------------------------------------------------------------
*/
function assetsPath(_path) {
return path.posix.join('assets/', _path);
}
const chunks = [];
const chunks_inject = [
{
filename: path.resolve(`${config.distPaths.views}parts/webpack-header.html`),
file: `${config.srcPaths.views}parts/webpack-header.html`,
inject: false,
},
{
filename: path.resolve(`${config.distPaths.views}parts/site-scripts.html`),
file: `${config.srcPaths.views}parts/site-scripts.html`,
inject: false,
},
];
chunks_inject.forEach(chunk => {
const plugin = new HtmlWebpackPlugin({
filename: chunk.filename,
template: chunk.file,
inject: chunk.inject,
minify: false,
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency',
});
chunks.push(plugin);
});
/*
|--------------------------------------------------------------------------
| Setup for CSS Loaders
|--------------------------------------------------------------------------
*/
function resolve(dir) {
return path.join(__dirname, '..', dir);
}
/*
|--------------------------------------------------------------------------
| return webpack config object
|--------------------------------------------------------------------------
*/
module.exports = (env = { development: true }) => {
const { ifProduction, ifDevelopment } = getIfUtils(env);
const entry_points = {
app: './src/js/app.js',
};
if (ifDevelopment()) {
Object.keys(entry_points).forEach(
entry => (entry_points[entry] = [hot_client].concat(entry_points[entry])),
);
}
const CSS_LOADERS = removeEmpty([
ifDevelopment('style-loader', MiniCssExtractPlugin.loader),
{
loader: 'css-loader',
options: { importLoaders: 1, minimize: ifDevelopment(false, true) },
},
'postcss-loader',
{
loader: 'sass-loader',
options: {
includePaths: [resolve(config.srcPaths.base), resolve(config.srcPaths.css)],
data: "@import 'settings';\n" + "@import 'tools';",
},
},
]);
return {
// we have to use source map for css source maps, slightly longer compile times
devtool: ifDevelopment('eval-source-map', 'source-map'),
stats: 'none',
context: BASE_PATH,
mode: ifProduction('production', 'development'),
// entry is a function so that we can use environment variables
entry: removeEmpty(entry_points),
output: {
path: ASSETS_ROOT,
publicPath: '/assets/',
filename: ifDevelopment('js/[name].js', 'js/[name].[chunkhash].js'),
chunkFilename: ifProduction(
'js/[id].[chunkhash].js',
'js/[name].[id].js',
),
hotUpdateChunkFilename: 'js/[id].[hash].hot-update.js',
hotUpdateMainFilename: 'js/[hash].hot-update.json',
},
performance: {
hints: ifDevelopment(false, 'warning'),
},
optimization: {
splitChunks: {
chunks: 'initial',
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
},
},
runtimeChunk: {
name: 'manifest',
},
},
resolve: {
extensions: ['.js', '.json', '.vue'],
modules: [
resolve(config.srcPaths.base),
resolve('node_modules'),
resolve(`${config.srcPaths.views}modules/`),
resolve(`${config.srcPaths.views}glamlounge/`),
],
alias: WebpackAlias,
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter'),
},
enforce: 'pre',
include: resolve(config.srcPaths.base),
},
{
test: /\.js$/,
use: 'babel-loader',
include: resolve(config.srcPaths.base),
},
{
test: /\.css$/,
use: ifProduction(
[MiniCssExtractPlugin.loader, 'css-loader'],
['style-loader', 'css-loader'],
),
},
{
test: /\.scss$/,
include: resolve(config.srcPaths.base),
use: CSS_LOADERS,
},
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.svg$/,
oneOf: [
{
resourceQuery: /inline/, // foo.css?inline
use: 'vue-svg-loader',
},
{
loader: 'url-loader',
options: {
limit: 100,
name: filePath => {
const filename = path.basename(filePath);
const folder = path
.relative(config.srcPaths.images.svg.base, filePath)
.replace(filename, '');
return `images/svg/${folder}[name].[hash:4].[ext]`;
},
},
},
],
exclude: resolve(config.srcPaths.fonts),
},
{
test: /\.(png|jpg|gif)$/,
oneOf: [
{
loader: 'url-loader',
include: [resolve(`${config.srcPaths.views}glamlounge`)],
options: {
limit: 10000,
name: filePath => {
const filename = path.basename(filePath);
const folder = path
.relative(config.srcPaths.images.base, filePath)
.replace(filename, '');
return `images/bitmap/glamlounge/[name].[hash:4].[ext]`;
},
},
},
{
loader: 'url-loader',
exclude: [resolve(`${config.srcPaths.views}glamlounge`)],
options: {
limit: 10000,
name: filePath => {
const filename = path.basename(filePath);
const folder = path
.relative(config.srcPaths.images.base, filePath)
.replace(filename, '');
return `images/bitmap/${folder}/${filename}.[hash:4].[ext]`;
},
},
}
],
},
{
// Match woff2 in addition to patterns like .woff?v=1.1.1.
test: /\.(woff|woff2|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url-loader',
exclude: [resolve(config.srcPaths.images.svg.base), resolve(`${config.srcPaths.views}glamlounge`)],
options: {
// Limit at 10k. Above that it emits separate files
limit: 10000,
// url-loader sets mimetype if it's passed.
// Without this it derives it from the file extension
mimetype: 'application/font-woff',
// Output below fonts directory
name: path => 'fonts/[name].[ext]',
publicPath: '/assets/',
},
},
],
},
plugins: removeEmpty([
new WebpackBar(),
new CleanWebpackPlugin([config.distPaths.css, config.distPaths.js], {
root: BASE_PATH,
verbose: true,
}),
ifDevelopment(new webpack.HotModuleReplacementPlugin()),
new MiniCssExtractPlugin({
filename: ifDevelopment('css/[name].css', 'css/[name].[chunkhash].css'),
chunkFilename: ifDevelopment('css/[id].css', 'css/[id].[chunkhash].css'),
}),
new StylelintPlugin({
context: resolve('src/'),
}),
new VueLoaderPlugin(),
...chunks,
new HtmlWebpackPlugin({
filename: path.resolve(`${config.distPaths.base}/boilerplate/typography.html`),
template: `${config.srcPaths.base}boilerplates/typography.html`,
inject: true,
minify: false,
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency',
}),
new WriteFilePlugin({
log: false,
test: /^(?!.+(?:hot-update.(js|json))).+$/,
}),
new ManifestPlugin({
fileName: resolve(`${config.distPaths.base}/manifest.json`),
publicPath: '/assets/',
writeToFileEmit: true,
}),
ifDevelopment(new FriendlyErrors()),
//new FriendlyErrors(),
ifProduction(new Stylish()),
]),
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment