html-minifier 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE.txt +23 -0
  2. package/README.md +851 -110
  3. package/cli.js +991 -207
  4. package/dist/types/htmlminifier.d.ts +629 -0
  5. package/dist/types/htmlminifier.d.ts.map +1 -0
  6. package/dist/types/htmlparser.d.ts +28 -0
  7. package/dist/types/htmlparser.d.ts.map +1 -0
  8. package/dist/types/lib/attributes.d.ts +87 -0
  9. package/dist/types/lib/attributes.d.ts.map +1 -0
  10. package/dist/types/lib/constants.d.ts +421 -0
  11. package/dist/types/lib/constants.d.ts.map +1 -0
  12. package/dist/types/lib/content.d.ts +44 -0
  13. package/dist/types/lib/content.d.ts.map +1 -0
  14. package/dist/types/lib/elements.d.ts +48 -0
  15. package/dist/types/lib/elements.d.ts.map +1 -0
  16. package/dist/types/lib/fragments.d.ts +47 -0
  17. package/dist/types/lib/fragments.d.ts.map +1 -0
  18. package/dist/types/lib/option-definitions.d.ts +33 -0
  19. package/dist/types/lib/option-definitions.d.ts.map +1 -0
  20. package/dist/types/lib/options.d.ts +93 -0
  21. package/dist/types/lib/options.d.ts.map +1 -0
  22. package/dist/types/lib/unused-css.d.ts +76 -0
  23. package/dist/types/lib/unused-css.d.ts.map +1 -0
  24. package/dist/types/lib/urls.d.ts +15 -0
  25. package/dist/types/lib/urls.d.ts.map +1 -0
  26. package/dist/types/lib/utils.d.ts +132 -0
  27. package/dist/types/lib/utils.d.ts.map +1 -0
  28. package/dist/types/lib/whitespace.d.ts +97 -0
  29. package/dist/types/lib/whitespace.d.ts.map +1 -0
  30. package/dist/types/presets.d.ts +50 -0
  31. package/dist/types/presets.d.ts.map +1 -0
  32. package/dist/types/tokenchain.d.ts +36 -0
  33. package/dist/types/tokenchain.d.ts.map +1 -0
  34. package/html-minifier.schema.json +322 -0
  35. package/package.json +70 -58
  36. package/src/htmlminifier.js +2845 -1107
  37. package/src/htmlparser.js +1163 -452
  38. package/src/lib/attributes.js +1196 -0
  39. package/src/lib/constants.js +521 -0
  40. package/src/lib/content.js +237 -0
  41. package/src/lib/elements.js +268 -0
  42. package/src/lib/file-pool.js +193 -0
  43. package/src/lib/file-write.js +87 -0
  44. package/src/lib/fragments.js +340 -0
  45. package/src/lib/option-definitions.js +246 -0
  46. package/src/lib/options.js +841 -0
  47. package/src/lib/unused-css.js +499 -0
  48. package/src/lib/urls.js +146 -0
  49. package/src/lib/utils.js +1026 -0
  50. package/src/lib/whitespace.js +431 -0
  51. package/src/presets.js +58 -0
  52. package/src/tokenchain.js +142 -56
  53. package/LICENSE +0 -22
  54. package/sample-cli-config-file.conf +0 -40
  55. package/src/utils.js +0 -18
@@ -0,0 +1,521 @@
1
+ /**
2
+ * Regex patterns, element sets, and default configuration
3
+ */
4
+
5
+ const RE_WS_START = /^[ \n\r\t\f]+/;
6
+ const RE_WS_END = /[ \n\r\t\f]+$/;
7
+ const RE_WS_ONLY = /^[ \n\r\t\f]+$/;
8
+ const RE_WS_NBSP_END = /[ \n\r\t\f\xA0]$/;
9
+ const RE_ALL_WS_NBSP = /[ \n\r\t\f\xA0]+/g;
10
+ const RE_NBSP_LEADING_GROUP = /(^|\xA0+)[^\xA0]+/g;
11
+ const RE_NBSP_LEAD_GROUP = /(\xA0+)[^\xA0]+/g;
12
+ const RE_NBSP_TRAILING_GROUP = /[^\xA0]+(\xA0+)/g;
13
+ const RE_NBSP_TRAILING_STRIP = /[^\xA0]+$/;
14
+ const RE_EVENT_ATTR_DEFAULT = /^on[a-z]{3,}$/;
15
+ const RE_CAN_REMOVE_ATTR_QUOTES = /^[^ \t\n\f\r"'`=<>]+$/;
16
+ const RE_TRAILING_SEMICOLON = /;$/;
17
+ // Line breaks HTML reads as line feeds when raw
18
+ const RE_CR = /\r/g;
19
+ const RE_LINE_BREAK_CR = /\r\n?/g;
20
+ // Named references HTML resolves without a semicolon
21
+ const LEGACY_REFERENCE_NAMES = 'Iacute|aacute|uacute|plusmn|Otilde|otilde|agrave|Agrave|Yacute|yacute|Oslash|oslash|atilde|Atilde|brvbar|ccedil|Ccedil|Ograve|curren|divide|eacute|Eacute|ograve|Oacute|egrave|Egrave|Ugrave|frac12|frac14|frac34|ugrave|oacute|iacute|Ntilde|ntilde|Uacute|middot|igrave|Igrave|iquest|Aacute|cedil|laquo|micro|iexcl|Icirc|icirc|acirc|Ucirc|Ecirc|ocirc|Ocirc|ecirc|ucirc|Aring|aring|AElig|aelig|acute|pound|raquo|Acirc|times|THORN|szlig|thorn|COPY|auml|ordf|ordm|Uuml|macr|uuml|Auml|ouml|Ouml|para|nbsp|euml|quot|QUOT|Euml|yuml|cent|sect|copy|sup1|sup2|sup3|iuml|Iuml|ETH|shy|reg|not|yen|amp|AMP|REG|uml|eth|deg|gt|GT|LT|lt';
22
+ // Ampersands text would read as starting a reference, for `&amp` to precede
23
+ const RE_LEGACY_ENTITIES = new RegExp(`&((?:${LEGACY_REFERENCE_NAMES})(?!;)|[0-9a-zA-Z]+;|#[0-9]|#[xX][0-9a-fA-F])`, 'g');
24
+ // Ampersands an attribute value would read as starting a reference (which a legacy name
25
+ // followed by `=` or an alphanumeric does not)
26
+ const RE_AMP_REFERENCE_ATTR = new RegExp(`&(?=(?:${LEGACY_REFERENCE_NAMES})(?![=0-9a-zA-Z])|[0-9a-zA-Z]+;|#[0-9]|#[xX][0-9a-fA-F])`, 'g');
27
+ const RE_ESCAPE_LT = /</g;
28
+ // A `base` element, also as character references may write it, or as a script may name it
29
+ // (e.g., `createElement('base')`)
30
+ const RE_BASE_ELEMENT = /(?:<|&lt;?|&#0*60;?|&#x0*3c;?)base[\s/>]|(["'`])base\1/i;
31
+ // What decoded text and attribute values hold that XML reads otherwise: markup, and
32
+ // line breaks and tabs XML normalizes (quotes are left to attribute quoting)
33
+ const RE_XML_ESCAPE_TEXT = /[&<\r]|]]>/g;
34
+ const RE_XML_ESCAPE_ATTR = /[&<\t\n\r]/g;
35
+ // In escapable raw text only the element’s own end tag ends the element, so nothing else
36
+ // that `decodeEntities` decoded to a `<` needs escaping there
37
+ // (the record has no prototype, so a tag named after an inherited property—`constructor`,
38
+ // `__proto__`—finds nothing here and falls back to escaping every `<`)
39
+ const RE_ESCAPE_LT_RAW_TEXT = /** @type {Record<string, RegExp>} */ (Object.assign(Object.create(null), {
40
+ textarea: /<(?=\/textarea[\s/>])/gi,
41
+ title: /<(?=\/title[\s/>])/gi
42
+ }));
43
+ // Encodings that make an `annotation-xml` element hold HTML rather than MathML
44
+ // https://html.spec.whatwg.org/multipage/parsing.html#html-integration-point
45
+ const RE_HTML_ENCODING = /^(text\/html|application\/xhtml\+xml)$/i;
46
+ // A doctype’s name, public identifier, and system identifier (quoted),
47
+ // and what follows them, as the tokenizer reads them; unless a system
48
+ // identifier precedes it, anything but `>` sets quirks mode
49
+ // https://html.spec.whatwg.org/multipage/parsing.html#doctype-state
50
+ const RE_DOCTYPE = /^<!doctype[ \n\r\t\f]*([^ \n\r\t\f>]+)[ \n\r\t\f]*(?:public[ \n\r\t\f]*("[^">]*"|'[^'>]*')[ \n\r\t\f]*("[^">]*"|'[^'>]*')?|system[ \n\r\t\f]*("[^">]*"|'[^'>]*'))?([^]*)$/i;
51
+ const RE_ATTR_WS_CHECK = /[ \n\r\t\f]/;
52
+ const RE_ATTR_WS_COLLAPSE = /[ \n\r\t\f]+/g;
53
+ const RE_WS_CHAR = /\s/;
54
+ const RE_STYLE_ELEMENT = /<style[\s/>]/i;
55
+ const RE_EMPTY_ATTRIBUTE = new RegExp(
56
+ '^(?:class|id|style|title|lang|dir|on(?:focus|blur|change|click|dblclick|mouse(' +
57
+ '?:down|up|over|move|out)|key(?:press|down|up)))$');
58
+
59
+ // Inline element sets for whitespace handling
60
+
61
+ // Non-empty elements that will maintain whitespace around them
62
+ const inlineElementsToKeepWhitespaceAround = new Set(['a', 'abbr', 'acronym', 'audio', 'b', 'bdi', 'bdo', 'big', 'button', 'canvas', 'cite', 'code', 'data', 'del', 'dfn', 'em', 'embed', 'font', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'mark', 'math', 'meter', 'nobr', 'object', 'output', 'picture', 'progress', 'q', 'rb', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'select', 'selectedcontent', 'slot', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'textarea', 'time', 'tt', 'u', 'var', 'video', 'wbr']);
63
+
64
+ // Non-empty elements that will maintain whitespace within them
65
+ const inlineElementsToKeepWhitespaceWithin = new Set(['a', 'abbr', 'acronym', 'b', 'big', 'data', 'del', 'em', 'font', 'i', 'ins', 'kbd', 'map', 'mark', 'nobr', 's', 'samp', 'slot', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'time', 'tt', 'u', 'var']);
66
+
67
+ // Text-level elements that keep whitespace between them and other elements under
68
+ // `collapseInlineTagWhitespace`
69
+ const inlineElementsToKeepWhitespaceBetween = new Set([...inlineElementsToKeepWhitespaceWithin, 'bdi', 'bdo', 'cite', 'code', 'dfn', 'label', 'math', 'q', 'ruby']);
70
+
71
+ // Phrasing elements that don’t render, whose neighbors decide on the whitespace around them
72
+ const inlineElementsTransparent = new Set(['area', 'datalist', 'link', 'meta', 'noscript', 'script', 'style', 'template']);
73
+ const inlineElementsTransparentVoid = new Set(['area', 'link', 'meta']);
74
+
75
+ // What the parser reports as the tag next to text that borders a processing instruction—
76
+ // no tag name can be `?`, and content may be inserted there, so whitespace around it stays
77
+ const processingInstructionTag = '?';
78
+
79
+ // Elements that will always maintain whitespace around them
80
+ const inlineElementsToKeepWhitespace = new Set(['comment', 'embed', 'img', 'input', 'wbr', processingInstructionTag]);
81
+
82
+ // Formatting elements, which browsers reopen after a block that closed them, so their end tags
83
+ // count even when stray, https://html.spec.whatwg.org/multipage/parsing.html#formatting
84
+ const formattingElements = new Set(['a', 'b', 'big', 'code', 'em', 'font', 'i', 'nobr', 's', 'small', 'strike', 'strong', 'tt', 'u']);
85
+
86
+ // Form control elements (for conditional whitespace collapsing)
87
+ const formControlElements = new Set(['input', 'button', 'select', 'textarea', 'output', 'meter', 'progress']);
88
+
89
+ // End-tag spellings (`/a`) of a set of element names
90
+ /** @param {Iterable<string>} names */
91
+ const toEndTags = names => new Set(Array.from(names, name => '/' + name));
92
+
93
+ // Membership under either spelling, for the whitespace checks that ignore the slash;
94
+ // looking a tag up as it comes keeps the hot path from slicing one string per call
95
+ const formControlElementsEither = new Set([...formControlElements, ...toEndTags(formControlElements)]);
96
+
97
+ const inlineElementsToKeepWhitespaceBetweenEnd = toEndTags(inlineElementsToKeepWhitespaceBetween);
98
+ // Spelled as the tags that sit before and after the element: a start tag before it, an end tag
99
+ // (or, for void elements, the start tag) after it
100
+ const inlineElementsTransparentAfter = new Set([...toEndTags(inlineElementsTransparent), ...inlineElementsTransparentVoid]);
101
+
102
+ // Default attribute values (could apply to any element)
103
+ const generalDefaults = {
104
+ autocorrect: 'on',
105
+ fetchpriority: 'auto',
106
+ loading: 'eager',
107
+ popovertargetaction: 'toggle'
108
+ };
109
+
110
+ // Tag-specific default attribute values
111
+ const tagDefaults = {
112
+ area: { shape: 'rect' },
113
+ button: { type: 'submit' },
114
+ col: { span: '1' },
115
+ colgroup: { span: '1' },
116
+ form: {
117
+ autocomplete: 'on',
118
+ enctype: 'application/x-www-form-urlencoded',
119
+ method: 'get'
120
+ },
121
+ html: { dir: 'ltr' },
122
+ iframe: { scrolling: 'auto' },
123
+ img: { decoding: 'auto' },
124
+ input: {
125
+ colorspace: 'limited-srgb',
126
+ type: 'text'
127
+ },
128
+ link: { media: 'all' },
129
+ marquee: {
130
+ behavior: 'scroll',
131
+ direction: 'left'
132
+ },
133
+ meta: { media: 'all' },
134
+ meter: {
135
+ max: '1',
136
+ min: '0'
137
+ },
138
+ // Not `start` on reversed lists, which count down from their number of items
139
+ ol: {
140
+ start: '1',
141
+ type: '1'
142
+ },
143
+ progress: { max: '1' },
144
+ source: { media: 'all' },
145
+ style: { media: 'all' },
146
+ td: {
147
+ colspan: '1',
148
+ rowspan: '1'
149
+ },
150
+ textarea: {
151
+ cols: '20',
152
+ rows: '2',
153
+ wrap: 'soft'
154
+ },
155
+ th: {
156
+ colspan: '1',
157
+ rowspan: '1',
158
+ scope: 'auto'
159
+ },
160
+ track: { kind: 'subtitles' }
161
+ };
162
+
163
+ // SVG and MathML elements whose children HTML reads as HTML, but for MathML `mglyph` and
164
+ // `malignmark` (`foreignObject` and `annotation-xml` get a context of their own)
165
+ // https://html.spec.whatwg.org/multipage/parsing.html#mathml-text-integration-point
166
+ const integrationPointsForeign = {
167
+ svg: new Set(['desc', 'title']),
168
+ math: new Set(['mi', 'mn', 'mo', 'ms', 'mtext'])
169
+ };
170
+
171
+ // `meta` element `http-equiv` and `name` values no current browser acts on, in lowercase
172
+ const metaHttpEquivNoEffect = new Set(['cleartype', 'content-script-type', 'content-style-type', 'imagetoolbar', 'msthemecompatible', 'page-enter', 'page-exit', 'pics-label', 'site-enter', 'site-exit', 'window-target', 'x-ua-compatible']);
173
+ const metaNamesNoEffect = new Set(['handheldfriendly', 'ie_rm_off', 'mobileoptimized', 'mssmarttagspreventparsing']);
174
+
175
+ // Elements whose `target` defaults to `_self` where no `base` element sets another
176
+ const targetSelfElements = new Set(['a', 'area', 'form']);
177
+
178
+ const transferFunctionDefaults = { amplitude: '1', exponent: '1', intercept: '0', offset: '0', slope: '1', type: 'identity' };
179
+
180
+ // SVG default attribute values, keyed by element and attribute names in lowercase, with
181
+ // values in lowercase as well; presentation attributes are left out, as they inherit
182
+ const tagDefaultsSVG = {
183
+ animatemotion: { rotate: '0' },
184
+ animatetransform: { type: 'translate' },
185
+ circle: { cx: '0', cy: '0' },
186
+ clippath: { clippathunits: 'userspaceonuse' },
187
+ ellipse: { cx: '0', cy: '0' },
188
+ feblend: { mode: 'normal' },
189
+ fecolormatrix: { type: 'matrix' },
190
+ fecomposite: { k1: '0', k2: '0', k3: '0', k4: '0', operator: 'over' },
191
+ feconvolvematrix: { bias: '0', edgemode: 'duplicate', order: '3', preservealpha: 'false' },
192
+ fediffuselighting: { diffuseconstant: '1', surfacescale: '1' },
193
+ fedisplacementmap: { scale: '0', xchannelselector: 'a', ychannelselector: 'a' },
194
+ fedistantlight: { azimuth: '0', elevation: '0' },
195
+ fedropshadow: { dx: '2', dy: '2', stddeviation: '2' },
196
+ fefunca: transferFunctionDefaults,
197
+ fefuncb: transferFunctionDefaults,
198
+ fefuncg: transferFunctionDefaults,
199
+ fefuncr: transferFunctionDefaults,
200
+ fegaussianblur: { stddeviation: '0' },
201
+ feimage: { preserveaspectratio: 'xmidymid meet' },
202
+ femorphology: { operator: 'erode', radius: '0' },
203
+ feoffset: { dx: '0', dy: '0' },
204
+ fepointlight: { x: '0', y: '0', z: '0' },
205
+ fespecularlighting: { specularconstant: '1', specularexponent: '1', surfacescale: '1' },
206
+ fespotlight: { pointsatx: '0', pointsaty: '0', pointsatz: '0', specularexponent: '1', x: '0', y: '0', z: '0' },
207
+ feturbulence: { basefrequency: '0', numoctaves: '1', seed: '0', stitchtiles: 'nostitch', type: 'turbulence' },
208
+ filter: { filterunits: 'objectboundingbox', height: '120%', primitiveunits: 'userspaceonuse', width: '120%', x: '-10%', y: '-10%' },
209
+ foreignobject: { x: '0', y: '0' },
210
+ image: { preserveaspectratio: 'xmidymid meet', x: '0', y: '0' },
211
+ line: { x1: '0', x2: '0', y1: '0', y2: '0' },
212
+ lineargradient: { gradientunits: 'objectboundingbox', spreadmethod: 'pad', x1: ['0', '0%'], x2: '100%', y1: ['0', '0%'], y2: ['0', '0%'] },
213
+ marker: { markerheight: '3', markerunits: 'strokewidth', markerwidth: '3', orient: '0', preserveaspectratio: 'xmidymid meet', refx: '0', refy: '0' },
214
+ mask: { height: '120%', maskcontentunits: 'userspaceonuse', maskunits: 'objectboundingbox', width: '120%', x: '-10%', y: '-10%' },
215
+ pattern: { height: '0', patterncontentunits: 'userspaceonuse', patternunits: 'objectboundingbox', preserveaspectratio: 'xmidymid meet', width: '0', x: '0', y: '0' },
216
+ radialgradient: { cx: '50%', cy: '50%', fr: ['0', '0%'], gradientunits: 'objectboundingbox', r: '50%', spreadmethod: 'pad' },
217
+ rect: { x: '0', y: '0' },
218
+ stop: { offset: '0' },
219
+ svg: { preserveaspectratio: 'xmidymid meet', version: '1.1', x: '0', y: '0' },
220
+ // Not `refX` and `refY` on `symbol`, which, unset, set no reference point
221
+ symbol: { preserveaspectratio: 'xmidymid meet' },
222
+ // Unlike in `tspan`, where they continue from the text before
223
+ text: { lengthadjust: 'spacing', x: '0', y: '0' },
224
+ textpath: { lengthadjust: 'spacing', method: 'align', spacing: 'exact', startoffset: '0' },
225
+ tspan: { lengthadjust: 'spacing' },
226
+ use: { x: '0', y: '0' },
227
+ view: { preserveaspectratio: 'xmidymid meet' }
228
+ };
229
+
230
+ // SVG elements taking what they do not set from the element they refer to
231
+ const svgElementsInheritingByReference = new Set(['filter', 'lineargradient', 'pattern', 'radialgradient']);
232
+
233
+ // The namespaces HTML puts elements in, which `xmlns` may only repeat
234
+ const namespaceHTML = 'http://www.w3.org/1999/xhtml';
235
+ const namespaceSVG = 'http://www.w3.org/2000/svg';
236
+ const namespaceMathML = 'http://www.w3.org/1998/Math/MathML';
237
+ const namespaceXLink = 'http://www.w3.org/1999/xlink';
238
+
239
+ // Script MIME types
240
+
241
+ // https://mathiasbynens.be/demo/javascript-mime-type
242
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script
243
+ const executableScriptsMimetypes = new Set([
244
+ 'text/javascript',
245
+ 'text/x-javascript',
246
+ 'text/ecmascript',
247
+ 'text/x-ecmascript',
248
+ 'text/jscript',
249
+ 'application/javascript',
250
+ 'application/x-javascript',
251
+ 'application/ecmascript',
252
+ 'application/x-ecmascript',
253
+ 'module'
254
+ ]);
255
+
256
+ const keepScriptsMimetypes = new Set([
257
+ 'module'
258
+ ]);
259
+
260
+ // Boolean attribute sets
261
+
262
+ const isSimpleBoolean = new Set(['allowfullscreen', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'declare', 'default', 'defaultchecked', 'defaultmuted', 'defaultselected', 'defer', 'disabled', 'enabled', 'formnovalidate', 'hidden', 'indeterminate', 'inert', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nohref', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'pauseonexit', 'readonly', 'required', 'reversed', 'scoped', 'seamless', 'selected', 'sortable', 'truespeed', 'typemustmatch', 'visible']);
263
+
264
+ const isBooleanValue = new Set(['true', 'false']);
265
+
266
+ // Values that collapse to the bare attribute name:
267
+ // - `crossorigin=""` and `crossorigin="anonymous"` → `crossorigin` (anonymous is the default)
268
+ // - `contenteditable=""` → `contenteditable` (empty string means inherit/true)
269
+ const collapsibleValues = new Map([
270
+ ['crossorigin', new Set(['', 'anonymous'])],
271
+ ['contenteditable', new Set([''])]
272
+ ]);
273
+
274
+ const srcsetElements = new Set(['img', 'source']);
275
+
276
+ const jsonScriptTypes = new Set([
277
+ 'application/json',
278
+ 'application/ld+json',
279
+ 'application/manifest+json',
280
+ 'application/vnd.geo+json',
281
+ 'application/problem+json',
282
+ 'application/merge-patch+json',
283
+ 'application/json-patch+json',
284
+ 'importmap',
285
+ 'speculationrules',
286
+ ]);
287
+
288
+ // Tag omission rules from https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
289
+ // with the following extensions:
290
+ // - retain `<body>` if followed by `<noscript>`
291
+ // - `<rb>`, `<rt>`, `<rtc>`, `<rp>` follow the HTML Ruby Markup Extensions draft:
292
+ // https://www.w3.org/TR/html-ruby-extensions/
293
+ // - retain all tags which are adjacent to non-standard HTML tags
294
+
295
+ const optionalStartTags = new Set(['html', 'head', 'body', 'colgroup', 'tbody']);
296
+
297
+ const optionalEndTags = new Set(['html', 'head', 'body', 'li', 'dt', 'dd', 'p', 'rb', 'rt', 'rtc', 'rp', 'optgroup', 'option', 'colgroup', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th']);
298
+
299
+ const headerElements = new Set(['meta', 'link', 'script', 'style', 'template', 'noscript']);
300
+
301
+ const descriptionElements = new Set(['dt', 'dd']);
302
+
303
+ const pBlockElements = new Set(['address', 'article', 'aside', 'blockquote', 'details', 'dialog', 'div', 'dl', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'menu', 'nav', 'ol', 'p', 'pre', 'search', 'section', 'table', 'ul']);
304
+
305
+ // Elements whose end tags close an open `p` in browsers, so that `</p>` may be omitted before them
306
+ const pClosingParents = new Set(['address', 'applet', 'article', 'aside', 'blockquote', 'body', 'button', 'caption', 'center', 'dd', 'details', 'dialog', 'dir', 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'html', 'li', 'listing', 'main', 'marquee', 'menu', 'nav', 'object', 'ol', 'pre', 'search', 'section', 'summary', 'td', 'template', 'th', 'ul']);
307
+
308
+ const rubyEndTagOmission = new Set(['rb', 'rt', 'rtc', 'rp']); // `</rb>`, `</rt>`, `</rp>` can be omitted if followed by `<rb>`, `<rt>`, `<rtc>`, or `<rp>`
309
+
310
+ const rubyRtcEndTagOmission = new Set(['rb', 'rtc']); // `</rtc>` can be omitted if followed by `<rb>` or `<rtc>` (not `<rt>` or `<rp>`)
311
+
312
+ const optionElements = new Set(['option', 'optgroup']);
313
+
314
+ const tableContentElements = new Set(['tbody', 'tfoot']);
315
+
316
+ const tableSectionElements = new Set(['thead', 'tbody', 'tfoot']);
317
+
318
+ const cellElements = new Set(['td', 'th']);
319
+
320
+ const topLevelElements = new Set(['html', 'head', 'body']);
321
+
322
+ const compactElements = new Set(['html', 'body']);
323
+
324
+ const looseElements = new Set(['head', 'colgroup', 'caption']);
325
+
326
+ const trailingElements = new Set(['dt', 'thead']);
327
+
328
+ const htmlElements = new Set(['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'bgsound', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'content', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'image', 'img', 'input', 'ins', 'isindex', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'listing', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meta', 'meter', 'multicol', 'nav', 'nobr', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'plaintext', 'pre', 'progress', 'q', 'rb', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'selectedcontent', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr', 'xmp']);
329
+
330
+ // Doctype identifiers that set quirks mode, lowercase for their case-insensitive comparison
331
+ // https://html.spec.whatwg.org/multipage/parsing.html#the-initial-insertion-mode
332
+
333
+ const quirksPublicIds = new Set(['-//w3o//dtd w3 html strict 3.0//en//', '-/w3c/dtd html 4.0 transitional/en', 'html']);
334
+
335
+ const quirksSystemIds = new Set(['http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd']);
336
+
337
+ const quirksPublicIdPrefixes = ['+//silmaril//dtd html pro v0r11 19970101//', '-//as//dtd html 3.0 aswedit + extensions//', '-//advasoft ltd//dtd html 3.0 aswedit + extensions//', '-//ietf//dtd html 2.0 level 1//', '-//ietf//dtd html 2.0 level 2//', '-//ietf//dtd html 2.0 strict level 1//', '-//ietf//dtd html 2.0 strict level 2//', '-//ietf//dtd html 2.0 strict//', '-//ietf//dtd html 2.0//', '-//ietf//dtd html 2.1e//', '-//ietf//dtd html 3.0//', '-//ietf//dtd html 3.2 final//', '-//ietf//dtd html 3.2//', '-//ietf//dtd html 3//', '-//ietf//dtd html level 0//', '-//ietf//dtd html level 1//', '-//ietf//dtd html level 2//', '-//ietf//dtd html level 3//', '-//ietf//dtd html strict level 0//', '-//ietf//dtd html strict level 1//', '-//ietf//dtd html strict level 2//', '-//ietf//dtd html strict level 3//', '-//ietf//dtd html strict//', '-//ietf//dtd html//', '-//metrius//dtd metrius presentational//', '-//microsoft//dtd internet explorer 2.0 html strict//', '-//microsoft//dtd internet explorer 2.0 html//', '-//microsoft//dtd internet explorer 2.0 tables//', '-//microsoft//dtd internet explorer 3.0 html strict//', '-//microsoft//dtd internet explorer 3.0 html//', '-//microsoft//dtd internet explorer 3.0 tables//', '-//netscape comm. corp.//dtd html//', '-//netscape comm. corp.//dtd strict html//', '-//o\'reilly and associates//dtd html 2.0//', '-//o\'reilly and associates//dtd html extended 1.0//', '-//o\'reilly and associates//dtd html extended relaxed 1.0//', '-//sq//dtd html 2.0 hotmetal + extensions//', '-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//', '-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//', '-//spyglass//dtd html 2.0 extended//', '-//sun microsystems corp.//dtd hotjava html//', '-//sun microsystems corp.//dtd hotjava strict html//', '-//w3c//dtd html 3 1995-03-24//', '-//w3c//dtd html 3.2 draft//', '-//w3c//dtd html 3.2 final//', '-//w3c//dtd html 3.2//', '-//w3c//dtd html 3.2s draft//', '-//w3c//dtd html 4.0 frameset//', '-//w3c//dtd html 4.0 transitional//', '-//w3c//dtd html experimental 19960712//', '-//w3c//dtd html experimental 970421//', '-//w3c//dtd w3 html//', '-//w3o//dtd w3 html 3.0//', '-//c.0//', '-//webtechs//dtd mozilla html//'];
338
+
339
+ // Prefixes that set quirks mode only without a system identifier or with an empty one
340
+ // (limited-quirks mode otherwise)
341
+ const quirksPublicIdPrefixesNoSystemId = ['-//w3c//dtd html 4.01 frameset//', '-//w3c//dtd html 4.01 transitional//'];
342
+
343
+ const specialContentElements = new Set(['script', 'style']);
344
+
345
+ // Elements the parser reads as raw text as well, though only `script` and `style` are raw text
346
+ // elements by name—these are HTML’s alone, where `script` and `style` hold text in SVG, too
347
+ // https://html.spec.whatwg.org/multipage/parsing.html#generic-raw-text-element-parsing-algorithm
348
+ //
349
+ // (`noscript`, `noframes`, and `noembed` are raw text, too, but stay markup here: What they
350
+ // hold is markup to the UA that displays it—one without scripting or frames—so minifying it
351
+ // as markup keeps what that UA sees)
352
+ const genericRawTextElements = new Set(['iframe', 'plaintext', 'xmp']);
353
+
354
+ // `plaintext` is the one element among these whose content has no end: The text runs to the
355
+ // end of the document, `</plaintext>` and all, as nothing switches the parser back to markup
356
+ // https://html.spec.whatwg.org/multipage/parsing.html#plaintext-state
357
+ const endlessRawTextElements = new Set(['plaintext']);
358
+
359
+ // Escapable raw text elements, whose content is text rather than markup
360
+ // https://html.spec.whatwg.org/multipage/syntax.html#elements-2
361
+ const escapableRawTextElements = new Set(['textarea', 'title']);
362
+
363
+ // Raw text is read without resolving character references, so its text keeps them—unlike that
364
+ // of `textarea` and `title`, where they are resolved as they are anywhere else
365
+ const rawTextElements = new Set([...specialContentElements, ...genericRawTextElements]);
366
+
367
+ // Marks the error for a minifier that was asked for but isn’t available—a
368
+ // configuration error, and so not one that `continueOnMinifyError` waves through
369
+ const MISSING_DEPENDENCY = 'HM_MISSING_DEPENDENCY';
370
+
371
+ // SVGO’s `preset-default` treats an SVG as a file of its own, but an inline SVG’s
372
+ // IDs, `style` rules, hidden sprites, and empty attributes (`[data-v-…]` hooks, `alt=""`)
373
+ // reach into the rest of the document, so the plugins that assume otherwise stay off,
374
+ // and `minifyStyles` keeps the rules the SVG itself does not use
375
+ const svgoPluginsInline = [{
376
+ name: 'preset-default',
377
+ params: {
378
+ overrides: {
379
+ cleanupIds: false,
380
+ inlineStyles: false,
381
+ minifyStyles: { usage: false },
382
+ removeEmptyAttrs: false,
383
+ removeHiddenElems: false,
384
+ removeUnknownsAndDefaults: { keepRoleAttr: true }
385
+ }
386
+ }
387
+ }];
388
+
389
+ // Mixed-case names the HTML parser gives SVG elements and attributes and MathML attributes, keyed
390
+ // by the lowercase it reads every other name as (no lowercase name is both an element and an
391
+ // attribute of different case, so one table serves both)
392
+ // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inforeign
393
+ const namesForeignMixedCase = new Map([
394
+ // SVG elements
395
+ 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animateColor', 'animateMotion', 'animateTransform',
396
+ 'clipPath', 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix',
397
+ 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA',
398
+ 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode',
399
+ 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile',
400
+ 'feTurbulence', 'foreignObject', 'glyphRef', 'linearGradient', 'radialGradient', 'textPath',
401
+ // SVG attributes
402
+ 'attributeName', 'attributeType', 'baseFrequency', 'baseProfile', 'calcMode', 'clipPathUnits',
403
+ 'diffuseConstant', 'edgeMode', 'filterUnits', 'gradientTransform', 'gradientUnits',
404
+ 'kernelMatrix', 'kernelUnitLength', 'keyPoints', 'keySplines', 'keyTimes', 'lengthAdjust',
405
+ 'limitingConeAngle', 'markerHeight', 'markerUnits', 'markerWidth', 'maskContentUnits', 'maskUnits',
406
+ 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform', 'patternUnits', 'pointsAtX',
407
+ 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits', 'refX', 'refY',
408
+ 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'specularConstant',
409
+ 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', 'stitchTiles', 'surfaceScale',
410
+ 'systemLanguage', 'tableValues', 'targetX', 'targetY', 'textLength', 'viewBox', 'viewTarget',
411
+ 'xChannelSelector', 'yChannelSelector', 'zoomAndPan',
412
+ // MathML attributes
413
+ 'definitionURL'
414
+ ].map(name => [name.toLowerCase(), name]));
415
+
416
+ export {
417
+ // Regex patterns
418
+ RE_WS_START,
419
+ RE_WS_END,
420
+ RE_WS_ONLY,
421
+ RE_WS_NBSP_END,
422
+ RE_ALL_WS_NBSP,
423
+ RE_NBSP_LEADING_GROUP,
424
+ RE_NBSP_LEAD_GROUP,
425
+ RE_NBSP_TRAILING_GROUP,
426
+ RE_NBSP_TRAILING_STRIP,
427
+ RE_EVENT_ATTR_DEFAULT,
428
+ RE_CAN_REMOVE_ATTR_QUOTES,
429
+ RE_TRAILING_SEMICOLON,
430
+ RE_AMP_REFERENCE_ATTR,
431
+ RE_CR,
432
+ RE_LINE_BREAK_CR,
433
+ RE_LEGACY_ENTITIES,
434
+ RE_ESCAPE_LT,
435
+ RE_BASE_ELEMENT,
436
+ RE_ESCAPE_LT_RAW_TEXT,
437
+ RE_XML_ESCAPE_TEXT,
438
+ RE_XML_ESCAPE_ATTR,
439
+ RE_HTML_ENCODING,
440
+ RE_DOCTYPE,
441
+ RE_ATTR_WS_CHECK,
442
+ RE_ATTR_WS_COLLAPSE,
443
+ RE_WS_CHAR,
444
+ RE_STYLE_ELEMENT,
445
+ RE_EMPTY_ATTRIBUTE,
446
+
447
+ // Inline element sets
448
+ formattingElements,
449
+ inlineElementsToKeepWhitespaceAround,
450
+ inlineElementsToKeepWhitespaceWithin,
451
+ inlineElementsToKeepWhitespaceBetween,
452
+ inlineElementsToKeepWhitespaceBetweenEnd,
453
+ inlineElementsTransparent,
454
+ inlineElementsTransparentAfter,
455
+ inlineElementsToKeepWhitespace,
456
+ processingInstructionTag,
457
+ formControlElementsEither,
458
+ toEndTags,
459
+
460
+ // Default values
461
+ generalDefaults,
462
+ tagDefaults,
463
+ metaHttpEquivNoEffect,
464
+ metaNamesNoEffect,
465
+ targetSelfElements,
466
+ integrationPointsForeign,
467
+ tagDefaultsSVG,
468
+ svgElementsInheritingByReference,
469
+ namespaceHTML,
470
+ namespaceSVG,
471
+ namespaceMathML,
472
+ namespaceXLink,
473
+
474
+ // Script/style constants
475
+ executableScriptsMimetypes,
476
+ keepScriptsMimetypes,
477
+ jsonScriptTypes,
478
+
479
+ // Boolean sets
480
+ isSimpleBoolean,
481
+ isBooleanValue,
482
+ collapsibleValues,
483
+
484
+ // Misc
485
+ srcsetElements,
486
+ MISSING_DEPENDENCY,
487
+ namesForeignMixedCase,
488
+ svgoPluginsInline,
489
+
490
+ // Tag omission rules
491
+ optionalStartTags,
492
+ optionalEndTags,
493
+ headerElements,
494
+ descriptionElements,
495
+ pBlockElements,
496
+ pClosingParents,
497
+ rubyEndTagOmission,
498
+ rubyRtcEndTagOmission,
499
+ optionElements,
500
+ tableContentElements,
501
+ tableSectionElements,
502
+ cellElements,
503
+ topLevelElements,
504
+ compactElements,
505
+ looseElements,
506
+ trailingElements,
507
+ htmlElements,
508
+
509
+ // Doctype identifiers
510
+ quirksPublicIds,
511
+ quirksSystemIds,
512
+ quirksPublicIdPrefixes,
513
+ quirksPublicIdPrefixesNoSystemId,
514
+
515
+ // Special content elements
516
+ specialContentElements,
517
+ genericRawTextElements,
518
+ endlessRawTextElements,
519
+ escapableRawTextElements,
520
+ rawTextElements
521
+ };