Skip to content

Commit ed18e7f

Browse files
refactor: the minimize option (#216)
BREAKING CHANGE: the `minimize` option is `true` by default in `production` mode. You need to list all options for `html-minifier` if you use `object` notation.
1 parent 9a1f8cd commit ed18e7f

File tree

6 files changed

+135
-60
lines changed

6 files changed

+135
-60
lines changed

README.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ You may need to specify loaders for images in your configuration (recommended `f
5555

5656
## Options
5757

58-
| Name | Type | Default | Description |
59-
| :-------------------------------: | :-----------------: | :-----------: | :--------------------------------------- |
60-
| **[`attrs`](#attrs)** | `{Array\|String}` | `['img:src']` | Enables/Disables attributes handling |
61-
| **[`root`](#root)** | `{String}` | `undefiend` | Allow to handle root-relative attributes |
62-
| **[`interpolate`](#interpolate)** | `{Boolean\|String}` | `false` | Allow to use expressions in HTML syntax |
63-
| **[`minimize`](#minimize)** | `{Boolean\|Object}` | `false` | Tell `html-loader` to minimize HTML |
64-
| **[`esModule`](#esmodule)** | `{Boolean}` | `false` | Use ES modules syntax |
58+
| Name | Type | Default | Description |
59+
| :-------------------------------: | :-----------------: | :------------------------------------------: | :--------------------------------------- |
60+
| **[`attrs`](#attrs)** | `{Array\|String}` | `['img:src']` | Enables/Disables attributes handling |
61+
| **[`root`](#root)** | `{String}` | `undefiend` | Allow to handle root-relative attributes |
62+
| **[`interpolate`](#interpolate)** | `{Boolean\|String}` | `false` | Allow to use expressions in HTML syntax |
63+
| **[`minimize`](#minimize)** | `{Boolean\|Object}` | `true` in production mode, otherwise `false` | Tell `html-loader` to minimize HTML |
64+
| **[`esModule`](#esmodule)** | `{Boolean}` | `false` | Use ES modules syntax |
6565

6666
### `attrs`
6767

@@ -197,12 +197,25 @@ This may be useful for template syntaxes. For example:
197197
### `minimize`
198198

199199
Type: `Boolean|Object`
200-
Default: `false`
200+
Default: `true` in production mode, otherwise `false`
201201

202202
Tell `html-loader` to minimize HTML.
203203

204204
#### `Boolean`
205205

206+
The enabled rules for minimizing by default are the following ones:
207+
208+
- collapseWhitespace
209+
- conservativeCollapse
210+
- keepClosingSlash
211+
- minifyCSS
212+
- minifyJS
213+
- removeAttributeQuotes
214+
- removeComments
215+
- removeScriptTypeAttributes
216+
- removeStyleTypeAttributes
217+
- useShortDoctype
218+
206219
**webpack.config.js**
207220

208221
```js
@@ -408,5 +421,3 @@ Please take a moment to read our contributing guidelines if you haven't yet done
408421
[chat-url]: https://gitter.im/webpack/webpack
409422
[size]: https://packagephobia.now.sh/badge?p=html-loader
410423
[size-url]: https://packagephobia.now.sh/result?p=html-loader
411-
412-
[]: #interpolate

src/constants.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,3 @@ export const GET_URL_CODE =
44
export const IDENT_REGEX = /xxxHTMLLINKxxx[0-9.]+xxx/g;
55

66
export const REQUIRE_REGEX = /\${require\([^)]*\)}/g;
7-
8-
export const MINIMIZE_SETTINGS = [
9-
'removeComments',
10-
'removeCommentsFromCDATA',
11-
'removeCDATASectionsFromCDATA',
12-
'collapseWhitespace',
13-
'conservativeCollapse',
14-
'removeAttributeQuotes',
15-
'useShortDoctype',
16-
'keepClosingSlash',
17-
'minifyJS',
18-
'minifyCSS',
19-
'removeScriptTypeAttributes',
20-
'removeStyleTypeAttributes',
21-
];

src/index.js

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,14 @@ import { getOptions, isUrlRequest, urlToRequest } from 'loader-utils';
66

77
import validateOptions from 'schema-utils';
88

9+
import { GET_URL_CODE, IDENT_REGEX, REQUIRE_REGEX } from './constants';
910
import {
10-
GET_URL_CODE,
11-
IDENT_REGEX,
12-
MINIMIZE_SETTINGS,
13-
REQUIRE_REGEX,
14-
} from './constants';
15-
import {
16-
convertMapToObject,
1711
getAttributes,
1812
getExportsString,
1913
getLinks,
2014
getUniqueIdent,
2115
replaceLinkWithIdent,
16+
isProductionMode,
2217
} from './utils';
2318

2419
import schema from './options.json';
@@ -90,16 +85,29 @@ export default function htmlLoader(source) {
9085
}
9186
}
9287

93-
if (options.minimize) {
94-
const minimizeOptions = new Map(Object.entries(options.minimize));
95-
96-
for (const setting of MINIMIZE_SETTINGS) {
97-
if (!minimizeOptions.has(setting)) {
98-
minimizeOptions.set(setting, true);
99-
}
100-
}
101-
102-
content = minify(content, convertMapToObject(minimizeOptions));
88+
const minimize =
89+
typeof options.minimize === 'undefined'
90+
? isProductionMode(this)
91+
: options.minimize;
92+
93+
if (minimize) {
94+
const minimizeOptions =
95+
typeof minimize === 'boolean'
96+
? {
97+
collapseWhitespace: true,
98+
conservativeCollapse: true,
99+
keepClosingSlash: true,
100+
minifyCSS: true,
101+
minifyJS: true,
102+
removeAttributeQuotes: true,
103+
removeComments: true,
104+
removeScriptTypeAttributes: true,
105+
removeStyleLinkTypeAttributes: true,
106+
useShortDoctype: true,
107+
}
108+
: minimize;
109+
110+
content = minify(content, minimizeOptions);
103111
}
104112

105113
if (options.interpolate && options.interpolate !== 'require') {
@@ -122,6 +130,7 @@ export default function htmlLoader(source) {
122130
}
123131
124132
let request = urlToRequest(data.get(match), options.root);
133+
125134
if (options.interpolate === 'require') {
126135
request = data.get(match);
127136
}

src/utils.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,6 @@ function randomIdent() {
44
return `xxxHTMLLINKxxx${Math.random()}${Math.random()}xxx`;
55
}
66

7-
export function convertMapToObject(map) {
8-
const obj = {};
9-
10-
for (const prop of map) {
11-
// eslint-disable-next-line prefer-destructuring
12-
obj[prop[0]] = prop[1];
13-
}
14-
15-
return obj;
16-
}
17-
187
export function getAttributes(options) {
198
if (typeof options.attrs !== 'undefined') {
209
if (typeof options.attrs === 'string') {
@@ -74,3 +63,7 @@ export function replaceLinkWithIdent(source, link, ident, offset = 0) {
7463
source.substr(link.start + link.length + offset)
7564
);
7665
}
66+
67+
export function isProductionMode(loaderContext) {
68+
return loaderContext.mode === 'production' || !loaderContext.mode;
69+
}

test/loader.test.js

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { GET_URL_CODE } from '../src/constants';
66
describe('loader', () => {
77
it('should convert to requires', () => {
88
const result = loader.call(
9-
{},
9+
{ mode: 'development' },
1010
'Text <img src="image.png"><img src="~bootstrap-img"> Text <img src="">'
1111
);
1212

@@ -18,6 +18,7 @@ describe('loader', () => {
1818
it('should accept attrs from query', () => {
1919
const result = loader.call(
2020
{
21+
mode: 'development',
2122
query: '?attrs=script:src',
2223
},
2324
'Text <script src="script.js"><img src="image.png">'
@@ -30,6 +31,7 @@ describe('loader', () => {
3031
it('should accept attrs from query (space separated)', () => {
3132
const result = loader.call(
3233
{
34+
mode: 'development',
3335
query: '?attrs=script:src img:src',
3436
},
3537
'Text <script src="script.js"><img src="image.png">'
@@ -42,6 +44,7 @@ describe('loader', () => {
4244
it('should accept attrs from query (multiple)', () => {
4345
const result = loader.call(
4446
{
47+
mode: 'development',
4548
query: '?attrs[]=script:src&attrs[]=img:src',
4649
},
4750
'Text <script src="script.js"><img src="image.png">'
@@ -54,6 +57,7 @@ describe('loader', () => {
5457
it('should accept :attribute (empty tag) from query', () => {
5558
const result = loader.call(
5659
{
60+
mode: 'development',
5761
query: '?attrs[]=:custom-src',
5862
},
5963
'Text <custom-element custom-src="image1.png"><custom-img custom-src="image2.png"/></custom-element>'
@@ -66,6 +70,7 @@ describe('loader', () => {
6670
it('should accept :attribute (empty tag) from query and not collide with similar attributes', () => {
6771
const result = loader.call(
6872
{
73+
mode: 'development',
6974
query: '?attrs[]=:custom-src',
7075
},
7176
'Text <custom-element custom-src="image1.png" custom-src-other="other.png"><custom-img custom-src="image2.png"/></custom-element>'
@@ -77,7 +82,7 @@ describe('loader', () => {
7782
});
7883
it('should not make bad things with templates', () => {
7984
const result = loader.call(
80-
{},
85+
{ mode: 'development' },
8186
'<h3>#{number} {customer}</h3>\n<p> {title} </p>'
8287
);
8388

@@ -88,7 +93,7 @@ describe('loader', () => {
8893

8994
it('should preserve escaped quotes', () => {
9095
const result = loader.call(
91-
{},
96+
{ mode: 'development' },
9297
'<script>{"json": "with \\"quotes\\" in value"}</script>'
9398
);
9499

@@ -98,7 +103,10 @@ describe('loader', () => {
98103
});
99104

100105
it('should not translate root-relative urls (without root query)', () => {
101-
const result = loader.call({}, 'Text <img src="/image.png">');
106+
const result = loader.call(
107+
{ mode: 'development' },
108+
'Text <img src="/image.png">'
109+
);
102110

103111
expect(result).toBe(
104112
`${GET_URL_CODE}module.exports = "Text <img src=\\"/image.png\\">";`
@@ -107,6 +115,7 @@ describe('loader', () => {
107115
it('should accept root from query', () => {
108116
const result = loader.call(
109117
{
118+
mode: 'development',
110119
query: '?root=/test',
111120
},
112121
'Text <img src="/image.png">'
@@ -117,32 +126,41 @@ describe('loader', () => {
117126
);
118127
});
119128
it('should ignore hash fragments in URLs', () => {
120-
const result = loader.call({}, '<img src="icons.svg#hash">');
129+
const result = loader.call(
130+
{ mode: 'development' },
131+
'<img src="icons.svg#hash">'
132+
);
121133

122134
expect(result).toBe(
123135
`${GET_URL_CODE}module.exports = "<img src=\\"" + __url__(require("./icons.svg")) + "#hash\\">";`
124136
);
125137
});
126138
it("should ignore anchor with 'mailto:' in the href attribute", () => {
127139
const result = loader.call(
128-
{},
140+
{ mode: 'development' },
129141
'<a href="mailto:username@exampledomain.com"></a>'
130142
);
131143

132144
expect(result).toBe(
133145
`${GET_URL_CODE}module.exports = "<a href=\\"mailto:username@exampledomain.com\\"></a>";`
134146
);
135147
});
148+
136149
it('should ignore interpolations by default', () => {
137-
const result = loader.call({}, '<img src="${"Hello " + (1+1)}">');
150+
const result = loader.call(
151+
{ mode: 'development' },
152+
'<img src="${"Hello " + (1+1)}">'
153+
);
138154

139155
expect(result).toBe(
140156
`${GET_URL_CODE}module.exports = "<img src=\\"\${\\"Hello \\" + (1+1)}\\">";`
141157
);
142158
});
159+
143160
it('should enable interpolations when using interpolate flag', () => {
144161
const result = loader.call(
145162
{
163+
mode: 'development',
146164
query: '?interpolate',
147165
},
148166
'<img src="${"Hello " + (1+1)}">'
@@ -155,6 +173,7 @@ describe('loader', () => {
155173
it('should not change handling of quotes when interpolation is enabled', () => {
156174
const result = loader.call(
157175
{
176+
mode: 'development',
158177
query: '?interpolate',
159178
},
160179
'<script>{"json": "with \\"quotes\\" in value"}</script>'
@@ -167,6 +186,7 @@ describe('loader', () => {
167186
it('should enable interpolations when using interpolate=require flag and only require function be translate', () => {
168187
const result = loader.call(
169188
{
189+
mode: 'development',
170190
query: '?interpolate=require',
171191
},
172192
'<a href="${list.href}"><img src="${require("./test.jpg")}" /></a>'

0 commit comments

Comments
 (0)