Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack Dev Server Refactor + Build Tools Updates #923

Merged
merged 9 commits into from
Oct 24, 2018
Merged
6 changes: 5 additions & 1 deletion apps/bolt-site/.boltrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ const config = {
lang: ['en'],
renderingService: false, // starts PHP service for rendering Twig templates
openServerAtStart: false,
webpackDevServer: true,
webpackDevServer: {
enabled: true,
watchedExtensions: ['.html'],
},
// Environmental variable / preset to use
env: 'static',
startPath: '/',
buildDir: '../../www/build/',
srcDir: './pages',
wwwDir: '../../www',
enableCache: true,
extraTwigNamespaces: {
'bolt-assets': {
recursive: true,
Expand Down
6 changes: 5 additions & 1 deletion apps/pattern-lab/.boltrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ const config = {
plConfigFile: './config/config.yml',
verbosity: 2,
schemaErrorReporting: 'cli',
webpackDevServer: true,
enableCache: true,
webpackDevServer: {
enabled: true,
watchedExtensions: ['.markup-only.html'],
},
extraTwigNamespaces: {
bolt: {
recursive: true,
Expand Down
103 changes: 81 additions & 22 deletions packages/build-tools/create-webpack-config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const autoprefixer = require('autoprefixer');
Expand All @@ -12,8 +13,12 @@ const resolve = require('resolve');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const globImporter = require('node-sass-glob-importer');
const npmSass = require('npm-sass');
const WriteAssetsWebpackPlugin = require('write-assets-webpack-plugin');
const { BoltCache, getFileHash } = require('@bolt/build-tools/utils/cache');
const chokidar = require('chokidar');
const { getConfig } = require('./utils/config-store');
const SassDocPlugin = require('./plugins/sassdoc-webpack-plugin');
const { boltWebpackProgress } = require('./utils/webpack-helpers');

const {
getBoltManifest,
Expand Down Expand Up @@ -149,6 +154,10 @@ async function createWebpackConfig(buildConfig) {
if (components.global) {
entry[globalEntryName] = [];

if (!config.prod && config.webpackDevServer) {
entry[globalEntryName].push('webpack/hot/dev-server');
}

components.global.forEach(component => {
if (component.assets.style) {
entry[globalEntryName].push(component.assets.style);
Expand Down Expand Up @@ -320,6 +329,7 @@ async function createWebpackConfig(buildConfig) {

// THIS IS IT!! The object that gets passed in as WebPack's config object.
const webpackConfig = {
target: 'web',
entry: await buildWebpackEntry(),
// watchOptions: {
// ignored: [
Expand All @@ -334,11 +344,9 @@ async function createWebpackConfig(buildConfig) {
chunkFilename: `[name]-bundle${langSuffix}-[chunkhash].js`,
publicPath,
},
cache: true,
resolve: {
mainFields: ['esnext', 'jsnext:main', 'browser', 'module', 'main'],
extensions: ['.js', '.jsx', '.mjs', '.json', '.svg', '.scss'],
unsafeCache: true,
alias: {
react: 'preact-compat',
'react-dom': 'preact-compat',
Expand Down Expand Up @@ -369,7 +377,6 @@ async function createWebpackConfig(buildConfig) {
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
babelrc: false,
presets: ['@bolt/babel-preset-bolt'],
},
Expand Down Expand Up @@ -401,6 +408,11 @@ async function createWebpackConfig(buildConfig) {
mergeDuplicateChunks: true,
},
plugins: [
new webpack.ProgressPlugin(boltWebpackProgress), // Ties together the Bolt custom Webpack messages + % complete
new WriteAssetsWebpackPlugin({
force: true,
extension: ['js', 'json', 'css'],
}),
new MiniCssExtractPlugin({
filename: `[name]${langSuffix}.css`,
chunkFilename: `[id]${langSuffix}.css`,
Expand Down Expand Up @@ -432,18 +444,39 @@ async function createWebpackConfig(buildConfig) {
],
};

if (!config.prod && config.webpackDevServer) {
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
}

// Enable new experimental cache mode to significantly speed up the initial build times
if (config.enableCache && !config.prod) {
webpackConfig.plugins.push(
new HardSourceWebpackPlugin({
info: {
level: 'warn',
},
// Clean up large, old caches automatically.
cachePrune: {
// Caches younger than `maxAge` are not considered for deletion. They must
// be at least this (default: 2 days) old in milliseconds.
maxAge: 2 * 24 * 60 * 60 * 1000,
// All caches together must be larger than `sizeThreshold` before any
// caches will be deleted. Together they must be at least 300MB in size
sizeThreshold: 300 * 1024 * 1024,
},
}),
);
}

if (config.prod) {
// Optimize JS - https://webpack.js.org/plugins/uglifyjs-webpack-plugin/
// Config recommendation based off of https://slack.engineering/keep-webpack-fast-a-field-guide-for-better-build-performance-f56a5995e8f1#f548
webpackConfig.plugins.push(
new UglifyJsPlugin({
sourceMap: config.sourceMaps,
parallel: true,
cache: true,
uglifyOptions: {
cache: true,
compress: true,

mangle: true,
},
}),
Expand Down Expand Up @@ -485,27 +518,53 @@ async function createWebpackConfig(buildConfig) {

if (config.wwwDir) {
webpackConfig.devServer = {
contentBase: [
path.resolve(process.cwd(), config.wwwDir),
// @TODO: add Pattern Lab Styleguidekit Assets Default dist path here
],
compress: true,
logLevel: 'silent',
contentBase: path.resolve(process.cwd(), config.wwwDir),
quiet: true,
clientLogLevel: 'none',
port: config.proxyPort,
port: config.port,
stats: statsPreset(webpackStats[config.verbosity]),
overlay: {
errors: true,
},
hot: !!config.prod,
hot: config.prod ? false : true,
inline: true,
noInfo: true, // webpackTasks.watch handles output info related to success & failure
publicPath,
watchContentBase: true,
historyApiFallback: true,
watchOptions: {
aggregateTimeout: 200,
// ignored: /(annotations|fonts|bower_components|dist\/styleguide|node_modules|styleguide|images|fonts|assets)/
// Poll using interval (in ms, accepts boolean too)
watchContentBase: false,
before(app, server) {
if (!config.prod && config.webpackDevServer) {
if (config.webpackDevServer.watchedExtensions) {
const watchedPaths = [];

// generate wwwDir globbed paths for each file extension being watched
config.webpackDevServer.watchedExtensions.forEach(ext => {
watchedPaths.push(path.join(config.wwwDir, '**/*' + ext));
});

// The watch event ~ same engine gulp uses https://www.npmjs.com/package/chokidar
const watcher = chokidar.watch(watchedPaths, {
ignoreInitial: true,
cwd: process.cwd(),
ignored: ['**/node_modules/**', '**/vendor/**'],
});

// only auto-refresh when a particular file's contents has changed to reduce browser thrashing
watcher.on('all', (event, filePath) => {
if (event === 'add' || event === 'change') {
getFileHash(filePath, function(hash) {
let previousFileHash = BoltCache.get(filePath);
let currentFileHash = hash;

if (
previousFileHash === undefined ||
previousFileHash !== currentFileHash
) {
BoltCache.set(filePath, currentFileHash);
server.sockWrite(server.sockets, 'content-changed');
}
});
}
});
}
}
},
};
}
Expand Down
10 changes: 6 additions & 4 deletions packages/build-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
}
],
"dependencies": {
"address": "^1.0.3",
"hard-source-webpack-plugin": "git+https://github.com/talkable/hard-source-webpack-plugin.git#e774ef34ac7de7badf83f8560cca13861e76f23c",
"@bolt/babel-preset-bolt": "^2.1.6",
"@bolt/config-browserlist": "^2.1.6",
"@bolt/fast-sass-loader": "github:bolt-design-system/fast-sass-loader#master",
Expand All @@ -41,7 +43,6 @@
"css-loader": "^1.0.0",
"deepmerge": "^2.1.1",
"del": "^3.0.0",
"ejs": "^2.6.1",
"eslint-loader": "^2.1.0",
"execa": "^1.0.0",
"express": "^4.16.3",
Expand Down Expand Up @@ -80,10 +81,11 @@
"svgo": "^1.0.5",
"uglifyjs-webpack-plugin": "^1.3.0",
"url-loader": "^1.1.1",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0",
"webpack-manifest-plugin": "^2.0.3",
"webpack-serve": "^2.0.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.9",
"webpack": "4.19.0",
"write-assets-webpack-plugin": "^1.0.4",
"yaml-loader": "^0.5.0",
"yargs": "^12.0.1"
},
Expand Down
18 changes: 2 additions & 16 deletions packages/build-tools/plugins/postcss-themify/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ const postcss = require('postcss');
const fs = require('fs-extra');
const hexToRgba = require('hex-to-rgba');
const rgb2hex = require('rgb2hex');
const crypto = require('crypto');
const chokidar = require('chokidar');
const BoltCache = require('@bolt/build-tools/utils/cache');
const { BoltCache, getFileHash } = require('@bolt/build-tools/utils/cache');
const { minifyCSS } = require('./helpers/css.util');

const THEMIFY = 'bolt-themify';
Expand All @@ -15,19 +14,6 @@ let output;
let colorPaletteHash;
let isWatchingForChanges = false;

function getHash(filePath, callback) {
var stream = fs.ReadStream(filePath);
var md5sum = crypto.createHash('md5');

stream.on('data', function(data) {
md5sum.update(data);
});

stream.on('end', function() {
callback(md5sum.digest('hex'));
});
}

//----------------------------------------------------
function watchColorPaletteFile(filePath, callback) {
chokidar
Expand All @@ -36,7 +22,7 @@ function watchColorPaletteFile(filePath, callback) {
})
.on('all', function(event, path, stats) {
if (event === 'add' || event === 'change') {
getHash(filePath, function(hash) {
getFileHash(filePath, function(hash) {
if (hash !== colorPaletteHash) {
colorPaletteHash = hash;
console.log('Color palette changed! Invalidating cache...');
Expand Down
2 changes: 1 addition & 1 deletion packages/build-tools/plugins/sassdoc-webpack-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const path = require('path');
const sassdoc = require('sassdoc');
const yaml = require('js-yaml');
const crypto = require('crypto');
const BoltCache = require('../utils/cache');
const { BoltCache } = require('../utils/cache');

class SassDocPlugin {
constructor(options, pluginOptions) {
Expand Down
8 changes: 2 additions & 6 deletions packages/build-tools/tasks/server-tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const log = require('../utils/log');
const sh = require('../utils/sh');
const { handleRequest } = require('../api');
const server = browserSync.create();
const webpackServeWaitpage = require('./webpack-serve-waitpage');
let config;

async function phpServer() {
Expand Down Expand Up @@ -45,7 +44,7 @@ async function getServerConfig() {

// https://www.browsersync.io/docs/options
const serverConfig = {
open: false, // now handled by Webpack Serve
open: config.openServerAtStart ? config.openServerAtStart : false,
startPath: config.startPath, // Since `/` doesn't do anything and we want to avoid double browserSync notifications from the very beginning
port: config.port,
host: '127.0.0.1',
Expand All @@ -56,10 +55,7 @@ async function getServerConfig() {
notify: false, // Hide notifications till we come up with a less disruptive refresh UI
reloadOnRestart: true,
ui: false,
files: [config.wwwDir + '**/*.css', config.wwwDir + '**/*.html'],
snippetOptions: {
async: true,
},
files: [config.wwwDir + '**/*.html'],
};

if (config.renderingService) {
Expand Down
7 changes: 4 additions & 3 deletions packages/build-tools/tasks/task-collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,10 @@ async function serve(buildTime = timer.start()) {
serverTasks.push(extraTasks.server.phpServer());
}
if (config.wwwDir) {
serverTasks.push(extraTasks.server.serve());
if (config.webpackDevServer && config.watch !== false) {
serverTasks.push(webpackTasks.server(buildTime));
serverTasks.push(webpackTasks.server());
} else if (config.webpackDevServer === false && config.watch !== false) {
serverTasks.push(extraTasks.server.serve());
}
}

Expand Down Expand Up @@ -219,7 +220,7 @@ async function start() {
return Promise.all([
serve(buildTime, true),
await compileBasedOnEnvironment(),
watch(),
await watch(),
]);
} catch (error) {
log.errorAndExit('Start failed', error);
Expand Down
Loading