Created
June 13, 2020 11:59
-
-
Save bymathias/35d913859054072f259a105d0c5fab50 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*! webpack | Webpack configuration */ | |
| import dotenv from 'dotenv' | |
| import { resolve, basename } from 'path' | |
| import { readFileSync } from 'fs' | |
| import { sync } from 'glob' | |
| import { dot } from 'dot-object' | |
| import fmp from 'front-matter-pug' | |
| import webpack from 'webpack' | |
| import { CleanWebpackPlugin } from 'clean-webpack-plugin' | |
| import webpackNodeExternals from 'webpack-node-externals' | |
| 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 HtmlWebpackPlugin from 'html-webpack-plugin' | |
| import CopyWebpackPlugin from 'copy-webpack-plugin' | |
| import PurgecssWebpackPlugin from 'purgecss-webpack-plugin' | |
| import CompressionWebpackPlugin from 'compression-webpack-plugin' | |
| import config from './config' | |
| dotenv.config() | |
| const resolvePath = (...args) => resolve(__dirname, ...args) | |
| const allViews = sync(resolvePath('src', 'client', '[^_]*.pug')) | |
| const now = new Date() | |
| // Webpack config: Exporting a Function | |
| module.exports = (env = {}, argv) => { | |
| // Merge environment variables | |
| env = { ...env, ...process.env } | |
| // Set `development` as Webpack default mode | |
| argv.mode = argv.mode || 'development' | |
| // Set `production` mode using `--mode=production` | |
| env.PRODUCTION = argv.mode === 'production' | |
| // Enable debug mode using `--debug` | |
| env.DEBUG = argv.debug || false | |
| // Enable gzip compression using `--gzip` | |
| env.GZIP = argv.gzip || false | |
| const envSuffix = env.PRODUCTION ? `${env.npm_package_version}.min` : 'dev' | |
| const banner = [ | |
| now.toISOString().substr(0, 10), | |
| env.npm_package_name, | |
| `@version ${env.npm_package_version}`, | |
| `@license ${env.npm_package_license}`, | |
| `@author ${env.npm_package_author}`, | |
| 'Copyright (c) 2020' | |
| ].join('\n') | |
| // Exporting multiple Webpack configurations | |
| return([ | |
| // ----------------------------------------------- | |
| // Back-End: API Configuration | |
| // ----------------------------------------------- | |
| { | |
| target: 'node', | |
| name: 'api', | |
| context: resolvePath('src', 'api'), | |
| entry: { | |
| index: ['./index.js'] | |
| }, | |
| output: { | |
| // This tells the server bundle to use Node-style exports | |
| libraryTarget: 'commonjs2', | |
| path: resolvePath('dist', 'api'), | |
| publicPath: '/', | |
| filename: '[name].js' | |
| }, | |
| // Style of source mapping to enhance the debugging process | |
| devtool: '#source-map', | |
| node: { | |
| // If you don't put this is, __dirname | |
| // and __filename return blank or / | |
| __dirname: false, | |
| __filename: false | |
| }, | |
| externals: [ | |
| // In order to ignore all modules in node_modules folder | |
| webpackNodeExternals() | |
| ], | |
| // Options for resolving module requests | |
| resolve: { | |
| extensions: [ '.js' ], | |
| alias: { | |
| '@': resolvePath('src', 'api') | |
| } | |
| }, | |
| // Options affecting the normal modules | |
| module: { | |
| rules: [ | |
| { | |
| enforce: 'pre', | |
| test: /\.js$/, | |
| exclude: /node_modules/, | |
| loader: 'eslint-loader', | |
| options: { | |
| // eslintPath: '', // `.eslintrc` is used by default | |
| emitError: true, | |
| emitWarning: !env.PRODUCTION, | |
| failOnError: env.PRODUCTION | |
| } | |
| }, | |
| { | |
| test: /\.js$/, | |
| exclude: /node_modules/, | |
| loader: 'babel-loader' | |
| } | |
| ] | |
| }, | |
| // Add plugins to the compiler | |
| plugins: [ | |
| // Remove output folder(s) before building | |
| new CleanWebpackPlugin({ | |
| verbose: env.DEBUG | |
| }), | |
| // Add dynamic banner to output bundle(s) | |
| env.PRODUCTION | |
| ? new webpack.BannerPlugin(banner) | |
| : 0 | |
| ].filter(Boolean), | |
| // Optimizations depending on the chosen mode | |
| optimization: { | |
| minimize: env.PRODUCTION, | |
| minimizer: [ | |
| // JavaScript parser, mangler and compressor toolkit for ES6+ | |
| new TerserWebpackPlugin({ | |
| sourceMap: true, | |
| cache: true, | |
| parallel: true, | |
| extractComments: false, | |
| terserOptions: { | |
| sourceMap: true, | |
| warnings: true, | |
| compress: true, | |
| mangle: true, | |
| ie8: false, | |
| safari10: false | |
| } | |
| }) | |
| ] | |
| } | |
| }, | |
| // ----------------------------------------------- | |
| // Front-End: Client Configuration | |
| // ----------------------------------------------- | |
| { | |
| target: 'web', // default | |
| name: 'client', | |
| context: resolvePath('src', 'client'), | |
| entry: { | |
| main: [ | |
| './scss/main.scss', | |
| './js/main.js', | |
| ] | |
| }, | |
| output: { | |
| path: resolvePath('dist', 'client'), | |
| // chunkFilename: 'js/[name].[id].js', | |
| filename: `js/[name].${envSuffix}.js` | |
| }, | |
| // Style of source mapping to enhance the debugging process | |
| devtool: env.PRODUCTION ? '#source-map' : '#cheap-module-eval-source-map', | |
| // 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 for resolving module requests | |
| resolve: { | |
| extensions: ['.js', '.css', '.scss', '.pug'], | |
| modules: ['node_modules'] | |
| // alias: {} | |
| }, | |
| // Local development server configuration | |
| devServer: { | |
| // clientLogLevel: DEBUG ? 'debug' : 'silent', | |
| stats: env.DEBUG ? 'verbose' : 'minimal', | |
| // turn off all error logging | |
| // required by `friendly-errors-webpack-plugin` | |
| // quiet: true, | |
| contentBase: [ resolvePath('dist', 'client') ], | |
| watchContentBase: true, | |
| writeToDisk: true, | |
| host: 'localhost', | |
| port: env.APP_CLIENT_PORT, | |
| public: env.APP_CLIENT_URL, | |
| publicPath: '/', | |
| allowedHosts: [ | |
| env.APP_API_URL | |
| ], | |
| // proxy: { | |
| // '/api': { | |
| // target: 'http://localhost:8081', | |
| // changeOrigin: true | |
| // } | |
| // }, | |
| inline: true, | |
| hot: true, | |
| disableHostCheck: true, | |
| historyApiFallback: { index: '/404.html' }, | |
| // openPage: ['index.html'], | |
| open: true | |
| }, | |
| // Options affecting the normal modules | |
| module: { | |
| rules: [ | |
| { | |
| enforce: 'pre', | |
| test: /\.js$/, | |
| exclude: /node_modules/, | |
| loader: 'eslint-loader', | |
| options: { | |
| // eslintPath: '', // `.eslintrc` is used by default | |
| emitError: true, | |
| emitWarning: !env.PRODUCTION, | |
| failOnError: env.PRODUCTION | |
| } | |
| }, | |
| { | |
| test: /\.js$/, | |
| exclude: /node_modules/, | |
| loader: 'babel-loader', | |
| options: { | |
| presets: [ | |
| [ | |
| '@babel/preset-env', | |
| { | |
| 'useBuiltIns': 'usage', | |
| 'corejs': 3 | |
| } | |
| ] | |
| ] | |
| } | |
| }, | |
| { | |
| test: /\.s[ac]ss$/i, | |
| exclude: /node_modules/, | |
| use: [ | |
| env.PRODUCTION ? MiniCssExtractPlugin.loader : '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', 'client', 'img') | |
| ], | |
| loader: 'file-loader', | |
| options: { | |
| name: '[name].[ext]', | |
| outputPath: 'img/' | |
| } | |
| }, | |
| { | |
| test: /\.(woff|woff2|ttf|eot|svg)$/, | |
| include: [ | |
| /@fortawesome/, | |
| resolvePath ('src', 'client', 'img') | |
| ], | |
| loader: 'file-loader', | |
| options: { | |
| name: '[name].[ext]', | |
| outputPath: 'font/' | |
| } | |
| }, | |
| { | |
| test: /\.pug$/, | |
| exclude: /node_modules/, | |
| loader: 'pug-loader', | |
| options: { | |
| self: true, | |
| globals: true, | |
| pretty: true, | |
| root: resolvePath('src', 'client') | |
| // filters: {} | |
| } | |
| } | |
| ] | |
| }, | |
| // Add plugins to the compiler | |
| plugins: [ | |
| // Remove output folder(s) before building | |
| new CleanWebpackPlugin({ | |
| verbose: env.DEBUG | |
| }), | |
| // Lint all CSS files with `stylelint` | |
| new StylelintWebpackPlugin({ | |
| context: resolvePath('src', 'client', 'scss'), | |
| files: ['**/*.scss'], | |
| emitError: true, | |
| failOnError: env.PRODUCTION | |
| }), | |
| // Move CSS into a separate output file | |
| new MiniCssExtractPlugin({ | |
| filename: `css/[name].${envSuffix}.css` | |
| }), | |
| // Generate HTML file(s) | |
| ...generateViews(allViews, { | |
| inject: false, | |
| minify: env.PRODUCTION | |
| ? { | |
| collapseWhitespace: true, | |
| removeComments: true, | |
| removeRedundantAttributes: true, | |
| removeScriptTypeAttributes: true, | |
| removeStyleLinkTypeAttributes: true, | |
| useShortDoctype: true, | |
| minifyCSS: true, | |
| minifyJS: true | |
| } | |
| : false, | |
| env, | |
| config | |
| }, env.PRODUCTION), | |
| // Copies individual files or entire directories | |
| new CopyWebpackPlugin({ | |
| patterns: [ | |
| { | |
| context: resolvePath('src', 'client'), | |
| from: '*.@(txt|xml|webmanifest)', | |
| to: resolvePath('dist', 'client'), | |
| transform (content) { // (content, path) | |
| return replaceContent(content, config) | |
| } | |
| }, | |
| { | |
| context: resolvePath('src', 'client', 'img', 'ico'), | |
| from: '*.@(svg|png)', | |
| to: resolvePath('dist', 'client'), | |
| } | |
| ] | |
| }), | |
| // Optimize all images for production env only | |
| new ImageminWebpackPlugin({ | |
| disable: !env.PRODUCTION, | |
| test: /\.(png|jpe?g|gif|svg)$/i, | |
| pngquant: { | |
| quality: '65-90', | |
| speed: 4 | |
| }, | |
| optipng: { | |
| interlaced: false, | |
| optimizationLevel: 5 | |
| }, | |
| jpegtran: { | |
| progressive: true | |
| }, | |
| gifsicle: { | |
| interlaced: true, | |
| optimizationLevel: 2 | |
| } | |
| }), | |
| // Add dynamic banner to output bundle(s) | |
| env.PRODUCTION | |
| ? new webpack.BannerPlugin(banner) | |
| : 0, | |
| // Remove unused CSS with PurgeCSS | |
| env.PRODUCTION | |
| ? new PurgecssWebpackPlugin({ | |
| paths: sync(`${resolvePath('src', 'client')}/**/*`, { nodir: true }), | |
| }) | |
| : 0, | |
| // Prepare compressed versions of assets to serve them with Content-Encoding | |
| env.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: env.PRODUCTION, | |
| minimizer: [ | |
| // JavaScript parser, mangler and compressor toolkit for ES6+ | |
| new TerserWebpackPlugin({ | |
| sourceMap: true, | |
| extractComments: false, | |
| terserOptions: { | |
| sourceMap: true, | |
| warnings: true, | |
| ecma: 5, | |
| mangle: false | |
| } | |
| }), | |
| // Plugin to optimize\minimize CSS assets using cssnano | |
| new OptimizeCssAssetsWebpackPlugin({ | |
| sourceMap: true, | |
| cssProcessorOptions: { | |
| map: { inline: false }, | |
| autoprefixer: false | |
| } | |
| }), | |
| ], | |
| // Create a `vendors` chunk, which includes all code | |
| // shared between entrypoints from `node_modules` | |
| splitChunks: { | |
| cacheGroups: { | |
| default: false, | |
| commons: { | |
| test: /[\\/]node_modules[\\/]/, | |
| name: 'vendors', | |
| chunks: 'all', | |
| minChunks: 2 | |
| } | |
| } | |
| } | |
| } | |
| } | |
| ]) | |
| } | |
| /** | |
| * readContent | |
| * | |
| * @param item {String} Path to the .pug file | |
| * @returns {Object} With these properties (attributes, body, frontmatter) | |
| */ | |
| function readContent (item) { | |
| const data = readFileSync(item, 'utf8') | |
| return fmp(data) | |
| } | |
| /** | |
| * replaceContent | |
| * | |
| * @param content {Object} File content | |
| * @param data {Object} Configuration | |
| * @returns {String} The new file content | |
| */ | |
| function replaceContent (content, data) { | |
| const db = dot(data) | |
| let str = content.toString() | |
| Object.entries(db).forEach(([key, value]) => { | |
| const dataKey = `{{ ${key} }}` | |
| const regexStr = new RegExp(dataKey, 'g') | |
| str = str.replace(regexStr, value) | |
| }) | |
| return str | |
| } | |
| /** | |
| * generateViews | |
| * | |
| * @param items {Array} List of paths to the handlebars files | |
| * @param options {Object} html-webpack-plugin options | |
| * @param env {Boolean} the environment mode | |
| * @returns {Array} New instances of HtmlWebpackPlugin | |
| */ | |
| function generateViews(items, options) { | |
| const views = [] | |
| items.map(item => { | |
| const data = readContent(item) | |
| const fileName = basename(item, '.pug') | |
| views.push( | |
| new HtmlWebpackPlugin({ | |
| template: resolvePath('src', 'client', `${fileName}.pug`), | |
| filename: `${data.attributes.output || fileName}.html`, | |
| chunks: data.attributes.chunks, | |
| page: data.attributes.page, | |
| ...options | |
| }) | |
| ) | |
| }) | |
| return views | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment