From 0ba2677dd7a4016705e04452158123504b97a9f0 Mon Sep 17 00:00:00 2001 From: Lyrkan Date: Wed, 11 Apr 2018 13:52:14 +0200 Subject: [PATCH 1/4] Add Encore.configureUrlLoader() method to the public API --- fixtures/css/url-loader.css | 8 ++++++ index.js | 27 +++++++++++++++++++ lib/WebpackConfig.js | 20 ++++++++++++++ lib/config-generator.js | 40 ++++++++++++++++++++------- package.json | 1 + test/WebpackConfig.js | 33 +++++++++++++++++++++++ test/config-generator.js | 54 +++++++++++++++++++++++++++++++++++++ test/functional.js | 30 +++++++++++++++++++++ test/index.js | 9 +++++++ yarn.lock | 19 +++++++++++++ 10 files changed, 231 insertions(+), 10 deletions(-) create mode 100644 fixtures/css/url-loader.css diff --git a/fixtures/css/url-loader.css b/fixtures/css/url-loader.css new file mode 100644 index 00000000..b3941404 --- /dev/null +++ b/fixtures/css/url-loader.css @@ -0,0 +1,8 @@ +@font-face { + font-family: 'Roboto'; + src: url('./../fonts/Roboto.woff2') format('woff2'); +} + +.foo { + background: url('./../images/symfony_logo.png'); +} diff --git a/index.js b/index.js index 61b430db..e345b756 100644 --- a/index.js +++ b/index.js @@ -771,6 +771,33 @@ class Encore { return this; } + /** + * Allows to configure the URL loader. + * + * https://github.com/webpack-contrib/url-loader + * + * Encore.configureUrlLoader({ + * images: { + * limit: 8192, + * mimetype: 'image/png' + * }, + * fonts: { + * limit: 4096 + * } + * }); + * + * If a key (e.g. fonts) doesn't exists or contains a + * falsy value the file-loader will be used instead. + * + * @param {object} urlLoaderOptions + * @return {Encore} + */ + configureUrlLoader(urlLoaderOptions = {}) { + webpackConfig.configureUrlLoader(urlLoaderOptions); + + return this; + } + /** * If enabled, the output directory is emptied between each build (to remove old files). * diff --git a/lib/WebpackConfig.js b/lib/WebpackConfig.js index 68545720..6f505124 100644 --- a/lib/WebpackConfig.js +++ b/lib/WebpackConfig.js @@ -73,6 +73,10 @@ class WebpackConfig { this.preactOptions = { preactCompat: false }; + this.urlLoaderOptions = { + images: false, + fonts: false + }; // Features/Loaders options callbacks this.postCssLoaderOptionsCallback = () => {}; @@ -466,6 +470,22 @@ class WebpackConfig { this.configuredFilenames = configuredFilenames; } + configureUrlLoader(urlLoaderOptions = {}) { + if (typeof urlLoaderOptions !== 'object') { + throw new Error('Argument 1 to configureUrlLoader() must be an object.'); + } + + // Check allowed keys + const validKeys = ['images', 'fonts']; + for (const key of Object.keys(urlLoaderOptions)) { + if (validKeys.indexOf(key) === -1) { + throw new Error(`"${key}" is not a valid key for configureUrlLoader(). Valid keys: ${validKeys.join(', ')}.`); + } + } + + this.urlLoaderOptions = urlLoaderOptions; + } + cleanupOutputBeforeBuild(paths = ['**/*'], cleanWebpackPluginOptionsCallback = () => {}) { if (!Array.isArray(paths)) { throw new Error('Argument 1 to cleanupOutputBeforeBuild() must be an Array of paths - e.g. [\'**/*\']'); diff --git a/lib/config-generator.js b/lib/config-generator.js index b6820a09..c9fb509c 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -149,13 +149,23 @@ class ConfigGenerator { filename = this.webpackConfig.configuredFilenames.images; } + // The url-loader can be used instead of the default file-loader by + // calling Encore.configureUrlLoader({ images: {/* ... */}}) + let loaderName = 'file-loader'; + let loaderOptions = { + name: filename, + publicPath: this.webpackConfig.getRealPublicPath() + }; + + if (this.webpackConfig.urlLoaderOptions.images) { + loaderName = 'url-loader'; + loaderOptions = this.webpackConfig.urlLoaderOptions.images; + } + rules.push({ test: /\.(png|jpg|jpeg|gif|ico|svg|webp)$/, - loader: 'file-loader', - options: { - name: filename, - publicPath: this.webpackConfig.getRealPublicPath() - } + loader: loaderName, + options: loaderOptions }); } @@ -166,13 +176,23 @@ class ConfigGenerator { filename = this.webpackConfig.configuredFilenames.fonts; } + // The url-loader can be used instead of the default file-loader by + // calling Encore.configureUrlLoader({ fonts: {/* ... */}}) + let loaderName = 'file-loader'; + let loaderOptions = { + name: filename, + publicPath: this.webpackConfig.getRealPublicPath() + }; + + if (this.webpackConfig.urlLoaderOptions.fonts) { + loaderName = 'url-loader'; + loaderOptions = this.webpackConfig.urlLoaderOptions.fonts; + } + rules.push({ test: /\.(woff|woff2|ttf|eot|otf)$/, - loader: 'file-loader', - options: { - name: filename, - publicPath: this.webpackConfig.getRealPublicPath() - } + loader: loaderName, + options: loaderOptions }); } diff --git a/package.json b/package.json index b1b7b106..ef266c41 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "pretty-error": "^2.1.1", "resolve-url-loader": "^2.0.2", "style-loader": "^0.13.2", + "url-loader": "^1.0.1", "webpack": ">=2.2.0 <4", "webpack-chunk-hash": "^0.5.0", "webpack-dev-server": "^2.4.5", diff --git a/test/WebpackConfig.js b/test/WebpackConfig.js index 1a4200da..6d57eefb 100644 --- a/test/WebpackConfig.js +++ b/test/WebpackConfig.js @@ -838,4 +838,37 @@ describe('WebpackConfig object', () => { }).to.throw('"foo" is not a valid key'); }); }); + + describe('configureUrlLoader', () => { + it('Calling method sets it', () => { + const config = createConfig(); + config.configureUrlLoader({ + images: { limit: 8192 }, + fonts: { limit: 4096 } + }); + + expect(config.urlLoaderOptions).to.deep.equals({ + images: { limit: 8192 }, + fonts: { limit: 4096 } + }); + }); + + it('Calling with non-object throws an error', () => { + const config = createConfig(); + + expect(() => { + config.configureUrlLoader('FOO'); + }).to.throw('must be an object'); + }); + + it('Calling with an unknown key throws an error', () => { + const config = createConfig(); + + expect(() => { + config.configureUrlLoader({ + foo: 'bar' + }); + }).to.throw('"foo" is not a valid key'); + }); + }); }); diff --git a/test/config-generator.js b/test/config-generator.js index d755d528..93de0281 100644 --- a/test/config-generator.js +++ b/test/config-generator.js @@ -674,6 +674,60 @@ describe('The config-generator function', () => { }); }); + describe('configureUrlLoader() allows to use the URL loader for fonts/images', () => { + it('without configureUrlLoader()', () => { + const config = createConfig(); + config.outputPath = '/tmp/public-path'; + config.publicPath = '/public-path'; + config.addEntry('main', './main'); + + const actualConfig = configGenerator(config); + + const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg|webp)$/, actualConfig.module.rules); + expect(imagesRule.loader).to.equal('file-loader'); + + const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules); + expect(fontsRule.loader).to.equal('file-loader'); + }); + + it('with configureUrlLoader() and missing keys', () => { + const config = createConfig(); + config.outputPath = '/tmp/public-path'; + config.publicPath = '/public-path'; + config.addEntry('main', './main'); + config.configureUrlLoader({}); + + const actualConfig = configGenerator(config); + + const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg|webp)$/, actualConfig.module.rules); + expect(imagesRule.loader).to.equal('file-loader'); + + const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules); + expect(fontsRule.loader).to.equal('file-loader'); + }); + + it('with configureUrlLoader()', () => { + const config = createConfig(); + config.outputPath = '/tmp/public-path'; + config.publicPath = '/public-path'; + config.addEntry('main', './main'); + config.configureUrlLoader({ + images: { limit: 8192 }, + fonts: { limit: 4096 } + }); + + const actualConfig = configGenerator(config); + + const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg|webp)$/, actualConfig.module.rules); + expect(imagesRule.loader).to.equal('url-loader'); + expect(imagesRule.options.limit).to.equal(8192); + + const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules); + expect(fontsRule.loader).to.equal('url-loader'); + expect(fontsRule.options.limit).to.equal(4096); + }); + }); + describe('Test preact preset', () => { describe('Without preact-compat', () => { it('enablePreactPreset() does not add aliases to use preact-compat', () => { diff --git a/test/functional.js b/test/functional.js index 651e7d72..63a504c8 100644 --- a/test/functional.js +++ b/test/functional.js @@ -977,5 +977,35 @@ module.exports = { done(); }, true); }); + + it('configureUrlLoader() allows to use the URL loader for images/fonts', (done) => { + const config = createWebpackConfig('web/build', 'dev'); + config.setPublicPath('/build'); + config.addStyleEntry('url-loader', './css/url-loader.css'); + config.configureUrlLoader({ + images: { limit: 102400 }, + fonts: { limit: 102400 } + }); + + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'url-loader.css', + 'manifest.json' + ]); + + webpackAssert.assertOutputFileContains( + 'url-loader.css', + 'url(data:font/woff2;base64,' + ); + + webpackAssert.assertOutputFileContains( + 'url-loader.css', + 'url(data:image/png;base64,' + ); + + done(); + }); + }); }); }); diff --git a/test/index.js b/test/index.js index 12cbe280..aef7e23b 100644 --- a/test/index.js +++ b/test/index.js @@ -287,6 +287,15 @@ describe('Public API', () => { }); + describe('configureUrlLoader', () => { + + it('must return the API object', () => { + const returnedValue = api.configureUrlLoader({}); + expect(returnedValue).to.equal(api); + }); + + }); + describe('cleanupOutputBeforeBuild', () => { it('must return the API object', () => { diff --git a/yarn.lock b/yarn.lock index a2f6d807..5da33b57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4114,6 +4114,10 @@ mime@^1.2.11, mime@^1.3.4, mime@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" +mime@^2.0.3: + version "2.2.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.2.tgz#6b4c109d88031d7b5c23635f5b923da336d79121" + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -5670,6 +5674,13 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" +schema-utils@^0.4.3: + version "0.4.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" @@ -6506,6 +6517,14 @@ url-join@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" +url-loader@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.0.1.tgz#61bc53f1f184d7343da2728a1289ef8722ea45ee" + dependencies: + loader-utils "^1.1.0" + mime "^2.0.3" + schema-utils "^0.4.3" + url-parse@1.0.x: version "1.0.5" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" From 69e2cfe53515c69080c06245aba46c10ea4fecfe Mon Sep 17 00:00:00 2001 From: Lyrkan Date: Wed, 11 Apr 2018 17:14:45 +0200 Subject: [PATCH 2/4] Encore.configureUrlLoader() - Merge options instead of replacing them --- lib/config-generator.js | 4 ++-- test/config-generator.js | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/config-generator.js b/lib/config-generator.js index c9fb509c..8a904b05 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -159,7 +159,7 @@ class ConfigGenerator { if (this.webpackConfig.urlLoaderOptions.images) { loaderName = 'url-loader'; - loaderOptions = this.webpackConfig.urlLoaderOptions.images; + Object.assign(loaderOptions, this.webpackConfig.urlLoaderOptions.images); } rules.push({ @@ -186,7 +186,7 @@ class ConfigGenerator { if (this.webpackConfig.urlLoaderOptions.fonts) { loaderName = 'url-loader'; - loaderOptions = this.webpackConfig.urlLoaderOptions.fonts; + Object.assign(loaderOptions, this.webpackConfig.urlLoaderOptions.fonts); } rules.push({ diff --git a/test/config-generator.js b/test/config-generator.js index 93de0281..f4a819e7 100644 --- a/test/config-generator.js +++ b/test/config-generator.js @@ -711,6 +711,10 @@ describe('The config-generator function', () => { config.outputPath = '/tmp/public-path'; config.publicPath = '/public-path'; config.addEntry('main', './main'); + config.configureFilenames({ + images: '[name].foo.[ext]', + fonts: '[name].bar.[ext]' + }); config.configureUrlLoader({ images: { limit: 8192 }, fonts: { limit: 4096 } @@ -720,11 +724,13 @@ describe('The config-generator function', () => { const imagesRule = findRule(/\.(png|jpg|jpeg|gif|ico|svg|webp)$/, actualConfig.module.rules); expect(imagesRule.loader).to.equal('url-loader'); + expect(imagesRule.options.name).to.equal('[name].foo.[ext]'); expect(imagesRule.options.limit).to.equal(8192); const fontsRule = findRule(/\.(woff|woff2|ttf|eot|otf)$/, actualConfig.module.rules); expect(fontsRule.loader).to.equal('url-loader'); expect(fontsRule.options.limit).to.equal(4096); + expect(fontsRule.options.name).to.equal('[name].bar.[ext]'); }); }); From 9ca96a5a0535cfddcee6339469adedab63ad14cd Mon Sep 17 00:00:00 2001 From: Lyrkan Date: Wed, 11 Apr 2018 17:23:58 +0200 Subject: [PATCH 3/4] Encore.configureUrlLoader() - Move url-loader to dev dependencies --- lib/config-generator.js | 3 +++ lib/features.js | 5 +++++ package.json | 2 +- yarn.lock | 4 ++-- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/config-generator.js b/lib/config-generator.js index 8a904b05..2e0e9cec 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -11,6 +11,7 @@ const extractText = require('./loaders/extract-text'); const pathUtil = require('./config/path-util'); +const loaderFeatures = require('./features'); // loaders utils const cssLoaderUtil = require('./loaders/css'); const sassLoaderUtil = require('./loaders/sass'); @@ -158,6 +159,7 @@ class ConfigGenerator { }; if (this.webpackConfig.urlLoaderOptions.images) { + loaderFeatures.ensurePackagesExist('urlloader'); loaderName = 'url-loader'; Object.assign(loaderOptions, this.webpackConfig.urlLoaderOptions.images); } @@ -185,6 +187,7 @@ class ConfigGenerator { }; if (this.webpackConfig.urlLoaderOptions.fonts) { + loaderFeatures.ensurePackagesExist('urlloader'); loaderName = 'url-loader'; Object.assign(loaderOptions, this.webpackConfig.urlLoaderOptions.fonts); } diff --git a/lib/features.js b/lib/features.js index 9e8d5fce..5eb92b4c 100644 --- a/lib/features.js +++ b/lib/features.js @@ -72,6 +72,11 @@ const features = { method: 'enableBuildNotifications()', packages: ['webpack-notifier'], description: 'display build notifications' + }, + urlloader: { + method: 'configureUrlLoader()', + packages: ['url-loader'], + description: 'use the url-loader' } }; diff --git a/package.json b/package.json index ef266c41..6495ac49 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "pretty-error": "^2.1.1", "resolve-url-loader": "^2.0.2", "style-loader": "^0.13.2", - "url-loader": "^1.0.1", "webpack": ">=2.2.0 <4", "webpack-chunk-hash": "^0.5.0", "webpack-dev-server": "^2.4.5", @@ -77,6 +76,7 @@ "stylus-loader": "^3.0.1", "ts-loader": "^2.1.0", "typescript": "^2.3.4", + "url-loader": "^1.0.1", "vue": "^2.3.4", "vue-loader": "^12.2.1", "vue-template-compiler": "^2.3.4", diff --git a/yarn.lock b/yarn.lock index 5da33b57..c4a7d854 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4115,8 +4115,8 @@ mime@^1.2.11, mime@^1.3.4, mime@^1.5.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" mime@^2.0.3: - version "2.2.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.2.tgz#6b4c109d88031d7b5c23635f5b923da336d79121" + version "2.3.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" mimic-fn@^1.0.0: version "1.2.0" From 92ea9c4bc3d1b555fe28eac63026a43f14730f94 Mon Sep 17 00:00:00 2001 From: Lyrkan Date: Wed, 11 Apr 2018 17:58:37 +0200 Subject: [PATCH 4/4] Encore.configureUrlLoader() - Replace some 'let' by 'const' --- lib/config-generator.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/config-generator.js b/lib/config-generator.js index 2e0e9cec..36d8af2f 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -153,7 +153,7 @@ class ConfigGenerator { // The url-loader can be used instead of the default file-loader by // calling Encore.configureUrlLoader({ images: {/* ... */}}) let loaderName = 'file-loader'; - let loaderOptions = { + const loaderOptions = { name: filename, publicPath: this.webpackConfig.getRealPublicPath() }; @@ -181,7 +181,7 @@ class ConfigGenerator { // The url-loader can be used instead of the default file-loader by // calling Encore.configureUrlLoader({ fonts: {/* ... */}}) let loaderName = 'file-loader'; - let loaderOptions = { + const loaderOptions = { name: filename, publicPath: this.webpackConfig.getRealPublicPath() };