From 85f6eda32b32e477a582d14c512437ceae71002c Mon Sep 17 00:00:00 2001 From: Bert Hertogen Date: Mon, 14 May 2018 13:45:19 +0200 Subject: [PATCH 1/8] feat: allow filename to be a function --- src/index.js | 23 +++++++++++++++-- test/cases/filename-function/app.js | 1 + .../filename-function/expected/index.css | 2 ++ .../expected/this.is.app.css | 2 ++ test/cases/filename-function/index.js | 1 + test/cases/filename-function/style.css | 1 + .../cases/filename-function/webpack.config.js | 25 +++++++++++++++++++ 7 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 test/cases/filename-function/app.js create mode 100644 test/cases/filename-function/expected/index.css create mode 100644 test/cases/filename-function/expected/this.is.app.css create mode 100644 test/cases/filename-function/index.js create mode 100644 test/cases/filename-function/style.css create mode 100644 test/cases/filename-function/webpack.config.js diff --git a/src/index.js b/src/index.js index 6aa5436f..3d0dff8c 100644 --- a/src/index.js +++ b/src/index.js @@ -111,6 +111,11 @@ class MiniCssExtractPlugin { options ); if (!this.options.chunkFilename) { + if (this.isFunction(this.options.filename)) { + throw new Error( + `You are required to provide a value for chunkFilename when using a Function for this.options.filename, please specify the [name], [id] or [chunkhash] in this function` + ); + } const { filename } = this.options; const hasName = filename.includes('[name]'); const hasId = filename.includes('[id]'); @@ -170,7 +175,7 @@ class MiniCssExtractPlugin { renderedModules, compilation.runtimeTemplate.requestShortener ), - filenameTemplate: this.options.filename, + filenameTemplate: this.getFilename(chunk, this.options.filename), pathOptions: { chunk, contentHashType: NS, @@ -194,7 +199,10 @@ class MiniCssExtractPlugin { renderedModules, compilation.runtimeTemplate.requestShortener ), - filenameTemplate: this.options.chunkFilename, + filenameTemplate: this.getFilename( + chunk, + this.options.chunkFilename + ), pathOptions: { chunk, contentHashType: NS, @@ -366,6 +374,17 @@ class MiniCssExtractPlugin { }); } + getFilename(chunk, filename) { + return this.isFunction(filename) ? filename(chunk) : filename; + } + + isFunction(functionToCheck) { + return ( + functionToCheck && + {}.toString.call(functionToCheck) === '[object Function]' + ); + } + 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..eacf17d4 --- /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 == 'app' ? 'this.is.app.css' : '[name].css', + chunkFilename: (chunk) => '[name].css', + }), + ], +}; From 52cf7f6cf2d8d035ca9bd85d3fc999c1cde715c1 Mon Sep 17 00:00:00 2001 From: Bert Hertogen Date: Mon, 14 May 2018 14:05:55 +0200 Subject: [PATCH 2/8] feat: update tests --- src/index.js | 4 +++- test/cases/filename-function/webpack.config.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 3d0dff8c..de4a77aa 100644 --- a/src/index.js +++ b/src/index.js @@ -268,7 +268,9 @@ class MiniCssExtractPlugin { if (Object.keys(chunkMap).length > 0) { const chunkMaps = chunk.getChunkMaps(); const linkHrefPath = mainTemplate.getAssetPath( - JSON.stringify(this.options.chunkFilename), + JSON.stringify( + this.getFilename(chunk, this.options.chunkFilename) + ), { hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, hashWithLength: (length) => diff --git a/test/cases/filename-function/webpack.config.js b/test/cases/filename-function/webpack.config.js index eacf17d4..02892760 100644 --- a/test/cases/filename-function/webpack.config.js +++ b/test/cases/filename-function/webpack.config.js @@ -18,8 +18,8 @@ module.exports = { }, plugins: [ new Self({ - filename: (chunk) => chunk == 'app' ? 'this.is.app.css' : '[name].css', - chunkFilename: (chunk) => '[name].css', + filename: (data) => data.chunk.name == 'app' ? 'this.is.app.css' : `[name].css`, + chunkFilename: (data) => '[name].css', }), ], }; From cf7b731e7e96bf6a174ba95078b945ffbd370497 Mon Sep 17 00:00:00 2001 From: Bert Hertogen Date: Mon, 14 May 2018 14:09:05 +0200 Subject: [PATCH 3/8] feat: remove throw and change isFunction --- src/index.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/index.js b/src/index.js index de4a77aa..e2008c29 100644 --- a/src/index.js +++ b/src/index.js @@ -111,11 +111,6 @@ class MiniCssExtractPlugin { options ); if (!this.options.chunkFilename) { - if (this.isFunction(this.options.filename)) { - throw new Error( - `You are required to provide a value for chunkFilename when using a Function for this.options.filename, please specify the [name], [id] or [chunkhash] in this function` - ); - } const { filename } = this.options; const hasName = filename.includes('[name]'); const hasId = filename.includes('[id]'); @@ -381,10 +376,7 @@ class MiniCssExtractPlugin { } isFunction(functionToCheck) { - return ( - functionToCheck && - {}.toString.call(functionToCheck) === '[object Function]' - ); + return typeof functionToCheck === 'function'; } getCssChunkObject(mainChunk) { From a06f5df00f0b9a48000dc63a87f4d8e6fea4e07a Mon Sep 17 00:00:00 2001 From: Bert Hertogen Date: Mon, 14 May 2018 14:29:43 +0200 Subject: [PATCH 4/8] feat: pass correct chunk --- src/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index e2008c29..fa0eb8de 100644 --- a/src/index.js +++ b/src/index.js @@ -372,7 +372,9 @@ class MiniCssExtractPlugin { } getFilename(chunk, filename) { - return this.isFunction(filename) ? filename(chunk) : filename; + return this.isFunction(filename) + ? filename(chunk.chunk || chunk) + : filename; } isFunction(functionToCheck) { From 835624c0e91d0362778a2d897f3cac64ab57dbc2 Mon Sep 17 00:00:00 2001 From: Bert Hertogen Date: Mon, 14 May 2018 18:27:36 +0200 Subject: [PATCH 5/8] feat: fix test and names --- src/index.js | 6 +++--- test/cases/filename-function/webpack.config.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index fa0eb8de..24771019 100644 --- a/src/index.js +++ b/src/index.js @@ -372,9 +372,9 @@ class MiniCssExtractPlugin { } getFilename(chunk, filename) { - return this.isFunction(filename) - ? filename(chunk.chunk || chunk) - : filename; + console.log('--chunk--'); + console.log(chunk); + return this.isFunction(filename) ? filename(chunk) : filename; } isFunction(functionToCheck) { diff --git a/test/cases/filename-function/webpack.config.js b/test/cases/filename-function/webpack.config.js index 02892760..6f0c267f 100644 --- a/test/cases/filename-function/webpack.config.js +++ b/test/cases/filename-function/webpack.config.js @@ -18,8 +18,8 @@ module.exports = { }, plugins: [ new Self({ - filename: (data) => data.chunk.name == 'app' ? 'this.is.app.css' : `[name].css`, - chunkFilename: (data) => '[name].css', + filename: (chunk) => chunk.name == 'app' ? 'this.is.app.css' : `[name].css`, + chunkFilename: (chunk) => '[name].css', }), ], }; From 44cd88f4ad68b6139e21b7ec5381d0996566c7b2 Mon Sep 17 00:00:00 2001 From: Bert Hertogen Date: Fri, 25 May 2018 20:10:52 +0200 Subject: [PATCH 6/8] feat: remove console.log's Store filename in options, this way function is called once --- src/index.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index 24771019..74e14351 100644 --- a/src/index.js +++ b/src/index.js @@ -170,7 +170,11 @@ class MiniCssExtractPlugin { renderedModules, compilation.runtimeTemplate.requestShortener ), - filenameTemplate: this.getFilename(chunk, this.options.filename), + filenameTemplate: this.getFilename( + chunk, + this.options.filename, + this.options.processedFilename + ), pathOptions: { chunk, contentHashType: NS, @@ -196,7 +200,8 @@ class MiniCssExtractPlugin { ), filenameTemplate: this.getFilename( chunk, - this.options.chunkFilename + this.options.chunkFilename, + this.options.processedChunkFilename ), pathOptions: { chunk, @@ -264,7 +269,11 @@ class MiniCssExtractPlugin { const chunkMaps = chunk.getChunkMaps(); const linkHrefPath = mainTemplate.getAssetPath( JSON.stringify( - this.getFilename(chunk, this.options.chunkFilename) + this.getFilename( + chunk, + this.options.chunkFilename, + this.options.processedChunkFilename + ) ), { hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, @@ -371,10 +380,13 @@ class MiniCssExtractPlugin { }); } - getFilename(chunk, filename) { - console.log('--chunk--'); - console.log(chunk); - return this.isFunction(filename) ? filename(chunk) : filename; + getFilename(chunk, filename, processedFilename) { + if (!processedFilename) { + processedFilename = this.isFunction(filename) + ? filename(chunk) + : filename; + } + return processedFilename; } isFunction(functionToCheck) { From 5b45d7e29394e835271877f1d67dc92bf061219e Mon Sep 17 00:00:00 2001 From: Tom Fulcher Date: Fri, 3 Aug 2018 16:40:59 +1000 Subject: [PATCH 7/8] Filename as a function - Added to README.md, Clean up as per feedback, Adding try catch err --- README.md | 17 +++++++ src/index.js | 125 ++++++++++++++++++++++++--------------------------- 2 files changed, 76 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 1803f42c..2e9ae8fe 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 diff --git a/src/index.js b/src/index.js index 74e14351..1acba404 100644 --- a/src/index.js +++ b/src/index.js @@ -170,11 +170,7 @@ class MiniCssExtractPlugin { renderedModules, compilation.runtimeTemplate.requestShortener ), - filenameTemplate: this.getFilename( - chunk, - this.options.filename, - this.options.processedFilename - ), + filenameTemplate: this.getFilename(chunk, this.options.filename), pathOptions: { chunk, contentHashType: NS, @@ -200,8 +196,7 @@ class MiniCssExtractPlugin { ), filenameTemplate: this.getFilename( chunk, - this.options.chunkFilename, - this.options.processedChunkFilename + this.options.chunkFilename ), pathOptions: { chunk, @@ -267,62 +262,69 @@ class MiniCssExtractPlugin { const chunkMap = this.getCssChunkObject(chunk); if (Object.keys(chunkMap).length > 0) { const chunkMaps = chunk.getChunkMaps(); - const linkHrefPath = mainTemplate.getAssetPath( - JSON.stringify( - this.getFilename( - chunk, - this.options.chunkFilename, - this.options.processedChunkFilename - ) - ), - { - 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[ + let linkHrefPath; + try { + linkHrefPath = mainTemplate.getAssetPath( + JSON.stringify( + this.getFilename(chunk, 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[ chunkId ].substring(0, length); } } return `" + ${JSON.stringify( - shortContentHashMap + 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( + shortContentHashMap + )}[chunkId] + "`; + }, + }, + name: `" + (${JSON.stringify( + chunkMaps.name + )}[chunkId]||chunkId) + "`, }, - name: `" + (${JSON.stringify( - chunkMaps.name - )}[chunkId]||chunkId) + "`, - }, - contentHashType: NS, - } - ); + contentHashType: NS, + } + ); + } catch (err) { + throw new Error( + `Couldn't stringify JSON for filename provided as function: ${err}` + ); + } + return Template.asString([ source, '', @@ -380,17 +382,8 @@ class MiniCssExtractPlugin { }); } - getFilename(chunk, filename, processedFilename) { - if (!processedFilename) { - processedFilename = this.isFunction(filename) - ? filename(chunk) - : filename; - } - return processedFilename; - } - - isFunction(functionToCheck) { - return typeof functionToCheck === 'function'; + getFilename(chunk, filename) { + return typeof filename === 'function' ? filename(chunk) : filename; } getCssChunkObject(mainChunk) { From 6012a49623f727f093a2b3fcf4f4e07ce5ed8d0d Mon Sep 17 00:00:00 2001 From: Tom Fulcher Date: Wed, 8 Aug 2018 08:48:19 +1000 Subject: [PATCH 8/8] Corrections to try catch wrapping as suggested by @ooflorent --- README.md | 4 +- src/index.js | 108 +++++++++++++++++++++++++-------------------------- 2 files changed, 54 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 2e9ae8fe..25036d6f 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ Particularly useful when dealing with multiple entry points and wanting to get m ```javascript const miniCssExtractPlugin = new MiniCssExtractPlugin({ - filename: (chunk) => { + filename(chunk) { if (chunk.indexOf('admin') > -1 || chunk.indexOf('client') > -1) { return 'app.css'; } @@ -312,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 1acba404..1f5db24d 100644 --- a/src/index.js +++ b/src/index.js @@ -262,69 +262,65 @@ class MiniCssExtractPlugin { const chunkMap = this.getCssChunkObject(chunk); if (Object.keys(chunkMap).length > 0) { const chunkMaps = chunk.getChunkMaps(); - let linkHrefPath; + let chunkFilename; try { - linkHrefPath = mainTemplate.getAssetPath( - JSON.stringify( - this.getFilename(chunk, 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[ - 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( - shortContentHashMap - )}[chunkId] + "`; - }, - }, - name: `" + (${JSON.stringify( - chunkMaps.name - )}[chunkId]||chunkId) + "`, - }, - contentHashType: NS, - } + chunkFilename = JSON.stringify( + this.getFilename(chunk, this.options.chunkFilename) ); } catch (err) { throw new Error( - `Couldn't stringify JSON for filename provided as function: ${err}` + `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( + shortContentHashMap + )}[chunkId] + "`; + }, + }, + name: `" + (${JSON.stringify( + chunkMaps.name + )}[chunkId]||chunkId) + "`, + }, + contentHashType: NS, + }); + return Template.asString([ source, '',