diff --git a/src/htmlminifier.js b/src/htmlminifier.js
index 6c2d6dd..5093a79 100644
--- a/src/htmlminifier.js
+++ b/src/htmlminifier.js
@@ -904,14 +904,37 @@ async function minifyHTML(value, options, partialMarkup) {
});
const ids = [];
- new CleanCSS().minify(wrapCSS(text, type)).warnings.forEach(function (warning) {
- const match = uidPattern.exec(warning);
- if (match) {
- const id = uidAttr + match[2] + uidAttr;
- text = text.replace(id, ignoreCSS(id));
- ids.push(id);
+ // Loop removing a single ID at a time from the warnings, a
+ // warning might contain multiple IDs in the context, but we only
+ // handle the first match on each attempt.
+ while (true) {
+ const minifyTest = new CleanCSS().minify(wrapCSS(text, type));
+ if (minifyTest.warnings.length === 0) {
+ // There are no warnings.
+ break;
}
- });
+ minifyTest.warnings.forEach(function (warning) {
+ // It is very important to reset the RegExp before searching
+ // as it's re-used each time.
+ uidPattern.lastIndex = 0;
+ const match = uidPattern.exec(warning);
+ if (match) {
+ const id = uidAttr + match[2] + uidAttr;
+ // Only substitute each ID once, if this has come up
+ // multiple times, then we need to abort.
+ if (ids.indexOf(id) !== -1) {
+ uidPattern.lastIndex = 0;
+ } else {
+ text = text.replace(id, ignoreCSS(id));
+ ids.push(id);
+ }
+ }
+ });
+ if (uidPattern.lastIndex === 0) {
+ // There was a warning that didn't match the pattern.
+ break;
+ }
+ }
return fn(text, type).then(chunk => {
ids.forEach(function (id) {
diff --git a/tests/minifier.spec.js b/tests/minifier.spec.js
index 67b61a6..2fa6463 100644
--- a/tests/minifier.spec.js
+++ b/tests/minifier.spec.js
@@ -3594,3 +3594,14 @@ test('minify Content-Security-Policy', async () => {
input = '';
expect(await minify(input)).toBe(input);
});
+
+test('minify CSS multiple ignore in single warning', async () => {
+ const input = '';
+ const output = '';
+ expect(await minify(input, {
+ ignoreCustomFragments: [/\{%[\s\S]*?%\}/],
+ minifyCSS: {
+ level: 0
+ }
+ })).toBe(output);
+});