Repository: postcss/autoprefixer
Pull request #1202 (2019-03-13 19:23:53)

Add mask-composite
Fix for #1199
Some moments
1. if keyword is ```source-in```, add ```, xor``` ones. Even has many ```source-in```. Here [example](https://codepen.io/anon/pen/MxOvwj)
2. In the output I have warn from VS Code about lost mask-composite without prefix. What do you think? I think, don't need touch original mask value

ai: 😍 Do you have Twitter and VK to promote you in Autoprefixer accounts?

Patch:
diff --git a/lib/hacks/mask-composite.js b/lib/hacks/mask-composite.js
new file mode 100644
index 0000000..24c0867
--- /dev/null
+++ b/lib/hacks/mask-composite.js
@@ -0,0 +1,88 @@
+let Declaration = require('../declaration')
+
+class MaskComposite extends Declaration {
+  static names = ['mask', 'mask-composite'];
+
+  static oldValues = {
+    add: 'source-over',
+    substract: 'source-out',
+    intersect: 'source-in',
+    exclude: 'xor'
+  };
+
+  static regexp = new RegExp(
+    `\\s+(${ Object.keys(MaskComposite.oldValues).join(
+      '|'
+    ) })\\b(?!\\))\\s*(?=[,])`,
+    'ig'
+  );
+
+  /**
+   * Prefix mask-composite for webkit
+   */
+  insert (decl, prefix, prefixes) {
+    let isCompositeProp = decl.prop === 'mask-composite'
+
+    let compositeValues
+
+    if (isCompositeProp) {
+      compositeValues = decl.value.split(',')
+    } else {
+      compositeValues = decl.value.match(MaskComposite.regexp) || []
+    }
+
+    compositeValues = compositeValues.map(el => el.trim()).filter(el => el)
+    let hasCompositeValues = compositeValues.length
+
+    let compositeDecl
+
+    if (hasCompositeValues) {
+      compositeDecl = this.clone(decl)
+      compositeDecl.value = compositeValues
+        .map(value => MaskComposite.oldValues[value] || value)
+        .join(', ')
+
+      if (compositeValues.includes('intersect')) {
+        compositeDecl.value += ', xor'
+      }
+
+      compositeDecl.prop = prefix + 'mask-composite'
+    }
+
+    if (isCompositeProp) {
+      if (!hasCompositeValues) {
+        return undefined
+      }
+
+      if (this.needCascade(decl)) {
+        compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix)
+      }
+
+      return decl.parent.insertBefore(decl, compositeDecl)
+    }
+
+    let cloned = this.clone(decl)
+    cloned.prop = prefix + cloned.prop
+
+    if (hasCompositeValues) {
+      cloned.value = cloned.value.replace(MaskComposite.regexp, '')
+    }
+
+    if (this.needCascade(decl)) {
+      cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
+    }
+
+    decl.parent.insertBefore(decl, cloned)
+
+    if (!hasCompositeValues) {
+      return decl
+    }
+
+    if (this.needCascade(decl)) {
+      compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix)
+    }
+    return decl.parent.insertBefore(decl, compositeDecl)
+  }
+}
+
+module.exports = MaskComposite
diff --git a/lib/prefixes.js b/lib/prefixes.js
index 05790d3..47eea2a 100644
--- a/lib/prefixes.js
+++ b/lib/prefixes.js
@@ -29,6 +29,7 @@ Declaration.hack(require('./hacks/align-self'))
 Declaration.hack(require('./hacks/appearance'))
 Declaration.hack(require('./hacks/flex-basis'))
 Declaration.hack(require('./hacks/mask-border'))
+Declaration.hack(require('./hacks/mask-composite'))
 Declaration.hack(require('./hacks/align-items'))
 Declaration.hack(require('./hacks/flex-shrink'))
 Declaration.hack(require('./hacks/break-props'))


Test patch:
diff --git a/test/autoprefixer.test.js b/test/autoprefixer.test.js
index 7c74429..97a66a0 100644
--- a/test/autoprefixer.test.js
+++ b/test/autoprefixer.test.js
@@ -502,6 +502,7 @@ describe('hacks', () => {
   it('supports all placeholders', () => check('placeholder'))
   it('supports image-rendering', () => check('image-rendering'))
   it('supports border-box mask', () => check('mask-border'))
+  it('supports mask-composite', () => check('mask-composite'))
   it('supports image-set()', () => check('image-set'))
   it('supports writing-mode', () => check('writing-mode'))
   it('supports cross-fade()', () => check('cross-fade'))
diff --git a/test/cases/mask-composite.css b/test/cases/mask-composite.css
new file mode 100644
index 0000000..9a9953b
--- /dev/null
+++ b/test/cases/mask-composite.css
@@ -0,0 +1,23 @@
+a {
+  mask: url(add.png) add, url(substract.png);
+}
+
+a {
+  mask: url(intersect.png) intersect, url(exclude.png);
+}
+
+a {
+  mask: url(image.png) intersect, url(image.png) add, url(image.png);
+}
+
+a {
+  mask-composite: add;
+}
+
+a {
+  mask-composite:;
+}
+
+a {
+  mask-composite: add, substract, exclude;
+}
\ No newline at end of file
diff --git a/test/cases/mask-composite.out.css b/test/cases/mask-composite.out.css
new file mode 100644
index 0000000..dacc77f
--- /dev/null
+++ b/test/cases/mask-composite.out.css
@@ -0,0 +1,31 @@
+a {
+  -webkit-mask: url(add.png), url(substract.png);
+  -webkit-mask-composite: source-over;
+          mask: url(add.png) add, url(substract.png);
+}
+
+a {
+  -webkit-mask: url(intersect.png), url(exclude.png);
+  -webkit-mask-composite: source-in, xor;
+          mask: url(intersect.png) intersect, url(exclude.png);
+}
+
+a {
+  -webkit-mask: url(image.png), url(image.png), url(image.png);
+  -webkit-mask-composite: source-in, source-over, xor;
+          mask: url(image.png) intersect, url(image.png) add, url(image.png);
+}
+
+a {
+  -webkit-mask-composite: source-over;
+          mask-composite: add;
+}
+
+a {
+  mask-composite:;
+}
+
+a {
+  -webkit-mask-composite: source-over, source-out, xor;
+          mask-composite: add, substract, exclude;
+}
\ No newline at end of file