Skip to content

Commit 0e33308

Browse files
committed
feature #137 Add Encore.configureFilenames() method to the public API (Lyrkan)
This PR was merged into the master branch. Discussion ---------- Add Encore.configureFilenames() method to the public API This PR adds the `Encore.configureFilenames()` method to the public API (fixes #125). **Example:** ```js Encore.configureFilenames({ js: '[name].[chunkhash].js', css: '[name].[contenthash].css', images: 'images/[name].[hash:8].[ext]', fonts: 'fonts/[name].[hash:8].[ext]' }); ``` If one key is defined in the first parameter it will be used for the filenames of the related category. If it isn't the behavior will stay the same as before. For now I kept the old naming strategies as the default values. We could eventually change that following the discussion in #136. Commits ------- 0c9a8fe Add Encore.configureFilenames() method to the public API
2 parents c0b4632 + 0c9a8fe commit 0e33308

File tree

7 files changed

+173
-4
lines changed

7 files changed

+173
-4
lines changed

index.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,34 @@ const publicApi = {
471471
return this;
472472
},
473473

474+
/**
475+
* Call this to change how the name of each output
476+
* file is generated.
477+
*
478+
* Encore.configureFilenames({
479+
* js: '[name].[chunkhash].js',
480+
* css: '[name].[contenthash].css',
481+
* images: 'images/[name].[hash:8].[ext]',
482+
* fonts: 'fonts/[name].[hash:8].[ext]'
483+
* });
484+
*
485+
* Only the filenames defined in the first parameter
486+
* of this method will be modified.
487+
*
488+
* If you are using Encore.enableVersioning()
489+
* make sure that your "js" filenames contain
490+
* "[chunkhash]" and your "css" filenames contain
491+
* "[contenthash]".
492+
*
493+
* @param {object} filenames
494+
* @returns {exports}
495+
*/
496+
configureFilenames(filenames) {
497+
webpackConfig.configureFilenames(filenames);
498+
499+
return this;
500+
},
501+
474502
/**
475503
* If enabled, the output directory is emptied between
476504
* each build (to remove old files).

lib/WebpackConfig.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class WebpackConfig {
6262
this.forkedTypeScriptTypesCheckOptionsCallback = () => {};
6363
this.useImagesLoader = true;
6464
this.useFontsLoader = true;
65+
this.configuredFilenames = {};
6566
}
6667

6768
getContext() {
@@ -285,6 +286,22 @@ class WebpackConfig {
285286
this.useFontsLoader = false;
286287
}
287288

289+
configureFilenames(configuredFilenames = {}) {
290+
if (typeof configuredFilenames !== 'object') {
291+
throw new Error('Argument 1 to configureFilenames() must be an object.');
292+
}
293+
294+
// Check allowed keys
295+
const validKeys = ['js', 'css', 'images', 'fonts'];
296+
for (const key of Object.keys(configuredFilenames)) {
297+
if (validKeys.indexOf(key) === -1) {
298+
throw new Error(`"${key}" is not a valid key for configureFilenames(). Valid keys: ${validKeys.join(', ')}.`);
299+
}
300+
}
301+
302+
this.configuredFilenames = configuredFilenames;
303+
}
304+
288305
cleanupOutputBeforeBuild() {
289306
this.cleanupOutput = true;
290307
}

lib/config-generator.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,15 @@ class ConfigGenerator {
101101
}
102102

103103
buildOutputConfig() {
104+
// Default filename can be overriden using Encore.configureFilenames({ js: '...' })
105+
let filename = this.webpackConfig.useVersioning ? '[name].[chunkhash].js' : '[name].js';
106+
if (this.webpackConfig.configuredFilenames.js) {
107+
filename = this.webpackConfig.configuredFilenames.js;
108+
}
109+
104110
return {
105111
path: this.webpackConfig.outputPath,
106-
filename: this.webpackConfig.useVersioning ? '[name].[chunkhash].js' : '[name].js',
112+
filename: filename,
107113
// will use the CDN path (if one is available) so that split
108114
// chunks load internally through the CDN.
109115
publicPath: this.webpackConfig.getRealPublicPath(),
@@ -126,22 +132,34 @@ class ConfigGenerator {
126132
];
127133

128134
if (this.webpackConfig.useImagesLoader) {
135+
// Default filename can be overriden using Encore.configureFilenames({ images: '...' })
136+
let filename = 'images/[name].[hash:8].[ext]';
137+
if (this.webpackConfig.configuredFilenames.images) {
138+
filename = this.webpackConfig.configuredFilenames.images;
139+
}
140+
129141
rules.push({
130142
test: /\.(png|jpg|jpeg|gif|ico|svg)$/,
131143
loader: 'file-loader',
132144
options: {
133-
name: 'images/[name].[hash:8].[ext]',
145+
name: filename,
134146
publicPath: this.webpackConfig.getRealPublicPath()
135147
}
136148
});
137149
}
138150

139151
if (this.webpackConfig.useFontsLoader) {
152+
// Default filename can be overriden using Encore.configureFilenames({ fonts: '...' })
153+
let filename = 'fonts/[name].[hash:8].[ext]';
154+
if (this.webpackConfig.configuredFilenames.fonts) {
155+
filename = this.webpackConfig.configuredFilenames.fonts;
156+
}
157+
140158
rules.push({
141159
test: /\.(woff|woff2|ttf|eot|otf)$/,
142160
loader: 'file-loader',
143161
options: {
144-
name: 'fonts/[name].[hash:8].[ext]',
162+
name: filename,
145163
publicPath: this.webpackConfig.getRealPublicPath()
146164
}
147165
});

lib/plugins/extract-text.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,15 @@ module.exports = function(plugins, webpackConfig, extractTextOptions = {}) {
3030
* link tag for an entry point's CSS (unless no CSS file
3131
* was imported - in which case no CSS file will be dumped).
3232
*/
33+
34+
// Default filename can be overriden using Encore.configureFilenames({ css: '...' })
35+
let filename = webpackConfig.useVersioning ? '[name].[contenthash].css' : '[name].css';
36+
if (webpackConfig.configuredFilenames.css) {
37+
filename = webpackConfig.configuredFilenames.css;
38+
}
39+
3340
let config = Object.assign({}, extractTextOptions, {
34-
filename: webpackConfig.useVersioning ? '[name].[contenthash].css' : '[name].css',
41+
filename: filename,
3542
// if true, async CSS (e.g. loaded via require.ensure())
3643
// is extracted to the entry point CSS. If false, it's
3744
// inlined in the AJAX-loaded .js file.

test/WebpackConfig.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,4 +460,41 @@ describe('WebpackConfig object', () => {
460460
expect(config.useFontsLoader).to.be.false;
461461
});
462462
});
463+
464+
describe('configureFilenames', () => {
465+
it('Calling method sets it', () => {
466+
const config = createConfig();
467+
config.configureFilenames({
468+
js: '[name].[chunkhash].js',
469+
css: '[name].[contenthash].css',
470+
images: 'images/[name].[hash:8].[ext]',
471+
fonts: 'fonts/[name].[hash:8].[ext]'
472+
});
473+
474+
expect(config.configuredFilenames).to.deep.equals({
475+
js: '[name].[chunkhash].js',
476+
css: '[name].[contenthash].css',
477+
images: 'images/[name].[hash:8].[ext]',
478+
fonts: 'fonts/[name].[hash:8].[ext]'
479+
});
480+
});
481+
482+
it('Calling with non-object throws an error', () => {
483+
const config = createConfig();
484+
485+
expect(() => {
486+
config.configureFilenames('FOO');
487+
}).to.throw('must be an object');
488+
});
489+
490+
it('Calling with an unknown key throws an error', () => {
491+
const config = createConfig();
492+
493+
expect(() => {
494+
config.configureFilenames({
495+
foo: 'bar'
496+
});
497+
}).to.throw('"foo" is not a valid key');
498+
});
499+
});
463500
});

test/config-generator.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,4 +476,57 @@ describe('The config-generator function', () => {
476476
}).to.throw();
477477
});
478478
});
479+
480+
describe('Test filenames changes', () => {
481+
it('without versioning', () => {
482+
const config = createConfig();
483+
config.outputPath = '/tmp/public-path';
484+
config.publicPath = '/public-path';
485+
config.addEntry('main', './main');
486+
config.configureFilenames({
487+
js: '[name].foo.js',
488+
css: '[name].foo.css',
489+
images: '[name].foo.[ext]',
490+
fonts: '[name].bar.[ext]'
491+
});
492+
493+
const actualConfig = configGenerator(config);
494+
expect(actualConfig.output.filename).to.equal('[name].foo.js');
495+
496+
const extractTextPlugin = findPlugin(ExtractTextPlugin, actualConfig.plugins);
497+
expect(extractTextPlugin.filename).to.equal('[name].foo.css');
498+
499+
const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg)$/, actualConfig.module.rules);
500+
expect(imagesRule.options.name).to.equal('[name].foo.[ext]');
501+
502+
const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules);
503+
expect(fontsRule.options.name).to.equal('[name].bar.[ext]');
504+
});
505+
506+
it('with versioning', () => {
507+
const config = createConfig();
508+
config.outputPath = '/tmp/public-path';
509+
config.publicPath = '/public-path';
510+
config.addEntry('main', './main');
511+
config.enableVersioning();
512+
config.configureFilenames({
513+
js: '[name].foo.js',
514+
css: '[name].foo.css',
515+
images: '[name].foo.[ext]',
516+
fonts: '[name].bar.[ext]'
517+
});
518+
519+
const actualConfig = configGenerator(config);
520+
expect(actualConfig.output.filename).to.equal('[name].foo.js');
521+
522+
const extractTextPlugin = findPlugin(ExtractTextPlugin, actualConfig.plugins);
523+
expect(extractTextPlugin.filename).to.equal('[name].foo.css');
524+
525+
const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg)$/, actualConfig.module.rules);
526+
expect(imagesRule.options.name).to.equal('[name].foo.[ext]');
527+
528+
const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules);
529+
expect(fontsRule.options.name).to.equal('[name].bar.[ext]');
530+
});
531+
});
479532
});

test/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,15 @@ describe('Public API', () => {
224224

225225
});
226226

227+
describe('configureFilenames', () => {
228+
229+
it('must return the API object', () => {
230+
const returnedValue = api.configureFilenames({});
231+
expect(returnedValue).to.equal(api);
232+
});
233+
234+
});
235+
227236
describe('cleanupOutputBeforeBuild', () => {
228237

229238
it('must return the API object', () => {

0 commit comments

Comments
 (0)