single-file-core 1.6.13 → 1.6.15
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.
- package/core/helper.js +21 -2
- package/core/index.js +48 -0
- package/core/infobar.js +18 -4
- package/core/lib/processor-helper-common.js +2 -14
- package/core/lib/processor-helper-inline.js +0 -6
- package/core/lib/processor-helper.js +0 -6
- package/core/util.js +28 -5
- package/modules/css-rules-minifier.js +39 -14
- package/package.json +1 -1
- package/vendor/index.js +0 -2
- package/vendor/css-minifier.js +0 -801
package/core/helper.js
CHANGED
|
@@ -51,6 +51,9 @@ const KEPT_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "kept-content
|
|
|
51
51
|
const HIDDEN_FRAME_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "hidden-frame";
|
|
52
52
|
const PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "preserved-space-element";
|
|
53
53
|
const SHADOW_ROOT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "shadow-root-element";
|
|
54
|
+
const SLOT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "slot";
|
|
55
|
+
const ASSIGNED_SLOT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "assigned-slot";
|
|
56
|
+
const ASSIGNED_SLOT_SEPARATOR = ".";
|
|
54
57
|
const WIN_ID_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "win-id";
|
|
55
58
|
const IMAGE_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "image";
|
|
56
59
|
const POSTER_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "poster";
|
|
@@ -165,6 +168,9 @@ export {
|
|
|
165
168
|
INPUT_VALUE_ATTRIBUTE_NAME,
|
|
166
169
|
INPUT_CHECKED_ATTRIBUTE_NAME,
|
|
167
170
|
SHADOW_ROOT_ATTRIBUTE_NAME,
|
|
171
|
+
SLOT_ATTRIBUTE_NAME,
|
|
172
|
+
ASSIGNED_SLOT_ATTRIBUTE_NAME,
|
|
173
|
+
ASSIGNED_SLOT_SEPARATOR,
|
|
168
174
|
STYLE_ATTRIBUTE_NAME,
|
|
169
175
|
LAZY_SRC_ATTRIBUTE_NAME,
|
|
170
176
|
STYLESHEET_ATTRIBUTE_NAME,
|
|
@@ -714,9 +720,20 @@ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map
|
|
|
714
720
|
}
|
|
715
721
|
getElementsInfo(win, doc, shadowRoot, options, data, adoptedStyleSheetsCache, elementHidden);
|
|
716
722
|
setNestingMarkersData(shadowRoot);
|
|
723
|
+
if (shadowRoot.slotAssignment == "manual") {
|
|
724
|
+
shadowRootInfo.slotAssignment = shadowRoot.slotAssignment;
|
|
725
|
+
shadowRoot.querySelectorAll("slot").forEach((slotElement, indexSlot) => {
|
|
726
|
+
slotElement.setAttribute(SLOT_ATTRIBUTE_NAME, indexSlot);
|
|
727
|
+
data.markedElements.push(slotElement);
|
|
728
|
+
slotElement.assignedNodes().filter(node => node.nodeType == ELEMENT_NODE_TYPE).forEach((node, indexNode) => {
|
|
729
|
+
node.setAttribute(ASSIGNED_SLOT_ATTRIBUTE_NAME, indexSlot + ASSIGNED_SLOT_SEPARATOR + indexNode);
|
|
730
|
+
data.markedElements.push(node);
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
}
|
|
717
734
|
shadowRootInfo.content = shadowRoot.innerHTML;
|
|
718
735
|
shadowRootInfo.mode = shadowRoot.mode;
|
|
719
|
-
shadowRootInfo.
|
|
736
|
+
shadowRootInfo.delegatesFocus = shadowRoot.delegatesFocus;
|
|
720
737
|
shadowRootInfo.clonable = shadowRoot.clonable;
|
|
721
738
|
shadowRootInfo.serializable = shadowRoot.serializable;
|
|
722
739
|
try {
|
|
@@ -1070,7 +1087,7 @@ function postProcessDoc(doc, markedElements, invalidElements) {
|
|
|
1070
1087
|
doc.head.querySelectorAll("*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)").forEach(element => element.removeAttribute("hidden"));
|
|
1071
1088
|
}
|
|
1072
1089
|
if (!markedElements) {
|
|
1073
|
-
const singleFileAttributes = [REMOVED_CONTENT_ATTRIBUTE_NAME, HIDDEN_FRAME_ATTRIBUTE_NAME, HIDDEN_CONTENT_ATTRIBUTE_NAME, PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, IMAGE_ATTRIBUTE_NAME, POSTER_ATTRIBUTE_NAME, VIDEO_ATTRIBUTE_NAME, CANVAS_ATTRIBUTE_NAME, INPUT_VALUE_ATTRIBUTE_NAME, INPUT_CHECKED_ATTRIBUTE_NAME, SHADOW_ROOT_ATTRIBUTE_NAME, STYLESHEET_ATTRIBUTE_NAME, ASYNC_SCRIPT_ATTRIBUTE_NAME];
|
|
1090
|
+
const singleFileAttributes = [REMOVED_CONTENT_ATTRIBUTE_NAME, HIDDEN_FRAME_ATTRIBUTE_NAME, HIDDEN_CONTENT_ATTRIBUTE_NAME, PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, IMAGE_ATTRIBUTE_NAME, POSTER_ATTRIBUTE_NAME, VIDEO_ATTRIBUTE_NAME, CANVAS_ATTRIBUTE_NAME, INPUT_VALUE_ATTRIBUTE_NAME, INPUT_CHECKED_ATTRIBUTE_NAME, SHADOW_ROOT_ATTRIBUTE_NAME, SLOT_ATTRIBUTE_NAME, ASSIGNED_SLOT_ATTRIBUTE_NAME, STYLESHEET_ATTRIBUTE_NAME, ASYNC_SCRIPT_ATTRIBUTE_NAME];
|
|
1074
1091
|
markedElements = doc.querySelectorAll(singleFileAttributes.map(name => "[" + name + "]").join(","));
|
|
1075
1092
|
}
|
|
1076
1093
|
markedElements.forEach(element => {
|
|
@@ -1086,6 +1103,8 @@ function postProcessDoc(doc, markedElements, invalidElements) {
|
|
|
1086
1103
|
element.removeAttribute(INPUT_VALUE_ATTRIBUTE_NAME);
|
|
1087
1104
|
element.removeAttribute(INPUT_CHECKED_ATTRIBUTE_NAME);
|
|
1088
1105
|
element.removeAttribute(SHADOW_ROOT_ATTRIBUTE_NAME);
|
|
1106
|
+
element.removeAttribute(SLOT_ATTRIBUTE_NAME);
|
|
1107
|
+
element.removeAttribute(ASSIGNED_SLOT_ATTRIBUTE_NAME);
|
|
1089
1108
|
element.removeAttribute(STYLESHEET_ATTRIBUTE_NAME);
|
|
1090
1109
|
element.removeAttribute(ASYNC_SCRIPT_ATTRIBUTE_NAME);
|
|
1091
1110
|
element.removeAttribute(STYLE_ATTRIBUTE_NAME);
|
package/core/index.js
CHANGED
|
@@ -434,6 +434,9 @@ const SHADOWROOT_ATTRIBUTE_NAME = "shadowrootmode";
|
|
|
434
434
|
const SHADOWROOT_DELEGATES_FOCUS = "shadowrootdelegatesfocus";
|
|
435
435
|
const SHADOWROOT_CLONABLE = "shadowrootclonable";
|
|
436
436
|
const SHADOWROOT_SERIALIZABLE = "shadowrootserializable";
|
|
437
|
+
const SLOT_NAME_PREFIX = "single-file-slot-";
|
|
438
|
+
const UNASSIGNED_SLOT_NAME = SLOT_NAME_PREFIX + "unassigned";
|
|
439
|
+
const SLOT_NAME_SEPARATOR = "-";
|
|
437
440
|
const SCRIPT_OPTIONS = "data-single-file-options";
|
|
438
441
|
const JAVASCRIPT_URI_PROTOCOL = "javascript:";
|
|
439
442
|
const DISABLED_SCRIPT_URI = "javascript:void(0)";
|
|
@@ -1504,6 +1507,23 @@ class Processor {
|
|
|
1504
1507
|
if (templateElement.querySelector(`[${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}]`)) {
|
|
1505
1508
|
templateElement.setAttribute(SHADOWROOT_ATTRIBUTE_NAME, "open");
|
|
1506
1509
|
}
|
|
1510
|
+
if (shadowRootData.slotAssignment == "manual") {
|
|
1511
|
+
const splitSlots = nameAssignedElements(element);
|
|
1512
|
+
templateElement.querySelectorAll("[" + util.SLOT_ATTRIBUTE_NAME + "]").forEach(slotElement => {
|
|
1513
|
+
const indexSlot = slotElement.getAttribute(util.SLOT_ATTRIBUTE_NAME);
|
|
1514
|
+
slotElement.removeAttribute(util.SLOT_ATTRIBUTE_NAME);
|
|
1515
|
+
if (splitSlots.has(indexSlot)) {
|
|
1516
|
+
splitSlots.get(indexSlot).forEach(position => {
|
|
1517
|
+
const splitSlotElement = slotElement.cloneNode(false);
|
|
1518
|
+
splitSlotElement.setAttribute("name", SLOT_NAME_PREFIX + indexSlot + SLOT_NAME_SEPARATOR + position);
|
|
1519
|
+
slotElement.before(splitSlotElement);
|
|
1520
|
+
});
|
|
1521
|
+
slotElement.remove();
|
|
1522
|
+
} else {
|
|
1523
|
+
slotElement.setAttribute("name", SLOT_NAME_PREFIX + indexSlot);
|
|
1524
|
+
}
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1507
1527
|
processElement(templateElement);
|
|
1508
1528
|
if (element.firstChild) {
|
|
1509
1529
|
element.insertBefore(templateElement, element.firstChild);
|
|
@@ -1831,6 +1851,34 @@ function collapseRecreatedElements(doc) {
|
|
|
1831
1851
|
return elements.length;
|
|
1832
1852
|
}
|
|
1833
1853
|
|
|
1854
|
+
function nameAssignedElements(hostElement) {
|
|
1855
|
+
const assignedElements = new Map();
|
|
1856
|
+
Array.from(hostElement.children).forEach(childElement => {
|
|
1857
|
+
const assignedSlot = childElement.getAttribute(util.ASSIGNED_SLOT_ATTRIBUTE_NAME);
|
|
1858
|
+
childElement.removeAttribute(util.ASSIGNED_SLOT_ATTRIBUTE_NAME);
|
|
1859
|
+
if (assignedSlot === null) {
|
|
1860
|
+
childElement.setAttribute("slot", UNASSIGNED_SLOT_NAME);
|
|
1861
|
+
} else {
|
|
1862
|
+
const [indexSlot, position] = assignedSlot.split(util.ASSIGNED_SLOT_SEPARATOR);
|
|
1863
|
+
if (!assignedElements.has(indexSlot)) {
|
|
1864
|
+
assignedElements.set(indexSlot, []);
|
|
1865
|
+
}
|
|
1866
|
+
assignedElements.get(indexSlot).push({ childElement, position: Number(position) });
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
const splitSlots = new Map();
|
|
1870
|
+
assignedElements.forEach((assigned, indexSlot) => {
|
|
1871
|
+
const inTreeOrder = assigned.every((item, index) => !index || assigned[index - 1].position < item.position);
|
|
1872
|
+
if (inTreeOrder) {
|
|
1873
|
+
assigned.forEach(({ childElement }) => childElement.setAttribute("slot", SLOT_NAME_PREFIX + indexSlot));
|
|
1874
|
+
} else {
|
|
1875
|
+
assigned.forEach(({ childElement, position }) => childElement.setAttribute("slot", SLOT_NAME_PREFIX + indexSlot + SLOT_NAME_SEPARATOR + position));
|
|
1876
|
+
splitSlots.set(indexSlot, assigned.map(({ position }) => position).sort((positionA, positionB) => positionA - positionB));
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1879
|
+
return splitSlots;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1834
1882
|
function removeInsertedParagraphs(doc) {
|
|
1835
1883
|
getInsertedParagraphs(doc).forEach((_, paragraph) => paragraph.remove());
|
|
1836
1884
|
}
|
package/core/infobar.js
CHANGED
|
@@ -149,12 +149,26 @@ const INFOBAR_STYLES = `
|
|
|
149
149
|
}
|
|
150
150
|
`;
|
|
151
151
|
const INFOBAR_ANIMATIONS_STYLES = `
|
|
152
|
-
.infobar {
|
|
152
|
+
.infobar::before {
|
|
153
|
+
content: "";
|
|
154
|
+
position: absolute;
|
|
155
|
+
inset: 0;
|
|
156
|
+
z-index: -1;
|
|
157
|
+
background-color: #dd6a00;
|
|
158
|
+
border-radius: inherit;
|
|
159
|
+
opacity: 0;
|
|
160
|
+
pointer-events: none;
|
|
153
161
|
animation-name: flash;
|
|
154
162
|
animation-duration: .5s;
|
|
155
163
|
animation-timing-function: cubic-bezier(0.39, 0.58, 0.57, 1);
|
|
156
164
|
animation-delay: 1s;
|
|
157
165
|
animation-iteration-count: 2;
|
|
166
|
+
transition: visibility 0s 999999s;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.infobar:focus-within::before, .infobar.infobar-focus::before {
|
|
170
|
+
visibility: hidden;
|
|
171
|
+
transition-delay: 0s;
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
.infobar::after {
|
|
@@ -180,10 +194,10 @@ const INFOBAR_ANIMATIONS_STYLES = `
|
|
|
180
194
|
|
|
181
195
|
@keyframes flash {
|
|
182
196
|
0%, 100% {
|
|
183
|
-
|
|
197
|
+
opacity: 0;
|
|
184
198
|
}
|
|
185
199
|
50% {
|
|
186
|
-
|
|
200
|
+
opacity: 1;
|
|
187
201
|
}
|
|
188
202
|
}
|
|
189
203
|
|
|
@@ -199,7 +213,7 @@ const INFOBAR_ANIMATIONS_STYLES = `
|
|
|
199
213
|
}
|
|
200
214
|
|
|
201
215
|
@media (prefers-reduced-motion: reduce) {
|
|
202
|
-
.infobar,
|
|
216
|
+
.infobar::before,
|
|
203
217
|
.infobar::after {
|
|
204
218
|
animation-name: none;
|
|
205
219
|
}
|
|
@@ -115,7 +115,8 @@ class ProcessorHelperCommon {
|
|
|
115
115
|
resourcePromises.push(this.processAttribute(doc, doc.querySelectorAll("audio[src], audio > source[src]"), "src", baseURI, options, "audio", resources, false, batchRequest, styles));
|
|
116
116
|
resourcePromises.push(this.processAttribute(doc, doc.querySelectorAll("video[src], video > source[src]"), "src", baseURI, options, "video", resources, false, batchRequest, styles));
|
|
117
117
|
resourcePromises.push(this.processAttribute(doc, doc.querySelectorAll("audio track[src], video track[src]"), "src", baseURI, options, null, resources, false, batchRequest, styles));
|
|
118
|
-
resourcePromises.push(this.processAttribute(doc, doc.querySelectorAll("model[src]"), "src", baseURI, options,
|
|
118
|
+
resourcePromises.push(this.processAttribute(doc, doc.querySelectorAll("model[src], model > source[src]"), "src", baseURI, options, "model", resources, false, batchRequest, styles));
|
|
119
|
+
resourcePromises.push(this.processAttribute(doc, doc.querySelectorAll("model[environmentmap]"), "environmentmap", baseURI, options, "model", resources, false, batchRequest, styles));
|
|
119
120
|
await Promise.all(resourcePromises);
|
|
120
121
|
if (options.saveFavicon) {
|
|
121
122
|
this.processShortcutIcons(doc);
|
|
@@ -333,19 +334,6 @@ class ProcessorHelperCommon {
|
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
|
|
336
|
-
removeSingleLineCssComments(stylesheet) {
|
|
337
|
-
if (stylesheet.children) {
|
|
338
|
-
const removedRules = [];
|
|
339
|
-
for (let cssRule = stylesheet.children.head; cssRule; cssRule = cssRule.next) {
|
|
340
|
-
const ruleData = cssRule.data;
|
|
341
|
-
if (ruleData.type == "Raw" && ruleData.value && ruleData.value.trim().startsWith("//")) {
|
|
342
|
-
removedRules.push(cssRule);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
removedRules.forEach(cssRule => stylesheet.children.remove(cssRule));
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
337
|
replacePseudoClassDefined(stylesheet) {
|
|
350
338
|
cssTree.walk(stylesheet, {
|
|
351
339
|
enter: function (node, item, list) {
|
|
@@ -109,9 +109,6 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
if (stylesheet && stylesheet.children) {
|
|
112
|
-
if (options.compressCSS) {
|
|
113
|
-
this.removeSingleLineCssComments(stylesheet);
|
|
114
|
-
}
|
|
115
112
|
this.replacePseudoClassDefined(stylesheet);
|
|
116
113
|
options.inlineStylesheets.forEach(({ content, styleElement }, index) => {
|
|
117
114
|
if (content === element.textContent) {
|
|
@@ -471,9 +468,6 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
471
468
|
|
|
472
469
|
generateStylesheetContent(stylesheet, options) {
|
|
473
470
|
let stylesheetContent = cssTree.generate(stylesheet);
|
|
474
|
-
if (options.compressCSS) {
|
|
475
|
-
stylesheetContent = util.compressCSS(stylesheetContent);
|
|
476
|
-
}
|
|
477
471
|
if (options.saveOriginalURLs) {
|
|
478
472
|
stylesheetContent = replaceOriginalURLs(stylesheetContent);
|
|
479
473
|
}
|
|
@@ -504,14 +504,8 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
generateStylesheetContent(stylesheet, options) {
|
|
507
|
-
if (options.compressCSS) {
|
|
508
|
-
this.removeSingleLineCssComments(stylesheet);
|
|
509
|
-
}
|
|
510
507
|
this.replacePseudoClassDefined(stylesheet);
|
|
511
508
|
let stylesheetContent = cssTree.generate(stylesheet);
|
|
512
|
-
if (options.compressCSS) {
|
|
513
|
-
stylesheetContent = util.compressCSS(stylesheetContent);
|
|
514
|
-
}
|
|
515
509
|
if (options.saveOriginalURLs) {
|
|
516
510
|
stylesheetContent = replaceOriginalURLs(stylesheetContent);
|
|
517
511
|
}
|
package/core/util.js
CHANGED
|
@@ -65,8 +65,11 @@ const CONTENT_TYPE_EXTENSIONS = {
|
|
|
65
65
|
const CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";
|
|
66
66
|
const TRANSPORT_STREAM_SYNC_BYTE = 71;
|
|
67
67
|
const TRANSPORT_STREAM_PACKET_SIZE = 188;
|
|
68
|
+
const ZIP_FILENAME_LENGTH_OFFSET = 26;
|
|
69
|
+
const ZIP_FILENAME_OFFSET = 30;
|
|
70
|
+
const USD_FILENAME_REGEXP = /\.usd[ac]?$/i;
|
|
68
71
|
const CONTENT_TYPES_HTML = ["text/html", "application/xhtml+xml"];
|
|
69
|
-
const EXPECTED_TYPES_MEDIA = ["font", "image", "video", "audio"];
|
|
72
|
+
const EXPECTED_TYPES_MEDIA = ["font", "image", "video", "audio", "model"];
|
|
70
73
|
|
|
71
74
|
const URL = globalThis.URL;
|
|
72
75
|
const DOMParser = globalThis.DOMParser;
|
|
@@ -167,9 +170,6 @@ function getInstance(utilOptions) {
|
|
|
167
170
|
removeUnusedFonts(doc, stylesheets, styles, options) {
|
|
168
171
|
return modules.fontsMinifier.process(doc, stylesheets, styles, options);
|
|
169
172
|
},
|
|
170
|
-
compressCSS(content, options) {
|
|
171
|
-
return vendor.cssMinifier.processString(content, options);
|
|
172
|
-
},
|
|
173
173
|
minifyMedias(stylesheets, options) {
|
|
174
174
|
return modules.mediasAltMinifier.process(stylesheets, options);
|
|
175
175
|
},
|
|
@@ -220,6 +220,9 @@ function getInstance(utilOptions) {
|
|
|
220
220
|
INPUT_VALUE_ATTRIBUTE_NAME: helper.INPUT_VALUE_ATTRIBUTE_NAME,
|
|
221
221
|
INPUT_CHECKED_ATTRIBUTE_NAME: helper.INPUT_CHECKED_ATTRIBUTE_NAME,
|
|
222
222
|
SHADOW_ROOT_ATTRIBUTE_NAME: helper.SHADOW_ROOT_ATTRIBUTE_NAME,
|
|
223
|
+
SLOT_ATTRIBUTE_NAME: helper.SLOT_ATTRIBUTE_NAME,
|
|
224
|
+
ASSIGNED_SLOT_ATTRIBUTE_NAME: helper.ASSIGNED_SLOT_ATTRIBUTE_NAME,
|
|
225
|
+
ASSIGNED_SLOT_SEPARATOR: helper.ASSIGNED_SLOT_SEPARATOR,
|
|
223
226
|
PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME: helper.PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME,
|
|
224
227
|
STYLESHEET_ATTRIBUTE_NAME: helper.STYLESHEET_ATTRIBUTE_NAME,
|
|
225
228
|
SELECTED_CONTENT_ATTRIBUTE_NAME: helper.SELECTED_CONTENT_ATTRIBUTE_NAME,
|
|
@@ -260,7 +263,7 @@ function getInstance(utilOptions) {
|
|
|
260
263
|
});
|
|
261
264
|
}
|
|
262
265
|
try {
|
|
263
|
-
const accept = options.acceptHeaders
|
|
266
|
+
const accept = (options.acceptHeaders && options.acceptHeaders[options.expectedType]) || "*/*";
|
|
264
267
|
if (options.frameId) {
|
|
265
268
|
try {
|
|
266
269
|
response = await Promise.race([
|
|
@@ -503,6 +506,26 @@ function guessMIMEType(expectedType, buffer) {
|
|
|
503
506
|
return "audio/3gpp";
|
|
504
507
|
}
|
|
505
508
|
}
|
|
509
|
+
if (expectedType == "model") {
|
|
510
|
+
if (compareBytes([255, 255, 255, 255], [103, 108, 84, 70])) {
|
|
511
|
+
return "model/gltf-binary";
|
|
512
|
+
}
|
|
513
|
+
if (compareBytes([255, 255, 255, 255], [80, 75, 3, 4]) && isUSDZ()) {
|
|
514
|
+
return "model/vnd.usdz+zip";
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function isUSDZ() {
|
|
519
|
+
const value = new Uint8Array(buffer);
|
|
520
|
+
if (value.length > ZIP_FILENAME_OFFSET) {
|
|
521
|
+
const filenameLength = value[ZIP_FILENAME_LENGTH_OFFSET] | (value[ZIP_FILENAME_LENGTH_OFFSET + 1] << 8);
|
|
522
|
+
if (value.length >= ZIP_FILENAME_OFFSET + filenameLength) {
|
|
523
|
+
const filename = new TextDecoder().decode(value.subarray(ZIP_FILENAME_OFFSET, ZIP_FILENAME_OFFSET + filenameLength));
|
|
524
|
+
return USD_FILENAME_REGEXP.test(filename);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
506
529
|
|
|
507
530
|
function isTransportStream() {
|
|
508
531
|
const value = new Uint8Array(buffer);
|
|
@@ -33,9 +33,7 @@ const PSEUDO_ELEMENT_SYNONYMS = new Set(["after", "before", "first-letter", "fir
|
|
|
33
33
|
const FUNCTIONAL_PSEUDO_CLASS_NAMES = new Set(["not", "is", "where", "has", "nth-child", "nth-last-child"]);
|
|
34
34
|
const MEDIA_AT_RULE_NAME = "media";
|
|
35
35
|
const SUPPORTS_AT_RULE_NAME = "supports";
|
|
36
|
-
const
|
|
37
|
-
const CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, "container", STARTING_STYLE_AT_RULE_NAME]);
|
|
38
|
-
const UNCERTAIN_CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, STARTING_STYLE_AT_RULE_NAME]);
|
|
36
|
+
const CERTAIN_CONDITIONAL_AT_RULE_NAMES = new Set(["container"]);
|
|
39
37
|
const RULE_TYPE = "Rule";
|
|
40
38
|
const AT_RULE_TYPE = "Atrule";
|
|
41
39
|
const NESTING_SELECTOR_TYPE = "NestingSelector";
|
|
@@ -48,6 +46,7 @@ const IDENTIFIER_TYPE = "Identifier";
|
|
|
48
46
|
const PSEUDO_ELEMENT_SELECTOR_TYPE = "PseudoElementSelector";
|
|
49
47
|
const LAYER_NAME = "layer";
|
|
50
48
|
const SCOPE_NAME = "scope";
|
|
49
|
+
const UNCONDITIONAL_AT_RULE_NAMES = new Set([LAYER_NAME, SCOPE_NAME]);
|
|
51
50
|
const IMPORT_NAME = "import";
|
|
52
51
|
const FONT_FACE_NAME = "font-face";
|
|
53
52
|
const KEYFRAMES_NAME = "keyframes";
|
|
@@ -84,8 +83,9 @@ const CUSTOM_PROPERTY_PREFIX = "--";
|
|
|
84
83
|
const LAYER_NAME_SEPARATOR = ".";
|
|
85
84
|
const LAYER_NAME_KEY_SEPARATOR = "\u0000";
|
|
86
85
|
const CONTEXT_KEY_SEPARATOR = "|";
|
|
87
|
-
const
|
|
88
|
-
const
|
|
86
|
+
const REVERT_RULE_KEYWORD = "revert-rule";
|
|
87
|
+
const REVERT_LAYER_KEYWORDS = ["revert-layer", REVERT_RULE_KEYWORD];
|
|
88
|
+
const REVERT_LAYER_TEST = /(^|[^-\w])revert-(layer|rule)([^-\w]|$)/i;
|
|
89
89
|
const NAMESPACE_AT_RULE_NAME = "namespace";
|
|
90
90
|
const URL_TYPE = "Url";
|
|
91
91
|
const STRING_TYPE = "String";
|
|
@@ -120,7 +120,6 @@ function getValueValidity(property, value) {
|
|
|
120
120
|
if (name && INVALID_CSS_ESCAPE_TEST.test(name)) {
|
|
121
121
|
return VALIDITY_INVALID;
|
|
122
122
|
}
|
|
123
|
-
const isVendorValue = Boolean(name && name.startsWith(VENDOR_PREFIX));
|
|
124
123
|
if (globalThis.CSS && globalThis.CSS.supports) {
|
|
125
124
|
let supported;
|
|
126
125
|
try {
|
|
@@ -128,14 +127,15 @@ function getValueValidity(property, value) {
|
|
|
128
127
|
} catch {
|
|
129
128
|
return VALIDITY_UNKNOWN;
|
|
130
129
|
}
|
|
131
|
-
|
|
132
|
-
return VALIDITY_VALID;
|
|
133
|
-
}
|
|
134
|
-
return isVendorValue ? VALIDITY_INVALID : VALIDITY_UNKNOWN;
|
|
130
|
+
return supported ? VALIDITY_VALID : VALIDITY_UNKNOWN;
|
|
135
131
|
}
|
|
132
|
+
const isVendorValue = Boolean(name && name.startsWith(VENDOR_PREFIX));
|
|
136
133
|
if (cssTree.find(value, node => node.type === FUNCTION_TYPE && decodeName(node.name) === VAR_FUNCTION_NAME)) {
|
|
137
134
|
return VALIDITY_VALID;
|
|
138
135
|
}
|
|
136
|
+
if (name && decodeIdentifier(name).toLowerCase() === REVERT_RULE_KEYWORD) {
|
|
137
|
+
return VALIDITY_VALID;
|
|
138
|
+
}
|
|
139
139
|
let match;
|
|
140
140
|
try {
|
|
141
141
|
match = cssTree.lexer.matchProperty(property, value);
|
|
@@ -175,6 +175,7 @@ function process(doc, stylesheets) {
|
|
|
175
175
|
scopedSelectorTexts: new Map(),
|
|
176
176
|
supportedSelectors: new Map(),
|
|
177
177
|
valueValidities: new Map(),
|
|
178
|
+
baselineValues: new Map(),
|
|
178
179
|
preludeTexts: new Map(),
|
|
179
180
|
rulesCounter: 0,
|
|
180
181
|
scopeIdCounter: 0
|
|
@@ -306,7 +307,7 @@ function createAnonymousLayerSegment(docContext) {
|
|
|
306
307
|
}
|
|
307
308
|
|
|
308
309
|
function buildConditionalStack(conditionalStack, ruleData, docContext) {
|
|
309
|
-
const isConditional =
|
|
310
|
+
const isConditional = !UNCONDITIONAL_AT_RULE_NAMES.has(decodeName(ruleData.name));
|
|
310
311
|
return isConditional
|
|
311
312
|
? [...conditionalStack, { name: ruleData.name, prelude: getPreludeText(ruleData.prelude, docContext) }]
|
|
312
313
|
: conditionalStack;
|
|
@@ -340,7 +341,7 @@ function isImportRule(ruleData) {
|
|
|
340
341
|
|
|
341
342
|
function registerLayerDeclaration(layerStack, layerSegments, conditionalStack, docContext) {
|
|
342
343
|
const position = docContext.layerDeclarationCounter++;
|
|
343
|
-
const certain = !conditionalStack.some(context =>
|
|
344
|
+
const certain = !conditionalStack.some(context => !CERTAIN_CONDITIONAL_AT_RULE_NAMES.has(decodeName(context.name)));
|
|
344
345
|
const segments = [...layerStack, ...layerSegments];
|
|
345
346
|
for (let length = layerStack.length + 1; length <= segments.length; length++) {
|
|
346
347
|
const fullLayerName = getFullLayerName(segments.slice(0, length));
|
|
@@ -822,7 +823,9 @@ function computeCascadedStylesForElement(element, winningDeclarations, docContex
|
|
|
822
823
|
winningDeclarations.add(declaration);
|
|
823
824
|
if (validity === VALIDITY_VALID) {
|
|
824
825
|
hasWinningRevertLayer ||= hasRevertLayerKeyword(declaration, docContext);
|
|
825
|
-
|
|
826
|
+
if (isBaselineValue(declaration, docContext)) {
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
826
829
|
}
|
|
827
830
|
}
|
|
828
831
|
});
|
|
@@ -903,7 +906,7 @@ function findRevertLayerKeyword(declaration) {
|
|
|
903
906
|
if (value.type === RAW_TYPE) {
|
|
904
907
|
return REVERT_LAYER_TEST.test(decodeIdentifier(value.value));
|
|
905
908
|
}
|
|
906
|
-
return Boolean(cssTree.find(value, node => node.type === IDENTIFIER_TYPE && decodeIdentifier(node.name).toLowerCase()
|
|
909
|
+
return Boolean(cssTree.find(value, node => node.type === IDENTIFIER_TYPE && REVERT_LAYER_KEYWORDS.includes(decodeIdentifier(node.name).toLowerCase())));
|
|
907
910
|
}
|
|
908
911
|
|
|
909
912
|
function createContextKey(conditionalStack) {
|
|
@@ -952,6 +955,28 @@ function collectDeclarationItemsForElement(element, docContext) {
|
|
|
952
955
|
}
|
|
953
956
|
}
|
|
954
957
|
|
|
958
|
+
function isBaselineValue(declaration, docContext) {
|
|
959
|
+
const { property, value } = declaration.data;
|
|
960
|
+
if (property.startsWith(CUSTOM_PROPERTY_PREFIX)) {
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
if (!docContext.baselineValues.has(value)) {
|
|
964
|
+
docContext.baselineValues.set(value, getBaselineValue(property, value));
|
|
965
|
+
}
|
|
966
|
+
return docContext.baselineValues.get(value);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function getBaselineValue(property, value) {
|
|
970
|
+
if (cssTree.find(value, node => node.type === FUNCTION_TYPE && decodeName(node.name) === VAR_FUNCTION_NAME)) {
|
|
971
|
+
return true;
|
|
972
|
+
}
|
|
973
|
+
try {
|
|
974
|
+
return Boolean(cssTree.lexer.matchProperty(property, value).matched);
|
|
975
|
+
} catch {
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
955
980
|
function getCachedValueValidity(property, value, docContext) {
|
|
956
981
|
if (property.startsWith(CUSTOM_PROPERTY_PREFIX)) {
|
|
957
982
|
return VALIDITY_VALID;
|
package/package.json
CHANGED
package/vendor/index.js
CHANGED
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
import * as zip from "./zip/zip.js";
|
|
25
25
|
import * as fontPropertyParser from "./css-font-property-parser.js";
|
|
26
26
|
import * as mediaQueryParser from "./css-media-query-parser.js";
|
|
27
|
-
import * as cssMinifier from "./css-minifier.js";
|
|
28
27
|
import * as cssUnescape from "./css-unescape.js";
|
|
29
28
|
import * as srcsetParser from "./html-srcset-parser.js";
|
|
30
29
|
import { MIMEType } from "./mime-type-parser.js";
|
|
@@ -33,7 +32,6 @@ export {
|
|
|
33
32
|
zip,
|
|
34
33
|
fontPropertyParser,
|
|
35
34
|
mediaQueryParser,
|
|
36
|
-
cssMinifier,
|
|
37
35
|
cssUnescape,
|
|
38
36
|
srcsetParser,
|
|
39
37
|
MIMEType
|
package/vendor/css-minifier.js
DELETED
|
@@ -1,801 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* The MIT License (MIT)
|
|
3
|
-
*
|
|
4
|
-
* Author: Gildas Lormeau
|
|
5
|
-
*
|
|
6
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
* in the Software without restriction, including without limitation the rights
|
|
9
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
* furnished to do so, subject to the following conditions:
|
|
12
|
-
*
|
|
13
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
* copies or substantial portions of the Software.
|
|
15
|
-
*
|
|
16
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
* SOFTWARE.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
// derived from https://github.com/fmarcia/UglifyCSS
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* UglifyCSS
|
|
29
|
-
* Port of YUI CSS Compressor to NodeJS
|
|
30
|
-
* Author: Franck Marcia - https://github.com/fmarcia
|
|
31
|
-
* MIT licenced
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* cssmin.js
|
|
36
|
-
* Author: Stoyan Stefanov - http://phpied.com/
|
|
37
|
-
* This is a JavaScript port of the CSS minification tool
|
|
38
|
-
* distributed with YUICompressor, itself a port
|
|
39
|
-
* of the cssmin utility by Isaac Schlueter - http://foohack.com/
|
|
40
|
-
* Permission is hereby granted to use the JavaScript version under the same
|
|
41
|
-
* conditions as the YUICompressor (original YUICompressor note below).
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* YUI Compressor
|
|
46
|
-
* http://developer.yahoo.com/yui/compressor/
|
|
47
|
-
* Author: Julien Lecomte - http://www.julienlecomte.net/
|
|
48
|
-
* Copyright (c) 2011 Yahoo! Inc. All rights reserved.
|
|
49
|
-
* The copyrights embodied in the content of this file are licensed
|
|
50
|
-
* by Yahoo! Inc. under the BSD (revised) open source license.
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @type {string} - placeholder prefix
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
const ___PRESERVED_TOKEN_ = "___PRESERVED_TOKEN_";
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @typedef {object} options - UglifyCSS options
|
|
61
|
-
* @property {number} [maxLineLen=0] - Maximum line length of uglified CSS
|
|
62
|
-
* @property {boolean} [expandVars=false] - Expand variables
|
|
63
|
-
* @property {boolean} [uglyComments=false] - Removes newlines within preserved comments
|
|
64
|
-
* @property {boolean} [cuteComments=false] - Preserves newlines within and around preserved comments
|
|
65
|
-
* @property {boolean} [debug=false] - Prints full error stack on error
|
|
66
|
-
* @property {string} [output=''] - Output file name
|
|
67
|
-
*/
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @type {options} - UglifyCSS options
|
|
71
|
-
*/
|
|
72
|
-
|
|
73
|
-
const defaultOptions = {
|
|
74
|
-
maxLineLen: 0,
|
|
75
|
-
expandVars: false,
|
|
76
|
-
uglyComments: false,
|
|
77
|
-
cuteComments: false,
|
|
78
|
-
debug: false,
|
|
79
|
-
output: ""
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const REGEXP_DATA_URI = /url\(\s*(["']?)data:/g;
|
|
83
|
-
const REGEXP_WHITE_SPACES = /\s+/g;
|
|
84
|
-
const REGEXP_NEW_LINE = /\n/g;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* extractDataUrls replaces all data urls with tokens before we start
|
|
88
|
-
* compressing, to avoid performance issues running some of the subsequent
|
|
89
|
-
* regexes against large strings chunks.
|
|
90
|
-
*
|
|
91
|
-
* @param {string} css - CSS content
|
|
92
|
-
* @param {string[]} preservedTokens - Global array of tokens to preserve
|
|
93
|
-
*
|
|
94
|
-
* @return {string} Processed CSS
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
function extractDataUrls(css, preservedTokens) {
|
|
98
|
-
|
|
99
|
-
// Leave data urls alone to increase parse performance.
|
|
100
|
-
const pattern = REGEXP_DATA_URI;
|
|
101
|
-
const maxIndex = css.length - 1;
|
|
102
|
-
const sb = [];
|
|
103
|
-
|
|
104
|
-
let appendIndex = 0, match;
|
|
105
|
-
|
|
106
|
-
// Since we need to account for non-base64 data urls, we need to handle
|
|
107
|
-
// ' and ) being part of the data string. Hence switching to indexOf,
|
|
108
|
-
// to determine whether or not we have matching string terminators and
|
|
109
|
-
// handling sb appends directly, instead of using matcher.append* methods.
|
|
110
|
-
|
|
111
|
-
while ((match = pattern.exec(css)) !== null) {
|
|
112
|
-
|
|
113
|
-
const startIndex = match.index + 4; // 'url('.length()
|
|
114
|
-
let terminator = match[1]; // ', " or empty (not quoted)
|
|
115
|
-
|
|
116
|
-
if (terminator.length === 0) {
|
|
117
|
-
terminator = ")";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
let foundTerminator = false, endIndex = pattern.lastIndex - 1;
|
|
121
|
-
|
|
122
|
-
while (foundTerminator === false && endIndex + 1 <= maxIndex && endIndex != -1) {
|
|
123
|
-
endIndex = css.indexOf(terminator, endIndex + 1);
|
|
124
|
-
|
|
125
|
-
// endIndex == 0 doesn't really apply here
|
|
126
|
-
if ((endIndex > 0) && (css.charAt(endIndex - 1) !== "\\")) {
|
|
127
|
-
foundTerminator = true;
|
|
128
|
-
if (")" != terminator) {
|
|
129
|
-
endIndex = css.indexOf(")", endIndex);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Enough searching, start moving stuff over to the buffer
|
|
135
|
-
sb.push(css.substring(appendIndex, match.index));
|
|
136
|
-
|
|
137
|
-
if (foundTerminator) {
|
|
138
|
-
|
|
139
|
-
let token = css.substring(startIndex, endIndex);
|
|
140
|
-
const parts = token.split(",");
|
|
141
|
-
if (parts.length > 1 && parts[0].slice(-7) == ";base64") {
|
|
142
|
-
token = token.replace(REGEXP_WHITE_SPACES, "");
|
|
143
|
-
} else {
|
|
144
|
-
token = token.replace(REGEXP_NEW_LINE, " ");
|
|
145
|
-
token = token.replace(REGEXP_WHITE_SPACES, " ");
|
|
146
|
-
token = token.replace(REGEXP_PRESERVE_HSLA1, "");
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
preservedTokens.push(token);
|
|
150
|
-
|
|
151
|
-
const preserver = "url(" + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___)";
|
|
152
|
-
sb.push(preserver);
|
|
153
|
-
|
|
154
|
-
appendIndex = endIndex + 1;
|
|
155
|
-
} else {
|
|
156
|
-
// No end terminator found, re-add the whole match. Should we throw/warn here?
|
|
157
|
-
sb.push(css.substring(match.index, pattern.lastIndex));
|
|
158
|
-
appendIndex = pattern.lastIndex;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
sb.push(css.substring(appendIndex));
|
|
163
|
-
|
|
164
|
-
return sb.join("");
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const REGEXP_HEX_COLORS = /(=\s*?["']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/gi;
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* compressHexColors compresses hex color values of the form #AABBCC to #ABC.
|
|
171
|
-
*
|
|
172
|
-
* DOES NOT compress CSS ID selectors which match the above pattern (which would
|
|
173
|
-
* break things), like #AddressForm { ... }
|
|
174
|
-
*
|
|
175
|
-
* DOES NOT compress IE filters, which have hex color values (which would break
|
|
176
|
-
* things), like chroma(color='#FFFFFF');
|
|
177
|
-
*
|
|
178
|
-
* DOES NOT compress invalid hex values, like background-color: #aabbccdd
|
|
179
|
-
*
|
|
180
|
-
* @param {string} css - CSS content
|
|
181
|
-
*
|
|
182
|
-
* @return {string} Processed CSS
|
|
183
|
-
*/
|
|
184
|
-
|
|
185
|
-
function compressHexColors(css) {
|
|
186
|
-
|
|
187
|
-
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
|
|
188
|
-
|
|
189
|
-
const pattern = REGEXP_HEX_COLORS;
|
|
190
|
-
const sb = [];
|
|
191
|
-
|
|
192
|
-
let index = 0, match;
|
|
193
|
-
|
|
194
|
-
while ((match = pattern.exec(css)) !== null) {
|
|
195
|
-
|
|
196
|
-
sb.push(css.substring(index, match.index));
|
|
197
|
-
|
|
198
|
-
const isFilter = match[1];
|
|
199
|
-
|
|
200
|
-
if (isFilter) {
|
|
201
|
-
// Restore, maintain case, otherwise filter will break
|
|
202
|
-
sb.push(match[1] + "#" + (match[2] + match[3] + match[4] + match[5] + match[6] + match[7]));
|
|
203
|
-
} else {
|
|
204
|
-
if (match[2].toLowerCase() == match[3].toLowerCase() &&
|
|
205
|
-
match[4].toLowerCase() == match[5].toLowerCase() &&
|
|
206
|
-
match[6].toLowerCase() == match[7].toLowerCase()) {
|
|
207
|
-
|
|
208
|
-
// Compress.
|
|
209
|
-
sb.push("#" + (match[3] + match[5] + match[7]).toLowerCase());
|
|
210
|
-
} else {
|
|
211
|
-
// Non compressible color, restore but lower case.
|
|
212
|
-
sb.push("#" + (match[2] + match[3] + match[4] + match[5] + match[6] + match[7]).toLowerCase());
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
index = pattern.lastIndex = pattern.lastIndex - match[8].length;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
sb.push(css.substring(index));
|
|
220
|
-
|
|
221
|
-
return sb.join("");
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const REGEXP_KEYFRAMES = /@[a-z0-9-_]*keyframes\s+[a-z0-9-_]+\s*{/gi;
|
|
225
|
-
const REGEXP_WHITE_SPACE = /(^\s|\s$)/g;
|
|
226
|
-
|
|
227
|
-
/** keyframes preserves 0 followed by unit in keyframes steps
|
|
228
|
-
*
|
|
229
|
-
* @param {string} content - CSS content
|
|
230
|
-
* @param {string[]} preservedTokens - Global array of tokens to preserve
|
|
231
|
-
*
|
|
232
|
-
* @return {string} Processed CSS
|
|
233
|
-
*/
|
|
234
|
-
|
|
235
|
-
function keyframes(content, preservedTokens) {
|
|
236
|
-
|
|
237
|
-
const pattern = REGEXP_KEYFRAMES;
|
|
238
|
-
|
|
239
|
-
let index = 0, buffer;
|
|
240
|
-
|
|
241
|
-
const preserve = (part, i) => {
|
|
242
|
-
part = part.replace(REGEXP_WHITE_SPACE, "");
|
|
243
|
-
if (part.charAt(0) === "0") {
|
|
244
|
-
preservedTokens.push(part);
|
|
245
|
-
buffer[i] = ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
while (true) {
|
|
250
|
-
|
|
251
|
-
let level = 0;
|
|
252
|
-
buffer = "";
|
|
253
|
-
|
|
254
|
-
let startIndex = content.slice(index).search(pattern);
|
|
255
|
-
if (startIndex < 0) {
|
|
256
|
-
break;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
index += startIndex;
|
|
260
|
-
startIndex = index;
|
|
261
|
-
|
|
262
|
-
const len = content.length;
|
|
263
|
-
const buffers = [];
|
|
264
|
-
|
|
265
|
-
for (; index < len; ++index) {
|
|
266
|
-
|
|
267
|
-
const ch = content.charAt(index);
|
|
268
|
-
|
|
269
|
-
if (ch === "{") {
|
|
270
|
-
|
|
271
|
-
if (level === 0) {
|
|
272
|
-
buffers.push(buffer.replace(REGEXP_WHITE_SPACE, ""));
|
|
273
|
-
|
|
274
|
-
} else if (level === 1) {
|
|
275
|
-
|
|
276
|
-
buffer = buffer.split(",");
|
|
277
|
-
|
|
278
|
-
buffer.forEach(preserve);
|
|
279
|
-
|
|
280
|
-
buffers.push(buffer.join(",").replace(REGEXP_WHITE_SPACE, ""));
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
buffer = "";
|
|
284
|
-
level += 1;
|
|
285
|
-
|
|
286
|
-
} else if (ch === "}") {
|
|
287
|
-
|
|
288
|
-
if (level === 2) {
|
|
289
|
-
buffers.push("{" + buffer.replace(REGEXP_WHITE_SPACE, "") + "}");
|
|
290
|
-
buffer = "";
|
|
291
|
-
|
|
292
|
-
} else if (level === 1) {
|
|
293
|
-
content = content.slice(0, startIndex) +
|
|
294
|
-
buffers.shift() + "{" +
|
|
295
|
-
buffers.join("") +
|
|
296
|
-
content.slice(index);
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
level -= 1;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (level < 0) {
|
|
304
|
-
break;
|
|
305
|
-
|
|
306
|
-
} else if (ch !== "{" && ch !== "}") {
|
|
307
|
-
buffer += ch;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return content;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* collectComments collects all comment blocks and return new content with comment placeholders
|
|
317
|
-
*
|
|
318
|
-
* @param {string} content - CSS content
|
|
319
|
-
* @param {string[]} comments - Global array of extracted comments
|
|
320
|
-
*
|
|
321
|
-
* @return {string} Processed CSS
|
|
322
|
-
*/
|
|
323
|
-
|
|
324
|
-
function collectComments(content, comments) {
|
|
325
|
-
|
|
326
|
-
const table = [];
|
|
327
|
-
|
|
328
|
-
let from = 0, end;
|
|
329
|
-
|
|
330
|
-
while (true) {
|
|
331
|
-
|
|
332
|
-
const start = content.indexOf("/*", from);
|
|
333
|
-
|
|
334
|
-
if (start > -1) {
|
|
335
|
-
|
|
336
|
-
end = content.indexOf("*/", start + 2);
|
|
337
|
-
|
|
338
|
-
if (end > -1) {
|
|
339
|
-
comments.push(content.slice(start + 2, end));
|
|
340
|
-
table.push(content.slice(from, start));
|
|
341
|
-
table.push("/*___PRESERVE_CANDIDATE_COMMENT_" + (comments.length - 1) + "___*/");
|
|
342
|
-
from = end + 2;
|
|
343
|
-
|
|
344
|
-
} else {
|
|
345
|
-
// unterminated comment
|
|
346
|
-
end = -2;
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
} else {
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
table.push(content.slice(end + 2));
|
|
356
|
-
|
|
357
|
-
return table.join("");
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* processString uglifies a CSS string
|
|
362
|
-
*
|
|
363
|
-
* @param {string} content - CSS string
|
|
364
|
-
* @param {options} options - UglifyCSS options
|
|
365
|
-
*
|
|
366
|
-
* @return {string} Uglified result
|
|
367
|
-
*/
|
|
368
|
-
|
|
369
|
-
// const REGEXP_EMPTY_RULES = /[^};{/]+\{\}/g;
|
|
370
|
-
const REGEXP_PRESERVE_STRING1 = /"([^\\"])*"/g;
|
|
371
|
-
const REGEXP_PRESERVE_STRING1_BIS = /"(\\.)*"/g;
|
|
372
|
-
const REGEXP_PRESERVE_STRING1_TER = /"(\\)*"/g;
|
|
373
|
-
const REGEXP_PRESERVE_STRING2 = /'([^\\'])*'/g;
|
|
374
|
-
const REGEXP_PRESERVE_STRING2_BIS = /'(\\.)*'/g;
|
|
375
|
-
const REGEXP_PRESERVE_STRING2_TER = /'(\\)*'/g;
|
|
376
|
-
const REGEXP_MINIFY_ALPHA = /progid:DXImageTransform.Microsoft.Alpha\(Opacity=/gi;
|
|
377
|
-
const REGEXP_PRESERVE_TOKEN1 = /\r\n/g;
|
|
378
|
-
const REGEXP_PRESERVE_TOKEN2 = /[\r\n]/g;
|
|
379
|
-
const REGEXP_VARIABLES = /@variables\s*\{\s*([^}]+)\s*\}/g;
|
|
380
|
-
const REGEXP_VARIABLE = /\s*([a-z0-9-]+)\s*:\s*([^;}]+)\s*/gi;
|
|
381
|
-
const REGEXP_VARIABLE_VALUE = /var\s*\(\s*([^)]+)\s*\)/g;
|
|
382
|
-
const REGEXP_PRESERVE_CALC = /calc\(([^;}]*)\)/g;
|
|
383
|
-
const REGEXP_TRIM = /(^\s*|\s*$)/g;
|
|
384
|
-
const REGEXP_PRESERVE_CALC2 = /\( /g;
|
|
385
|
-
const REGEXP_PRESERVE_CALC3 = / \)/g;
|
|
386
|
-
const REGEXP_PRESERVE_MATRIX = /\s*filter:\s*progid:DXImageTransform.Microsoft.Matrix\(([^)]+)\);/g;
|
|
387
|
-
const REGEXP_REMOVE_SPACES = /(^|\})(([^{:])+:)+([^{]*{)/g;
|
|
388
|
-
const REGEXP_REMOVE_SPACES2 = /\s+([!{;:>+()\],])/g;
|
|
389
|
-
const REGEXP_REMOVE_SPACES2_BIS = /([^\\])\s+([}])/g;
|
|
390
|
-
const REGEXP_RESTORE_SPACE_IMPORTANT = /!important/g;
|
|
391
|
-
const REGEXP_PSEUDOCLASSCOLON = /___PSEUDOCLASSCOLON___/g;
|
|
392
|
-
const REGEXP_COLUMN = /:/g;
|
|
393
|
-
const REGEXP_PRESERVE_ZERO_UNIT = /\s*(animation|animation-delay|animation-duration|transition|transition-delay|transition-duration):\s*([^;}]+)/gi;
|
|
394
|
-
const REGEXP_PRESERVE_ZERO_UNIT1 = /(^|\D)0?\.?0(m?s)/gi;
|
|
395
|
-
const REGEXP_PRESERVE_FLEX = /\s*(flex|flex-basis):\s*([^;}]+)/gi;
|
|
396
|
-
const REGEXP_SPACES = /\s+/;
|
|
397
|
-
const REGEXP_PRESERVE_HSLA = /(hsla?)\(([^)]+)\)/g;
|
|
398
|
-
const REGEXP_PRESERVE_HSLA1 = /(^\s+|\s+$)/g;
|
|
399
|
-
const REGEXP_RETAIN_SPACE_IE6 = /:first-(line|letter)(\{|,)/gi;
|
|
400
|
-
const REGEXP_CHARSET = /^(.*)(@charset)( "[^"]*";)/gi;
|
|
401
|
-
const REGEXP_REMOVE_SECOND_CHARSET = /^((\s*)(@charset)( [^;]+;\s*))+/gi;
|
|
402
|
-
const REGEXP_LOWERCASE_DIRECTIVES = /@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/gi;
|
|
403
|
-
const REGEXP_LOWERCASE_PSEUDO_ELEMENTS = /:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/gi;
|
|
404
|
-
const REGEXP_CHARSET2 = /^(.*)(@charset "[^"]*";)/g;
|
|
405
|
-
const REGEXP_CHARSET3 = /^(\s*@charset [^;]+;\s*)+/g;
|
|
406
|
-
const REGEXP_LOWERCASE_FUNCTIONS = /:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?any)\(/gi;
|
|
407
|
-
const REGEXP_LOWERCASE_FUNCTIONS2 = /([:,( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/gi;
|
|
408
|
-
const REGEXP_NEWLINE1 = /\s*\/\*/g;
|
|
409
|
-
const REGEXP_NEWLINE2 = /\*\/\s*/g;
|
|
410
|
-
const REGEXP_RESTORE_SPACE1 = /\band\(/gi;
|
|
411
|
-
const REGEXP_RESTORE_SPACE2 = /([^:])not\(/gi;
|
|
412
|
-
const REGEXP_RESTORE_SPACE3 = /\bor\(/gi;
|
|
413
|
-
const REGEXP_REMOVE_SPACES3 = /([!{}:;>+([,])\s+/g;
|
|
414
|
-
const REGEXP_REMOVE_SEMI_COLUMNS = /;+\}/g;
|
|
415
|
-
// const REGEXP_REPLACE_ZERO = /(^|[^.0-9\\])(?:0?\.)?0(?:ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|ms|k?Hz|dpi|dpcm|dppx|%)(?![a-z0-9])/gi;
|
|
416
|
-
const REGEXP_REPLACE_ZERO_DOT = /([0-9])\.0(ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%| |;)/gi;
|
|
417
|
-
const REGEXP_REPLACE_4_ZEROS = /:0 0 0 0(;|\})/g;
|
|
418
|
-
const REGEXP_REPLACE_3_ZEROS = /:0 0 0(;|\})/g;
|
|
419
|
-
// const REGEXP_REPLACE_2_ZEROS = /:0 0(;|\})/g;
|
|
420
|
-
const REGEXP_REPLACE_1_ZERO = /(transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin|box-shadow):0(;|\})/gi;
|
|
421
|
-
const REGEXP_REPLACE_ZERO_DOT_DECIMAL = /(:|\s)0+\.(\d+)/g;
|
|
422
|
-
const REGEXP_REPLACE_RGB = /rgb\s*\(\s*([0-9,\s]+)\s*\)/gi;
|
|
423
|
-
const REGEXP_REPLACE_BORDER_ZERO = /(border|border-top|border-right|border-bottom|border-left|outline|background):none(;|\})/gi;
|
|
424
|
-
const REGEXP_REPLACE_IE_OPACITY = /progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi;
|
|
425
|
-
const REGEXP_REPLACE_QUERY_FRACTION = /\(([-A-Za-z]+):([0-9]+)\/([0-9]+)\)/g;
|
|
426
|
-
const REGEXP_QUERY_FRACTION = /___QUERY_FRACTION___/g;
|
|
427
|
-
const REGEXP_REPLACE_SEMI_COLUMNS = /;;+/g;
|
|
428
|
-
const REGEXP_REPLACE_HASH_COLOR = /(:|\s)(#f00)(;|})/g;
|
|
429
|
-
const REGEXP_PRESERVED_NEWLINE = /___PRESERVED_NEWLINE___/g;
|
|
430
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT1 = /(:|\s)(#000080)(;|})/g;
|
|
431
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT2 = /(:|\s)(#808080)(;|})/g;
|
|
432
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT3 = /(:|\s)(#808000)(;|})/g;
|
|
433
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT4 = /(:|\s)(#800080)(;|})/g;
|
|
434
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT5 = /(:|\s)(#c0c0c0)(;|})/g;
|
|
435
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT6 = /(:|\s)(#008080)(;|})/g;
|
|
436
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT7 = /(:|\s)(#ffa500)(;|})/g;
|
|
437
|
-
const REGEXP_REPLACE_HASH_COLOR_SHORT8 = /(:|\s)(#800000)(;|})/g;
|
|
438
|
-
|
|
439
|
-
function processString(content = "", options = defaultOptions) {
|
|
440
|
-
|
|
441
|
-
const comments = [];
|
|
442
|
-
const preservedTokens = [];
|
|
443
|
-
|
|
444
|
-
let pattern;
|
|
445
|
-
|
|
446
|
-
const originalContent = content;
|
|
447
|
-
content = extractDataUrls(content, preservedTokens);
|
|
448
|
-
content = collectComments(content, comments);
|
|
449
|
-
|
|
450
|
-
preserveString(REGEXP_PRESERVE_STRING1);
|
|
451
|
-
preserveString(REGEXP_PRESERVE_STRING1_BIS);
|
|
452
|
-
preserveString(REGEXP_PRESERVE_STRING1_TER);
|
|
453
|
-
preserveString(REGEXP_PRESERVE_STRING2);
|
|
454
|
-
preserveString(REGEXP_PRESERVE_STRING2_BIS);
|
|
455
|
-
preserveString(REGEXP_PRESERVE_STRING2_TER);
|
|
456
|
-
|
|
457
|
-
function preserveString(pattern) {
|
|
458
|
-
content = content.replace(pattern, token => {
|
|
459
|
-
const quote = token.substring(0, 1);
|
|
460
|
-
token = token.slice(1, -1);
|
|
461
|
-
// maybe the string contains a comment-like substring or more? put'em back then
|
|
462
|
-
if (token.indexOf("___PRESERVE_CANDIDATE_COMMENT_") >= 0) {
|
|
463
|
-
for (let i = 0, len = comments.length; i < len; i += 1) {
|
|
464
|
-
token = token.replace("___PRESERVE_CANDIDATE_COMMENT_" + i + "___", comments[i]);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
// minify alpha opacity in filter strings
|
|
468
|
-
token = token.replace(REGEXP_MINIFY_ALPHA, "alpha(opacity=");
|
|
469
|
-
preservedTokens.push(token);
|
|
470
|
-
return quote + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___" + quote;
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// strings are safe, now wrestle the comments
|
|
475
|
-
for (let i = 0, len = comments.length; i < len; i += 1) {
|
|
476
|
-
|
|
477
|
-
const token = comments[i];
|
|
478
|
-
const placeholder = "___PRESERVE_CANDIDATE_COMMENT_" + i + "___";
|
|
479
|
-
|
|
480
|
-
// ! in the first position of the comment means preserve
|
|
481
|
-
// so push to the preserved tokens keeping the !
|
|
482
|
-
if (token.charAt(0) === "!") {
|
|
483
|
-
if (options.cuteComments) {
|
|
484
|
-
preservedTokens.push(token.substring(1).replace(REGEXP_PRESERVE_TOKEN1, "\n"));
|
|
485
|
-
} else if (options.uglyComments) {
|
|
486
|
-
preservedTokens.push(token.substring(1).replace(REGEXP_PRESERVE_TOKEN2, ""));
|
|
487
|
-
} else {
|
|
488
|
-
preservedTokens.push(token);
|
|
489
|
-
}
|
|
490
|
-
content = content.replace(placeholder, ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// \ in the last position looks like hack for Mac/IE5
|
|
495
|
-
// shorten that to /*\*/ and the next one to /**/
|
|
496
|
-
if (token.charAt(token.length - 1) === "\\") {
|
|
497
|
-
preservedTokens.push("\\");
|
|
498
|
-
content = content.replace(placeholder, ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
|
|
499
|
-
i = i + 1; // attn: advancing the loop
|
|
500
|
-
preservedTokens.push("");
|
|
501
|
-
content = content.replace(
|
|
502
|
-
"___PRESERVE_CANDIDATE_COMMENT_" + i + "___",
|
|
503
|
-
___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___"
|
|
504
|
-
);
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// keep empty comments after child selectors (IE7 hack)
|
|
509
|
-
// e.g. html >/**/ body
|
|
510
|
-
if (token.length === 0) {
|
|
511
|
-
const startIndex = content.indexOf(placeholder);
|
|
512
|
-
if (startIndex > 2) {
|
|
513
|
-
if (content.charAt(startIndex - 3) === ">") {
|
|
514
|
-
preservedTokens.push("");
|
|
515
|
-
content = content.replace(placeholder, ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// in all other cases kill the comment
|
|
521
|
-
content = content.replace(`/*${placeholder}*/`, "");
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// parse simple @variables blocks and remove them
|
|
525
|
-
if (options.expandVars) {
|
|
526
|
-
const vars = {};
|
|
527
|
-
pattern = REGEXP_VARIABLES;
|
|
528
|
-
content = content.replace(pattern, (_, f1) => {
|
|
529
|
-
pattern = REGEXP_VARIABLE;
|
|
530
|
-
f1.replace(pattern, (_, f1, f2) => {
|
|
531
|
-
if (f1 && f2) {
|
|
532
|
-
vars[f1] = f2;
|
|
533
|
-
}
|
|
534
|
-
return "";
|
|
535
|
-
});
|
|
536
|
-
return "";
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
// replace var(x) with the value of x
|
|
540
|
-
pattern = REGEXP_VARIABLE_VALUE;
|
|
541
|
-
content = content.replace(pattern, (_, f1) => {
|
|
542
|
-
return vars[f1] || "none";
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// normalize all whitespace strings to single spaces. Easier to work with that way.
|
|
547
|
-
content = content.replace(REGEXP_WHITE_SPACES, " ");
|
|
548
|
-
|
|
549
|
-
// preserve formulas in calc() before removing spaces
|
|
550
|
-
pattern = REGEXP_PRESERVE_CALC;
|
|
551
|
-
content = content.replace(pattern, (_, f1) => {
|
|
552
|
-
preservedTokens.push(
|
|
553
|
-
"calc(" +
|
|
554
|
-
f1.replace(REGEXP_TRIM, "")
|
|
555
|
-
.replace(REGEXP_PRESERVE_CALC2, "(")
|
|
556
|
-
.replace(REGEXP_PRESERVE_CALC3, ")") +
|
|
557
|
-
")"
|
|
558
|
-
);
|
|
559
|
-
return ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
// preserve matrix
|
|
563
|
-
pattern = REGEXP_PRESERVE_MATRIX;
|
|
564
|
-
content = content.replace(pattern, (_, f1) => {
|
|
565
|
-
preservedTokens.push(f1);
|
|
566
|
-
return "filter:progid:DXImageTransform.Microsoft.Matrix(" + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___);";
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
// remove the spaces before the things that should not have spaces before them.
|
|
570
|
-
// but, be careful not to turn 'p :link {...}' into 'p:link{...}'
|
|
571
|
-
// swap out any pseudo-class colons with the token, and then swap back.
|
|
572
|
-
try {
|
|
573
|
-
pattern = REGEXP_REMOVE_SPACES;
|
|
574
|
-
content = content.replace(pattern, token => token.replace(REGEXP_COLUMN, "___PSEUDOCLASSCOLON___"));
|
|
575
|
-
// eslint-disable-next-line no-unused-vars
|
|
576
|
-
} catch (error) {
|
|
577
|
-
// ignored
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// remove spaces before the things that should not have spaces before them.
|
|
581
|
-
content = content.replace(REGEXP_REMOVE_SPACES2, "$1");
|
|
582
|
-
content = content.replace(REGEXP_REMOVE_SPACES2_BIS, "$1$2");
|
|
583
|
-
|
|
584
|
-
// restore spaces for !important
|
|
585
|
-
content = content.replace(REGEXP_RESTORE_SPACE_IMPORTANT, " !important");
|
|
586
|
-
|
|
587
|
-
// bring back the colon
|
|
588
|
-
content = content.replace(REGEXP_PSEUDOCLASSCOLON, ":");
|
|
589
|
-
|
|
590
|
-
// preserve 0 followed by a time unit for properties using time units
|
|
591
|
-
pattern = REGEXP_PRESERVE_ZERO_UNIT;
|
|
592
|
-
content = content.replace(pattern, (_, f1, f2) => {
|
|
593
|
-
|
|
594
|
-
f2 = f2.replace(REGEXP_PRESERVE_ZERO_UNIT1, (_, g1, g2) => {
|
|
595
|
-
preservedTokens.push("0" + g2);
|
|
596
|
-
return g1 + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
return f1 + ":" + f2;
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
// preserve unit for flex-basis within flex and flex-basis (ie10 bug)
|
|
603
|
-
pattern = REGEXP_PRESERVE_FLEX;
|
|
604
|
-
content = content.replace(pattern, (_, f1, f2) => {
|
|
605
|
-
let f2b = f2.split(REGEXP_SPACES);
|
|
606
|
-
preservedTokens.push(f2b.pop());
|
|
607
|
-
f2b.push(___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
|
|
608
|
-
f2b = f2b.join(" ");
|
|
609
|
-
return `${f1}:${f2b}`;
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
// preserve 0% in hsl and hsla color definitions
|
|
613
|
-
content = content.replace(REGEXP_PRESERVE_HSLA, (_, f1, f2) => {
|
|
614
|
-
const f0 = [];
|
|
615
|
-
f2.split(",").forEach(part => {
|
|
616
|
-
part = part.replace(REGEXP_PRESERVE_HSLA1, "");
|
|
617
|
-
if (part === "0%") {
|
|
618
|
-
preservedTokens.push("0%");
|
|
619
|
-
f0.push(___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
|
|
620
|
-
} else {
|
|
621
|
-
f0.push(part);
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
return f1 + "(" + f0.join(",") + ")";
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
// preserve 0 followed by unit in keyframes steps (WIP)
|
|
628
|
-
content = keyframes(content, preservedTokens);
|
|
629
|
-
|
|
630
|
-
// retain space for special IE6 cases
|
|
631
|
-
content = content.replace(REGEXP_RETAIN_SPACE_IE6, (_, f1, f2) => ":first-" + f1.toLowerCase() + " " + f2);
|
|
632
|
-
|
|
633
|
-
// newlines before and after the end of a preserved comment
|
|
634
|
-
if (options.cuteComments) {
|
|
635
|
-
content = content.replace(REGEXP_NEWLINE1, "___PRESERVED_NEWLINE___/*");
|
|
636
|
-
content = content.replace(REGEXP_NEWLINE2, "*/___PRESERVED_NEWLINE___");
|
|
637
|
-
// no space after the end of a preserved comment
|
|
638
|
-
} else {
|
|
639
|
-
content = content.replace(REGEXP_NEWLINE2, "*/");
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// If there are multiple @charset directives, push them to the top of the file.
|
|
643
|
-
pattern = REGEXP_CHARSET;
|
|
644
|
-
content = content.replace(pattern, (_, f1, f2, f3) => f2.toLowerCase() + f3 + f1);
|
|
645
|
-
|
|
646
|
-
// When all @charset are at the top, remove the second and after (as they are completely ignored).
|
|
647
|
-
pattern = REGEXP_REMOVE_SECOND_CHARSET;
|
|
648
|
-
content = content.replace(pattern, (_, __, f2, f3, f4) => f2 + f3.toLowerCase() + f4);
|
|
649
|
-
|
|
650
|
-
// lowercase some popular @directives (@charset is done right above)
|
|
651
|
-
pattern = REGEXP_LOWERCASE_DIRECTIVES;
|
|
652
|
-
content = content.replace(pattern, (_, f1) => "@" + f1.toLowerCase());
|
|
653
|
-
|
|
654
|
-
// lowercase some more common pseudo-elements
|
|
655
|
-
pattern = REGEXP_LOWERCASE_PSEUDO_ELEMENTS;
|
|
656
|
-
content = content.replace(pattern, (_, f1) => ":" + f1.toLowerCase());
|
|
657
|
-
|
|
658
|
-
// if there is a @charset, then only allow one, and push to the top of the file.
|
|
659
|
-
content = content.replace(REGEXP_CHARSET2, "$2$1");
|
|
660
|
-
content = content.replace(REGEXP_CHARSET3, "$1");
|
|
661
|
-
|
|
662
|
-
// lowercase some more common functions
|
|
663
|
-
pattern = REGEXP_LOWERCASE_FUNCTIONS;
|
|
664
|
-
content = content.replace(pattern, (_, f1) => ":" + f1.toLowerCase() + "(");
|
|
665
|
-
|
|
666
|
-
// lower case some common function that can be values
|
|
667
|
-
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us right after this
|
|
668
|
-
pattern = REGEXP_LOWERCASE_FUNCTIONS2;
|
|
669
|
-
content = content.replace(pattern, (_, f1, f2) => f1 + f2.toLowerCase());
|
|
670
|
-
|
|
671
|
-
// put the space back in some cases, to support stuff like
|
|
672
|
-
// @media screen and (-webkit-min-device-pixel-ratio:0){
|
|
673
|
-
content = content.replace(REGEXP_RESTORE_SPACE1, "and (");
|
|
674
|
-
content = content.replace(REGEXP_RESTORE_SPACE2, "$1not (");
|
|
675
|
-
content = content.replace(REGEXP_RESTORE_SPACE3, "or (");
|
|
676
|
-
|
|
677
|
-
// remove the spaces after the things that should not have spaces after them.
|
|
678
|
-
content = content.replace(REGEXP_REMOVE_SPACES3, "$1");
|
|
679
|
-
|
|
680
|
-
// remove unnecessary semicolons
|
|
681
|
-
content = content.replace(REGEXP_REMOVE_SEMI_COLUMNS, "}");
|
|
682
|
-
|
|
683
|
-
// replace 0(px,em,%) with 0.
|
|
684
|
-
// content = content.replace(REGEXP_REPLACE_ZERO, "$10");
|
|
685
|
-
|
|
686
|
-
// Replace x.0(px,em,%) with x(px,em,%).
|
|
687
|
-
content = content.replace(REGEXP_REPLACE_ZERO_DOT, "$1$2");
|
|
688
|
-
|
|
689
|
-
// replace 0 0 0 0; with 0.
|
|
690
|
-
content = content.replace(REGEXP_REPLACE_4_ZEROS, ":0$1");
|
|
691
|
-
content = content.replace(REGEXP_REPLACE_3_ZEROS, ":0$1");
|
|
692
|
-
// content = content.replace(REGEXP_REPLACE_2_ZEROS, ":0$1");
|
|
693
|
-
|
|
694
|
-
// replace background-position:0; with background-position:0 0;
|
|
695
|
-
// same for transform-origin and box-shadow
|
|
696
|
-
pattern = REGEXP_REPLACE_1_ZERO;
|
|
697
|
-
content = content.replace(pattern, (_, f1, f2) => f1.toLowerCase() + ":0 0" + f2);
|
|
698
|
-
|
|
699
|
-
// replace 0.6 to .6, but only when preceded by : or a white-space
|
|
700
|
-
content = content.replace(REGEXP_REPLACE_ZERO_DOT_DECIMAL, "$1.$2");
|
|
701
|
-
|
|
702
|
-
// shorten colors from rgb(51,102,153) to #336699
|
|
703
|
-
// this makes it more likely that it'll get further compressed in the next step.
|
|
704
|
-
pattern = REGEXP_REPLACE_RGB;
|
|
705
|
-
content = content.replace(pattern, (_, f1) => {
|
|
706
|
-
const rgbcolors = f1.split(",");
|
|
707
|
-
let hexcolor = "#";
|
|
708
|
-
for (let i = 0; i < rgbcolors.length; i += 1) {
|
|
709
|
-
let val = parseInt(rgbcolors[i], 10);
|
|
710
|
-
if (val < 16) {
|
|
711
|
-
hexcolor += "0";
|
|
712
|
-
}
|
|
713
|
-
if (val > 255) {
|
|
714
|
-
val = 255;
|
|
715
|
-
}
|
|
716
|
-
hexcolor += val.toString(16);
|
|
717
|
-
}
|
|
718
|
-
return hexcolor;
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
// Shorten colors from #AABBCC to #ABC.
|
|
722
|
-
content = compressHexColors(content);
|
|
723
|
-
|
|
724
|
-
// Replace #f00 -> red
|
|
725
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR, "$1red$3");
|
|
726
|
-
|
|
727
|
-
// Replace other short color keywords
|
|
728
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT1, "$1navy$3");
|
|
729
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT2, "$1gray$3");
|
|
730
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT3, "$1olive$3");
|
|
731
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT4, "$1purple$3");
|
|
732
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT5, "$1silver$3");
|
|
733
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT6, "$1teal$3");
|
|
734
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT7, "$1orange$3");
|
|
735
|
-
content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT8, "$1maroon$3");
|
|
736
|
-
|
|
737
|
-
// border: none -> border:0
|
|
738
|
-
pattern = REGEXP_REPLACE_BORDER_ZERO;
|
|
739
|
-
content = content.replace(pattern, (_, f1, f2) => f1.toLowerCase() + ":0" + f2);
|
|
740
|
-
|
|
741
|
-
// shorter opacity IE filter
|
|
742
|
-
content = content.replace(REGEXP_REPLACE_IE_OPACITY, "alpha(opacity=");
|
|
743
|
-
|
|
744
|
-
// Find a fraction that is used for Opera's -o-device-pixel-ratio query
|
|
745
|
-
// Add token to add the '\' back in later
|
|
746
|
-
content = content.replace(REGEXP_REPLACE_QUERY_FRACTION, "($1:$2___QUERY_FRACTION___$3)");
|
|
747
|
-
|
|
748
|
-
// remove empty rules.
|
|
749
|
-
// content = content.replace(REGEXP_EMPTY_RULES, "");
|
|
750
|
-
|
|
751
|
-
// Add '\' back to fix Opera -o-device-pixel-ratio query
|
|
752
|
-
content = content.replace(REGEXP_QUERY_FRACTION, "/");
|
|
753
|
-
|
|
754
|
-
// some source control tools don't like it when files containing lines longer
|
|
755
|
-
// than, say 8000 characters, are checked in. The linebreak option is used in
|
|
756
|
-
// that case to split long lines after a specific column.
|
|
757
|
-
if (options.maxLineLen > 0) {
|
|
758
|
-
const lines = [];
|
|
759
|
-
let line = [];
|
|
760
|
-
for (let i = 0, len = content.length; i < len; i += 1) {
|
|
761
|
-
const ch = content.charAt(i);
|
|
762
|
-
line.push(ch);
|
|
763
|
-
if (ch === "}" && line.length > options.maxLineLen) {
|
|
764
|
-
lines.push(line.join(""));
|
|
765
|
-
line = [];
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
if (line.length) {
|
|
769
|
-
lines.push(line.join(""));
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
content = lines.join("\n");
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// replace multiple semi-colons in a row by a single one
|
|
776
|
-
// see SF bug #1980989
|
|
777
|
-
content = content.replace(REGEXP_REPLACE_SEMI_COLUMNS, ";");
|
|
778
|
-
|
|
779
|
-
// trim the final string (for any leading or trailing white spaces)
|
|
780
|
-
content = content.replace(REGEXP_TRIM, "");
|
|
781
|
-
|
|
782
|
-
if (preservedTokens.length > 1000) {
|
|
783
|
-
return originalContent;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// restore preserved tokens
|
|
787
|
-
for (let i = preservedTokens.length - 1; i >= 0; i--) {
|
|
788
|
-
content = content.replace(___PRESERVED_TOKEN_ + i + "___", preservedTokens[i], "g");
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
// restore preserved newlines
|
|
792
|
-
content = content.replace(REGEXP_PRESERVED_NEWLINE, "\n");
|
|
793
|
-
|
|
794
|
-
// return
|
|
795
|
-
return content;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
export {
|
|
799
|
-
defaultOptions,
|
|
800
|
-
processString
|
|
801
|
-
};
|