Reputation: 3935
I'm looking for information on how to delete old webpack chunked files. Here is my current webpack configuration:
var path = require('path');
var webpack = require('webpack');
module.exports = {
debug: false,
outputPathinfo: true,
displayErrorDetails: true,
context: __dirname,
entry: {
common: ['./src/common.coffee'],
a: './src/a.cjsx',
b: './src/b.cjsx'
},
output: {
filename: '[name]-[chunkhash].js',
chunkFileName: '[name].[chunkhash].js',
path: path.join(__dirname, 'js')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('common', 'common-[chunkhash].js'),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false }
})
],
module: {
preLoaders: [
{
test: /\.coffee$/,
exclude: /node_modules/,
loader: 'coffeelint-loader'
}
],
loaders: [
{ test: /\.coffee/, loader: 'coffee' },
{ test: /\.cjsx$/, loaders: ['coffee', 'cjsx'] },
{ test: /\.js$/, loader: 'jsx-loader?harmony' }
]
}
}
If I am running $(npm bin)/webpack --config webpack.js --watch
and make changes to a.cjsx
, it compiles a newer version of that file with a new chunkedhash. However, the old one remains and I'd like it to be deleted right away.
Upvotes: 32
Views: 30013
Reputation: 51
clean-webpack-plugin is evil. I strongly do not recommend using it
I remove all unused chunks after emit using this script.
const exec = require('child_process').exec;
mix.webpackConfig({
devtool: 'inline-source-map',
output: {
chunkFilename: 'static/ui/js/chunks/[hash][name].js',
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
chunks: 'async',
idHint: 'vendor'
}
}
}
},
plugins: [
{ // Removing unused chunks after emit
apply: (compiler) => {
// Called after emitting assets to output directory
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
const CHUNKS_FOLDER = 'static/ui/js/chunks/';
// List of used chunks
const chunks = compilation.chunks.map(
chunk => chunk.files[0]
).filter(
chunk => chunk.startsWith(CHUNKS_FOLDER)
).map(
chunk => chunk.slice(CHUNKS_FOLDER.length)
)
// Substring to exclude used chunks
const chunksExcludingSubstr = chunks.map(
chunk => `-not -name ${chunk}`
).join(' ')
exec(
`find ${CHUNKS_FOLDER} -type f ${chunksExcludingSubstr} -delete`,
(err, stdout, stderr) =>
{
if (stderr) process.stderr.write(stderr);
}
);
});
}
}
]
})
Upvotes: 0
Reputation: 61
my case: webpack 5 + multipage application + themes.css via entry points
solution: https://github.com/webdiscus/webpack-remove-empty-scripts
this plugins don't work with webpack 5 entry points or with MiniCssExtractPlugin:
webpack-fix-style-only-entries, webpack-extraneous-file-cleanup-plugin, webpack-remove-empty-js-chunks-plugin, webpack-delete-no-js-entries-plugin.
my webpack.config.js
:
const fs = require('fs');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
const isProd = process.env.NODE_ENV === 'production';
const isDev = !isProd;
const PAGES = ['app', 'help'];
const getThemes = (themePath, alias) => {
let themes = {};
const longPath = './' + alias + '/' + themePath;
fs.readdirSync(longPath).forEach(function(fileName) {
const fileNameWithPath = path.join(themePath, fileName);
const fileNameWithLongPath = path.join(longPath, fileName);
const stat = fs.lstatSync(fileNameWithLongPath);
if (stat.isDirectory()) return;
if (!/\.scss$/.test(fileName)) return;
const nameWithoutExt = path.basename(fileName, '.scss');
themes[nameWithoutExt] = ['./' + fileNameWithPath];
});
console.log(themes);
return themes;
};
const themes = getThemes('scss/themes', 'src');
const getFilename = (filename, ext) => {
let name = filename == 'index' ? 'bundle' : filename;
const isTheme = (ext == 'css' && name.startsWith('theme')) ? true : false;
const needHash = (isDev || isTheme) ? false : true;
return needHash ? name +`.[fullhash].` + ext : name+'.'+ext;
};
const getCSSDirname = filename => {
const isTheme = filename.startsWith('theme');
return !isTheme? '/css/' : '/css/theme/';
};
const getHTMLWebpackPlugins = arr => {
// this function config multipages names and add to html-pages
// inside <head> tag our themes via tag <link rel="stylesheet" href="....css" ...>
// and return array of HTMLWebpackPlugins
};
module.exports = {
// ... //
entry: {
// mutipage:
app: ['./index.js', './scss/app.scss'],
help: ['./help.js', './scss/help.scss'],
// multitheme:
...themes,
},
optimization: {
removeEmptyChunks: true, // not work!!!
},
// ... //
plugins: [
// ... //
...getHTMLWebpackPlugins(PAGES),
new RemoveEmptyScriptsPlugin({
ignore: PAGES,
enabled: isDev === false,
}),
new MiniCssExtractPlugin({
filename: pathdata => {
return getCSSDirname(pathdata.chunk.name) + getFilename(pathdata.chunk.name, 'css');
},
chunkFilename: isDev ? '[id].css' : '[id].[contenthash].css',
}),
],
};
my src files:
[src]:
- index.js
- index.html
- help.js
- help.html
- [scss]:
- - app.scss
- - help.scss
- - [themes]:
- - - light.scss
- - - dark.scss
- - - blue.scss
after build:
[dist]:
- app.js
- index.html
- help$hash.js
- help$hash.html
- [css]:
- - app$hash.css
- - help$hash.css
- - [themes]:
- - - light.css
- - - dark.css
- - - blue.css
Upvotes: 1
Reputation: 4303
Looks like [email protected]+ has built-in support for this https://webpack.js.org/configuration/output/#outputclean. I use [chunkhash]
in my chunk filenames and they get cleared out if I stop comment out dynamic imports and added back in if I uncomment them.
Upvotes: 1
Reputation: 109
For Windows users
"scripts": {
"build": "npm run clean && webpack --mode production",
"clean": "del /f /s /q dist 1>nul"
}
Upvotes: 0
Reputation: 994
I've decided to write an answer because others - although trying to answer the question directly - overlooked the most important part in my opinion.
And the most important part is: you shouldn't be doing it this way. Using [hash]
placeholders in your development setup cause many headaches with other tooling (phpstorm's path autocomplete in symfony plugin for example). Also it's poor for webpack's incremental compilation performance and thus is not recommended by official webpack docs (reference).
So for future readers: just keep it simple for development config - define your filename
as [name].js
and move on.
There seems to be a confusion about what to do with the old chunk-files on the production server. Well, you don't do anything. Once a version is deployed it shouldn't be ever changed. You just keep creating new versions when deploying and keep previous as a backup. Why?
Because you want you're rollback to be reliable and for it to be possible your rollback needs to be extremely simple and atomic. If your rollback procedure is doing anything more than switching a symlink, rerouting to previous container (or similar simple operation) you're probably™ going to end up in trouble.
Rollback isn't a process of "re-deploying" the application again, but now to the previous version. It's a process of "un-doing" the deployment. So doing a git checkout
to the previous version followed by a npm build --but-please-be-hurry --and-im-begging-you-dont-fail
while your production app is hanging there, completely exploded doesn't cut here.
Rebuilding a previous version of the application - just like the deployment - may fail for many reasons. That's why a rollback should be switching/rerouting back to the exact same version-build that is proven to be working. Not ==
-the-same, 100% ===
-the-same. That's why you need to keep your previous version around, because that's the ===
-same. A "regenerated" one is - in best case scenario - only ==
-the-same, and so it is not proven to be working, only assumed.
And no, no amount of CI, staging environments or whatever will give you a guaranteed successful deployment. Part of doing it the right way is to be prepared for when things go wrong. And things will go wrong. Hopefully only from time to time, but still.
Of course once you have 3, 5 or <put-your-number-here>
versions backed up you may start to remove the oldest ones as you probably won't ever need more than 3.
Upvotes: 7
Reputation: 1015
You can solve the problem № 1 by using remove-files-webpack-plugin.
Use this plugin like this:
plugins: [
new RemovePlugin({
watch: {
test: [
{
folder: './js',
method: (absPath) => new RegExp(/(.*)-([^-\\\/]+)\.js/).test(absPath)
}
]
}
})
]
In "watch" mode (not normal compilation!) it grabs all files from ./js
folder and tests them with this regular expression /(.*)-([^-\\\/]+)\.js/
. Analyze this regular expression on regex101 (unit tests are included) if you have problems with understanding.
Note: i'm the creator of this plugin.
Upvotes: 1
Reputation: 11
I have solved that problem by adding below in webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
{
... your configs ...
plugins: [new CleanWebpackPlugin()]
}
Upvotes: 1
Reputation: 10237
Here is the webpack-clean-obsolete-chunks
plugin, which do what you want. It searches for all updated chunks and deletes obsolete files after each webpack
compilation.
Upvotes: 15
Reputation: 292
Take a look at this pull request: https://github.com/johnagan/clean-webpack-plugin/pull/32/files
Open raw file view and copy it to index.js of clean webpack plugin. Remember about config flag -> watch: true
Upvotes: 2
Reputation: 957
There is a clean-webpack-plugin for those purposes, or you can write a simple bash
script for npm
:
"scripts": {
"build": "rm -r dist/* && webpack -p",
"clean": "rm -r dist/*"
}
Upvotes: 23