Skip to content

Instantly share code, notes, and snippets.

@bymathias
Created December 14, 2019 06:00
Show Gist options
  • Save bymathias/85d7e3fd1ddc19f5a6fd6ce779427bd5 to your computer and use it in GitHub Desktop.
Save bymathias/85d7e3fd1ddc19f5a6fd6ce779427bd5 to your computer and use it in GitHub Desktop.
/*! webpack/index.js | Webpack configuration */
// devDependencies required
import fs from 'fs'
import { resolve } from 'path'
// Webpack devDependencies required
import webpack from 'webpack'
import DotenvWebpack from 'dotenv-webpack'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import webpackNodeExternals from 'webpack-node-externals'
import VueLoaderPlugin from 'vue-loader/lib/plugin'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import StylelintWebpackPlugin from 'stylelint-webpack-plugin'
import TerserWebpackPlugin from 'terser-webpack-plugin'
import OptimizeCssAssetsWebpackPlugin from 'optimize-css-assets-webpack-plugin'
import ImageminWebpackPlugin from 'imagemin-webpack-plugin'
import PreloadWebpackPlugin from 'preload-webpack-plugin'
import CompressionWebpackPlugin from 'compression-webpack-plugin'
import EslintFriendlyFormatter from 'eslint-friendly-formatter'
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import FaviconsWebpackPlugin from 'favicons-webpack-plugin'
// import SWPrecacheWebpackPlugin from 'sw-precache-webpack-plugin'
// App settings
import pkg from './package'
import minify from './.minifyrc'
module.exports = (env = {}, argv) => {
// Set `development` as Webpack default mode
argv.mode = argv.mode || 'development'
// Set `production` mode using `--mode=production`
const PRODUCTION = argv.mode === 'production'
// Enable debug mode using `--debug`
const DEBUG = argv.debug || false
// Enable gzip compression using `--gzip`
const GZIP = argv.gzip || false
// Miscs helpers used in configuration
const resolvePath = (...args) => resolve(__dirname, ...args)
const fileSuffix = PRODUCTION ? `${pkg.version}.min` : '[hash].dev'
// `webpack-dev-server` configuration
// @see https://webpack.js.org/configuration/dev-server/
const devServer = {
contentBase: [
resolvePath('dist', 'public_html')
],
watchContentBase: true,
writeToDisk: true,
host: 'localhost',
port: 3000,
//public: 'http://cactus.test',
publicPath: '/',
allowedHosts: [
'api.domain.test'
],
proxy: {
// '/api': env.APP_HOST,
'/api': {
target: 'http://localhost:3001',
secure: false,
changeOrigin: true
}
},
inline: true,
hot: true,
disableHostCheck: true,
historyApiFallback: true,
open: true,
// setup: function (app) {
// app.get('/service-worker.js', function (req, res) {
// res.set({ 'Content-Type': 'application/javascript; charset=utf-8' })
// res.send(fs.readFileSync('/dist/public_html/service-worker.js'))
// })
// },
// turn off all error logging
// required by `friendly-errors-webpack-plugin`
quiet: true
}
return ([ // multiple Webpack configurations
/**
* --------------------------------------------------------------------
* API configuration
* --------------------------------------------------------------------
*/
{
target: 'node',
name: 'api',
// The base directory, an absolute path, for resolving
// entry points and loaders from configuration
context: resolvePath('api'),
// The entry point for the api bundle(s)
entry: {
index: ['./index.js']
},
// Options related to how webpack emits results
output: {
// This tells the server bundle to use Node-style exports
libraryTarget: 'commonjs2',
path: resolvePath('dist', 'api'),
publicPath: '/',
filename: '[name].js'
},
// Configure whether to polyfill or mock
// certain Node.js globals and modules
node: {
// If you don't put this is, __dirname
// and __filename return blank or /
__dirname: false,
__filename: false
},
// Excluding dependencies from the output bundles
externals: [
// In order to ignore all modules in node_modules folder
webpackNodeExternals()
],
// Style of source mapping to enhance the debugging process
devtool: 'source-map',
// Options for resolving module requests
resolve: {
extensions: [ '.js' ],
alias: {
'@': resolvePath('api')
}
},
// Options affecting the normal modules
module: {
// Configure loaders, parser options, etc..
rules: [
{
enforce: 'pre',
test: /\.js$/,
include: resolvePath('api'),
loader: 'eslint-loader',
options: {
formatter: EslintFriendlyFormatter,
emitError: true,
emitWarning: !PRODUCTION,
failOnError: PRODUCTION
}
},
{
test: /\.js$/,
include: resolvePath('api'),
loader: 'babel-loader'
}
]
},
// Add plugins to the compiler
plugins: [
// Provide a better developer experience
new FriendlyErrorsWebpackPlugin(),
// Remove output folder(s) before building
new CleanWebpackPlugin({
verbose: DEBUG
})
],
// Optimizations depending on the chosen mode
optimization: {
minimize: PRODUCTION,
minimizer: [
// JavaScript parser, mangler and compressor toolkit for ES6+
new TerserWebpackPlugin({
sourceMap: true,
cache: true,
parallel: true,
extractComments: false,
// warningsFilter: () => { return false },
terserOptions: {
...minify.api.terser
}
})
]
}
},
/**
* --------------------------------------------------------------------
* CLIENT configuration
* --------------------------------------------------------------------
*/
{
name: 'client',
target: 'web', // default
devServer,
// The base directory, an absolute path, for resolving
// entry points and loaders from configuration
context: resolvePath('src'),
// The entry point for the client bundle(s)
entry: {
main: ['./main.js']
},
// Options related to how webpack emits results
output: {
path: resolvePath('dist', 'public_html'),
publicPath: '/',
filename: `js/[name].${fileSuffix}.js`,
chunkFilename: `js/[name].${fileSuffix}.js`
},
// Style of source mapping to enhance the debugging process
devtool: PRODUCTION ? '#source-map' : '#cheap-module-eval-source-map',
// Options for resolving module requests
resolve: {
extensions: [ '.js', '.vue', '.scss', '.json' ],
modules: [ 'node_modules' ],
alias: {
'~': resolvePath('src'),
vue$: 'vue/dist/vue.runtime.esm.js'
}
},
// How webpack notifies you of assets and entrypoints
// that exceed a specific file limit
performance: {
hints: false
},
// Make watching work properly
watchOptions: {
poll: true
},
// Options affecting the normal modules
module: {
noParse: /^(vue|vue-router|vuex)$/,
// Configure loaders, parser options, etc..
rules: [
{
enforce: 'pre',
test: /\.(js|vue)$/,
include: resolvePath('src'),
loader: 'eslint-loader',
options: {
cache: true,
formatter: EslintFriendlyFormatter,
emitError: true,
emitWarning: !PRODUCTION,
failOnError: PRODUCTION
}
},
{
test: /\.vue$/,
include: resolvePath('src'),
loader: 'vue-loader'
},
{
test: /\.js$/,
include: resolvePath('src'),
loader: 'babel-loader',
options: {
babelrc: false,
presets: ['@babel/preset-env']
}
},
{
test: /\.s[ac]ss$/i,
include: resolvePath('src'),
use: [
PRODUCTION
? MiniCssExtractPlugin.loader
: 'vue-style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 2
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: true,
ident: 'postcss',
plugins: (loader) => [
require('postcss-preset-env')()
]
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|jpe?g|gif|svg)$/,
include: [
resolvePath('src', 'assets', 'img')
],
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'img/'
}
},
{
test: /\.(woff|woff2|ttf|eot|svg)$/,
include: [
// /@fortawesome/,
resolvePath('src', 'assets', 'font')
],
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'font/'
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/,
include: [
resolvePath('src', 'assets', 'medias')
],
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]',
outputPath: 'media/'
}
}
]
},
// Add plugins to the compiler
plugins: [
// Supports dotenv and other environment variables
// and only exposes what you choose and use
new DotenvWebpack({
// Load '.env.example' to verify the '.env' variables are all set.
// Can also be a string to a different file.
safe: true,
// Load all the predefined 'process.env' variables
// which will trump anything local per dotenv specs.
systemvars: true,
// Hide any errors, unless is debug enabled
silent: DEBUG
}),
// Provide a better developer experience
new FriendlyErrorsWebpackPlugin(),
// Remove output folder(s) before building
new CleanWebpackPlugin({
verbose: DEBUG
}),
// Vue loader plugin
new VueLoaderPlugin(),
// Enables Hot Module Replacement, otherwise known as HMR
// new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
// NODE_ENV: PRODUCTION ? '"production"' : '"development"',
APP_VERSION: JSON.stringify(pkg.version),
BASE_URL: '"/"'
}
}),
// Lint all CSS files with `stylelint`
new StylelintWebpackPlugin({
context: resolvePath('src'),
files: ['**/*.{vue,html,css,scss,sass}'],
emitError: true,
failOnError: PRODUCTION
}),
// Move CSS into a separate output file
new MiniCssExtractPlugin({
filename: `css/[name].${fileSuffix}.css`
}),
// Generate index HTML
new HtmlWebpackPlugin({
template: resolvePath('public', 'index.html'),
filename: 'index.html',
title: 'My App',
minify: PRODUCTION ? minify.client.htmlminifier : false
}),
// Injecting <link rel='preload|prefetch'>
// into HtmlWebpackPlugin pages, with async chunk support
new PreloadWebpackPlugin({
rel: 'preload',
include: 'initial',
fileBlacklist: [
/\.map$/
]
}),
new PreloadWebpackPlugin({
rel: 'prefetch',
include: 'asyncChunks'
}),
// Generate all favicons and icons
new FaviconsWebpackPlugin({
logo: resolvePath('public', 'favicons.svg'),
// prefix: '/',
inject: true,
favicons: {
lang: 'en',
appleStatusBarStyle: 'black-translucent',
loadManifestWithCredentials: true,
icons: {
android: true,
appleIcon: true,
appleStartup: true,
coast: true,
favicons: true,
firefox: true,
windows: true,
yandex: true
}
}
}),
// Creates a service worker file with a collection of
// the file paths that had been created by Webpack
// new SWPrecacheWebpackPlugin({
// cacheId: 'my-project-name',
// dontCacheBustUrlsMatching: /\.\w{8}\./,
// filename: 'service-worker.js',
// stripPrefix: 'dist/public_html/',
// staticFileGlobs: [
// 'dist/public_html/**/*.{js,css}',
// 'dist/public_html/'
// ],
// minify: true
// }),
// Help to ensure the builds are consistent
// PRODUCTION ? new webpack.optimize.OccurrenceOrderPlugin() : 0,
// Add dynamic banner to output bundle(s)
PRODUCTION
? new webpack.BannerPlugin([
new Date().toISOString().substr(0, 10),
pkg.name,
`@version ${pkg.version}`,
`@license ${pkg.license}`,
`@author ${pkg.author}`,
'Copyright (c) 2019'
].join('\n'))
: 0,
// Prepare compressed versions of assets to serve them with Content-Encoding
GZIP
? new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css)$/,
filename: '[path].gz[query]',
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false
})
: 0
].filter(Boolean),
// Optimizations depending on the chosen mode
optimization: {
minimize: PRODUCTION,
minimizer: [
// JavaScript parser, mangler and compressor toolkit for ES6+
new TerserWebpackPlugin({
sourceMap: true,
cache: true,
parallel: true,
extractComments: false,
warningsFilter: () => { return false },
terserOptions: {
...minify.client.terser
}
}),
// Plugin to optimize\minimize CSS assets using cssnano
new OptimizeCssAssetsWebpackPlugin({
sourceMap: true,
cssProcessorOptions: {
...minify.client.cssnano
}
})
],
// Create a `vendors` chunk, which includes all code
// shared between entrypoints from `node_modules`
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
}
}
])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment