diff --git a/README.md b/README.md index 1803f42c..25036d6f 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,23 @@ module.exports = { } ``` + +#### Filename as function instead of string +You might also like more finely grained control over filename output. +Particularly useful when dealing with multiple entry points and wanting to get more control out of the filename for a given entry point/chunk. + +```javascript + const miniCssExtractPlugin = new MiniCssExtractPlugin({ + filename(chunk) { + if (chunk.indexOf('admin') > -1 || chunk.indexOf('client') > -1) { + return 'app.css'; + } + + return chunk + '.css'; + } + }); +``` + #### Extracting CSS based on entry You may also extract the CSS based on the webpack entry name. This is especially useful if you import routes dynamically @@ -295,7 +312,7 @@ For long term caching use `filename: "[contenthash].css"`. Optionally add `[name [deps]: https://david-dm.org/webpack-contrib/mini-css-extract-plugin.svg [deps-url]: https://david-dm.org/webpack-contrib/mini-css-extract-plugin -[tests]: https://img.shields.io/circleci/project/github/webpack-contrib/mini-css-extract-plugin.svg +[tests]: https://img.shields.io/circleci/project/github/webpack-contrib/mini-css-extract-plugin.svg [tests-url]: https://circleci.com/gh/webpack-contrib/mini-css-extract-plugin [cover]: https://codecov.io/gh/webpack-contrib/mini-css-extract-plugin/branch/master/graph/badge.svg diff --git a/src/index.js b/src/index.js index 6aa5436f..1f5db24d 100644 --- a/src/index.js +++ b/src/index.js @@ -170,7 +170,7 @@ class MiniCssExtractPlugin { renderedModules, compilation.runtimeTemplate.requestShortener ), - filenameTemplate: this.options.filename, + filenameTemplate: this.getFilename(chunk, this.options.filename), pathOptions: { chunk, contentHashType: NS, @@ -194,7 +194,10 @@ class MiniCssExtractPlugin { renderedModules, compilation.runtimeTemplate.requestShortener ), - filenameTemplate: this.options.chunkFilename, + filenameTemplate: this.getFilename( + chunk, + this.options.chunkFilename + ), pathOptions: { chunk, contentHashType: NS, @@ -259,56 +262,65 @@ class MiniCssExtractPlugin { const chunkMap = this.getCssChunkObject(chunk); if (Object.keys(chunkMap).length > 0) { const chunkMaps = chunk.getChunkMaps(); - const linkHrefPath = mainTemplate.getAssetPath( - JSON.stringify(this.options.chunkFilename), - { - hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, - hashWithLength: (length) => - `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`, - chunk: { - id: '" + chunkId + "', - hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, - hashWithLength(length) { - const shortChunkHashMap = Object.create(null); - for (const chunkId of Object.keys(chunkMaps.hash)) { - if (typeof chunkMaps.hash[chunkId] === 'string') { - shortChunkHashMap[chunkId] = chunkMaps.hash[ + let chunkFilename; + try { + chunkFilename = JSON.stringify( + this.getFilename(chunk, this.options.chunkFilename) + ); + } catch (err) { + throw new Error( + `Mini CSS Extract Plugin\n\nCouldn't stringify JSON for filename {Function}: ${err}\n` + ); + } + + const linkHrefPath = mainTemplate.getAssetPath(chunkFilename, { + hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, + hashWithLength: (length) => `" + + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`, + chunk: { + id: '" + chunkId + "', + hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, + hashWithLength(length) { + const shortChunkHashMap = Object.create(null); + for (const chunkId of Object.keys(chunkMaps.hash)) { + if (typeof chunkMaps.hash[chunkId] === 'string') { + shortChunkHashMap[chunkId] = chunkMaps.hash[ + chunkId + ].substring(0, length); + } + } + return `" + ${JSON.stringify( + shortChunkHashMap + )}[chunkId] + "`; + }, + contentHash: { + [NS]: `" + ${JSON.stringify( + chunkMaps.contentHash[NS] + )}[chunkId] + "`, + }, + contentHashWithLength: { + [NS]: (length) => { + const shortContentHashMap = {}; + const contentHash = chunkMaps.contentHash[NS]; + for (const chunkId of Object.keys(contentHash)) { + if (typeof contentHash[chunkId] === 'string') { + shortContentHashMap[chunkId] = contentHash[ chunkId ].substring(0, length); } } return `" + ${JSON.stringify( - shortChunkHashMap + shortContentHashMap )}[chunkId] + "`; }, - contentHash: { - [NS]: `" + ${JSON.stringify( - chunkMaps.contentHash[NS] - )}[chunkId] + "`, - }, - contentHashWithLength: { - [NS]: (length) => { - const shortContentHashMap = {}; - const contentHash = chunkMaps.contentHash[NS]; - for (const chunkId of Object.keys(contentHash)) { - if (typeof contentHash[chunkId] === 'string') { - shortContentHashMap[chunkId] = contentHash[ - chunkId - ].substring(0, length); - } - } - return `" + ${JSON.stringify( - shortContentHashMap - )}[chunkId] + "`; - }, - }, - name: `" + (${JSON.stringify( - chunkMaps.name - )}[chunkId]||chunkId) + "`, }, - contentHashType: NS, - } - ); + name: `" + (${JSON.stringify( + chunkMaps.name + )}[chunkId]||chunkId) + "`, + }, + contentHashType: NS, + }); + return Template.asString([ source, '', @@ -366,6 +378,10 @@ class MiniCssExtractPlugin { }); } + getFilename(chunk, filename) { + return typeof filename === 'function' ? filename(chunk) : filename; + } + getCssChunkObject(mainChunk) { const obj = {}; for (const chunk of mainChunk.getAllAsyncChunks()) { diff --git a/test/cases/filename-function/app.js b/test/cases/filename-function/app.js new file mode 100644 index 00000000..aa3357bf --- /dev/null +++ b/test/cases/filename-function/app.js @@ -0,0 +1 @@ +import './style.css'; diff --git a/test/cases/filename-function/expected/index.css b/test/cases/filename-function/expected/index.css new file mode 100644 index 00000000..aea53e43 --- /dev/null +++ b/test/cases/filename-function/expected/index.css @@ -0,0 +1,2 @@ +body { background: red; } + diff --git a/test/cases/filename-function/expected/this.is.app.css b/test/cases/filename-function/expected/this.is.app.css new file mode 100644 index 00000000..aea53e43 --- /dev/null +++ b/test/cases/filename-function/expected/this.is.app.css @@ -0,0 +1,2 @@ +body { background: red; } + diff --git a/test/cases/filename-function/index.js b/test/cases/filename-function/index.js new file mode 100644 index 00000000..aa3357bf --- /dev/null +++ b/test/cases/filename-function/index.js @@ -0,0 +1 @@ +import './style.css'; diff --git a/test/cases/filename-function/style.css b/test/cases/filename-function/style.css new file mode 100644 index 00000000..31fc5b8a --- /dev/null +++ b/test/cases/filename-function/style.css @@ -0,0 +1 @@ +body { background: red; } diff --git a/test/cases/filename-function/webpack.config.js b/test/cases/filename-function/webpack.config.js new file mode 100644 index 00000000..6f0c267f --- /dev/null +++ b/test/cases/filename-function/webpack.config.js @@ -0,0 +1,25 @@ +const Self = require('../../../'); + +module.exports = { + entry: { + index: './index.js', + app: './app.js', + }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + Self.loader, + 'css-loader', + ], + }, + ], + }, + plugins: [ + new Self({ + filename: (chunk) => chunk.name == 'app' ? 'this.is.app.css' : `[name].css`, + chunkFilename: (chunk) => '[name].css', + }), + ], +};