Skip to content

Commit 0c9a8fe

Browse files
committed
Add Encore.configureFilenames() method to the public API
1 parent 4d6381b commit 0c9a8fe

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)