This is the technology stack our project uses:
- Symfony 2.8†
- Twig 1.24†
- AngularJS 1.5†
- Webpack 1.12†
We have an issue that our app.min.js
file is cached and when we make changes that require a new app.min.js
file, often our clients keep using the cached file instead of loading the new file and end up generating errors and having to call our Support desk.
I've looked into fixing this by embedding the hash of the file contents into the HTTP request, then having the server serve the original file using a rewrite rule. I've added [contenthash].
to the filename in webpack.config.js
(see below) but when I run Webpack, it doesn't replace [contenthash]
, instead creating a file with that exact name.
module.exports = {
output: {
filename: 'app.min.[contenthash].js'
}
}
What I'd like to happen is:
- the output files are still called
app.min.js
andapp.min.js.map
- the generated map file
app.min.js.map
links toapp.min.6b1900549fed191ccbe8.js
- Webpack saves the hash somewhere in a configuration file
app_min_js_hash: '6b1900549fed191ccbe8'
- I change the Twig templates to include the hash as below:
<script src="/dist/js/app.min.{{ app_min_js_hash }}.js"></script>
So I have a few problems I haven't solved:
How to get Webpack to output the hash to a config fileHow to get Webpack to replace the keyword with the hash- How to get Webpack to create the file names without the hash
I already know how to solve the rewrite and Twig parts.
Following the comments in the linked question (HtmlWebpackPlugin load bundle.[hash].js/css into base twig file), I've now got:
webpack.config.js
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackMajorVersion = require('webpack/package.json').version.split('.')[0];
module.exports = {
console: true,
name: 'js',
entry: ['./src/base.js'],
output: {
path: '../web/dist/js/',
publicPath: '/dist/js/',
filename: 'app.min.[hash].js'
},
devtool: "source-map",
resolve : {
root : [path.join(__dirname, "src/")]
},
sassLoader: {
includePaths: './src/'
},
resolveLoader: {
root: __dirname+'/node_modules/',
modulesDirectories: [""]
},
jshint: {
esversion: 6
},
// Initialize module
preLoaders: [],
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'jshint-loader',
}
],
loaders: [{
// JS LOADER
// Reference: https://github.com/babel/babel-loader
// Transpile .js files using babel-loader
// Compiles ES6 and ES7 into ES5 code
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['es2015']
},
exclude: /node_modules/
}, {
// ASSET LOADER
// Reference: https://github.com/webpack/file-loader
// Copy png, jpg, jpeg, gif, svg, woff, woff2, ttf, eot files to output
// Rename the file using the asset hash
// Pass along the updated reference to your code
// You can add here any file extension you want to get copied to your output
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
loader: 'file'
}, {
// HTML LOADER
// Reference: https://github.com/webpack/raw-loader
// Allow loading html through js
test: /\.html$/,
loader: "ngtemplate?relativeTo=" + (path.resolve(__dirname, './src/')) + "/!html"
}, {
test: /\.scss$/,
loaders: ["style", "css", "sass"]
}],
resolve: {
// you can now require('file') instead of require('file.coffee')
extensions: ['', '.js', '.json', '.coffee']
}
},
plugins: [
new HtmlWebpackPlugin({
templateContent: function(params) {
return `
{% block jsAssets %}
${params.htmlWebpackPlugin.files.js.map(
file => `<script src="${file}"></script>`,
)}
{% endblock %}
{% block cssAssets %}
${params.htmlWebpackPlugin.files.css.map(
file => `<link rel="stylesheet" type="text/css" href="${file}">`,
)}
{% endblock %}`;
},
filename: '../../resources/templates/assets.html.twig',
inject: false,
// // The following settings are optional and only used for
// // demo purposes:
// meta: {
// charset: { charset: 'utf-8' },
// viewport: 'width=device-width, initial-scale=1'
// },
// minify: false
})
]
};
It's necessary to use
the generated Twig template before the Twig code {{ block("jsAssets", "assets.html.twig") }}
will do anything.
Twig:
{% use 'HTMLBundle:Layout:assets.html.twig' %}
...
{{ block("jsAssets", "assets.html.twig") }}
...
So now the only problem that remains is how to keep the output file name the same each time, while still generating a hash. Otherwise, the directory will keep filling up with different hashed versions (which aren't exactly small). Only the latest file is ever needed. We use a one-step deployment, so having to manually rename the file every time it's generated breaks that paradigm.
† I already know this version is way too old. It's not going to change in the short term.