Skip to content

Commit 7d9248f

Browse files
committed
feature #883 Updating images and fonts to use Webpack 5 Asset modules (weaverryan)
This PR was squashed before being merged into the main branch. Discussion ---------- Updating images and fonts to use Webpack 5 Asset modules This is one more big piece for proper Webpack 5 support - see #880 This also updates the CHANGELOG both for this PR and for the original in #645 And, this adds a new assertion method that helps check directory contents in functional.js in a way that is more resilient to hash changes. For the most part... this just worked! See the CHANGELOG. Because this will release on 1.0, I have decided to remove a few methods instead of deprecating them ). Cheers! Commits ------- 55495cf changing the default to asset/resource a663fbc moving require into loader function 8d3b401 fixing tests on the lowest deps due to version upgrades 3e9c787 Making tests more resilient to failure by fuzzy checking some hash filenames d86e115 upgrading Webpack min 64c6c2f updating CHANGELOG for original Webpack 5 changes in #645 16c0f12 Updating images and fonts to use Webpack 5 Asset modules
2 parents 9521a19 + 55495cf commit 7d9248f

15 files changed

+600
-503
lines changed

CHANGELOG.md

+51
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,56 @@
11
# CHANGELOG
22

3+
## 1.0.0
4+
5+
* [DEPENDENCY UPGRADE] Webpack was upgraded from version 4 to 5.
6+
7+
* [BC BREAK] Image and font processing was changed from using `file-loader`
8+
(and optionally `url-loader` via `configureUrlLoader()`) to Webpack 5's
9+
new [Asset Modules](https://webpack.js.org/guides/asset-modules/).
10+
In practice, unless you have a highly-configured system, this should
11+
not cause significant changes.
12+
13+
* [BC BREAK] The `configureUrlLoader()` method was removed. See
14+
`configureImageRule()` and `configureFontRule()` - specifically the
15+
`maxSize` option and type: 'asset'. The `url-loader` is no longer used.
16+
17+
* [BC BREAK] The `disableImagesLoader()` and `disableFontsLoader()` methods
18+
have been removed. See `configureImageRule()` and `configureFontRule()`
19+
for a new option to disable these.
20+
21+
* [BC BREAK] The `configureFilenames()` method no longer accepts paths
22+
for `images` or `fonts`. See `configureImageRule()` and `configureFontRule()`
23+
for how to configure these filenames. The `configureFilenames()` method
24+
*does* now accept an `assets` option, but out-of-the-box, this will not
25+
result in any filename changes. See `configureFilenames()` for more details.
26+
27+
* [BC BREAK] `css-minimizer-webpack-plugin` was replaced by
28+
`css-minimizer-webpack-plugin` and the `optimizeCssPluginOptionsCallback()`
29+
method was replaced by `cssMinimizerPluginOptionsCallback()`.
30+
31+
* [BC BREAK] The `file-loader` package is no longer required by Encore. If
32+
you use `copyFiles()`, you will need to install it manually (you
33+
will receive a clear error about this).
34+
35+
* [BC BREAK] All previously-deprecated methods & options were removed.
36+
37+
* [DEPENDENCY UPGRADES] The following packages had major version upgrades:
38+
* `css-loader` from 3 to 5
39+
* `mini-css-extract-plugin` from 0.4 to 1
40+
* `style-loader` from 1 to 2
41+
* `terser-webpack-plugin` from 1 to 4
42+
* `webpack-cli` from 3 to 4
43+
* `webpack-manifest-plugin` from 2 to 3
44+
45+
* [BEHAVIOR CHANGE] The `HashedModuleIdsPlugin` was previously used to
46+
help name "modules" when building for production. This has been removed
47+
and we now use Webpack's native `optimization.moduleIds` option, which
48+
is set to `deterministic`.
49+
50+
* [configureMiniCssExtractPlugin()] `configureMiniCssExtractPlugin()` was
51+
added to allow the `MiniCssExtractPlugin.loader` and `MiniCssExtractPlugin`
52+
to be configured.
53+
354
## 0.33.0
455

556
* [disableCssExtraction()] Added boolean argument to `disableCssExtraction()`

index.js

+67-44
Original file line numberDiff line numberDiff line change
@@ -1299,30 +1299,6 @@ class Encore {
12991299
return this;
13001300
}
13011301

1302-
/**
1303-
* Call this if you wish to disable the default
1304-
* images loader.
1305-
*
1306-
* @returns {Encore}
1307-
*/
1308-
disableImagesLoader() {
1309-
webpackConfig.disableImagesLoader();
1310-
1311-
return this;
1312-
}
1313-
1314-
/**
1315-
* Call this if you wish to disable the default
1316-
* fonts loader.
1317-
*
1318-
* @returns {Encore}
1319-
*/
1320-
disableFontsLoader() {
1321-
webpackConfig.disableFontsLoader();
1322-
1323-
return this;
1324-
}
1325-
13261302
/**
13271303
* Call this if you don't want imported CSS to be extracted
13281304
* into a .css file. All your styles will then be injected
@@ -1377,8 +1353,7 @@ class Encore {
13771353
* Encore.configureFilenames({
13781354
* js: '[name].[contenthash].js',
13791355
* css: '[name].[contenthash].css',
1380-
* images: 'images/[name].[hash:8].[ext]',
1381-
* fonts: 'fonts/[name].[hash:8].[ext]'
1356+
* assets: 'assets/[name].[hash:8][ext]',
13821357
* });
13831358
* ```
13841359
*
@@ -1389,6 +1364,10 @@ class Encore {
13891364
* make sure that your "js" and "css" filenames contain
13901365
* "[contenthash]".
13911366
*
1367+
* The "assets" key is used for the output.assetModuleFilename option,
1368+
* which is overridden for both fonts and images. See configureImageRule()
1369+
* and configureFontRule() to control those filenames.
1370+
*
13921371
* @param {object} filenames
13931372
* @returns {Encore}
13941373
*/
@@ -1399,30 +1378,73 @@ class Encore {
13991378
}
14001379

14011380
/**
1402-
* Allows to configure the URL loader.
1381+
* Configure how images are loaded/processed under module.rules.
1382+
*
1383+
* https://webpack.js.org/guides/asset-modules/
14031384
*
1404-
* https://github.com/webpack-contrib/url-loader
1385+
* The most important things can be controlled by passing
1386+
* an options object to the first argument:
14051387
*
14061388
* ```
1407-
* Encore.configureUrlLoader({
1408-
* images: {
1409-
* limit: 8192,
1410-
* mimetype: 'image/png'
1411-
* },
1412-
* fonts: {
1413-
* limit: 4096
1414-
* }
1389+
* Encore.configureImageRule({
1390+
* // common values: asset, asset/resource, asset/inline
1391+
* // Using "asset" will allow smaller images to be "inlined"
1392+
* // instead of copied.
1393+
* // javascript/auto caan be used to disable asset images (see next example)
1394+
* type: 'asset/resource',
1395+
*
1396+
* // applicable when for "type: asset": files smaller than this
1397+
* // size will be "inlined" into CSS, larer files will be extracted
1398+
* // into independent files
1399+
* maxSize: 4 * 1024, // 4 kb
1400+
*
1401+
* // control the output filename of images
1402+
* filename: 'images/[name].[hash:8][ext]',
1403+
*
1404+
* // you can also fully disable the image rule if you want
1405+
* // to control things yourself
1406+
* enabled: true,
14151407
* });
14161408
* ```
14171409
*
1418-
* If a key (e.g. fonts) doesn't exists or contains a
1419-
* falsy value the file-loader will be used instead.
1410+
* If you need more control, you can also pass a callback to the
1411+
* 2nd argument. This will be passed the specific Rule object,
1412+
* which you can modify:
14201413
*
1421-
* @param {object} urlLoaderOptions
1422-
* @return {Encore}
1414+
* https://webpack.js.org/configuration/module/#rule
1415+
*
1416+
* ```
1417+
* Encore.configureImageRule({}, function(rule) {
1418+
* // if you set "type: 'javascript/auto'" in the first argument,
1419+
* // then you can now specify a loader manually
1420+
* // rule.loader = 'file-loader';
1421+
* // rule.options = { filename: 'images/[name].[hash:8][ext]' }
1422+
* });
1423+
* ```
1424+
*
1425+
* @param {object} options
1426+
* @param {string|object|function} ruleCallback
1427+
* @returns {Encore}
1428+
*/
1429+
configureImageRule(options = {}, ruleCallback = (rule) => {}) {
1430+
webpackConfig.configureImageRule(options, ruleCallback);
1431+
1432+
return this;
1433+
}
1434+
1435+
/**
1436+
* Configure how fonts are processed/loaded under module.rules.
1437+
*
1438+
* https://webpack.js.org/guides/asset-modules/
1439+
*
1440+
* See configureImageRule() for more details.
1441+
*
1442+
* @param {object} options
1443+
* @param {string|object|function} ruleCallback
1444+
* @returns {Encore}
14231445
*/
1424-
configureUrlLoader(urlLoaderOptions = {}) {
1425-
webpackConfig.configureUrlLoader(urlLoaderOptions);
1446+
configureFontRule(options = {}, ruleCallback = (rule) => {}) {
1447+
webpackConfig.configureFontRule(options, ruleCallback);
14261448

14271449
return this;
14281450
}
@@ -1597,9 +1619,10 @@ class Encore {
15971619
*
15981620
* @param {string} environment
15991621
* @param {object} options
1622+
* @param {boolean} enablePackageJsonCheck
16001623
* @returns {Encore}
16011624
*/
1602-
configureRuntimeEnvironment(environment, options = {}) {
1625+
configureRuntimeEnvironment(environment, options = {}, enablePackageJsonCheck = true) {
16031626
runtimeConfig = parseRuntime(
16041627
Object.assign(
16051628
{},
@@ -1609,7 +1632,7 @@ class Encore {
16091632
process.cwd()
16101633
);
16111634

1612-
webpackConfig = new WebpackConfig(runtimeConfig, true);
1635+
webpackConfig = new WebpackConfig(runtimeConfig, enablePackageJsonCheck);
16131636

16141637
return this;
16151638
}

lib/WebpackConfig.js

+49-25
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,18 @@ class WebpackConfig {
9797
this.useSourceMaps = false;
9898
this.cleanupOutput = false;
9999
this.extractCss = true;
100-
this.useImagesLoader = true;
101-
this.useFontsLoader = true;
100+
this.imageRuleOptions = {
101+
type: 'asset/resource',
102+
maxSize: null,
103+
filename: 'images/[name].[hash:8][ext]',
104+
enabled: true,
105+
};
106+
this.fontRuleOptions = {
107+
type: 'asset/resource',
108+
maxSize: null,
109+
filename: 'fonts/[name].[hash:8][ext]',
110+
enabled: true,
111+
};
102112
this.usePostCssLoader = false;
103113
this.useLessLoader = false;
104114
this.useStylusLoader = false;
@@ -126,10 +136,6 @@ class WebpackConfig {
126136
this.preactOptions = {
127137
preactCompat: false
128138
};
129-
this.urlLoaderOptions = {
130-
images: false,
131-
fonts: false
132-
};
133139
this.babelOptions = {
134140
exclude: /(node_modules|bower_components)/,
135141
useBuiltIns: false,
@@ -146,6 +152,8 @@ class WebpackConfig {
146152
};
147153

148154
// Features/Loaders options callbacks
155+
this.imageRuleCallback = () => {};
156+
this.fontRuleCallback = () => {};
149157
this.postCssLoaderOptionsCallback = () => {};
150158
this.sassLoaderOptionsCallback = () => {};
151159
this.lessLoaderOptionsCallback = () => {};
@@ -813,14 +821,6 @@ class WebpackConfig {
813821
this.handlebarsConfigurationCallback = callback;
814822
}
815823

816-
disableImagesLoader() {
817-
this.useImagesLoader = false;
818-
}
819-
820-
disableFontsLoader() {
821-
this.useFontsLoader = false;
822-
}
823-
824824
disableCssExtraction(disabled = true) {
825825
this.extractCss = !disabled;
826826
}
@@ -831,30 +831,54 @@ class WebpackConfig {
831831
}
832832

833833
// Check allowed keys
834-
const validKeys = ['js', 'css', 'images', 'fonts'];
834+
const validKeys = ['js', 'css', 'assets'];
835835
for (const key of Object.keys(configuredFilenames)) {
836836
if (!validKeys.includes(key)) {
837-
throw new Error(`"${key}" is not a valid key for configureFilenames(). Valid keys: ${validKeys.join(', ')}.`);
837+
throw new Error(`"${key}" is not a valid key for configureFilenames(). Valid keys: ${validKeys.join(', ')}. Use configureImageRule() or configureFontRule() to control image or font filenames.`);
838838
}
839839
}
840840

841841
this.configuredFilenames = configuredFilenames;
842842
}
843843

844-
configureUrlLoader(urlLoaderOptions = {}) {
845-
if (typeof urlLoaderOptions !== 'object') {
846-
throw new Error('Argument 1 to configureUrlLoader() must be an object.');
844+
configureImageRule(options = {}, ruleCallback = () => {}) {
845+
for (const optionKey of Object.keys(options)) {
846+
if (!(optionKey in this.imageRuleOptions)) {
847+
throw new Error(`Invalid option "${optionKey}" passed to configureImageRule(). Valid keys are ${Object.keys(this.imageRuleOptions).join(', ')}`);
848+
}
849+
850+
this.imageRuleOptions[optionKey] = options[optionKey];
847851
}
848852

849-
// Check allowed keys
850-
const validKeys = ['images', 'fonts'];
851-
for (const key of Object.keys(urlLoaderOptions)) {
852-
if (!validKeys.includes(key)) {
853-
throw new Error(`"${key}" is not a valid key for configureUrlLoader(). Valid keys: ${validKeys.join(', ')}.`);
853+
if (this.imageRuleOptions.maxSize && this.imageRuleOptions.type !== 'asset') {
854+
throw new Error('Invalid option "maxSize" passed to configureImageRule(): this option is only valid when "type" is set to "asset".');
855+
}
856+
857+
if (typeof ruleCallback !== 'function') {
858+
throw new Error('Argument 2 to configureImageRule() must be a callback function.');
859+
}
860+
861+
this.imageRuleCallback = ruleCallback;
862+
}
863+
864+
configureFontRule(options = {}, ruleCallback = () => {}) {
865+
for (const optionKey of Object.keys(options)) {
866+
if (!(optionKey in this.fontRuleOptions)) {
867+
throw new Error(`Invalid option "${optionKey}" passed to configureFontRule(). Valid keys are ${Object.keys(this.fontRuleOptions).join(', ')}`);
854868
}
869+
870+
this.fontRuleOptions[optionKey] = options[optionKey];
871+
}
872+
873+
if (this.fontRuleOptions.maxSize && this.fontRuleOptions.type !== 'asset') {
874+
throw new Error('Invalid option "maxSize" passed to configureFontRule(): this option is only valid when "type" is set to "asset".');
875+
}
876+
877+
if (typeof ruleCallback !== 'function') {
878+
throw new Error('Argument 2 to configureFontRule() must be a callback function.');
855879
}
856880

857-
this.urlLoaderOptions = urlLoaderOptions;
881+
this.fontRuleCallback = ruleCallback;
858882
}
859883

860884
cleanupOutputBeforeBuild(paths = ['**/*'], cleanWebpackPluginOptionsCallback = () => {}) {

0 commit comments

Comments
 (0)