Skip to content

Commit 3a5e0e0

Browse files
committed
fix #1657: invalid css transform of margin/padding
1 parent cab83c9 commit 3a5e0e0

File tree

3 files changed

+60
-8
lines changed

3 files changed

+60
-8
lines changed

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,38 @@
2626

2727
With this release, esbuild will also automatically rewrite `.mjs` to `.mts` and `.cjs` to `.cts` when resolving import paths to files on the file system. This should make it possible to bundle code written in this new style. In addition, the extensions `.mts` and `.cts` are now also considered valid TypeScript file extensions by default along with the `.ts` extension.
2828

29+
* Fix invalid CSS minification of `margin` and `padding` ([#1657](https://github.com/evanw/esbuild/issues/1657))
30+
31+
CSS minification does collapsing of `margin` and `padding` related properties. For example:
32+
33+
```css
34+
/* Original CSS */
35+
div {
36+
margin: auto;
37+
margin-top: 5px;
38+
margin-left: 5px;
39+
}
40+
41+
/* Minified CSS */
42+
div{margin:5px auto auto 5px}
43+
```
44+
45+
However, while this works for the `auto` keyword, it doesn't work for other keywords. For example:
46+
47+
```css
48+
/* Original CSS */
49+
div {
50+
margin: inherit;
51+
margin-top: 5px;
52+
margin-left: 5px;
53+
}
54+
55+
/* Minified CSS */
56+
div{margin:inherit;margin-top:5px;margin-left:5px}
57+
```
58+
59+
Transforming this to `div{margin:5px inherit inherit 5px}`, as was done in previous releases of esbuild, is an invalid transformation and results in incorrect CSS. This release of esbuild fixes this CSS transformation bug.
60+
2961
## 0.13.3
3062

3163
* Support TypeScript type-only import/export specifiers ([#1637](https://github.com/evanw/esbuild/pull/1637))

internal/css_parser/css_decls_box.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ func (box *boxTracker) mangleSides(rules []css_ast.Rule, decl *css_ast.RDeclarat
3838
box.important = decl.Important
3939
}
4040

41-
if quad, ok := expandTokenQuad(decl.Value); ok {
41+
isMargin := decl.Key == css_ast.DMargin
42+
if quad, ok := expandTokenQuad(decl.Value); ok &&
43+
isNumericOrMarginAuto(&quad[0], isMargin) && isNumericOrMarginAuto(&quad[1], isMargin) &&
44+
isNumericOrMarginAuto(&quad[2], isMargin) && isNumericOrMarginAuto(&quad[3], isMargin) {
4245
isMargin := decl.Key == css_ast.DMargin
4346
for side, t := range quad {
4447
t.TurnLengthIntoNumberIfZero()
@@ -57,12 +60,12 @@ func (box *boxTracker) mangleSide(rules []css_ast.Rule, decl *css_ast.RDeclarati
5760
box.important = decl.Important
5861
}
5962

60-
if tokens := decl.Value; len(tokens) == 1 && tokens[0].Kind.IsNumericOrIdent() {
61-
isMargin := false
62-
switch decl.Key {
63-
case css_ast.DMarginTop, css_ast.DMarginRight, css_ast.DMarginBottom, css_ast.DMarginLeft:
64-
isMargin = true
65-
}
63+
isMargin := false
64+
switch decl.Key {
65+
case css_ast.DMarginTop, css_ast.DMarginRight, css_ast.DMarginBottom, css_ast.DMarginLeft:
66+
isMargin = true
67+
}
68+
if tokens := decl.Value; len(tokens) == 1 && isNumericOrMarginAuto(&tokens[0], isMargin) {
6669
t := tokens[0]
6770
if t.TurnLengthIntoNumberIfZero() {
6871
tokens[0] = t
@@ -114,3 +117,13 @@ func (box *boxTracker) compactRules(rules []css_ast.Rule, keyRange logger.Range,
114117
Important: box.important,
115118
}
116119
}
120+
121+
func isNumericOrMarginAuto(t *css_ast.Token, isMargin bool) bool {
122+
if t.Kind.IsNumeric() {
123+
return true
124+
}
125+
if isMargin && t.Kind == css_lexer.TIdent && t.Text == "auto" {
126+
return true
127+
}
128+
return false
129+
}

internal/css_parser/css_parser_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,6 @@ func TestMarginAndPadding(t *testing.T) {
980980
expectPrintedMangle(t, "a { "+x+"-top: 1; "+x+"-top: }", "a {\n "+x+"-top: 1;\n "+x+"-top:;\n}\n")
981981
expectPrintedMangle(t, "a { "+x+"-top: 1; "+x+"-top: 2 3 }", "a {\n "+x+"-top: 1;\n "+x+"-top: 2 3;\n}\n")
982982
expectPrintedMangle(t, "a { "+x+": 1 2 3 4; "+x+"-left: -4; "+x+"-right: -2 }", "a {\n "+x+": 1 -2 3 -4;\n}\n")
983-
expectPrintedMangle(t, "a { "+x+": 1 auto 3 4; "+x+"-left: auto }", "a {\n "+x+": 1 auto 3;\n}\n")
984983
expectPrintedMangle(t, "a { "+x+": 1 2; "+x+"-top: 5 }", "a {\n "+x+": 5 2 1;\n}\n")
985984
expectPrintedMangle(t, "a { "+x+": 1; "+x+"-top: 5 }", "a {\n "+x+": 5 1 1;\n}\n")
986985

@@ -999,6 +998,14 @@ func TestMarginAndPadding(t *testing.T) {
999998
// This should not be changed because "--x" and "--z" could be empty
1000999
expectPrintedMangle(t, "a { "+x+": var(--x) var(--y) var(--z) var(--y) }", "a {\n "+x+": var(--x) var(--y) var(--z) var(--y);\n}\n")
10011000
}
1001+
1002+
// "auto" is the only keyword allowed in a quad, and only for "margin" not for "padding"
1003+
expectPrintedMangle(t, "a { margin: 1 auto 3 4; margin-left: auto }", "a {\n margin: 1 auto 3;\n}\n")
1004+
expectPrintedMangle(t, "a { padding: 1 auto 3 4; padding-left: auto }", "a {\n padding: 1 auto 3 4;\n padding-left: auto;\n}\n")
1005+
expectPrintedMangle(t, "a { margin: auto; margin-left: 1px }", "a {\n margin: auto auto auto 1px;\n}\n")
1006+
expectPrintedMangle(t, "a { padding: auto; padding-left: 1px }", "a {\n padding: auto;\n padding-left: 1px;\n}\n")
1007+
expectPrintedMangle(t, "a { margin: inherit; margin-left: 1px }", "a {\n margin: inherit;\n margin-left: 1px;\n}\n")
1008+
expectPrintedMangle(t, "a { padding: inherit; padding-left: 1px }", "a {\n padding: inherit;\n padding-left: 1px;\n}\n")
10021009
}
10031010

10041011
func TestBorderRadius(t *testing.T) {

0 commit comments

Comments
 (0)