diff --git a/CHANGELOG.md b/CHANGELOG.md index e6e8901916d..e757ddfec3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,95 @@ # Changelog +## 0.20.1 + +* Fix a bug with the CSS nesting transform ([#3648](https://github.com/evanw/esbuild/issues/3648)) + + This release fixes a bug with the CSS nesting transform for older browsers where the generated CSS could be incorrect if a selector list contained a pseudo element followed by another selector. The bug was caused by incorrectly mutating the parent rule's selector list when filtering out pseudo elements for the child rules: + + ```css + /* Original code */ + .foo { + &:after, + & .bar { + color: red; + } + } + + /* Old output (with --supported:nesting=false) */ + .foo .bar, + .foo .bar { + color: red; + } + + /* New output (with --supported:nesting=false) */ + .foo:after, + .foo .bar { + color: red; + } + ``` + +* Constant folding for JavaScript inequality operators ([#3645](https://github.com/evanw/esbuild/issues/3645)) + + This release introduces constant folding for the `< > <= >=` operators. The minifier will now replace these operators with `true` or `false` when both sides are compile-time numeric or string constants: + + ```js + // Original code + console.log(1 < 2, '🍕' > '🧀') + + // Old output (with --minify) + console.log(1<2,"🍕">"🧀"); + + // New output (with --minify) + console.log(!0,!1); + ``` + +* Better handling of `__proto__` edge cases ([#3651](https://github.com/evanw/esbuild/pull/3651)) + + JavaScript object literal syntax contains a special case where a non-computed property with a key of `__proto__` sets the prototype of the object. This does not apply to computed properties or to properties that use the shorthand property syntax introduced in ES6. Previously esbuild didn't correctly preserve the "sets the prototype" status of properties inside an object literal, meaning a property that sets the prototype could accidentally be transformed into one that doesn't and vice versa. This has now been fixed: + + ```js + // Original code + function foo(__proto__) { + return { __proto__: __proto__ } // Note: sets the prototype + } + function bar(__proto__, proto) { + { + let __proto__ = proto + return { __proto__ } // Note: doesn't set the prototype + } + } + + // Old output + function foo(__proto__) { + return { __proto__ }; // Note: no longer sets the prototype (WRONG) + } + function bar(__proto__, proto) { + { + let __proto__2 = proto; + return { __proto__: __proto__2 }; // Note: now sets the prototype (WRONG) + } + } + + // New output + function foo(__proto__) { + return { __proto__: __proto__ }; // Note: sets the prototype (correct) + } + function bar(__proto__, proto) { + { + let __proto__2 = proto; + return { ["__proto__"]: __proto__2 }; // Note: doesn't set the prototype (correct) + } + } + ``` + +* Fix cross-platform non-determinism with CSS color space transformations ([#3650](https://github.com/evanw/esbuild/issues/3650)) + + The Go compiler takes advantage of "fused multiply and add" (FMA) instructions on certain processors which do the operation `x*y + z` without intermediate rounding. This causes esbuild's CSS color space math to differ on different processors (currently `ppc64le` and `s390x`), which breaks esbuild's guarantee of deterministic output. To avoid this, esbuild's color space math now inserts a `float64()` cast around every single math operation. This tells the Go compiler not to use the FMA optimization. + +* Fix a crash when resolving a path from a directory that doesn't exist ([#3634](https://github.com/evanw/esbuild/issues/3634)) + + This release fixes a regression where esbuild could crash when resolving an absolute path if the source directory for the path resolution operation doesn't exist. While this situation doesn't normally come up, it could come up when running esbuild concurrently with another operation that mutates the file system as esbuild is doing a build (such as using `git` to switch branches). The underlying problem was a regression that was introduced in version 0.18.0. + ## 0.20.0 **This release deliberately contains backwards-incompatible changes.** To avoid automatically picking up releases like this, you should either be pinning the exact version of `esbuild` in your `package.json` file (recommended) or be using a version range syntax that only accepts patch upgrades such as `^0.19.0` or `~0.19.0`. See npm's documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information. diff --git a/cmd/esbuild/version.go b/cmd/esbuild/version.go index a86916de90d..60ad3d116dd 100644 --- a/cmd/esbuild/version.go +++ b/cmd/esbuild/version.go @@ -1,3 +1,3 @@ package main -const esbuildVersion = "0.20.0" +const esbuildVersion = "0.20.1" diff --git a/internal/bundler_tests/bundler_default_test.go b/internal/bundler_tests/bundler_default_test.go index afad3fe1471..f89eb8ff862 100644 --- a/internal/bundler_tests/bundler_default_test.go +++ b/internal/bundler_tests/bundler_default_test.go @@ -8690,3 +8690,124 @@ func TestJSXDevSelfEdgeCases(t *testing.T) { }, }) } + +func TestObjectLiteralProtoSetterEdgeCases(t *testing.T) { + default_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/local-shorthand.js": ` + function foo(__proto__, bar) { + { + let __proto__, bar // These locals will be renamed + console.log( + 'this must not become "{ __proto__: ... }":', + { + __proto__, + bar, + }, + ) + } + } + `, + "/local-normal.js": ` + function foo(__proto__, bar) { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar: bar, + }, + ) + } + `, + "/import-shorthand.js": ` + import { __proto__, bar } from 'foo' + function foo() { + console.log( + 'this must not become "{ __proto__: ... }":', + { + __proto__, + bar, + }, + ) + } + `, + "/import-normal.js": ` + import { __proto__, bar } from 'foo' + function foo() { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar: bar, + }, + ) + } + `, + }, + entryPaths: []string{"*"}, + options: config.Options{ + AbsOutputDir: "/out", + }, + }) +} + +func TestObjectLiteralProtoSetterEdgeCasesMinifySyntax(t *testing.T) { + default_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/local-computed.js": ` + function foo(__proto__, bar) { + { + let __proto__, bar // These locals will be renamed + console.log( + 'this must not become "{ __proto__: ... }":', + { + ['__proto__']: __proto__, + ['bar']: bar, + }, + ) + } + } + `, + "/local-normal.js": ` + function foo(__proto__, bar) { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar: bar, + }, + ) + } + `, + "/import-computed.js": ` + import { __proto__, bar } from 'foo' + function foo() { + console.log( + 'this must not become "{ __proto__: ... }":', + { + ['__proto__']: __proto__, + ['bar']: bar, + }, + ) + } + `, + "/import-normal.js": ` + import { __proto__, bar } from 'foo' + function foo() { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar: bar, + }, + ) + } + `, + }, + entryPaths: []string{"*"}, + options: config.Options{ + AbsOutputDir: "/out", + MinifySyntax: true, + }, + }) +} diff --git a/internal/bundler_tests/snapshots/snapshots_dce.txt b/internal/bundler_tests/snapshots/snapshots_dce.txt index 18483cc5522..8e363e6d178 100644 --- a/internal/bundler_tests/snapshots/snapshots_dce.txt +++ b/internal/bundler_tests/snapshots/snapshots_dce.txt @@ -274,10 +274,10 @@ console.log([ 3 /* a */ % 6 /* b */, 3 /* a */ ** 6 /* b */ ], [ - 3 /* a */ < 6 /* b */, - 3 /* a */ > 6 /* b */, - 3 /* a */ <= 6 /* b */, - 3 /* a */ >= 6 /* b */, + !0, + !1, + !0, + !1, 3 /* a */ == 6 /* b */, 3 /* a */ != 6 /* b */, 3 /* a */ === 6 /* b */, @@ -312,10 +312,10 @@ console.log([ 3 % 6, 3 ** 6 ], [ - 3 < 6, - 3 > 6, - 3 <= 6, - 3 >= 6, + !0, + !1, + !0, + !1, 3 == 6, 3 != 6, 3 === 6, diff --git a/internal/bundler_tests/snapshots/snapshots_default.txt b/internal/bundler_tests/snapshots/snapshots_default.txt index b813d26f5e8..e8a1a32d6b7 100644 --- a/internal/bundler_tests/snapshots/snapshots_default.txt +++ b/internal/bundler_tests/snapshots/snapshots_default.txt @@ -5187,6 +5187,108 @@ export { i as aap }; +================================================================================ +TestObjectLiteralProtoSetterEdgeCases +---------- /out/import-normal.js ---------- +import { __proto__, bar } from "foo"; +function foo() { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar + } + ); +} + +---------- /out/import-shorthand.js ---------- +import { __proto__, bar } from "foo"; +function foo() { + console.log( + 'this must not become "{ __proto__: ... }":', + { + __proto__, + bar + } + ); +} + +---------- /out/local-normal.js ---------- +function foo(__proto__, bar) { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar + } + ); +} + +---------- /out/local-shorthand.js ---------- +function foo(__proto__, bar) { + { + let __proto__2, bar2; + console.log( + 'this must not become "{ __proto__: ... }":', + { + ["__proto__"]: __proto__2, + bar: bar2 + } + ); + } +} + +================================================================================ +TestObjectLiteralProtoSetterEdgeCasesMinifySyntax +---------- /out/import-computed.js ---------- +import { __proto__, bar } from "foo"; +function foo() { + console.log( + 'this must not become "{ __proto__: ... }":', + { + ["__proto__"]: __proto__, + bar + } + ); +} + +---------- /out/import-normal.js ---------- +import { __proto__, bar } from "foo"; +function foo() { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar + } + ); +} + +---------- /out/local-computed.js ---------- +function foo(__proto__, bar) { + { + let __proto__2, bar2; + console.log( + 'this must not become "{ __proto__: ... }":', + { + ["__proto__"]: __proto__2, + bar: bar2 + } + ); + } +} + +---------- /out/local-normal.js ---------- +function foo(__proto__, bar) { + console.log( + 'this must not become "{ __proto__ }":', + { + __proto__: __proto__, + bar + } + ); +} + ================================================================================ TestOutbase ---------- /out/a/b/c.js ---------- diff --git a/internal/css_parser/css_color_spaces.go b/internal/css_parser/css_color_spaces.go index e9d37359015..721ecd33c59 100644 --- a/internal/css_parser/css_color_spaces.go +++ b/internal/css_parser/css_color_spaces.go @@ -1,6 +1,13 @@ package css_parser -import "math" +import ( + "math" + + "github.com/evanw/esbuild/internal/helpers" +) + +// Wrap float64 math to avoid compiler optimizations that break determinism +type F64 = helpers.F64 // Reference: https://drafts.csswg.org/css-color/#color-conversion-code @@ -41,29 +48,29 @@ const ( decreasingHue ) -func lin_srgb(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { - if abs := math.Abs(val); abs < 0.04045 { - return val / 12.92 +func lin_srgb(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { + if abs := val.Abs(); abs.Value() < 0.04045 { + return val.DivConst(12.92) } else { - return math.Copysign(math.Pow((abs+0.055)/1.055, 2.4), val) + return abs.AddConst(0.055).DivConst(1.055).PowConst(2.4).WithSignFrom(val) } } return f(r), f(g), f(b) } -func gam_srgb(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { - if abs := math.Abs(val); abs > 0.0031308 { - return math.Copysign(1.055*math.Pow(abs, 1/2.4)-0.055, val) +func gam_srgb(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { + if abs := val.Abs(); abs.Value() > 0.0031308 { + return abs.PowConst(1 / 2.4).MulConst(1.055).SubConst(0.055).WithSignFrom(val) } else { - return 12.92 * val + return val.MulConst(12.92) } } return f(r), f(g), f(b) } -func lin_srgb_to_xyz(r float64, g float64, b float64) (float64, float64, float64) { +func lin_srgb_to_xyz(r F64, g F64, b F64) (F64, F64, F64) { M := [9]float64{ 506752.0 / 1228815, 87881.0 / 245763, 12673.0 / 70218, 87098.0 / 409605, 175762.0 / 245763, 12673.0 / 175545, @@ -72,7 +79,7 @@ func lin_srgb_to_xyz(r float64, g float64, b float64) (float64, float64, float64 return multiplyMatrices(M, r, g, b) } -func xyz_to_lin_srgb(x float64, y float64, z float64) (float64, float64, float64) { +func xyz_to_lin_srgb(x F64, y F64, z F64) (F64, F64, F64) { M := [9]float64{ 12831.0 / 3959, -329.0 / 214, -1974.0 / 3959, -851781.0 / 878810, 1648619.0 / 878810, 36519.0 / 878810, @@ -81,15 +88,15 @@ func xyz_to_lin_srgb(x float64, y float64, z float64) (float64, float64, float64 return multiplyMatrices(M, x, y, z) } -func lin_p3(r float64, g float64, b float64) (float64, float64, float64) { +func lin_p3(r F64, g F64, b F64) (F64, F64, F64) { return lin_srgb(r, g, b) } -func gam_p3(r float64, g float64, b float64) (float64, float64, float64) { +func gam_p3(r F64, g F64, b F64) (F64, F64, F64) { return gam_srgb(r, g, b) } -func lin_p3_to_xyz(r float64, g float64, b float64) (float64, float64, float64) { +func lin_p3_to_xyz(r F64, g F64, b F64) (F64, F64, F64) { M := [9]float64{ 608311.0 / 1250200, 189793.0 / 714400, 198249.0 / 1000160, 35783.0 / 156275, 247089.0 / 357200, 198249.0 / 2500400, @@ -98,7 +105,7 @@ func lin_p3_to_xyz(r float64, g float64, b float64) (float64, float64, float64) return multiplyMatrices(M, r, g, b) } -func xyz_to_lin_p3(x float64, y float64, z float64) (float64, float64, float64) { +func xyz_to_lin_p3(x F64, y F64, z F64) (F64, F64, F64) { M := [9]float64{ 446124.0 / 178915, -333277.0 / 357830, -72051.0 / 178915, -14852.0 / 17905, 63121.0 / 35810, 423.0 / 17905, @@ -107,31 +114,31 @@ func xyz_to_lin_p3(x float64, y float64, z float64) (float64, float64, float64) return multiplyMatrices(M, x, y, z) } -func lin_prophoto(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { +func lin_prophoto(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { const Et2 = 16.0 / 512 - if abs := math.Abs(val); abs <= Et2 { - return val / 16 + if abs := val.Abs(); abs.Value() <= Et2 { + return val.DivConst(16) } else { - return math.Copysign(math.Pow(abs, 1.8), val) + return abs.PowConst(1.8).WithSignFrom(val) } } return f(r), f(g), f(b) } -func gam_prophoto(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { +func gam_prophoto(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { const Et = 1.0 / 512 - if abs := math.Abs(val); abs >= Et { - return math.Copysign(math.Pow(abs, 1/1.8), val) + if abs := val.Abs(); abs.Value() >= Et { + return abs.PowConst(1 / 1.8).WithSignFrom(val) } else { - return 16 * val + return val.MulConst(16) } } return f(r), f(g), f(b) } -func lin_prophoto_to_xyz(r float64, g float64, b float64) (float64, float64, float64) { +func lin_prophoto_to_xyz(r F64, g F64, b F64) (F64, F64, F64) { M := [9]float64{ 0.7977604896723027, 0.13518583717574031, 0.0313493495815248, 0.2880711282292934, 0.7118432178101014, 0.00008565396060525902, @@ -140,7 +147,7 @@ func lin_prophoto_to_xyz(r float64, g float64, b float64) (float64, float64, flo return multiplyMatrices(M, r, g, b) } -func xyz_to_lin_prophoto(x float64, y float64, z float64) (float64, float64, float64) { +func xyz_to_lin_prophoto(x F64, y F64, z F64) (F64, F64, F64) { M := [9]float64{ 1.3457989731028281, -0.25558010007997534, -0.05110628506753401, -0.5446224939028347, 1.5082327413132781, 0.02053603239147973, @@ -149,21 +156,21 @@ func xyz_to_lin_prophoto(x float64, y float64, z float64) (float64, float64, flo return multiplyMatrices(M, x, y, z) } -func lin_a98rgb(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { - return math.Copysign(math.Pow(math.Abs(val), 563.0/256), val) +func lin_a98rgb(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { + return val.Abs().PowConst(563.0 / 256).WithSignFrom(val) } return f(r), f(g), f(b) } -func gam_a98rgb(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { - return math.Copysign(math.Pow(math.Abs(val), 256.0/563), val) +func gam_a98rgb(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { + return val.Abs().PowConst(256.0 / 563).WithSignFrom(val) } return f(r), f(g), f(b) } -func lin_a98rgb_to_xyz(r float64, g float64, b float64) (float64, float64, float64) { +func lin_a98rgb_to_xyz(r F64, g F64, b F64) (F64, F64, F64) { M := [9]float64{ 573536.0 / 994567, 263643.0 / 1420810, 187206.0 / 994567, 591459.0 / 1989134, 6239551.0 / 9945670, 374412.0 / 4972835, @@ -172,7 +179,7 @@ func lin_a98rgb_to_xyz(r float64, g float64, b float64) (float64, float64, float return multiplyMatrices(M, r, g, b) } -func xyz_to_lin_a98rgb(x float64, y float64, z float64) (float64, float64, float64) { +func xyz_to_lin_a98rgb(x F64, y F64, z F64) (F64, F64, F64) { M := [9]float64{ 1829569.0 / 896150, -506331.0 / 896150, -308931.0 / 896150, -851781.0 / 878810, 1648619.0 / 878810, 36519.0 / 878810, @@ -181,33 +188,33 @@ func xyz_to_lin_a98rgb(x float64, y float64, z float64) (float64, float64, float return multiplyMatrices(M, x, y, z) } -func lin_2020(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { +func lin_2020(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { const α = 1.09929682680944 const β = 0.018053968510807 - if abs := math.Abs(val); abs < β*4.5 { - return val / 4.5 + if abs := val.Abs(); abs.Value() < β*4.5 { + return val.DivConst(4.5) } else { - return math.Copysign(math.Pow((abs+(α-1))/α, 1/0.45), val) + return abs.AddConst(α - 1).DivConst(α).PowConst(1 / 0.45).WithSignFrom(val) } } return f(r), f(g), f(b) } -func gam_2020(r float64, g float64, b float64) (float64, float64, float64) { - f := func(val float64) float64 { +func gam_2020(r F64, g F64, b F64) (F64, F64, F64) { + f := func(val F64) F64 { const α = 1.09929682680944 const β = 0.018053968510807 - if abs := math.Abs(val); abs > β { - return math.Copysign(α*math.Pow(abs, 0.45)-(α-1), val) + if abs := val.Abs(); abs.Value() > β { + return abs.PowConst(0.45).MulConst(α).SubConst(α - 1).WithSignFrom(val) } else { - return 4.5 * val + return val.MulConst(4.5) } } return f(r), f(g), f(b) } -func lin_2020_to_xyz(r float64, g float64, b float64) (float64, float64, float64) { +func lin_2020_to_xyz(r F64, g F64, b F64) (F64, F64, F64) { var M = [9]float64{ 63426534.0 / 99577255, 20160776.0 / 139408157, 47086771.0 / 278816314, 26158966.0 / 99577255, 472592308.0 / 697040785, 8267143.0 / 139408157, @@ -216,7 +223,7 @@ func lin_2020_to_xyz(r float64, g float64, b float64) (float64, float64, float64 return multiplyMatrices(M, r, g, b) } -func xyz_to_lin_2020(x float64, y float64, z float64) (float64, float64, float64) { +func xyz_to_lin_2020(x F64, y F64, z F64) (F64, F64, F64) { M := [9]float64{ 30757411.0 / 17917100, -6372589.0 / 17917100, -4539589.0 / 17917100, -19765991.0 / 29648200, 47925759.0 / 29648200, 467509.0 / 29648200, @@ -225,7 +232,7 @@ func xyz_to_lin_2020(x float64, y float64, z float64) (float64, float64, float64 return multiplyMatrices(M, x, y, z) } -func d65_to_d50(x float64, y float64, z float64) (float64, float64, float64) { +func d65_to_d50(x F64, y F64, z F64) (F64, F64, F64) { M := [9]float64{ 1.0479297925449969, 0.022946870601609652, -0.05019226628920524, 0.02962780877005599, 0.9904344267538799, -0.017073799063418826, @@ -234,7 +241,7 @@ func d65_to_d50(x float64, y float64, z float64) (float64, float64, float64) { return multiplyMatrices(M, x, y, z) } -func d50_to_d65(x float64, y float64, z float64) (float64, float64, float64) { +func d50_to_d65(x F64, y F64, z F64) (F64, F64, F64) { M := [9]float64{ 0.955473421488075, -0.02309845494876471, 0.06325924320057072, -0.0283697093338637, 1.0099953980813041, 0.021041441191917323, @@ -246,83 +253,83 @@ func d50_to_d65(x float64, y float64, z float64) (float64, float64, float64) { const d50_x = 0.3457 / 0.3585 const d50_z = (1.0 - 0.3457 - 0.3585) / 0.3585 -func xyz_to_lab(x float64, y float64, z float64) (float64, float64, float64) { +func xyz_to_lab(x F64, y F64, z F64) (F64, F64, F64) { const ε = 216.0 / 24389 const κ = 24389.0 / 27 - x /= d50_x - z /= d50_z + x = x.DivConst(d50_x) + z = z.DivConst(d50_z) - var f0, f1, f2 float64 - if x > ε { - f0 = math.Cbrt(x) + var f0, f1, f2 F64 + if x.Value() > ε { + f0 = x.Cbrt() } else { - f0 = (κ*x + 16) / 116 + f0 = x.MulConst(κ).AddConst(16).DivConst(116) } - if y > ε { - f1 = math.Cbrt(y) + if y.Value() > ε { + f1 = y.Cbrt() } else { - f1 = (κ*y + 16) / 116 + f1 = y.MulConst(κ).AddConst(16).DivConst(116) } - if z > ε { - f2 = math.Cbrt(z) + if z.Value() > ε { + f2 = z.Cbrt() } else { - f2 = (κ*z + 16) / 116 + f2 = z.MulConst(κ).AddConst(16).DivConst(116) } - return (116 * f1) - 16, - 500 * (f0 - f1), - 200 * (f1 - f2) + return f1.MulConst(116).SubConst(16), + f0.Sub(f1).MulConst(500), + f1.Sub(f2).MulConst(200) } -func lab_to_xyz(l float64, a float64, b float64) (x float64, y float64, z float64) { +func lab_to_xyz(l F64, a F64, b F64) (x F64, y F64, z F64) { const κ = 24389.0 / 27 const ε = 216.0 / 24389 - f1 := (l + 16) / 116 - f0 := a/500 + f1 - f2 := f1 - b/200 + f1 := l.AddConst(16).DivConst(116) + f0 := a.DivConst(500).Add(f1) + f2 := f1.Sub(b.DivConst(200)) - f0_3 := f0 * f0 * f0 - f2_3 := f2 * f2 * f2 + f0_3 := f0.Cubed() + f2_3 := f2.Cubed() - if f0_3 > ε { + if f0_3.Value() > ε { x = f0_3 } else { - x = (116*f0 - 16) / κ + x = f0.MulConst(116).SubConst(16).DivConst(κ) } - if l > κ*ε { - y = (l + 16) / 116 - y = y * y * y + if l.Value() > κ*ε { + y = l.AddConst(16).DivConst(116) + y = y.Cubed() } else { - y = l / κ + y = l.DivConst(κ) } - if f2_3 > ε { + if f2_3.Value() > ε { z = f2_3 } else { - z = (116*f2 - 16) / κ + z = f2.MulConst(116).SubConst(16).DivConst(κ) } - return x * d50_x, y, z * d50_z + return x.MulConst(d50_x), y, z.MulConst(d50_z) } -func lab_to_lch(l float64, a float64, b float64) (float64, float64, float64) { - hue := math.Atan2(b, a) * (180 / math.Pi) - if hue < 0 { - hue += 360 +func lab_to_lch(l F64, a F64, b F64) (F64, F64, F64) { + hue := b.Atan2(a).MulConst(180 / math.Pi) + if hue.Value() < 0 { + hue = hue.AddConst(360) } return l, - math.Sqrt(a*a + b*b), + a.Squared().Add(b.Squared()).Sqrt(), hue } -func lch_to_lab(l float64, c float64, h float64) (float64, float64, float64) { +func lch_to_lab(l F64, c F64, h F64) (F64, F64, F64) { return l, - c * math.Cos(h*math.Pi/180), - c * math.Sin(h*math.Pi/180) + h.MulConst(math.Pi / 180).Cos().Mul(c), + h.MulConst(math.Pi / 180).Sin().Mul(c) } -func xyz_to_oklab(x float64, y float64, z float64) (float64, float64, float64) { +func xyz_to_oklab(x F64, y F64, z F64) (F64, F64, F64) { XYZtoLMS := [9]float64{ 0.8190224432164319, 0.3619062562801221, -0.12887378261216414, 0.0329836671980271, 0.9292868468965546, 0.03614466816999844, @@ -334,10 +341,10 @@ func xyz_to_oklab(x float64, y float64, z float64) (float64, float64, float64) { 0.0259040371, 0.7827717662, -0.8086757660, } l, m, s := multiplyMatrices(XYZtoLMS, x, y, z) - return multiplyMatrices(LMStoOKLab, math.Cbrt(l), math.Cbrt(m), math.Cbrt(s)) + return multiplyMatrices(LMStoOKLab, l.Cbrt(), m.Cbrt(), s.Cbrt()) } -func oklab_to_xyz(l float64, a float64, b float64) (float64, float64, float64) { +func oklab_to_xyz(l F64, a F64, b F64) (F64, F64, F64) { LMStoXYZ := [9]float64{ 1.2268798733741557, -0.5578149965554813, 0.28139105017721583, -0.04057576262431372, 1.1122868293970594, -0.07171106666151701, @@ -349,52 +356,54 @@ func oklab_to_xyz(l float64, a float64, b float64) (float64, float64, float64) { 1.0000000546724109177, -0.089484182094965759684, -1.2914855378640917399, } l, m, s := multiplyMatrices(OKLabtoLMS, l, a, b) - return multiplyMatrices(LMStoXYZ, l*l*l, m*m*m, s*s*s) + return multiplyMatrices(LMStoXYZ, l.Cubed(), m.Cubed(), s.Cubed()) } -func oklab_to_oklch(l float64, a float64, b float64) (float64, float64, float64) { +func oklab_to_oklch(l F64, a F64, b F64) (F64, F64, F64) { return lab_to_lch(l, a, b) } -func oklch_to_oklab(l float64, c float64, h float64) (float64, float64, float64) { +func oklch_to_oklab(l F64, c F64, h F64) (F64, F64, F64) { return lch_to_lab(l, c, h) } -func multiplyMatrices(A [9]float64, b0 float64, b1 float64, b2 float64) (float64, float64, float64) { - return A[0]*b0 + A[1]*b1 + A[2]*b2, - A[3]*b0 + A[4]*b1 + A[5]*b2, - A[6]*b0 + A[7]*b1 + A[8]*b2 +func multiplyMatrices(A [9]float64, b0 F64, b1 F64, b2 F64) (F64, F64, F64) { + return b0.MulConst(A[0]).Add(b1.MulConst(A[1])).Add(b2.MulConst(A[2])), + b0.MulConst(A[3]).Add(b1.MulConst(A[4])).Add(b2.MulConst(A[5])), + b0.MulConst(A[6]).Add(b1.MulConst(A[7])).Add(b2.MulConst(A[8])) } -func delta_eok(L1 float64, a1 float64, b1 float64, L2 float64, a2 float64, b2 float64) float64 { - ΔL := L1 - L2 - Δa := a1 - a2 - Δb := b1 - b2 - return math.Sqrt(ΔL*ΔL + Δa*Δa + Δb*Δb) +func delta_eok(L1 F64, a1 F64, b1 F64, L2 F64, a2 F64, b2 F64) F64 { + ΔL_sq := L1.Sub(L2).Squared() + Δa_sq := a1.Sub(a2).Squared() + Δb_sq := b1.Sub(b2).Squared() + return ΔL_sq.Add(Δa_sq).Add(Δb_sq).Sqrt() } -func gamut_mapping_xyz_to_srgb(x float64, y float64, z float64) (float64, float64, float64) { +func gamut_mapping_xyz_to_srgb(x F64, y F64, z F64) (F64, F64, F64) { origin_l, origin_c, origin_h := oklab_to_oklch(xyz_to_oklab(x, y, z)) - if origin_l >= 1 || origin_l <= 0 { + if origin_l.Value() >= 1 || origin_l.Value() <= 0 { return origin_l, origin_l, origin_l } - oklch_to_srgb := func(l float64, c float64, h float64) (float64, float64, float64) { + oklch_to_srgb := func(l F64, c F64, h F64) (F64, F64, F64) { l, a, b := oklch_to_oklab(l, c, h) x, y, z := oklab_to_xyz(l, a, b) r, g, b := xyz_to_lin_srgb(x, y, z) return gam_srgb(r, g, b) } - srgb_to_oklab := func(r float64, g float64, b float64) (float64, float64, float64) { + srgb_to_oklab := func(r F64, g F64, b F64) (F64, F64, F64) { r, g, b = lin_srgb(r, g, b) x, y, z := lin_srgb_to_xyz(r, g, b) return xyz_to_oklab(x, y, z) } - inGamut := func(r float64, g float64, b float64) bool { - return r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1 + inGamut := func(r F64, g F64, b F64) bool { + return r.Value() >= 0 && r.Value() <= 1 && + g.Value() >= 0 && g.Value() <= 1 && + b.Value() >= 0 && b.Value() <= 1 } r, g, b := oklch_to_srgb(origin_l, origin_c, origin_h) @@ -404,21 +413,21 @@ func gamut_mapping_xyz_to_srgb(x float64, y float64, z float64) (float64, float6 const JND = 0.02 const epsilon = 0.0001 - min := 0.0 + min := helpers.NewF64(0.0) max := origin_c - clip := func(x float64) float64 { - if x < 0 { - return 0 + clip := func(x F64) F64 { + if x.Value() < 0 { + return helpers.NewF64(0) } - if x > 1 { - return 1 + if x.Value() > 1 { + return helpers.NewF64(1) } return x } - for max-min > epsilon { - chroma := (min + max) / 2 + for max.Sub(min).Value() > epsilon { + chroma := min.Add(max).DivConst(2) origin_c = chroma r, g, b = oklch_to_srgb(origin_l, origin_c, origin_h) @@ -431,7 +440,7 @@ func gamut_mapping_xyz_to_srgb(x float64, y float64, z float64) (float64, float6 L1, a1, b1 := srgb_to_oklab(clipped_r, clipped_b, clipped_g) L2, a2, b2 := srgb_to_oklab(r, g, b) E := delta_eok(L1, a1, b1, L2, a2, b2) - if E < JND { + if E.Value() < JND { return clipped_r, clipped_g, clipped_b } @@ -441,77 +450,78 @@ func gamut_mapping_xyz_to_srgb(x float64, y float64, z float64) (float64, float6 return r, g, b } -func hsl_to_rgb(hue float64, sat float64, light float64) (float64, float64, float64) { - hue /= 360 - hue -= math.Floor(hue) - hue *= 360 +func hsl_to_rgb(hue F64, sat F64, light F64) (F64, F64, F64) { + hue = hue.DivConst(360) + hue = hue.Sub(hue.Floor()) + hue = hue.MulConst(360) - sat /= 100 - light /= 100 + sat = sat.DivConst(100) + light = light.DivConst(100) - f := func(n float64) float64 { - k := n + hue/30 - k /= 12 - k -= math.Floor(k) - k *= 12 - a := sat * math.Min(light, 1-light) - return light - a*math.Max(-1, math.Min(math.Min(k-3, 9-k), 1)) + f := func(n float64) F64 { + k := hue.DivConst(30).AddConst(n) + k = k.DivConst(12) + k = k.Sub(k.Floor()) + k = k.MulConst(12) + a := helpers.Min2(light, light.Neg().AddConst(1)).Mul(sat) + return light.Sub(helpers.Max2(helpers.NewF64(-1), helpers.Min3(k.SubConst(3), k.Neg().AddConst(9), helpers.NewF64(1))).Mul(a)) } return f(0), f(8), f(4) } -func rgb_to_hsl(red float64, green float64, blue float64) (float64, float64, float64) { - max := math.Max(math.Max(red, green), blue) - min := math.Min(math.Min(red, green), blue) - hue, sat, light := math.NaN(), 0.0, (min+max)/2 - d := max - min +func rgb_to_hsl(red F64, green F64, blue F64) (F64, F64, F64) { + max := helpers.Max3(red, green, blue) + min := helpers.Min3(red, green, blue) + hue, sat, light := helpers.NewF64(math.NaN()), helpers.NewF64(0.0), min.Add(max).DivConst(2) + d := max.Sub(min) - if d != 0 { - if div := math.Min(light, 1-light); div != 0 { - sat = (max - light) / div + if d.Value() != 0 { + if div := helpers.Min2(light, light.Neg().AddConst(1)); div.Value() != 0 { + sat = max.Sub(light).Div(div) } switch max { case red: - hue = (green - blue) / d - if green < blue { - hue += 6 + hue = green.Sub(blue).Div(d) + if green.Value() < blue.Value() { + hue = hue.AddConst(6) } case green: - hue = (blue-red)/d + 2 + hue = blue.Sub(red).Div(d).AddConst(2) case blue: - hue = (red-green)/d + 4 + hue = red.Sub(green).Div(d).AddConst(4) } - hue = hue * 60 + hue = hue.MulConst(60) } - return hue, sat * 100, light * 100 + return hue, sat.MulConst(100), light.MulConst(100) } -func hwb_to_rgb(hue float64, white float64, black float64) (float64, float64, float64) { - white /= 100 - black /= 100 - if white+black >= 1 { - gray := white / (white + black) +func hwb_to_rgb(hue F64, white F64, black F64) (F64, F64, F64) { + white = white.DivConst(100) + black = black.DivConst(100) + if white.Add(black).Value() >= 1 { + gray := white.Div(white.Add(black)) return gray, gray, gray } - r, g, b := hsl_to_rgb(hue, 100, 50) - r = white + r*(1-white-black) - g = white + g*(1-white-black) - b = white + b*(1-white-black) + delta := white.Add(black).Neg().AddConst(1) + r, g, b := hsl_to_rgb(hue, helpers.NewF64(100), helpers.NewF64(50)) + r = delta.Mul(r).Add(white) + g = delta.Mul(g).Add(white) + b = delta.Mul(b).Add(white) return r, g, b } -func rgb_to_hwb(red float64, green float64, blue float64) (float64, float64, float64) { +func rgb_to_hwb(red F64, green F64, blue F64) (F64, F64, F64) { h, _, _ := rgb_to_hsl(red, green, blue) - white := math.Min(math.Min(red, green), blue) - black := 1 - math.Max(math.Max(red, green), blue) - return h, white * 100, black * 100 + white := helpers.Min3(red, green, blue) + black := helpers.Max3(red, green, blue).Neg().AddConst(1) + return h, white.MulConst(100), black.MulConst(100) } -func xyz_to_colorSpace(x float64, y float64, z float64, colorSpace colorSpace) (float64, float64, float64) { +func xyz_to_colorSpace(x F64, y F64, z F64, colorSpace colorSpace) (F64, F64, F64) { switch colorSpace { case colorSpace_a98_rgb: return gam_a98rgb(xyz_to_lin_a98rgb(x, y, z)) @@ -560,7 +570,7 @@ func xyz_to_colorSpace(x float64, y float64, z float64, colorSpace colorSpace) ( } } -func colorSpace_to_xyz(v0 float64, v1 float64, v2 float64, colorSpace colorSpace) (float64, float64, float64) { +func colorSpace_to_xyz(v0 F64, v1 F64, v2 F64, colorSpace colorSpace) (F64, F64, F64) { switch colorSpace { case colorSpace_a98_rgb: return lin_a98rgb_to_xyz(lin_a98rgb(v0, v1, v2)) diff --git a/internal/css_parser/css_decls_color.go b/internal/css_parser/css_decls_color.go index 71e11960b6c..c6b66910347 100644 --- a/internal/css_parser/css_decls_color.go +++ b/internal/css_parser/css_decls_color.go @@ -9,6 +9,7 @@ import ( "github.com/evanw/esbuild/internal/compat" "github.com/evanw/esbuild/internal/css_ast" "github.com/evanw/esbuild/internal/css_lexer" + "github.com/evanw/esbuild/internal/helpers" ) // These names are shorter than their hex codes @@ -420,8 +421,8 @@ func (p *parser) lowerAndMinifyColor(token css_ast.Token, wouldClipColor *bool) } type parsedColor struct { - x, y, z float64 // color if hasColorSpace == true - hex uint32 // color and alpha if hasColorSpace == false, alpha if hasColorSpace == true + x, y, z F64 // color if hasColorSpace == true + hex uint32 // color and alpha if hasColorSpace == false, alpha if hasColorSpace == true hasColorSpace bool } @@ -571,7 +572,7 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { if s, ok := s.ClampedFractionForPercentage(); ok { if l, ok := l.ClampedFractionForPercentage(); ok { if a, ok := parseAlphaByte(a); ok { - r, g, b := hslToRgb(h, s, l) + r, g, b := hslToRgb(helpers.NewF64(h), helpers.NewF64(s), helpers.NewF64(l)) return parsedColor{hex: packRGBA(r, g, b, a)}, true } } @@ -599,7 +600,7 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { if white, ok := s.ClampedFractionForPercentage(); ok { if black, ok := l.ClampedFractionForPercentage(); ok { if a, ok := parseAlphaByte(a); ok { - r, g, b := hwbToRgb(h, white, black) + r, g, b := hwbToRgb(helpers.NewF64(h), helpers.NewF64(white), helpers.NewF64(black)) return parsedColor{hex: packRGBA(r, g, b, a)}, true } } @@ -627,6 +628,7 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { if v1, ok := args[2].NumberOrFractionForPercentage(1, 0); ok { if v2, ok := args[3].NumberOrFractionForPercentage(1, 0); ok { if a, ok := parseAlphaByte(alpha); ok { + v0, v1, v2 := helpers.NewF64(v0), helpers.NewF64(v1), helpers.NewF64(v2) switch strings.ToLower(colorSpace.Text) { case "a98-rgb": r, g, b := lin_a98rgb(v0, v1, v2) @@ -694,6 +696,7 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { if v0, ok := v0.NumberOrFractionForPercentage(100, 0); ok { if v1, ok := v1.NumberOrFractionForPercentage(125, css_ast.AllowAnyPercentage); ok { if v2, ok := v2.NumberOrFractionForPercentage(125, css_ast.AllowAnyPercentage); ok { + v0, v1, v2 := helpers.NewF64(v0), helpers.NewF64(v1), helpers.NewF64(v2) x, y, z := lab_to_xyz(v0, v1, v2) x, y, z = d50_to_d65(x, y, z) return parsedColor{hasColorSpace: true, x: x, y: y, z: z, hex: alpha}, true @@ -705,6 +708,7 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { if v0, ok := v0.NumberOrFractionForPercentage(100, 0); ok { if v1, ok := v1.NumberOrFractionForPercentage(125, css_ast.AllowPercentageAbove100); ok { if v2, ok := degreesForAngle(v2); ok { + v0, v1, v2 := helpers.NewF64(v0), helpers.NewF64(v1), helpers.NewF64(v2) l, a, b := lch_to_lab(v0, v1, v2) x, y, z := lab_to_xyz(l, a, b) x, y, z = d50_to_d65(x, y, z) @@ -717,6 +721,7 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { if v0, ok := v0.NumberOrFractionForPercentage(1, 0); ok { if v1, ok := v1.NumberOrFractionForPercentage(0.4, css_ast.AllowAnyPercentage); ok { if v2, ok := v2.NumberOrFractionForPercentage(0.4, css_ast.AllowAnyPercentage); ok { + v0, v1, v2 := helpers.NewF64(v0), helpers.NewF64(v1), helpers.NewF64(v2) x, y, z := oklab_to_xyz(v0, v1, v2) return parsedColor{hasColorSpace: true, x: x, y: y, z: z, hex: alpha}, true } @@ -727,6 +732,7 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { if v0, ok := v0.NumberOrFractionForPercentage(1, 0); ok { if v1, ok := v1.NumberOrFractionForPercentage(0.4, css_ast.AllowPercentageAbove100); ok { if v2, ok := degreesForAngle(v2); ok { + v0, v1, v2 := helpers.NewF64(v0), helpers.NewF64(v1), helpers.NewF64(v2) l, a, b := oklch_to_oklab(v0, v1, v2) x, y, z := oklab_to_xyz(l, a, b) return parsedColor{hasColorSpace: true, x: x, y: y, z: z, hex: alpha}, true @@ -743,55 +749,55 @@ func parseColor(token css_ast.Token) (parsedColor, bool) { } // Reference: https://drafts.csswg.org/css-color/#hwb-to-rgb -func hwbToRgb(hue float64, white float64, black float64) (r float64, g float64, b float64) { - if white+black >= 1 { - gray := white / (white + black) +func hwbToRgb(hue F64, white F64, black F64) (r F64, g F64, b F64) { + if white.Add(black).Value() >= 1 { + gray := white.Div(white.Add(black)) return gray, gray, gray } - delta := 1 - white - black - r, g, b = hslToRgb(hue, 1, 0.5) - r = white + delta*r - g = white + delta*g - b = white + delta*b + delta := white.Add(black).Neg().AddConst(1) + r, g, b = hslToRgb(hue, helpers.NewF64(1), helpers.NewF64(0.5)) + r = delta.Mul(r).Add(white) + g = delta.Mul(g).Add(white) + b = delta.Mul(b).Add(white) return } // Reference https://drafts.csswg.org/css-color/#hsl-to-rgb -func hslToRgb(hue float64, sat float64, light float64) (r float64, g float64, b float64) { - hue /= 360.0 - var t2 float64 - if light <= 0.5 { - t2 = light * (sat + 1) +func hslToRgb(hue F64, sat F64, light F64) (r F64, g F64, b F64) { + hue = hue.DivConst(360.0) + var t2 F64 + if light.Value() <= 0.5 { + t2 = sat.AddConst(1).Mul(light) } else { - t2 = light + sat - (light * sat) + t2 = light.Add(sat).Sub(light.Mul(sat)) } - t1 := light*2 - t2 - r = hueToRgb(t1, t2, hue+1.0/3.0) + t1 := light.MulConst(2).Sub(t2) + r = hueToRgb(t1, t2, hue.AddConst(1.0/3.0)) g = hueToRgb(t1, t2, hue) - b = hueToRgb(t1, t2, hue-1.0/3.0) + b = hueToRgb(t1, t2, hue.SubConst(1.0/3.0)) return } -func hueToRgb(t1 float64, t2 float64, hue float64) float64 { - hue -= math.Floor(hue) - hue *= 6.0 - var f float64 - if hue < 1 { - f = (t2-t1)*hue + t1 - } else if hue < 3 { +func hueToRgb(t1 F64, t2 F64, hue F64) F64 { + hue = hue.Sub(hue.Floor()) + hue = hue.MulConst(6) + var f F64 + if hue.Value() < 1 { + f = helpers.Lerp(t1, t2, hue) + } else if hue.Value() < 3 { f = t2 - } else if hue < 4 { - f = (t2-t1)*(4-hue) + t1 + } else if hue.Value() < 4 { + f = helpers.Lerp(t1, t2, hue.Neg().AddConst(4)) } else { f = t1 } return f } -func packRGBA(rf float64, gf float64, bf float64, a uint32) uint32 { - r := floatToByte(rf) - g := floatToByte(gf) - b := floatToByte(bf) +func packRGBA(rf F64, gf F64, bf F64, a uint32) uint32 { + r := floatToByte(rf.Value()) + g := floatToByte(gf.Value()) + b := floatToByte(bf.Value()) return (r << 24) | (g << 16) | (b << 8) | a } @@ -838,9 +844,11 @@ func parseColorByte(token css_ast.Token, scale float64) (uint32, bool) { return uint32(i), ok } -func tryToConvertToHexWithoutClipping(x float64, y float64, z float64, a uint32) (uint32, bool) { +func tryToConvertToHexWithoutClipping(x F64, y F64, z F64, a uint32) (uint32, bool) { r, g, b := gam_srgb(xyz_to_lin_srgb(x, y, z)) - if r < -0.5/255 || r > 255.5/255 || g < -0.5/255 || g > 255.5/255 || b < -0.5/255 || b > 255.5/255 { + if r.Value() < -0.5/255 || r.Value() > 255.5/255 || + g.Value() < -0.5/255 || g.Value() > 255.5/255 || + b.Value() < -0.5/255 || b.Value() > 255.5/255 { return 0, false } return packRGBA(r, g, b, a), true diff --git a/internal/css_parser/css_decls_gradient.go b/internal/css_parser/css_decls_gradient.go index f1139eaa47c..4edec479034 100644 --- a/internal/css_parser/css_decls_gradient.go +++ b/internal/css_parser/css_decls_gradient.go @@ -9,6 +9,7 @@ import ( "github.com/evanw/esbuild/internal/compat" "github.com/evanw/esbuild/internal/css_ast" "github.com/evanw/esbuild/internal/css_lexer" + "github.com/evanw/esbuild/internal/helpers" "github.com/evanw/esbuild/internal/logger" ) @@ -268,17 +269,17 @@ func removeImpliedPositions(kind gradientKind, colorStops []colorStop) []colorSt continue } } - positions[i].value = math.NaN() + positions[i].value = helpers.NewF64(math.NaN()) } start := 0 for start < len(colorStops) { - if startPos := positions[start]; !math.IsNaN(startPos.value) { + if startPos := positions[start]; !startPos.value.IsNaN() { end := start + 1 run: for colorStops[end-1].midpoint.Kind == css_lexer.T(0) && end < len(colorStops) { endPos := positions[end] - if math.IsNaN(endPos.value) || endPos.unit != startPos.unit { + if endPos.value.IsNaN() || endPos.unit != startPos.unit { break } @@ -286,9 +287,9 @@ func removeImpliedPositions(kind gradientKind, colorStops []colorStop) []colorSt // using the start and end positions instead of the first and second // positions because it's more accurate. for i := start + 1; i < end; i++ { - t := float64(i-start) / float64(end-start) - impliedValue := startPos.value + (endPos.value-startPos.value)*t - if math.Abs(positions[i].value-impliedValue) > 0.01 { + t := helpers.NewF64(float64(i - start)).DivConst(float64(end - start)) + impliedValue := helpers.Lerp(startPos.value, endPos.value, t) + if positions[i].value.Sub(impliedValue).Abs().Value() > 0.01 { break run } } @@ -440,7 +441,7 @@ func removeColorInterpolation(tokens []css_ast.Token) ([]css_ast.Token, colorSpa type valueWithUnit struct { unit string - value float64 + value F64 } type parsedColorStop struct { @@ -451,13 +452,13 @@ type parsedColorStop struct { midpoint *valueWithUnit // Non-premultiplied color information in XYZ space - x, y, z, alpha float64 + x, y, z, alpha F64 // Non-premultiplied color information in sRGB space - r, g, b float64 + r, g, b F64 // Premultiplied color information in the interpolation color space - v0, v1, v2 float64 + v0, v1, v2 F64 // True if the original color has a color space hasColorSpace bool @@ -471,11 +472,11 @@ func tryToParseColorStops(gradient parsedGradient) ([]parsedColorStop, bool) { if !ok { return nil, false } - var r, g, b float64 + var r, g, b F64 if !color.hasColorSpace { - r = float64(hexR(color.hex)) / 255 - g = float64(hexG(color.hex)) / 255 - b = float64(hexB(color.hex)) / 255 + r = helpers.NewF64(float64(hexR(color.hex))).DivConst(255) + g = helpers.NewF64(float64(hexG(color.hex))).DivConst(255) + b = helpers.NewF64(float64(hexB(color.hex))).DivConst(255) color.x, color.y, color.z = lin_srgb_to_xyz(lin_srgb(r, g, b)) } else { r, g, b = gam_srgb(xyz_to_lin_srgb(color.x, color.y, color.z)) @@ -487,7 +488,7 @@ func tryToParseColorStops(gradient parsedGradient) ([]parsedColorStop, bool) { r: r, g: g, b: b, - alpha: float64(hexA(color.hex)) / 255, + alpha: helpers.NewF64(float64(hexA(color.hex))).DivConst(255), hasColorSpace: color.hasColorSpace, } @@ -526,10 +527,10 @@ func tryToParseColorStops(gradient parsedGradient) ([]parsedColorStop, bool) { // Fill in missing positions for the endpoints first if first := &colorStops[0]; len(first.positionTerms) == 0 { - first.positionTerms = []valueWithUnit{{value: 0, unit: "%"}} + first.positionTerms = []valueWithUnit{{value: helpers.NewF64(0), unit: "%"}} } if last := &colorStops[len(colorStops)-1]; len(last.positionTerms) == 0 { - last.positionTerms = []valueWithUnit{{value: 100, unit: "%"}} + last.positionTerms = []valueWithUnit{{value: helpers.NewF64(100), unit: "%"}} } // Set all positions to be greater than the position before them @@ -548,12 +549,12 @@ func tryToParseColorStops(gradient parsedGradient) ([]parsedColorStop, bool) { } if len(stop.positionTerms) == 1 { if prevPos.unit == stop.positionTerms[0].unit { - stop.positionTerms[0].value = math.Max(prevPos.value, stop.positionTerms[0].value) + stop.positionTerms[0].value = helpers.Max2(prevPos.value, stop.positionTerms[0].value) } prevPos = stop.positionTerms[0] } if stop.midpoint != nil && prevPos.unit == stop.midpoint.unit { - stop.midpoint.value = math.Max(prevPos.value, stop.midpoint.value) + stop.midpoint.value = helpers.Max2(prevPos.value, stop.midpoint.value) } } @@ -599,18 +600,18 @@ func tryToParseColorStops(gradient parsedGradient) ([]parsedColorStop, bool) { for i, stop := range colorStops { if len(stop.positionTerms) != 1 { info := infos[i] - t := float64(info.fromCount) / float64(info.fromCount+info.toCount) + t := helpers.NewF64(float64(info.fromCount)).DivConst(float64(info.fromCount + info.toCount)) if info.fromPos.unit == info.toPos.unit { colorStops[i].positionTerms = []valueWithUnit{{ - value: info.fromPos.value + (info.toPos.value-info.fromPos.value)*t, + value: helpers.Lerp(info.fromPos.value, info.toPos.value, t), unit: info.fromPos.unit, }} } else { colorStops[i].positionTerms = []valueWithUnit{{ - value: info.fromPos.value * (1 - t), + value: t.Neg().AddConst(1).Mul(info.fromPos.value), unit: info.fromPos.unit, }, { - value: info.toPos.value * t, + value: t.Mul(info.toPos.value), unit: info.toPos.unit, }} } @@ -641,7 +642,7 @@ func tryToParseValue(token css_ast.Token, kind gradientKind) (result valueWithUn if !ok { return } - result.value = degrees * (100.0 / 360) + result.value = helpers.NewF64(degrees).MulConst(100.0 / 360) result.unit = "%" case css_lexer.TPercentage: @@ -649,7 +650,7 @@ func tryToParseValue(token css_ast.Token, kind gradientKind) (result valueWithUn if err != nil { return } - result.value = percent + result.value = helpers.NewF64(percent) result.unit = "%" default: @@ -663,7 +664,7 @@ func tryToParseValue(token css_ast.Token, kind gradientKind) (result valueWithUn if err != nil || zero != 0 { return } - result.value = 0 + result.value = helpers.NewF64(0) result.unit = "%" case css_lexer.TDimension: @@ -671,7 +672,7 @@ func tryToParseValue(token css_ast.Token, kind gradientKind) (result valueWithUn if err != nil { return } - result.value = dimensionValue + result.value = helpers.NewF64(dimensionValue) result.unit = token.DimensionUnit() case css_lexer.TPercentage: @@ -679,7 +680,7 @@ func tryToParseValue(token css_ast.Token, kind gradientKind) (result valueWithUn if err != nil { return } - result.value = percentageValue + result.value = helpers.NewF64(percentageValue) result.unit = "%" default: @@ -709,17 +710,17 @@ func tryToExpandGradient( // Duplicate the endpoints if they should wrap around to themselves if hueMethod == longerHue && colorSpace.isPolar() && len(colorStops) > 0 { if first := colorStops[0]; len(first.positionTerms) == 1 { - if first.positionTerms[0].value < 0 { - colorStops[0].positionTerms[0].value = 0 - } else if first.positionTerms[0].value > 0 { + if first.positionTerms[0].value.Value() < 0 { + colorStops[0].positionTerms[0].value = helpers.NewF64(0) + } else if first.positionTerms[0].value.Value() > 0 { first.midpoint = nil - first.positionTerms = []valueWithUnit{{value: 0, unit: first.positionTerms[0].unit}} + first.positionTerms = []valueWithUnit{{value: helpers.NewF64(0), unit: first.positionTerms[0].unit}} colorStops = append([]parsedColorStop{first}, colorStops...) } } if last := colorStops[len(colorStops)-1]; len(last.positionTerms) == 1 { - if last.positionTerms[0].unit != "%" || last.positionTerms[0].value < 100 { - last.positionTerms = []valueWithUnit{{value: 100, unit: "%"}} + if last.positionTerms[0].unit != "%" || last.positionTerms[0].value.Value() < 100 { + last.positionTerms = []valueWithUnit{{value: helpers.NewF64(100), unit: "%"}} colorStops = append(colorStops, last) } } @@ -728,51 +729,51 @@ func tryToExpandGradient( var newColorStops []colorStop var generateColorStops func( int, parsedColorStop, parsedColorStop, - float64, float64, float64, float64, float64, float64, float64, float64, - float64, float64, float64, float64, float64, float64, float64, float64, + F64, F64, F64, F64, F64, F64, F64, F64, + F64, F64, F64, F64, F64, F64, F64, F64, ) generateColorStops = func( depth int, from parsedColorStop, to parsedColorStop, - prevX, prevY, prevZ, prevR, prevG, prevB, prevA, prevT float64, - nextX, nextY, nextZ, nextR, nextG, nextB, nextA, nextT float64, + prevX, prevY, prevZ, prevR, prevG, prevB, prevA, prevT F64, + nextX, nextY, nextZ, nextR, nextG, nextB, nextA, nextT F64, ) { if depth > 4 { return } - t := (prevT + nextT) / 2 + t := prevT.Add(nextT).DivConst(2) positionT := t // Handle midpoints (which we have already checked uses the same units) if from.midpoint != nil { fromPos := from.positionTerms[0].value toPos := to.positionTerms[0].value - stopPos := fromPos + (toPos-fromPos)*t - H := (from.midpoint.value - fromPos) / (toPos - fromPos) - P := (stopPos - fromPos) / (toPos - fromPos) - if H <= 0 { - positionT = 1 - } else if H >= 1 { - positionT = 0 + stopPos := helpers.Lerp(fromPos, toPos, t) + H := from.midpoint.value.Sub(fromPos).Div(toPos.Sub(fromPos)) + P := stopPos.Sub(fromPos).Div(toPos.Sub(fromPos)) + if H.Value() <= 0 { + positionT = helpers.NewF64(1) + } else if H.Value() >= 1 { + positionT = helpers.NewF64(0) } else { - positionT = math.Pow(P, -1/math.Log2(H)) + positionT = P.Pow(helpers.NewF64(-1).Div(H.Log2())) } } v0, v1, v2 := interpolateColors(from.v0, from.v1, from.v2, to.v0, to.v1, to.v2, colorSpace, hueMethod, positionT) - a := from.alpha + (to.alpha-from.alpha)*positionT + a := helpers.Lerp(from.alpha, to.alpha, positionT) v0, v1, v2 = unpremultiply(v0, v1, v2, a, colorSpace) x, y, z := colorSpace_to_xyz(v0, v1, v2, colorSpace) // Stop when the color is similar enough to the sRGB midpoint const epsilon = 4.0 / 255 r, g, b := gam_srgb(xyz_to_lin_srgb(x, y, z)) - dr := r*a - (prevR*prevA+nextR*nextA)/2 - dg := g*a - (prevG*prevA+nextG*nextA)/2 - db := b*a - (prevB*prevA+nextB*nextA)/2 - if d := dr*dr + dg*dg + db*db; d < epsilon*epsilon { + dr := r.Mul(a).Sub(prevR.Mul(prevA).Add(nextR.Mul(nextA)).DivConst(2)) + dg := g.Mul(a).Sub(prevG.Mul(prevA).Add(nextG.Mul(nextA)).DivConst(2)) + db := b.Mul(a).Sub(prevB.Mul(prevA).Add(nextB.Mul(nextA)).DivConst(2)) + if d := dr.Squared().Add(dg.Squared()).Add(db.Squared()); d.Value() < epsilon*epsilon { return } @@ -810,8 +811,8 @@ func tryToExpandGradient( if i+1 < len(colorStops) { next := colorStops[i+1] generateColorStops(0, stop, next, - stop.x, stop.y, stop.z, stop.r, stop.g, stop.b, stop.alpha, 0, - next.x, next.y, next.z, next.r, next.g, next.b, next.alpha, 1) + stop.x, stop.y, stop.z, stop.r, stop.g, stop.b, stop.alpha, helpers.NewF64(0), + next.x, next.y, next.z, next.r, next.g, next.b, next.alpha, helpers.NewF64(1)) } } @@ -820,11 +821,11 @@ func tryToExpandGradient( return true } -func formatFloat(value float64, decimals int) string { - return strings.TrimSuffix(strings.TrimRight(strconv.FormatFloat(value, 'f', decimals, 64), "0"), ".") +func formatFloat(value F64, decimals int) string { + return strings.TrimSuffix(strings.TrimRight(strconv.FormatFloat(value.Value(), 'f', decimals, 64), "0"), ".") } -func makeDimensionOrPercentToken(loc logger.Loc, value float64, unit string) (token css_ast.Token) { +func makeDimensionOrPercentToken(loc logger.Loc, value F64, unit string) (token css_ast.Token) { token.Loc = loc token.Text = formatFloat(value, 2) if unit == "%" { @@ -863,9 +864,9 @@ func makePositionToken(loc logger.Loc, positionTerms []valueWithUnit) css_ast.To } } -func makeColorToken(loc logger.Loc, x float64, y float64, z float64, a float64) (color css_ast.Token) { +func makeColorToken(loc logger.Loc, x F64, y F64, z F64, a F64) (color css_ast.Token) { color.Loc = loc - alpha := uint32(math.Round(a * 255)) + alpha := uint32(a.MulConst(255).Round().Value()) if hex, ok := tryToConvertToHexWithoutClipping(x, y, z, alpha); ok { color.Kind = css_lexer.THash if alpha == 255 { @@ -900,7 +901,7 @@ func makeColorToken(loc logger.Loc, x float64, y float64, z float64, a float64) Whitespace: css_ast.WhitespaceBefore, }, } - if a < 1 { + if a.Value() < 1 { children = append(children, css_ast.Token{ Loc: loc, @@ -923,69 +924,69 @@ func makeColorToken(loc logger.Loc, x float64, y float64, z float64, a float64) return } -func interpolateHues(a, b, t float64, hueMethod hueMethod) float64 { - a /= 360 - b /= 360 - a -= math.Floor(a) - b -= math.Floor(b) +func interpolateHues(a, b, t F64, hueMethod hueMethod) F64 { + a = a.DivConst(360) + b = b.DivConst(360) + a = a.Sub(a.Floor()) + b = b.Sub(b.Floor()) switch hueMethod { case shorterHue: - delta := b - a - if delta > 0.5 { - a++ + delta := b.Sub(a) + if delta.Value() > 0.5 { + a = a.AddConst(1) } - if delta < -0.5 { - b++ + if delta.Value() < -0.5 { + b = b.AddConst(1) } case longerHue: - delta := b - a - if delta > 0 && delta < 0.5 { - a++ + delta := b.Sub(a) + if delta.Value() > 0 && delta.Value() < 0.5 { + a = a.AddConst(1) } - if delta > -0.5 && delta <= 0 { - b++ + if delta.Value() > -0.5 && delta.Value() <= 0 { + b = b.AddConst(1) } case increasingHue: - if b < a { - b++ + if b.Value() < a.Value() { + b = b.AddConst(1) } case decreasingHue: - if a < b { - a++ + if a.Value() < b.Value() { + a = a.AddConst(1) } } - return (a + (b-a)*t) * 360 + return helpers.Lerp(a, b, t).MulConst(360) } func interpolateColors( - a0, a1, a2 float64, b0, b1, b2 float64, - colorSpace colorSpace, hueMethod hueMethod, t float64, -) (v0 float64, v1 float64, v2 float64) { - v1 = a1 + (b1-a1)*t + a0, a1, a2 F64, b0, b1, b2 F64, + colorSpace colorSpace, hueMethod hueMethod, t F64, +) (v0 F64, v1 F64, v2 F64) { + v1 = helpers.Lerp(a1, b1, t) switch colorSpace { case colorSpace_hsl, colorSpace_hwb: - v2 = a2 + (b2-a2)*t + v2 = helpers.Lerp(a2, b2, t) v0 = interpolateHues(a0, b0, t, hueMethod) case colorSpace_lch, colorSpace_oklch: - v0 = a0 + (b0-a0)*t + v0 = helpers.Lerp(a0, b0, t) v2 = interpolateHues(a2, b2, t, hueMethod) default: - v0 = a0 + (b0-a0)*t - v2 = a2 + (b2-a2)*t + v0 = helpers.Lerp(a0, b0, t) + v2 = helpers.Lerp(a2, b2, t) } return v0, v1, v2 } -func interpolatePositions(a []valueWithUnit, b []valueWithUnit, t float64) (result []valueWithUnit) { +func interpolatePositions(a []valueWithUnit, b []valueWithUnit, t F64) (result []valueWithUnit) { findUnit := func(unit string) int { for i, x := range result { if x.unit == unit { @@ -998,19 +999,21 @@ func interpolatePositions(a []valueWithUnit, b []valueWithUnit, t float64) (resu // "result += a * (1 - t)" for _, term := range a { - result[findUnit(term.unit)].value += term.value * (1 - t) + ptr := &result[findUnit(term.unit)] + ptr.value = t.Neg().AddConst(1).Mul(term.value).Add(ptr.value) } // "result += b * t" for _, term := range b { - result[findUnit(term.unit)].value += term.value * t + ptr := &result[findUnit(term.unit)] + ptr.value = t.Mul(term.value).Add(ptr.value) } // Remove an extra zero value for neatness. We don't remove all // of them because it may be important to retain a single zero. if len(result) > 1 { for i, term := range result { - if term.value == 0 { + if term.value.Value() == 0 { copy(result[i:], result[i+1:]) result = result[:len(result)-1] break @@ -1021,34 +1024,34 @@ func interpolatePositions(a []valueWithUnit, b []valueWithUnit, t float64) (resu return } -func premultiply(v0, v1, v2, alpha float64, colorSpace colorSpace) (float64, float64, float64) { - if alpha < 1 { +func premultiply(v0, v1, v2, alpha F64, colorSpace colorSpace) (F64, F64, F64) { + if alpha.Value() < 1 { switch colorSpace { case colorSpace_hsl, colorSpace_hwb: - v2 *= alpha + v2 = v2.Mul(alpha) case colorSpace_lch, colorSpace_oklch: - v0 *= alpha + v0 = v0.Mul(alpha) default: - v0 *= alpha - v2 *= alpha + v0 = v0.Mul(alpha) + v2 = v2.Mul(alpha) } - v1 *= alpha + v1 = v1.Mul(alpha) } return v0, v1, v2 } -func unpremultiply(v0, v1, v2, alpha float64, colorSpace colorSpace) (float64, float64, float64) { - if alpha > 0 && alpha < 1 { +func unpremultiply(v0, v1, v2, alpha F64, colorSpace colorSpace) (F64, F64, F64) { + if alpha.Value() > 0 && alpha.Value() < 1 { switch colorSpace { case colorSpace_hsl, colorSpace_hwb: - v2 /= alpha + v2 = v2.Div(alpha) case colorSpace_lch, colorSpace_oklch: - v0 /= alpha + v0 = v0.Div(alpha) default: - v0 /= alpha - v2 /= alpha + v0 = v0.Div(alpha) + v2 = v2.Div(alpha) } - v1 /= alpha + v1 = v1.Div(alpha) } return v0, v1, v2 } diff --git a/internal/css_parser/css_nesting.go b/internal/css_parser/css_nesting.go index 31f3b602cb5..e2831cf3578 100644 --- a/internal/css_parser/css_nesting.go +++ b/internal/css_parser/css_nesting.go @@ -23,32 +23,35 @@ func (p *parser) lowerNestingInRule(rule css_ast.Rule, results []css_ast.Rule) [ } } - // Filter out pseudo elements because they are ignored by nested style - // rules. This is because pseudo-elements are not valid within :is(): - // https://www.w3.org/TR/selectors-4/#matches-pseudo. This restriction - // may be relaxed in the future, but this restriction hash shipped so - // we're stuck with it: https://github.com/w3c/csswg-drafts/issues/7433. - selectors := r.Selectors - n := 0 - for _, sel := range selectors { + parentSelectors := make([]css_ast.ComplexSelector, 0, len(r.Selectors)) + for i, sel := range r.Selectors { + // Top-level "&" should be replaced with ":scope" to avoid recursion. + // From https://www.w3.org/TR/css-nesting-1/#nest-selector: + // + // "When used in the selector of a nested style rule, the nesting + // selector represents the elements matched by the parent rule. When + // used in any other context, it represents the same elements as + // :scope in that context (unless otherwise defined)." + // + substituted := make([]css_ast.CompoundSelector, 0, len(sel.Selectors)) + for _, x := range sel.Selectors { + substituted = p.substituteAmpersandsInCompoundSelector(x, scope, substituted, keepLeadingCombinator) + } + r.Selectors[i] = css_ast.ComplexSelector{Selectors: substituted} + + // Filter out pseudo elements because they are ignored by nested style + // rules. This is because pseudo-elements are not valid within :is(): + // https://www.w3.org/TR/selectors-4/#matches-pseudo. This restriction + // may be relaxed in the future, but this restriction hash shipped so + // we're stuck with it: https://github.com/w3c/csswg-drafts/issues/7433. + // + // Note: This is only for the parent selector list that is used to + // substitute "&" within child rules. Do not filter out the pseudo + // element from the top-level selector list. if !sel.UsesPseudoElement() { - // Top-level "&" should be replaced with ":scope" to avoid recursion. - // From https://www.w3.org/TR/css-nesting-1/#nest-selector: - // - // "When used in the selector of a nested style rule, the nesting - // selector represents the elements matched by the parent rule. When - // used in any other context, it represents the same elements as - // :scope in that context (unless otherwise defined)." - // - substituted := make([]css_ast.CompoundSelector, 0, len(sel.Selectors)) - for _, x := range sel.Selectors { - substituted = p.substituteAmpersandsInCompoundSelector(x, scope, substituted, keepLeadingCombinator) - } - selectors[n] = css_ast.ComplexSelector{Selectors: substituted} - n++ + parentSelectors = append(parentSelectors, css_ast.ComplexSelector{Selectors: substituted}) } } - selectors = selectors[:n] // Emit this selector before its nested children start := len(results) @@ -56,7 +59,7 @@ func (p *parser) lowerNestingInRule(rule css_ast.Rule, results []css_ast.Rule) [ // Lower all children and filter out ones that become empty context := lowerNestingContext{ - parentSelectors: selectors, + parentSelectors: parentSelectors, loweredRules: results, } r.Rules = p.lowerNestingInRulesAndReturnRemaining(r.Rules, &context) diff --git a/internal/css_parser/css_parser.go b/internal/css_parser/css_parser.go index f39cfdd22d1..6fbcbeea011 100644 --- a/internal/css_parser/css_parser.go +++ b/internal/css_parser/css_parser.go @@ -586,10 +586,6 @@ func (p *parser) parseListOfDeclarations(opts listOfDeclarationsOpts) (list []cs } func (p *parser) mangleRules(rules []css_ast.Rule, isTopLevel bool) []css_ast.Rule { - type hashEntry struct { - indices []uint32 - } - // Remove empty rules mangledRules := make([]css_ast.Rule, 0, len(rules)) var prevNonComment css_ast.R @@ -728,7 +724,6 @@ type callEntry struct { } type DuplicateRuleRemover struct { - symbols ast.SymbolMap entries map[uint32]hashEntry calls []callEntry check css_ast.CrossFileEqualityCheck diff --git a/internal/css_parser/css_parser_test.go b/internal/css_parser/css_parser_test.go index abaeb7623c5..fd4dee7d64c 100644 --- a/internal/css_parser/css_parser_test.go +++ b/internal/css_parser/css_parser_test.go @@ -1213,6 +1213,8 @@ func TestNestedSelector(t *testing.T) { expectPrintedLowerUnsupported(t, nesting, ".foo, .bar:before { :hover & { color: red } }", ":hover .foo {\n color: red;\n}\n", "") expectPrintedLowerUnsupported(t, nesting, ".bar:before { &:hover { color: red } }", ":is():hover {\n color: red;\n}\n", "") expectPrintedLowerUnsupported(t, nesting, ".bar:before { :hover & { color: red } }", ":hover :is() {\n color: red;\n}\n", "") + expectPrintedLowerUnsupported(t, nesting, ".foo { &:after, & .bar { color: red } }", ".foo:after,\n.foo .bar {\n color: red;\n}\n", "") + expectPrintedLowerUnsupported(t, nesting, ".foo { & .bar, &:after { color: red } }", ".foo .bar,\n.foo:after {\n color: red;\n}\n", "") expectPrintedLowerUnsupported(t, nesting, ".xy { :where(&.foo) { color: red } }", ":where(.xy.foo) {\n color: red;\n}\n", "") expectPrintedLowerUnsupported(t, nesting, "div { :where(&.foo) { color: red } }", ":where(div.foo) {\n color: red;\n}\n", "") expectPrintedLowerUnsupported(t, nesting, ".xy { :where(.foo&) { color: red } }", ":where(.xy.foo) {\n color: red;\n}\n", "") diff --git a/internal/helpers/float.go b/internal/helpers/float.go new file mode 100644 index 00000000000..02b3ac97c97 --- /dev/null +++ b/internal/helpers/float.go @@ -0,0 +1,158 @@ +package helpers + +import "math" + +// This wraps float64 math operations. Why does this exist? The Go compiler +// contains some optimizations to take advantage of "fused multiply and add" +// (FMA) instructions on certain processors. These instructions lead to +// different output on those processors, which means esbuild's output is no +// longer deterministic across all platforms. From the Go specification itself +// (https://go.dev/ref/spec#Floating_point_operators): +// +// An implementation may combine multiple floating-point operations into a +// single fused operation, possibly across statements, and produce a result +// that differs from the value obtained by executing and rounding the +// instructions individually. An explicit floating-point type conversion +// rounds to the precision of the target type, preventing fusion that would +// discard that rounding. +// +// For instance, some architectures provide a "fused multiply and add" (FMA) +// instruction that computes x*y + z without rounding the intermediate result +// x*y. +// +// Therefore we need to add explicit type conversions such as "float64(x)" to +// prevent optimizations that break correctness. Rather than adding them on a +// case-by-case basis as real correctness issues are discovered, we instead +// preemptively force them to be added everywhere by using this wrapper type +// for all floating-point math. +type F64 struct { + value float64 +} + +func NewF64(a float64) F64 { + return F64{value: float64(a)} +} + +func (a F64) Value() float64 { + return a.value +} + +func (a F64) IsNaN() bool { + return math.IsNaN(a.value) +} + +func (a F64) Neg() F64 { + return NewF64(-a.value) +} + +func (a F64) Abs() F64 { + return NewF64(math.Abs(a.value)) +} + +func (a F64) Sin() F64 { + return NewF64(math.Sin(a.value)) +} + +func (a F64) Cos() F64 { + return NewF64(math.Cos(a.value)) +} + +func (a F64) Log2() F64 { + return NewF64(math.Log2(a.value)) +} + +func (a F64) Round() F64 { + return NewF64(math.Round(a.value)) +} + +func (a F64) Floor() F64 { + return NewF64(math.Floor(a.value)) +} + +func (a F64) Ceil() F64 { + return NewF64(math.Ceil(a.value)) +} + +func (a F64) Squared() F64 { + return a.Mul(a) +} + +func (a F64) Cubed() F64 { + return a.Mul(a).Mul(a) +} + +func (a F64) Sqrt() F64 { + return NewF64(math.Sqrt(a.value)) +} + +func (a F64) Cbrt() F64 { + return NewF64(math.Cbrt(a.value)) +} + +func (a F64) Add(b F64) F64 { + return NewF64(a.value + b.value) +} + +func (a F64) AddConst(b float64) F64 { + return NewF64(a.value + b) +} + +func (a F64) Sub(b F64) F64 { + return NewF64(a.value - b.value) +} + +func (a F64) SubConst(b float64) F64 { + return NewF64(a.value - b) +} + +func (a F64) Mul(b F64) F64 { + return NewF64(a.value * b.value) +} + +func (a F64) MulConst(b float64) F64 { + return NewF64(a.value * b) +} + +func (a F64) Div(b F64) F64 { + return NewF64(a.value / b.value) +} + +func (a F64) DivConst(b float64) F64 { + return NewF64(a.value / b) +} + +func (a F64) Pow(b F64) F64 { + return NewF64(math.Pow(a.value, b.value)) +} + +func (a F64) PowConst(b float64) F64 { + return NewF64(math.Pow(a.value, b)) +} + +func (a F64) Atan2(b F64) F64 { + return NewF64(math.Atan2(a.value, b.value)) +} + +func (a F64) WithSignFrom(b F64) F64 { + return NewF64(math.Copysign(a.value, b.value)) +} + +func Min2(a F64, b F64) F64 { + return NewF64(math.Min(a.value, b.value)) +} + +func Max2(a F64, b F64) F64 { + return NewF64(math.Max(a.value, b.value)) +} + +func Min3(a F64, b F64, c F64) F64 { + return NewF64(math.Min(math.Min(a.value, b.value), c.value)) +} + +func Max3(a F64, b F64, c F64) F64 { + return NewF64(math.Max(math.Max(a.value, b.value), c.value)) +} + +func Lerp(a F64, b F64, t F64) F64 { + return b.Sub(a).Mul(t).Add(a) +} diff --git a/internal/js_ast/js_ast_helpers.go b/internal/js_ast/js_ast_helpers.go index c51afd335c5..705cdc69f38 100644 --- a/internal/js_ast/js_ast_helpers.go +++ b/internal/js_ast/js_ast_helpers.go @@ -1086,6 +1086,45 @@ func extractNumericValues(left Expr, right Expr) (float64, float64, bool) { return 0, 0, false } +func extractStringValue(data E) ([]uint16, bool) { + switch e := data.(type) { + case *EAnnotation: + return extractStringValue(e.Value.Data) + + case *EInlinedEnum: + return extractStringValue(e.Value.Data) + + case *EString: + return e.Value, true + } + + return nil, false +} + +func extractStringValues(left Expr, right Expr) ([]uint16, []uint16, bool) { + if a, ok := extractStringValue(left.Data); ok { + if b, ok := extractStringValue(right.Data); ok { + return a, b, true + } + } + return nil, nil, false +} + +func stringCompareUCS2(a []uint16, b []uint16) int { + var n int + if len(a) < len(b) { + n = len(a) + } else { + n = len(b) + } + for i := 0; i < n; i++ { + if delta := int(a[i]) - int(b[i]); delta != 0 { + return delta + } + } + return len(a) - len(b) +} + func approximatePrintedIntCharCount(intValue float64) int { count := 1 + (int)(math.Max(0, math.Floor(math.Log10(math.Abs(intValue))))) if intValue < 0 { @@ -1106,7 +1145,11 @@ func ShouldFoldBinaryArithmeticWhenMinifying(binary *EBinary) bool { // are unlikely to result in larger output. BinOpBitwiseAnd, BinOpBitwiseOr, - BinOpBitwiseXor: + BinOpBitwiseXor, + BinOpLt, + BinOpGt, + BinOpLe, + BinOpGe: return true case BinOpAdd: @@ -1221,6 +1264,38 @@ func FoldBinaryArithmetic(loc logger.Loc, e *EBinary) Expr { if left, right, ok := extractNumericValues(e.Left, e.Right); ok { return Expr{Loc: loc, Data: &ENumber{Value: float64(ToInt32(left) ^ ToInt32(right))}} } + + case BinOpLt: + if left, right, ok := extractNumericValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: left < right}} + } + if left, right, ok := extractStringValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: stringCompareUCS2(left, right) < 0}} + } + + case BinOpGt: + if left, right, ok := extractNumericValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: left > right}} + } + if left, right, ok := extractStringValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: stringCompareUCS2(left, right) > 0}} + } + + case BinOpLe: + if left, right, ok := extractNumericValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: left <= right}} + } + if left, right, ok := extractStringValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: stringCompareUCS2(left, right) <= 0}} + } + + case BinOpGe: + if left, right, ok := extractNumericValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: left >= right}} + } + if left, right, ok := extractStringValues(e.Left, e.Right); ok { + return Expr{Loc: loc, Data: &EBoolean{Value: stringCompareUCS2(left, right) >= 0}} + } } return Expr{} diff --git a/internal/js_parser/js_parser_test.go b/internal/js_parser/js_parser_test.go index 88eefb54e3f..57bc7568e1e 100644 --- a/internal/js_parser/js_parser_test.go +++ b/internal/js_parser/js_parser_test.go @@ -4653,15 +4653,31 @@ func TestMangleBinaryConstantFolding(t *testing.T) { expectPrintedNormalAndMangle(t, "x = -123 / 0", "x = -123 / 0;\n", "x = -Infinity;\n") expectPrintedNormalAndMangle(t, "x = -123 / -0", "x = -123 / -0;\n", "x = Infinity;\n") - expectPrintedNormalAndMangle(t, "x = 3 < 6", "x = 3 < 6;\n", "x = 3 < 6;\n") - expectPrintedNormalAndMangle(t, "x = 3 > 6", "x = 3 > 6;\n", "x = 3 > 6;\n") - expectPrintedNormalAndMangle(t, "x = 3 <= 6", "x = 3 <= 6;\n", "x = 3 <= 6;\n") - expectPrintedNormalAndMangle(t, "x = 3 >= 6", "x = 3 >= 6;\n", "x = 3 >= 6;\n") + expectPrintedNormalAndMangle(t, "x = 3 < 6", "x = 3 < 6;\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = 3 > 6", "x = 3 > 6;\n", "x = false;\n") + expectPrintedNormalAndMangle(t, "x = 3 <= 6", "x = 3 <= 6;\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = 3 >= 6", "x = 3 >= 6;\n", "x = false;\n") expectPrintedNormalAndMangle(t, "x = 3 == 6", "x = false;\n", "x = false;\n") expectPrintedNormalAndMangle(t, "x = 3 != 6", "x = true;\n", "x = true;\n") expectPrintedNormalAndMangle(t, "x = 3 === 6", "x = false;\n", "x = false;\n") expectPrintedNormalAndMangle(t, "x = 3 !== 6", "x = true;\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = 'a' < 'b'", "x = \"a\" < \"b\";\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = 'a' > 'b'", "x = \"a\" > \"b\";\n", "x = false;\n") + expectPrintedNormalAndMangle(t, "x = 'a' <= 'b'", "x = \"a\" <= \"b\";\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = 'a' >= 'b'", "x = \"a\" >= \"b\";\n", "x = false;\n") + + expectPrintedNormalAndMangle(t, "x = 'ab' < 'abc'", "x = \"ab\" < \"abc\";\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = 'ab' > 'abc'", "x = \"ab\" > \"abc\";\n", "x = false;\n") + expectPrintedNormalAndMangle(t, "x = 'ab' <= 'abc'", "x = \"ab\" <= \"abc\";\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = 'ab' >= 'abc'", "x = \"ab\" >= \"abc\";\n", "x = false;\n") + + // This checks for comparing by code point vs. by code unit + expectPrintedNormalAndMangle(t, "x = '𐙩' < 'ﬡ'", "x = \"𐙩\" < \"ﬡ\";\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = '𐙩' > 'ﬡ'", "x = \"𐙩\" > \"ﬡ\";\n", "x = false;\n") + expectPrintedNormalAndMangle(t, "x = '𐙩' <= 'ﬡ'", "x = \"𐙩\" <= \"ﬡ\";\n", "x = true;\n") + expectPrintedNormalAndMangle(t, "x = '𐙩' >= 'ﬡ'", "x = \"𐙩\" >= \"ﬡ\";\n", "x = false;\n") + expectPrintedNormalAndMangle(t, "x = 3 in 6", "x = 3 in 6;\n", "x = 3 in 6;\n") expectPrintedNormalAndMangle(t, "x = 3 instanceof 6", "x = 3 instanceof 6;\n", "x = 3 instanceof 6;\n") expectPrintedNormalAndMangle(t, "x = (3, 6)", "x = (3, 6);\n", "x = 6;\n") diff --git a/internal/js_printer/js_printer.go b/internal/js_printer/js_printer.go index a42c83c6061..f6cc144f50f 100644 --- a/internal/js_printer/js_printer.go +++ b/internal/js_printer/js_printer.go @@ -1242,7 +1242,7 @@ func (p *printer) printProperty(property js_ast.Property) { if !p.options.UnsupportedFeatures.Has(compat.ObjectExtensions) && property.ValueOrNil.Data != nil && !p.willPrintExprCommentsAtLoc(property.ValueOrNil.Loc) { switch e := property.ValueOrNil.Data.(type) { case *js_ast.EIdentifier: - if helpers.UTF16EqualsString(key.Value, p.renamer.NameForSymbol(e.Ref)) { + if canUseShorthandProperty(key.Value, p.renamer.NameForSymbol(e.Ref), property.Flags) { if p.options.AddSourceMappings { p.addSourceMappingForName(property.Key.Loc, helpers.UTF16ToString(key.Value), e.Ref) } @@ -1259,7 +1259,7 @@ func (p *printer) printProperty(property js_ast.Property) { case *js_ast.EImportIdentifier: // Make sure we're not using a property access instead of an identifier ref := ast.FollowSymbols(p.symbols, e.Ref) - if symbol := p.symbols.Get(ref); symbol.NamespaceAlias == nil && helpers.UTF16EqualsString(key.Value, p.renamer.NameForSymbol(ref)) && + if symbol := p.symbols.Get(ref); symbol.NamespaceAlias == nil && canUseShorthandProperty(key.Value, p.renamer.NameForSymbol(ref), property.Flags) && p.options.ConstValues[ref].Kind == js_ast.ConstValueNone { if p.options.AddSourceMappings { p.addSourceMappingForName(property.Key.Loc, helpers.UTF16ToString(key.Value), ref) @@ -1276,6 +1276,21 @@ func (p *printer) printProperty(property js_ast.Property) { } } + // The JavaScript specification special-cases the property identifier + // "__proto__" with a colon after it to set the prototype of the object. + // If we keep the identifier but add a colon then we'll cause a behavior + // change because the prototype will now be set. Avoid using an identifier + // by using a computed property with a string instead. For more info see: + // https://tc39.es/ecma262/#sec-runtime-semantics-propertydefinitionevaluation + if property.Flags.Has(js_ast.PropertyWasShorthand) && !p.options.UnsupportedFeatures.Has(compat.ObjectExtensions) && + helpers.UTF16EqualsString(key.Value, "__proto__") { + p.print("[") + p.addSourceMapping(property.Key.Loc) + p.printQuotedUTF16(key.Value, 0) + p.print("]") + break + } + p.addSourceMapping(property.Key.Loc) p.printIdentifierUTF16(key.Value) } else { @@ -1314,6 +1329,20 @@ func (p *printer) printProperty(property js_ast.Property) { } } +func canUseShorthandProperty(key []uint16, name string, flags js_ast.PropertyFlags) bool { + // The JavaScript specification special-cases the property identifier + // "__proto__" with a colon after it to set the prototype of the object. If + // we remove the colon then we'll cause a behavior change because the + // prototype will no longer be set, but we also don't want to add a colon + // if it was omitted. Always use a shorthand property if the property is not + // "__proto__", otherwise try to preserve the original shorthand status. See: + // https://tc39.es/ecma262/#sec-runtime-semantics-propertydefinitionevaluation + if !helpers.UTF16EqualsString(key, name) { + return false + } + return helpers.UTF16EqualsString(key, name) && (name != "__proto__" || flags.Has(js_ast.PropertyWasShorthand)) +} + func (p *printer) printQuotedUTF16(data []uint16, flags printQuotedFlags) { if p.options.UnsupportedFeatures.Has(compat.TemplateLiteral) { flags &= ^printQuotedAllowBacktick diff --git a/internal/js_printer/js_printer_test.go b/internal/js_printer/js_printer_test.go index 6a5759daa99..fed0c71a9d6 100644 --- a/internal/js_printer/js_printer_test.go +++ b/internal/js_printer/js_printer_test.go @@ -13,13 +13,6 @@ import ( "github.com/evanw/esbuild/internal/test" ) -func assertEqual(t *testing.T, a interface{}, b interface{}) { - t.Helper() - if a != b { - t.Fatalf("%s != %s", a, b) - } -} - func expectPrintedCommon(t *testing.T, name string, contents string, expected string, options config.Options) { t.Helper() t.Run(name, func(t *testing.T) { @@ -511,6 +504,17 @@ func TestObject(t *testing.T) { expectPrinted(t, "let x = () => ({}.x)", "let x = () => ({}).x;\n") expectPrinted(t, "let x = () => ({} = {})", "let x = () => ({} = {});\n") expectPrinted(t, "let x = () => (x, {} = {})", "let x = () => (x, {} = {});\n") + + // "{ __proto__: __proto__ }" must not become "{ __proto__ }" + expectPrinted(t, "function foo(__proto__) { return { __proto__: __proto__ } }", "function foo(__proto__) {\n return { __proto__: __proto__ };\n}\n") + expectPrinted(t, "function foo(__proto__) { return { '__proto__': __proto__ } }", "function foo(__proto__) {\n return { \"__proto__\": __proto__ };\n}\n") + expectPrinted(t, "function foo(__proto__) { return { ['__proto__']: __proto__ } }", "function foo(__proto__) {\n return { [\"__proto__\"]: __proto__ };\n}\n") + expectPrinted(t, "import { __proto__ } from 'foo'; let foo = () => ({ __proto__: __proto__ })", "import { __proto__ } from \"foo\";\nlet foo = () => ({ __proto__: __proto__ });\n") + expectPrinted(t, "import { __proto__ } from 'foo'; let foo = () => ({ '__proto__': __proto__ })", "import { __proto__ } from \"foo\";\nlet foo = () => ({ \"__proto__\": __proto__ });\n") + expectPrinted(t, "import { __proto__ } from 'foo'; let foo = () => ({ ['__proto__']: __proto__ })", "import { __proto__ } from \"foo\";\nlet foo = () => ({ [\"__proto__\"]: __proto__ });\n") + + // Don't use ES6+ features (such as a shorthand or computed property name) in ES5 + expectPrintedTarget(t, 5, "function foo(__proto__) { return { __proto__ } }", "function foo(__proto__) {\n return { __proto__: __proto__ };\n}\n") } func TestFor(t *testing.T) { diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 15c379ecaa7..77dac02a311 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -504,7 +504,6 @@ func (res *Resolver) Resolve(sourceDir string, importPath string, kind ast.Impor r.mutex.Lock() defer r.mutex.Unlock() - sourceDirInfo := r.dirInfoCached(sourceDir) // Check for the Yarn PnP manifest if it hasn't already been checked for if !r.pnpManifestWasChecked { @@ -533,6 +532,12 @@ func (res *Resolver) Resolve(sourceDir string, importPath string, kind ast.Impor } } + sourceDirInfo := r.dirInfoCached(sourceDir) + if sourceDirInfo == nil { + // Bail if the directory is missing for some reason + return nil, debugMeta + } + result := r.resolveWithoutSymlinks(sourceDir, sourceDirInfo, importPath) if result == nil { // If resolution failed, try again with the URL query and/or hash removed @@ -1006,11 +1011,6 @@ func (r resolverQuery) resolveWithoutSymlinks(sourceDir string, sourceDirInfo *d } if checkPackage { - if sourceDirInfo == nil { - // Bail if the directory is missing for some reason - return nil - } - // Support remapping one package path to another via the "browser" field if remapped, ok := r.checkBrowserMap(sourceDirInfo, importPath, packagePathKind); ok { if remapped == nil { diff --git a/npm/@esbuild/aix-ppc64/package.json b/npm/@esbuild/aix-ppc64/package.json index 28a101140f4..020125cbc49 100644 --- a/npm/@esbuild/aix-ppc64/package.json +++ b/npm/@esbuild/aix-ppc64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/aix-ppc64", - "version": "0.20.0", + "version": "0.20.1", "description": "The IBM AIX PowerPC 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/android-arm/package.json b/npm/@esbuild/android-arm/package.json index bc51f230ea7..c85124c05f4 100644 --- a/npm/@esbuild/android-arm/package.json +++ b/npm/@esbuild/android-arm/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/android-arm", - "version": "0.20.0", + "version": "0.20.1", "description": "A WebAssembly shim for esbuild on Android ARM.", "repository": { "type": "git", diff --git a/npm/@esbuild/android-arm64/package.json b/npm/@esbuild/android-arm64/package.json index aa8c2b2481b..3d281341c0d 100644 --- a/npm/@esbuild/android-arm64/package.json +++ b/npm/@esbuild/android-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/android-arm64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Android ARM 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/android-x64/package.json b/npm/@esbuild/android-x64/package.json index c616033e025..536f4711558 100644 --- a/npm/@esbuild/android-x64/package.json +++ b/npm/@esbuild/android-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/android-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "A WebAssembly shim for esbuild on Android x64.", "repository": "https://github.com/evanw/esbuild", "license": "MIT", diff --git a/npm/@esbuild/darwin-arm64/package.json b/npm/@esbuild/darwin-arm64/package.json index 0edc280807f..46d8b9aad1d 100644 --- a/npm/@esbuild/darwin-arm64/package.json +++ b/npm/@esbuild/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/darwin-arm64", - "version": "0.20.0", + "version": "0.20.1", "description": "The macOS ARM 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/darwin-x64/package.json b/npm/@esbuild/darwin-x64/package.json index 63ade21c1ca..f72c01b36ca 100644 --- a/npm/@esbuild/darwin-x64/package.json +++ b/npm/@esbuild/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/darwin-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "The macOS 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/freebsd-arm64/package.json b/npm/@esbuild/freebsd-arm64/package.json index fa81ea18268..5de122d175b 100644 --- a/npm/@esbuild/freebsd-arm64/package.json +++ b/npm/@esbuild/freebsd-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/freebsd-arm64", - "version": "0.20.0", + "version": "0.20.1", "description": "The FreeBSD ARM 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/freebsd-x64/package.json b/npm/@esbuild/freebsd-x64/package.json index c926f3d9d9e..161d998ae48 100644 --- a/npm/@esbuild/freebsd-x64/package.json +++ b/npm/@esbuild/freebsd-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/freebsd-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "The FreeBSD 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-arm/package.json b/npm/@esbuild/linux-arm/package.json index e4525c5fd7a..b3d9a79733b 100644 --- a/npm/@esbuild/linux-arm/package.json +++ b/npm/@esbuild/linux-arm/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-arm", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux ARM binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-arm64/package.json b/npm/@esbuild/linux-arm64/package.json index cab265dc328..fc02b59ef1f 100644 --- a/npm/@esbuild/linux-arm64/package.json +++ b/npm/@esbuild/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-arm64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux ARM 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-ia32/package.json b/npm/@esbuild/linux-ia32/package.json index 9caa2d96ebf..eeed43aa456 100644 --- a/npm/@esbuild/linux-ia32/package.json +++ b/npm/@esbuild/linux-ia32/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-ia32", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux 32-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-loong64/package.json b/npm/@esbuild/linux-loong64/package.json index 5f332cb0c78..42f106e3e5a 100644 --- a/npm/@esbuild/linux-loong64/package.json +++ b/npm/@esbuild/linux-loong64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-loong64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux LoongArch 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-mips64el/package.json b/npm/@esbuild/linux-mips64el/package.json index 76f7d55d14b..84e7a387015 100644 --- a/npm/@esbuild/linux-mips64el/package.json +++ b/npm/@esbuild/linux-mips64el/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-mips64el", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux MIPS 64-bit Little Endian binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-ppc64/package.json b/npm/@esbuild/linux-ppc64/package.json index 3827c8f34fe..c155f972b0a 100644 --- a/npm/@esbuild/linux-ppc64/package.json +++ b/npm/@esbuild/linux-ppc64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-ppc64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux PowerPC 64-bit Little Endian binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-riscv64/package.json b/npm/@esbuild/linux-riscv64/package.json index b6952c33785..ae7664d6271 100644 --- a/npm/@esbuild/linux-riscv64/package.json +++ b/npm/@esbuild/linux-riscv64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-riscv64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux RISC-V 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-s390x/package.json b/npm/@esbuild/linux-s390x/package.json index 220c3526427..6b953cb45ef 100644 --- a/npm/@esbuild/linux-s390x/package.json +++ b/npm/@esbuild/linux-s390x/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-s390x", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux IBM Z 64-bit Big Endian binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/linux-x64/package.json b/npm/@esbuild/linux-x64/package.json index 5f676687057..bfebfa06ecb 100644 --- a/npm/@esbuild/linux-x64/package.json +++ b/npm/@esbuild/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/linux-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Linux 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/netbsd-x64/package.json b/npm/@esbuild/netbsd-x64/package.json index 360b2e948a3..bf60b1d9159 100644 --- a/npm/@esbuild/netbsd-x64/package.json +++ b/npm/@esbuild/netbsd-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/netbsd-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "The NetBSD AMD64 binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/openbsd-x64/package.json b/npm/@esbuild/openbsd-x64/package.json index 6ca200929f8..ca6ae3a4466 100644 --- a/npm/@esbuild/openbsd-x64/package.json +++ b/npm/@esbuild/openbsd-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/openbsd-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "The OpenBSD 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/sunos-x64/package.json b/npm/@esbuild/sunos-x64/package.json index 9627a214e01..6e98a7c287a 100644 --- a/npm/@esbuild/sunos-x64/package.json +++ b/npm/@esbuild/sunos-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/sunos-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "The illumos 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/win32-arm64/package.json b/npm/@esbuild/win32-arm64/package.json index 3c574721efa..86301240d29 100644 --- a/npm/@esbuild/win32-arm64/package.json +++ b/npm/@esbuild/win32-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/win32-arm64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Windows ARM 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/win32-ia32/package.json b/npm/@esbuild/win32-ia32/package.json index 559096ec62e..eb62a511937 100644 --- a/npm/@esbuild/win32-ia32/package.json +++ b/npm/@esbuild/win32-ia32/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/win32-ia32", - "version": "0.20.0", + "version": "0.20.1", "description": "The Windows 32-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/@esbuild/win32-x64/package.json b/npm/@esbuild/win32-x64/package.json index d8d8a30052d..208dae4b92f 100644 --- a/npm/@esbuild/win32-x64/package.json +++ b/npm/@esbuild/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@esbuild/win32-x64", - "version": "0.20.0", + "version": "0.20.1", "description": "The Windows 64-bit binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/esbuild-wasm/package.json b/npm/esbuild-wasm/package.json index 31d42c28dc1..fade2e1e800 100644 --- a/npm/esbuild-wasm/package.json +++ b/npm/esbuild-wasm/package.json @@ -1,6 +1,6 @@ { "name": "esbuild-wasm", - "version": "0.20.0", + "version": "0.20.1", "description": "The cross-platform WebAssembly binary for esbuild, a JavaScript bundler.", "repository": { "type": "git", diff --git a/npm/esbuild/package.json b/npm/esbuild/package.json index 3ef08dce7a7..310826e04ed 100644 --- a/npm/esbuild/package.json +++ b/npm/esbuild/package.json @@ -1,6 +1,6 @@ { "name": "esbuild", - "version": "0.20.0", + "version": "0.20.1", "description": "An extremely fast JavaScript and CSS bundler and minifier.", "repository": { "type": "git", @@ -18,29 +18,29 @@ "esbuild": "bin/esbuild" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.0", - "@esbuild/android-arm": "0.20.0", - "@esbuild/android-arm64": "0.20.0", - "@esbuild/android-x64": "0.20.0", - "@esbuild/darwin-arm64": "0.20.0", - "@esbuild/darwin-x64": "0.20.0", - "@esbuild/freebsd-arm64": "0.20.0", - "@esbuild/freebsd-x64": "0.20.0", - "@esbuild/linux-arm": "0.20.0", - "@esbuild/linux-arm64": "0.20.0", - "@esbuild/linux-ia32": "0.20.0", - "@esbuild/linux-loong64": "0.20.0", - "@esbuild/linux-mips64el": "0.20.0", - "@esbuild/linux-ppc64": "0.20.0", - "@esbuild/linux-riscv64": "0.20.0", - "@esbuild/linux-s390x": "0.20.0", - "@esbuild/linux-x64": "0.20.0", - "@esbuild/netbsd-x64": "0.20.0", - "@esbuild/openbsd-x64": "0.20.0", - "@esbuild/sunos-x64": "0.20.0", - "@esbuild/win32-arm64": "0.20.0", - "@esbuild/win32-ia32": "0.20.0", - "@esbuild/win32-x64": "0.20.0" + "@esbuild/aix-ppc64": "0.20.1", + "@esbuild/android-arm": "0.20.1", + "@esbuild/android-arm64": "0.20.1", + "@esbuild/android-x64": "0.20.1", + "@esbuild/darwin-arm64": "0.20.1", + "@esbuild/darwin-x64": "0.20.1", + "@esbuild/freebsd-arm64": "0.20.1", + "@esbuild/freebsd-x64": "0.20.1", + "@esbuild/linux-arm": "0.20.1", + "@esbuild/linux-arm64": "0.20.1", + "@esbuild/linux-ia32": "0.20.1", + "@esbuild/linux-loong64": "0.20.1", + "@esbuild/linux-mips64el": "0.20.1", + "@esbuild/linux-ppc64": "0.20.1", + "@esbuild/linux-riscv64": "0.20.1", + "@esbuild/linux-s390x": "0.20.1", + "@esbuild/linux-x64": "0.20.1", + "@esbuild/netbsd-x64": "0.20.1", + "@esbuild/openbsd-x64": "0.20.1", + "@esbuild/sunos-x64": "0.20.1", + "@esbuild/win32-arm64": "0.20.1", + "@esbuild/win32-ia32": "0.20.1", + "@esbuild/win32-x64": "0.20.1" }, "license": "MIT" } diff --git a/scripts/plugin-tests.js b/scripts/plugin-tests.js index 695d0c4a50a..9a7551c2f48 100644 --- a/scripts/plugin-tests.js +++ b/scripts/plugin-tests.js @@ -2522,6 +2522,25 @@ var foo_default2 = "🍕"; console.log(foo_default, foo_default2); `) }, + + async internalCrashIssue3634({ esbuild }) { + await esbuild.build({ + entryPoints: [], + bundle: true, + plugins: [{ + name: 'abc', + setup(build) { + build.onStart(async () => { + const result = await build.resolve('/foo', { + kind: 'require-call', + resolveDir: 'bar', + }) + assert.strictEqual(result.errors.length, 1) + }) + } + }], + }) + }, } const makeRebuildUntilPlugin = () => { diff --git a/version.txt b/version.txt index 5a03fb737b3..847e9aef6d1 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.20.0 +0.20.1