I have extended default web pack config in Ionic v3 for forcing cache busting.
I am able to fingerprint generated JavaScript artifacts, but I am unable to fingerprint images and JSON files under the assets folder. I took Help from Bundled files and cache-busting
.
An excerpt of webpack config.js
module.exports = { // ... output: { filename: '[name].[chunkhash].js', chunkFilename: '[name].[chunkhash].js', }, plugins: [ new WebpackChunkHash({algorithm: 'md5'}) // 'md5' is default value ] }
The above is the approach for fingerprinting JavaScript bundles, and it’s working fine. I want to add hashes/fingerprint images and JSON files inside the assets folder. I used the same approach for images also, but it did not work.
I have extended webpack config.js and added a new rule for images. By default webpack directly copies the images and assets to the output folder.
Copy Config.js
module.exports = { copyAssets: { src: ['{{SRC}}/assets/**/*'], dest: '{{WWW}}/assets' }, copyIndexContent: { src: ['{{SRC}}/index.html', '{{SRC}}/manifest.json', '{{SRC}}/service-worker.js'], dest: '{{WWW}}' }, copyFonts: { src: ['{{ROOT}}/node_modules/ionicons/dist/fonts/**/*', '{{ROOT}}/node_modules/ionic-angular/fonts/**/*'], dest: '{{WWW}}/assets/fonts' },
Here images and other assets are directly copied. I have added a new rule in extended webpack.config.js, but the build process is ignoring it. How do I fix this issue?
Excerpt of webpack config.js
{ test: /.(png|jpg|gif)$/, loader: 'file-loader', options: { name:'[name].[hash].[ext]',//adding hash for cache busting outputPath:'assets/imgs', publicPath:'assets/imgs' },
entire Webpack.config.js
/* * The webpack config exports an object that has a valid webpack configuration * For each environment name. By default, there are two Ionic environments: * "dev" and "prod". As such, the webpack.config.js exports a dictionary object * with "keys" for "dev" and "prod", where the value is a valid webpack configuration * For details on configuring webpack, see their documentation here * https://webpack.js.org/configuration/ */ var path = require('path'); var webpack = require('webpack'); var ionicWebpackFactory = require(process.env.IONIC_WEBPACK_FACTORY); var ModuleConcatPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin'); var PurifyPlugin = require('@angular-devkit/build-optimizer').PurifyPlugin; var optimizedProdLoaders = [ { test: /.json$/, loader: 'json-loader' }, { test: /.js$/, loader: [ { loader: process.env.IONIC_CACHE_LOADER }, { loader: '@angular-devkit/build-optimizer/webpack-loader', options: { sourceMap: true } }, ] }, { test: /.ts$/, loader: [ { loader: process.env.IONIC_CACHE_LOADER }, { loader: '@angular-devkit/build-optimizer/webpack-loader', options: { sourceMap: true } }, { test: /.(png|jpg|gif)$/, loader: 'file-loader', options: { name:'[name].[hash].[ext]', outputPath:'assets/imgs', publicPath:'assets/imgs' }, }, { loader: process.env.IONIC_WEBPACK_LOADER } ] } ]; function getProdLoaders() { if (process.env.IONIC_OPTIMIZE_JS === 'true') { return optimizedProdLoaders; } return devConfig.module.loaders; } var devConfig = { entry: process.env.IONIC_APP_ENTRY_POINT, output: { path: '{{BUILD}}', publicPath: 'build/', filename: '[name].js', devtoolModuleFilenameTemplate: ionicWebpackFactory.getSourceMapperFunction(), }, devtool: process.env.IONIC_SOURCE_MAP_TYPE, resolve: { extensions: ['.ts', '.js', '.json'], modules: [path.resolve('node_modules')] }, module: { loaders: [ { test: /.json$/, loader: 'json-loader' }, { test: /.ts$/, loader: process.env.IONIC_WEBPACK_LOADER }, { test: /.(jpg|png)$/, use: { loader: "file-loader", options: { name: "[name].[hash].[ext]", outputPath:'assets/imgs', publicPath:'assets/imgs' }, }}, ] }, plugins: [ ionicWebpackFactory.getIonicEnvironmentPlugin(), ionicWebpackFactory.getCommonChunksPlugin() ], // Some libraries import Node.js modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. node: { fs: 'empty', net: 'empty', tls: 'empty' } }; var prodConfig = { entry: process.env.IONIC_APP_ENTRY_POINT, output: { path: '{{BUILD}}', publicPath: 'build/', filename: '[name].js', devtoolModuleFilenameTemplate: ionicWebpackFactory.getSourceMapperFunction(), }, devtool: process.env.IONIC_SOURCE_MAP_TYPE, resolve: { extensions: ['.ts', '.js', '.json'], modules: [path.resolve('node_modules')] }, module: { loaders: getProdLoaders() }, plugins: [ ionicWebpackFactory.getIonicEnvironmentPlugin(), ionicWebpackFactory.getCommonChunksPlugin(), new ModuleConcatPlugin(), new PurifyPlugin() ], // Some libraries import Node.js modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. node: { fs: 'empty', net: 'empty', tls: 'empty' } }; module.exports = { dev: devConfig, prod: prodConfig }
Advertisement
Answer
Eventually, I used gulp for fingerprinting static assets.
- Drop Angular Output hashing and build the application.
ng build --prod --aot --output-hashing none
. - post-build execute a gulp script which would fingerprint all the assets and update the references.
npm i gulp gulp-rev gulp-rev-delete-original gulp-rev-collector
gulpfile.js
const gulp = require('gulp'); const rev = require('gulp-rev'); const revdel = require('gulp-rev-delete-original'); const collect = require('gulp-rev-collector'); // finger priniting static assets gulp.task('revision:fingerprint', () => { return gulp .src([ 'dist/welcome/**/*.css', 'dist/welcome/**/*.js', 'dist/welcome/**/*.{jpg,png,jpeg,gif,svg,json,xml,ico,eot,ttf,woff,woff2}' ]) .pipe(rev()) .pipe(revdel()) .pipe(gulp.dest('dist/welcome')) .pipe(rev.manifest({ path: 'manifest-hash.json' })) .pipe(gulp.dest('dist')); }); gulp.task('revision:update-fingerprinted-references', () => { return gulp .src(['dist/manifest-hash.json', 'dist/**/*.{html,json,css,js}']) .pipe(collect()) .pipe(gulp.dest('dist')); }); gulp.task( 'revision', gulp.series( 'revision:fingerprint', 'revision:update-fingerprinted-references'));
Add a new script in package.json
"gulp-revision": "gulp revision"
Execute npm run gulp-revision
Post-build.