Skip to content

Commit 0ba2677

Browse files
committed
Add Encore.configureUrlLoader() method to the public API
1 parent ccbae35 commit 0ba2677

File tree

10 files changed

+231
-10
lines changed

10 files changed

+231
-10
lines changed

fixtures/css/url-loader.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@font-face {
2+
font-family: 'Roboto';
3+
src: url('./../fonts/Roboto.woff2') format('woff2');
4+
}
5+
6+
.foo {
7+
background: url('./../images/symfony_logo.png');
8+
}

index.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,33 @@ class Encore {
771771
return this;
772772
}
773773

774+
/**
775+
* Allows to configure the URL loader.
776+
*
777+
* https://github.com/webpack-contrib/url-loader
778+
*
779+
* Encore.configureUrlLoader({
780+
* images: {
781+
* limit: 8192,
782+
* mimetype: 'image/png'
783+
* },
784+
* fonts: {
785+
* limit: 4096
786+
* }
787+
* });
788+
*
789+
* If a key (e.g. fonts) doesn't exists or contains a
790+
* falsy value the file-loader will be used instead.
791+
*
792+
* @param {object} urlLoaderOptions
793+
* @return {Encore}
794+
*/
795+
configureUrlLoader(urlLoaderOptions = {}) {
796+
webpackConfig.configureUrlLoader(urlLoaderOptions);
797+
798+
return this;
799+
}
800+
774801
/**
775802
* If enabled, the output directory is emptied between each build (to remove old files).
776803
*

lib/WebpackConfig.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ class WebpackConfig {
7373
this.preactOptions = {
7474
preactCompat: false
7575
};
76+
this.urlLoaderOptions = {
77+
images: false,
78+
fonts: false
79+
};
7680

7781
// Features/Loaders options callbacks
7882
this.postCssLoaderOptionsCallback = () => {};
@@ -466,6 +470,22 @@ class WebpackConfig {
466470
this.configuredFilenames = configuredFilenames;
467471
}
468472

473+
configureUrlLoader(urlLoaderOptions = {}) {
474+
if (typeof urlLoaderOptions !== 'object') {
475+
throw new Error('Argument 1 to configureUrlLoader() must be an object.');
476+
}
477+
478+
// Check allowed keys
479+
const validKeys = ['images', 'fonts'];
480+
for (const key of Object.keys(urlLoaderOptions)) {
481+
if (validKeys.indexOf(key) === -1) {
482+
throw new Error(`"${key}" is not a valid key for configureUrlLoader(). Valid keys: ${validKeys.join(', ')}.`);
483+
}
484+
}
485+
486+
this.urlLoaderOptions = urlLoaderOptions;
487+
}
488+
469489
cleanupOutputBeforeBuild(paths = ['**/*'], cleanWebpackPluginOptionsCallback = () => {}) {
470490
if (!Array.isArray(paths)) {
471491
throw new Error('Argument 1 to cleanupOutputBeforeBuild() must be an Array of paths - e.g. [\'**/*\']');

lib/config-generator.js

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,23 @@ class ConfigGenerator {
149149
filename = this.webpackConfig.configuredFilenames.images;
150150
}
151151

152+
// The url-loader can be used instead of the default file-loader by
153+
// calling Encore.configureUrlLoader({ images: {/* ... */}})
154+
let loaderName = 'file-loader';
155+
let loaderOptions = {
156+
name: filename,
157+
publicPath: this.webpackConfig.getRealPublicPath()
158+
};
159+
160+
if (this.webpackConfig.urlLoaderOptions.images) {
161+
loaderName = 'url-loader';
162+
loaderOptions = this.webpackConfig.urlLoaderOptions.images;
163+
}
164+
152165
rules.push({
153166
test: /\.(png|jpg|jpeg|gif|ico|svg|webp)$/,
154-
loader: 'file-loader',
155-
options: {
156-
name: filename,
157-
publicPath: this.webpackConfig.getRealPublicPath()
158-
}
167+
loader: loaderName,
168+
options: loaderOptions
159169
});
160170
}
161171

@@ -166,13 +176,23 @@ class ConfigGenerator {
166176
filename = this.webpackConfig.configuredFilenames.fonts;
167177
}
168178

179+
// The url-loader can be used instead of the default file-loader by
180+
// calling Encore.configureUrlLoader({ fonts: {/* ... */}})
181+
let loaderName = 'file-loader';
182+
let loaderOptions = {
183+
name: filename,
184+
publicPath: this.webpackConfig.getRealPublicPath()
185+
};
186+
187+
if (this.webpackConfig.urlLoaderOptions.fonts) {
188+
loaderName = 'url-loader';
189+
loaderOptions = this.webpackConfig.urlLoaderOptions.fonts;
190+
}
191+
169192
rules.push({
170193
test: /\.(woff|woff2|ttf|eot|otf)$/,
171-
loader: 'file-loader',
172-
options: {
173-
name: filename,
174-
publicPath: this.webpackConfig.getRealPublicPath()
175-
}
194+
loader: loaderName,
195+
options: loaderOptions
176196
});
177197
}
178198

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"pretty-error": "^2.1.1",
4444
"resolve-url-loader": "^2.0.2",
4545
"style-loader": "^0.13.2",
46+
"url-loader": "^1.0.1",
4647
"webpack": ">=2.2.0 <4",
4748
"webpack-chunk-hash": "^0.5.0",
4849
"webpack-dev-server": "^2.4.5",

test/WebpackConfig.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,4 +838,37 @@ describe('WebpackConfig object', () => {
838838
}).to.throw('"foo" is not a valid key');
839839
});
840840
});
841+
842+
describe('configureUrlLoader', () => {
843+
it('Calling method sets it', () => {
844+
const config = createConfig();
845+
config.configureUrlLoader({
846+
images: { limit: 8192 },
847+
fonts: { limit: 4096 }
848+
});
849+
850+
expect(config.urlLoaderOptions).to.deep.equals({
851+
images: { limit: 8192 },
852+
fonts: { limit: 4096 }
853+
});
854+
});
855+
856+
it('Calling with non-object throws an error', () => {
857+
const config = createConfig();
858+
859+
expect(() => {
860+
config.configureUrlLoader('FOO');
861+
}).to.throw('must be an object');
862+
});
863+
864+
it('Calling with an unknown key throws an error', () => {
865+
const config = createConfig();
866+
867+
expect(() => {
868+
config.configureUrlLoader({
869+
foo: 'bar'
870+
});
871+
}).to.throw('"foo" is not a valid key');
872+
});
873+
});
841874
});

test/config-generator.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,60 @@ describe('The config-generator function', () => {
674674
});
675675
});
676676

677+
describe('configureUrlLoader() allows to use the URL loader for fonts/images', () => {
678+
it('without configureUrlLoader()', () => {
679+
const config = createConfig();
680+
config.outputPath = '/tmp/public-path';
681+
config.publicPath = '/public-path';
682+
config.addEntry('main', './main');
683+
684+
const actualConfig = configGenerator(config);
685+
686+
const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg|webp)$/, actualConfig.module.rules);
687+
expect(imagesRule.loader).to.equal('file-loader');
688+
689+
const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules);
690+
expect(fontsRule.loader).to.equal('file-loader');
691+
});
692+
693+
it('with configureUrlLoader() and missing keys', () => {
694+
const config = createConfig();
695+
config.outputPath = '/tmp/public-path';
696+
config.publicPath = '/public-path';
697+
config.addEntry('main', './main');
698+
config.configureUrlLoader({});
699+
700+
const actualConfig = configGenerator(config);
701+
702+
const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg|webp)$/, actualConfig.module.rules);
703+
expect(imagesRule.loader).to.equal('file-loader');
704+
705+
const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules);
706+
expect(fontsRule.loader).to.equal('file-loader');
707+
});
708+
709+
it('with configureUrlLoader()', () => {
710+
const config = createConfig();
711+
config.outputPath = '/tmp/public-path';
712+
config.publicPath = '/public-path';
713+
config.addEntry('main', './main');
714+
config.configureUrlLoader({
715+
images: { limit: 8192 },
716+
fonts: { limit: 4096 }
717+
});
718+
719+
const actualConfig = configGenerator(config);
720+
721+
const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg|webp)$/, actualConfig.module.rules);
722+
expect(imagesRule.loader).to.equal('url-loader');
723+
expect(imagesRule.options.limit).to.equal(8192);
724+
725+
const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules);
726+
expect(fontsRule.loader).to.equal('url-loader');
727+
expect(fontsRule.options.limit).to.equal(4096);
728+
});
729+
});
730+
677731
describe('Test preact preset', () => {
678732
describe('Without preact-compat', () => {
679733
it('enablePreactPreset() does not add aliases to use preact-compat', () => {

test/functional.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,5 +977,35 @@ module.exports = {
977977
done();
978978
}, true);
979979
});
980+
981+
it('configureUrlLoader() allows to use the URL loader for images/fonts', (done) => {
982+
const config = createWebpackConfig('web/build', 'dev');
983+
config.setPublicPath('/build');
984+
config.addStyleEntry('url-loader', './css/url-loader.css');
985+
config.configureUrlLoader({
986+
images: { limit: 102400 },
987+
fonts: { limit: 102400 }
988+
});
989+
990+
testSetup.runWebpack(config, (webpackAssert) => {
991+
expect(config.outputPath).to.be.a.directory()
992+
.with.files([
993+
'url-loader.css',
994+
'manifest.json'
995+
]);
996+
997+
webpackAssert.assertOutputFileContains(
998+
'url-loader.css',
999+
'url(data:font/woff2;base64,'
1000+
);
1001+
1002+
webpackAssert.assertOutputFileContains(
1003+
'url-loader.css',
1004+
'url(data:image/png;base64,'
1005+
);
1006+
1007+
done();
1008+
});
1009+
});
9801010
});
9811011
});

test/index.js

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

288288
});
289289

290+
describe('configureUrlLoader', () => {
291+
292+
it('must return the API object', () => {
293+
const returnedValue = api.configureUrlLoader({});
294+
expect(returnedValue).to.equal(api);
295+
});
296+
297+
});
298+
290299
describe('cleanupOutputBeforeBuild', () => {
291300

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

yarn.lock

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4114,6 +4114,10 @@ mime@^1.2.11, mime@^1.3.4, mime@^1.5.0:
41144114
version "1.6.0"
41154115
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
41164116

4117+
mime@^2.0.3:
4118+
version "2.2.2"
4119+
resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.2.tgz#6b4c109d88031d7b5c23635f5b923da336d79121"
4120+
41174121
mimic-fn@^1.0.0:
41184122
version "1.2.0"
41194123
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
@@ -5670,6 +5674,13 @@ schema-utils@^0.3.0:
56705674
dependencies:
56715675
ajv "^5.0.0"
56725676

5677+
schema-utils@^0.4.3:
5678+
version "0.4.5"
5679+
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
5680+
dependencies:
5681+
ajv "^6.1.0"
5682+
ajv-keywords "^3.1.0"
5683+
56735684
scss-tokenizer@^0.2.3:
56745685
version "0.2.3"
56755686
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
@@ -6506,6 +6517,14 @@ url-join@^1.0.0:
65066517
version "1.1.0"
65076518
resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78"
65086519

6520+
url-loader@^1.0.1:
6521+
version "1.0.1"
6522+
resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.0.1.tgz#61bc53f1f184d7343da2728a1289ef8722ea45ee"
6523+
dependencies:
6524+
loader-utils "^1.1.0"
6525+
mime "^2.0.3"
6526+
schema-utils "^0.4.3"
6527+
65096528
url-parse@1.0.x:
65106529
version "1.0.5"
65116530
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b"

0 commit comments

Comments
 (0)