Browse Source

vendor bundles and hashes for long term caching

master
salvatore-fxpig 1 year ago
parent
commit
1ba8b1d3d5
  1. 0
      .null/.nocontent
  2. 5
      babel.config.js
  3. 20
      bundlesize.config.json
  4. 13
      cli/develop.js
  5. 2
      cli/view.js
  6. 4
      index.html
  7. 1006
      package-lock.json
  8. 3
      package.json
  9. 1
      src/index.js
  10. 6
      src/root.js
  11. 126
      webpack.config.js

0
.null/.nocontent

5
babel.config.js

@ -12,10 +12,9 @@ module.exports = function babelConfig(api) {
[
"@babel/preset-env",
{
useBuiltIns: "entry",
useBuiltIns: false, // use the full lib to make sure vendor bundle stays stable
targets: "cover 95%",
bugfixes: true,
corejs: 3
bugfixes: true
}
],
"@babel/preset-react"

20
bundlesize.config.json

@ -1,12 +1,24 @@
{
"files": [
{
"path": "./dist/auspice.bundle.js",
"maxSize": "200 kB"
"path": "./dist/auspice.bundle.*.js",
"maxSize": "60 kB"
},
{
"path": "./dist/auspice.chunk.*.bundle.js",
"maxSize": "120 kB"
"path": "./dist/auspice.chunk.+([0-9]).bundle.*.js",
"maxSize": "75 kB"
},
{
"path": "./dist/auspice.chunk.core-vendors.bundle.08ffc3a769477339a352.js",
"maxSize": "220 kB"
},
{
"path": "./dist/auspice.chunk.other-vendors.bundle.fb028f17c0ac14d133af.js",
"maxSize": "100 kB"
},
{
"path": "./dist/auspice.chunk.locales.bundle.*.js",
"maxSize": "50 kB"
}
]
}

13
cli/develop.js

@ -50,6 +50,14 @@ const run = (args) => {
process.env.BABEL_ENV = "development";
process.env.BABEL_EXTENSION_PATH = extensionPath;
/* Redirects / to webpack-generated index */
app.use((req, res, next) => {
if (!/^\/__webpack_hmr|^\/charon|\.[A-Za-z0-9]{1,4}$/.test(req.path)) {
req.url = webpackConfig.output.publicPath;
}
next();
});
app.use((webpackDevMiddleware)(
compiler,
{logLevel: 'warn', publicPath: webpackConfig.output.publicPath}
@ -66,10 +74,7 @@ const run = (args) => {
handlerMsg = loadAndAddHandlers({app, handlersArg: args.handlers, datasetDir: args.datasetDir, narrativeDir: args.narrativeDir});
}
/* this must be the last "get" handler, else the "*" swallows all other requests */
app.get("*", (req, res) => {
res.sendFile(path.join(baseDir, "index.html"));
});
app.get("*", (req, res) => res.redirect("/"));
const server = app.listen(app.get('port'), app.get('host'), () => {
utils.log("\n\n---------------------------------------------------");

2
cli/view.js

@ -125,7 +125,7 @@ const run = (args) => {
/* this must be the last "get" handler, else the "*" swallows all other requests */
app.get("*", (req, res) => {
res.sendFile(path.join(auspiceBuild.baseDir, "index.html"));
res.sendFile(path.join(auspiceBuild.baseDir, "dist/index.html"));
});
const server = app.listen(app.get('port'), app.get('host'), () => {

4
index.html

@ -11,9 +11,5 @@
<body class="viewport">
<div id='root'>
</div>
<!-- asyncronously load the (entry) react bundle -->
<script async src="/dist/auspice.bundle.js"></script>
</body>
</html>

1006
package-lock.json
File diff suppressed because it is too large
View File

3
package.json

@ -55,6 +55,7 @@
"binomial": "^0.2.0",
"bundlesize": "^0.18.0",
"chalk": "^2.4.1",
"clean-webpack-plugin": "^3.0.0",
"compression": "^1.7.3",
"compression-webpack-plugin": "^3.0.1",
"core-js": "^3.6.5",
@ -80,6 +81,7 @@
"express-naked-redirect": "^0.1.2",
"express-static-gzip": "^0.2.2",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^4.3.0",
"i18next": "^19.3.2",
"i18next-resource-store-loader": "^0.1.2",
"json-loader": "^0.5.1",
@ -116,6 +118,7 @@
"styled-components": "^4.0.3",
"typeface-lato": "^0.0.75",
"webpack": "^4.30.0",
"webpack-chunk-hash": "^0.6.0",
"webpack-cli": "^3.1.2",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.24.3",

1
src/index.js

@ -8,7 +8,6 @@ import "regenerator-runtime";
import "react-hot-loader";
import React from "react";
import ReactDOM from "react-dom";
import Select from "react-select/lib/Select";
import { Provider } from "react-redux";
/* A U S P I C E I M P O R T S */
import configureStore from "./store";

6
src/root.js

@ -6,11 +6,11 @@ import Monitor from "./components/framework/monitor";
import DatasetLoader from "./components/datasetLoader";
import Spinner from "./components/framework/spinner";
import Head from "./components/framework/head";
import Notifications from "./components/notifications/notifications";
const Main = lazy(() => import("./components/main"));
const Splash = lazy(() => import("./components/splash"));
const Status = lazy(() => import("./components/status"));
const Notifications = lazy(() => import("./components/notifications/notifications"));
/* Hot reload components decorated with i18n */
if (module.hot) {
@ -54,9 +54,7 @@ const Root = () => {
<div>
<Head/>
<Monitor/>
<Suspense fallback={null}>
<Notifications/>
</Suspense>
<Notifications/>
<MainComponentSwitch/>
</div>
);

126
webpack.config.js

@ -4,7 +4,9 @@ const webpack = require("webpack");
const CompressionPlugin = require('compression-webpack-plugin');
const fs = require('fs');
const utils = require('./cli/utils');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const WebpackChunkHash = require('webpack-chunk-hash');
/* Webpack config generator */
@ -16,7 +18,7 @@ const generateConfig = ({extensionPath, devMode=false, customOutputPath, analyze
/* webpack alias' used in code import / require statements */
const aliasesToResolve = {
"@extensions": '.', /* must provide a default, else it won't compile */
"@extensions": path.join(__dirname, '.null'), /* must provide a default, else it won't compile */
"@auspice": path.join(__dirname, 'src'),
"@libraries": path.join(__dirname, 'node_modules'),
// Pins all react stuff to auspice dir, and uses hot loader's dom (can be used safely in production)
@ -24,7 +26,8 @@ const generateConfig = ({extensionPath, devMode=false, customOutputPath, analyze
"react-hot-loader": path.join(__dirname, 'node_modules/react-hot-loader'),
'react-dom': path.join(__dirname, 'node_modules/@hot-loader/react-dom'),
'regenerator-runtime': path.join(__dirname, 'node_modules/regenerator-runtime'),
'core-js': path.join(__dirname, 'node_modules/core-js')
'core-js': path.join(__dirname, 'node_modules/core-js'),
'styled-components': path.join(__dirname, 'node_modules/styled-components')
};
let extensionData;
@ -50,18 +53,29 @@ const generateConfig = ({extensionPath, devMode=false, customOutputPath, analyze
const pluginCompress = new CompressionPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: /\.js$|\.css$|\.html$/,
threshold: 10240,
minRatio: 0.8
test: /\.(js|css|html)$/,
threshold: 4096
});
const pluginHtml = new HtmlWebpackPlugin({
filename: 'index.html',
template: './index.html'
});
const cleanWebpackPlugin = new CleanWebpackPlugin({
cleanStaleWebpackAssets: true
});
const plugins = devMode ? [
new webpack.HotModuleReplacementPlugin(),
pluginProcessEnvData,
new webpack.NoEmitOnErrorsPlugin()
new webpack.NoEmitOnErrorsPlugin(),
pluginHtml,
cleanWebpackPlugin
] : [
pluginProcessEnvData,
new webpack.optimize.AggressiveMergingPlugin({minSizeReduce: 1.2}), // merge chunks - https://github.com/webpack/docs/wiki/list-of-plugins#aggressivemergingplugin
pluginCompress
new webpack.HashedModuleIdsPlugin({}),
new WebpackChunkHash({algorithm: 'md5'}),
pluginCompress,
pluginHtml,
cleanWebpackPlugin
];
if (analyzeBundle) {
@ -84,6 +98,60 @@ const generateConfig = ({extensionPath, devMode=false, customOutputPath, analyze
path.resolve(__dirname, "dist");
utils.verbose(`Webpack writing output to: ${outputPath}`);
/**
* Here we put the libraries that are unlikely to change for a long time.
* Every change or update of any of these libraries will change the hash
* of the big vendor bundle, so we must be sure they're stable both internally
* and with respect to the implementation.
* The hashes of the bundles are hardcoded in bundlesize so it will trigger
* a check error if it is unadvertently changed.
*/
const coreVendors = [
"@babel/runtime",
"core-js",
"regenerator-runtime",
"whatwg-fetch",
"style-loader",
"@hot-loader/react-dom",
"react(-(redux|select|helmet|i18next))?",
"leaflet",
"redux",
"leaflet(-gesture-handling)?",
"i18next",
"lodash",
"styled-components",
"stylis",
"@emotion"
]; // <= needs some review from somebody with more knowledge of the whole codebase to decide what goes in
/**
* Here we put the (big) libraries that are more prone to change/update.
* For example d3-.* is here even if it's a core library, because if we
* include some new d3 feature, the whole bundle will change.
* The hashes of the bundles are hardcoded in bundlesize so it will trigger
* a check error if it is unadvertently changed.
*/
const bigVendors = [
"d3-.*",
"awesomplete",
"react-transition-group",
"react-icons",
"create-react-class",
"mousetrap",
"react-input-autosize",
"typeface-lato",
// "papaparse", <= This is only for the drag-and-drop of files and can be separated
"dom-to-image"
// "marked",
// "dompurify", <= These two are only for MD display and can be separated
];
/**
* It's better to keep small libraries out of the vendor bundles because
* the more libraries we include, the more small changes or updates
* of a single small library will cause the hash to change.
*/
const config = {
mode: devMode ? 'development' : 'production',
context: __dirname,
@ -92,8 +160,8 @@ const generateConfig = ({extensionPath, devMode=false, customOutputPath, analyze
entry,
output: {
path: outputPath,
filename: "auspice.bundle.js",
chunkFilename: 'auspice.chunk.[name].bundle.js',
filename: `auspice.bundle${!devMode ? ".[chunkhash]" : ""}.js`,
chunkFilename: `auspice.chunk.[name].bundle${!devMode ? ".[chunkhash]" : ""}.js`,
publicPath: "/dist/"
},
resolve: {
@ -104,7 +172,41 @@ const generateConfig = ({extensionPath, devMode=false, customOutputPath, analyze
},
plugins,
optimization: {
minimize: !devMode
minimize: !devMode,
splitChunks: {
minChunks: 3,
minSize: 8192,
cacheGroups: {
coreVendors: {
test: new RegExp("\\/node_modules\\/(" + coreVendors.join("|") + ")\\/"),
name: "core-vendors",
enforce: true,
chunks: "all"
},
otherVendors: {
test: new RegExp("\\/node_modules\\/(" + bigVendors.join("|") + ")\\/"),
name: "other-vendors",
enforce: true,
chunks: "all"
},
/**
* ATM the package size is <15kB and so not worth splitting,
* but it can be split further if it becomes huge
*/
locales: {
test: /\/src\/locales\//,
name: "locales",
enforce: true,
chunks: "all"
},
default: {
minChunks: 3,
minSize: 8192,
reuseExistingChunk: false
},
defaultVendors: false
}
}
},
module: {
rules: [

Loading…
Cancel
Save