Skip to content

Commit

Permalink
fix #3493: -webkit- prefix for mask-composite
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Nov 18, 2023
1 parent 645734f commit 478807e
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 1 deletion.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@
document.onkeydown=o=>o.keyCode===65&&console.log("🧀");
```

* Automatically prefix the `mask-composite` CSS property for WebKit ([#3493](https://github.com/evanw/esbuild/issues/3493))

The `mask-composite` property will now be prefixed as `-webkit-mask-composite` for older WebKit-based browsers. In addition to prefixing the property name, handling older browsers also requires rewriting the values since WebKit uses non-standard names for the mask composite modes:

```css
/* Original code */
div {
mask-composite: add, subtract, intersect, exclude;
}

/* New output (with --target=chrome100) */
div {
-webkit-mask-composite:
source-over,
source-out,
source-in,
xor;
mask-composite:
add,
subtract,
intersect,
exclude;
}
```

* Parse upcoming changes to TypeScript syntax ([#3490](https://github.com/evanw/esbuild/issues/3490), [#3491](https://github.com/evanw/esbuild/pull/3491))

With this release, you can now use `from` as the name of a default type-only import in TypeScript code, as well as `of` as the name of an `await using` loop iteration variable:
Expand Down
1 change: 1 addition & 0 deletions compat-table/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export const cssProperties = {
DFontKerning: true,
DHyphens: true,
DInitialLetter: true,
DMaskComposite: true,
DMaskImage: true,
DMaskOrigin: true,
DMaskPosition: true,
Expand Down
29 changes: 28 additions & 1 deletion compat-table/src/mdn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,15 @@ const cssFeatures: Partial<Record<CSSFeature, string | string[]>> = {
Nesting: 'css.selectors.nesting',
}

const similarPrefixedProperty: Record<string, { prefix: string, property: string }> = {
'css.properties.mask-composite': {
prefix: '-webkit-',
property: 'css.properties.-webkit-mask-composite',
},
}

const cssPrefixFeatures: Record<string, CSSProperty> = {
'css.properties.mask-composite': 'DMaskComposite',
'css.properties.mask-image': 'DMaskImage',
'css.properties.mask-origin': 'DMaskOrigin',
'css.properties.mask-position': 'DMaskPosition',
Expand Down Expand Up @@ -141,6 +149,9 @@ for (const fullKey in cssPrefixFeatures) {

// Figure out which version this property can be used unprefixed, if any.
// This assumes that support for these CSS properties is never removed.
// This assumption is wrong (Edge removed many features when it changed
// its engine from EdgeHTML to Blink, basically becoming another browser)
// but we ignore those cases for now.
let version_unprefixed: string | undefined
for (const { prefix, flags, version_added, version_removed } of entries) {
if (!prefix && !flags && typeof version_added === 'string' && !version_removed && isSemver.test(version_added)) {
Expand All @@ -151,9 +162,25 @@ for (const fullKey in cssPrefixFeatures) {
type PrefixRange = { prefix: string, start: string, end?: string }
const ranges: PrefixRange[] = []

// The MDN dataset sometimes doesn't list prefixes if the values for the
// prefixed property are sufficiently different. In that case, we may need
// to search for the prefix information within another property instead.
const similar = similarPrefixedProperty[fullKey]
if (similar) {
const similarSupport: SupportBlock = extractProperty(bcd, similar.property).__compat.support
const similarEntries = similarSupport[env as BrowserName]
if (!similarEntries) continue
entries = Array.isArray(similarEntries) ? similarEntries : [similarEntries]
}

// Find all version ranges where a given prefix is supported
for (let i = 0; i < entries.length; i++) {
const { prefix, flags, version_added, version_removed } = entries[i]
let { prefix, flags, version_added, version_removed } = entries[i]

if (similar) {
if (prefix) throw new Error(`Unexpected prefix "${prefix}" for similar property "${similar.property}"`)
prefix = similar.prefix
}

if (prefix && !flags && typeof version_added === 'string' && isSemver.test(version_added)) {
const range: PrefixRange = { prefix, start: version_added }
Expand Down
7 changes: 7 additions & 0 deletions internal/compat/css_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ var cssPrefixTable = map[css_ast.D][]prefixData{
{engine: IOS, prefix: WebkitPrefix},
{engine: Safari, prefix: WebkitPrefix},
},
css_ast.DMaskComposite: {
{engine: Chrome, prefix: WebkitPrefix, withoutPrefix: v{120, 0, 0}},
{engine: Edge, prefix: WebkitPrefix},
{engine: IOS, prefix: WebkitPrefix, withoutPrefix: v{15, 4, 0}},
{engine: Opera, prefix: WebkitPrefix},
{engine: Safari, prefix: WebkitPrefix, withoutPrefix: v{15, 4, 0}},
},
css_ast.DMaskImage: {
{engine: Chrome, prefix: WebkitPrefix, withoutPrefix: v{120, 0, 0}},
{engine: Edge, prefix: WebkitPrefix},
Expand Down
19 changes: 19 additions & 0 deletions internal/css_parser/css_decls.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,25 @@ func (p *parser) insertPrefixedDeclaration(rules []css_ast.Rule, prefix string,
if prefix == "-moz-" && len(value) == 1 && value[0].Kind == css_lexer.TIdent && strings.EqualFold(value[0].Text, "none") {
value[0].Text = "-moz-none"
}

case css_ast.DMaskComposite:
// WebKit uses different names for these values
if prefix == "-webkit-" {
for i, token := range value {
if token.Kind == css_lexer.TIdent {
switch token.Text {
case "add":
value[i].Text = "source-over"
case "subtract":
value[i].Text = "source-out"
case "intersect":
value[i].Text = "source-in"
case "exclude":
value[i].Text = "xor"
}
}
}
}
}

// Overwrite the latest declaration with the prefixed declaration
Expand Down
2 changes: 2 additions & 0 deletions internal/css_parser/css_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2362,6 +2362,8 @@ func TestPrefixInsertion(t *testing.T) {
expectPrintedWithAllPrefixes(t, "a { text-decoration-line: none }", "a {\n -webkit-text-decoration-line: none;\n -moz-text-decoration-line: none;\n text-decoration-line: none;\n}\n", "")
expectPrintedWithAllPrefixes(t, "a { text-size-adjust: none }", "a {\n -webkit-text-size-adjust: none;\n -ms-text-size-adjust: none;\n text-size-adjust: none;\n}\n", "")
expectPrintedWithAllPrefixes(t, "a { user-select: none }", "a {\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: -moz-none;\n -ms-user-select: none;\n user-select: none;\n}\n", "")
expectPrintedWithAllPrefixes(t, "a { mask-composite: add, subtract, intersect, exclude }",
"a {\n -webkit-mask-composite:\n source-over,\n source-out,\n source-in,\n xor;\n mask-composite:\n add,\n subtract,\n intersect,\n exclude;\n}\n", "")

// Check that we insert prefixed rules each time an unprefixed rule is
// encountered. This matches the behavior of the popular "autoprefixer" tool.
Expand Down

0 comments on commit 478807e

Please sign in to comment.