Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(index): allow filename to be a {Function} (options.filename) #225

Closed
Closed
19 changes: 18 additions & 1 deletion README.md
Expand Up @@ -199,6 +199,23 @@ module.exports = {
}
```


#### Filename as function instead of string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a \n after the header

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
Expand Down Expand Up @@ -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
Expand Down
104 changes: 60 additions & 44 deletions src/index.js
Expand Up @@ -170,7 +170,7 @@ class MiniCssExtractPlugin {
renderedModules,
compilation.runtimeTemplate.requestShortener
),
filenameTemplate: this.options.filename,
filenameTemplate: this.getFilename(chunk, this.options.filename),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you can pass a function to filenameTemplate too. webpack will call it with an information object.

pathOptions: {
chunk,
contentHashType: NS,
Expand All @@ -194,7 +194,10 @@ class MiniCssExtractPlugin {
renderedModules,
compilation.runtimeTemplate.requestShortener
),
filenameTemplate: this.options.chunkFilename,
filenameTemplate: this.getFilename(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

chunk,
this.options.chunkFilename
),
pathOptions: {
chunk,
contentHashType: NS,
Expand Down Expand Up @@ -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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's incorrect. chunkFilename can't be a function. The chunkFilename generation logic need to be embedded into the runtime code.

chunk is the runtime chunk here and the referenced chunk is not known.

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) => `"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- hashWithLength: (length) => {} 
+ hashWithLength (length)  {} 

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michael-ciniawsky we need eslint rule in webpack-defaults for this 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, eslint-config-webpack ;)

+ ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you add a new line here?

Copy link
Author

@owlyowl owlyowl Aug 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry end of day and lack of coffee!

Copy link
Contributor

@ooflorent ooflorent Aug 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inside a string. You must not add one here.

Copy link
Author

@owlyowl owlyowl Aug 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry edited comment above! But was following feedback from Michael as to how he wanted the header formatted

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,
'',
Expand Down Expand Up @@ -366,6 +378,10 @@ class MiniCssExtractPlugin {
});
}

getFilename(chunk, filename) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed

return typeof filename === 'function' ? filename(chunk) : filename;
}

getCssChunkObject(mainChunk) {
const obj = {};
for (const chunk of mainChunk.getAllAsyncChunks()) {
Expand Down
1 change: 1 addition & 0 deletions test/cases/filename-function/app.js
@@ -0,0 +1 @@
import './style.css';
2 changes: 2 additions & 0 deletions test/cases/filename-function/expected/index.css
@@ -0,0 +1,2 @@
body { background: red; }

2 changes: 2 additions & 0 deletions test/cases/filename-function/expected/this.is.app.css
@@ -0,0 +1,2 @@
body { background: red; }

1 change: 1 addition & 0 deletions test/cases/filename-function/index.js
@@ -0,0 +1 @@
import './style.css';
1 change: 1 addition & 0 deletions test/cases/filename-function/style.css
@@ -0,0 +1 @@
body { background: red; }
25 changes: 25 additions & 0 deletions 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',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chunkFilename can't be a function

}),
],
};