Skip to content

Commit ea9c644

Browse files
committed
fix #3388: do something with pre-release versions
1 parent 673ad10 commit ea9c644

File tree

13 files changed

+166
-84
lines changed

13 files changed

+166
-84
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* Allow pre-release versions to be passed to `target` ([#3388](https://github.com/evanw/esbuild/issues/3388))
6+
7+
People want to be able to pass version numbers for unreleased versions of node (which have extra stuff after the version numbers) to esbuild's `target` setting and have esbuild do something reasonable with them. These version strings are of course not present in esbuild's internal feature compatibility table because an unreleased version has not been released yet (by definition). With this release, esbuild will now attempt to accept these version strings passed to `target` and do something reasonable with them.
8+
39
## 0.19.3
410

511
* Fix `list-style-type` with the `local-css` loader ([#3325](https://github.com/evanw/esbuild/issues/3325))

compat-table/src/css_table.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ ${Object.keys(map).sort().map(feature => `\t${feature}: ${cssTableMap(map[featur
8989
}
9090
9191
// Return all features that are not available in at least one environment
92-
func UnsupportedCSSFeatures(constraints map[Engine][]int) (unsupported CSSFeature) {
92+
func UnsupportedCSSFeatures(constraints map[Engine]Semver) (unsupported CSSFeature) {
9393
\tfor feature, engines := range cssTable {
9494
\t\tif feature == InlineStyle {
9595
\t\t\tcontinue // This is purely user-specified
@@ -131,7 +131,7 @@ var cssPrefixTable = map[css_ast.D][]prefixData{
131131
${Object.keys(prefixes).sort().map(property => `\tcss_ast.${property}: ${cssPrefixMap(prefixes[property as CSSProperty]!)},`).join('\n')}
132132
}
133133
134-
func CSSPrefixData(constraints map[Engine][]int) (entries map[css_ast.D]CSSPrefix) {
134+
func CSSPrefixData(constraints map[Engine]Semver) (entries map[css_ast.D]CSSPrefix) {
135135
\tfor property, items := range cssPrefixTable {
136136
\t\tprefixes := NoPrefix
137137
\t\tfor engine, version := range constraints {

compat-table/src/js_table.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ ${Object.keys(map).sort().map(feature => `\t${feature}: ${jsTableMap(map[feature
9696
}
9797
9898
// Return all features that are not available in at least one environment
99-
func UnsupportedJSFeatures(constraints map[Engine][]int) (unsupported JSFeature) {
99+
func UnsupportedJSFeatures(constraints map[Engine]Semver) (unsupported JSFeature) {
100100
\tfor feature, engines := range jsTable {
101101
\t\tif feature == InlineScript {
102102
\t\t\tcontinue // This is purely user-specified

internal/bundler_tests/bundler_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import (
2727
)
2828

2929
func es(version int) compat.JSFeature {
30-
return compat.UnsupportedJSFeatures(map[compat.Engine][]int{
31-
compat.ES: {version},
30+
return compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
31+
compat.ES: {Parts: []int{version}},
3232
})
3333
}
3434

internal/compat/compat.go

+35-9
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,59 @@
11
package compat
22

3-
import "github.com/evanw/esbuild/internal/ast"
3+
import (
4+
"strconv"
5+
"strings"
6+
7+
"github.com/evanw/esbuild/internal/ast"
8+
)
49

510
type v struct {
611
major uint16
712
minor uint8
813
patch uint8
914
}
1015

16+
type Semver struct {
17+
// "1.2.3-alpha" => { Parts: {1, 2, 3}, PreRelease: "-alpha" }
18+
Parts []int
19+
PreRelease string
20+
}
21+
22+
func (v Semver) String() string {
23+
b := strings.Builder{}
24+
for _, part := range v.Parts {
25+
if b.Len() > 0 {
26+
b.WriteRune('.')
27+
}
28+
b.WriteString(strconv.Itoa(part))
29+
}
30+
b.WriteString(v.PreRelease)
31+
return b.String()
32+
}
33+
1134
// Returns <0 if "a < b"
1235
// Returns 0 if "a == b"
1336
// Returns >0 if "a > b"
14-
func compareVersions(a v, b []int) int {
37+
func compareVersions(a v, b Semver) int {
1538
diff := int(a.major)
16-
if len(b) > 0 {
17-
diff -= b[0]
39+
if len(b.Parts) > 0 {
40+
diff -= b.Parts[0]
1841
}
1942
if diff == 0 {
2043
diff = int(a.minor)
21-
if len(b) > 1 {
22-
diff -= b[1]
44+
if len(b.Parts) > 1 {
45+
diff -= b.Parts[1]
2346
}
2447
}
2548
if diff == 0 {
2649
diff = int(a.patch)
27-
if len(b) > 2 {
28-
diff -= b[2]
50+
if len(b.Parts) > 2 {
51+
diff -= b.Parts[2]
2952
}
3053
}
54+
if diff == 0 && len(b.PreRelease) != 0 {
55+
return 1 // "1.0.0" > "1.0.0-alpha"
56+
}
3157
return diff
3258
}
3359

@@ -37,7 +63,7 @@ type versionRange struct {
3763
end v // Use 0.0.0 for "no end"
3864
}
3965

40-
func isVersionSupported(ranges []versionRange, version []int) bool {
66+
func isVersionSupported(ranges []versionRange, version Semver) bool {
4167
for _, r := range ranges {
4268
if compareVersions(r.start, version) <= 0 && (r.end == (v{}) || compareVersions(r.end, version) > 0) {
4369
return true

internal/compat/compat_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package compat
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/evanw/esbuild/internal/test"
8+
)
9+
10+
func TestCompareVersions(t *testing.T) {
11+
t.Helper()
12+
13+
check := func(a v, b Semver, expected rune) {
14+
t.Helper()
15+
16+
at := fmt.Sprintf("%d.%d.%d", a.major, a.minor, a.patch)
17+
bt := b.String()
18+
19+
t.Run(fmt.Sprintf("%q ? %q", at, bt), func(t *testing.T) {
20+
observed := '='
21+
if result := compareVersions(a, b); result < 0 {
22+
observed = '<'
23+
} else if result > 0 {
24+
observed = '>'
25+
}
26+
if observed != expected {
27+
test.AssertEqual(t, fmt.Sprintf("%c", observed), fmt.Sprintf("%c", expected))
28+
}
29+
})
30+
}
31+
32+
check(v{0, 0, 0}, Semver{}, '=')
33+
34+
check(v{1, 0, 0}, Semver{}, '>')
35+
check(v{0, 1, 0}, Semver{}, '>')
36+
check(v{0, 0, 1}, Semver{}, '>')
37+
38+
check(v{0, 0, 0}, Semver{Parts: []int{1}}, '<')
39+
check(v{0, 0, 0}, Semver{Parts: []int{0, 1}}, '<')
40+
check(v{0, 0, 0}, Semver{Parts: []int{0, 0, 1}}, '<')
41+
42+
check(v{0, 4, 0}, Semver{Parts: []int{0, 5, 0}}, '<')
43+
check(v{0, 5, 0}, Semver{Parts: []int{0, 5, 0}}, '=')
44+
check(v{0, 6, 0}, Semver{Parts: []int{0, 5, 0}}, '>')
45+
46+
check(v{0, 5, 0}, Semver{Parts: []int{0, 5, 1}}, '<')
47+
check(v{0, 5, 0}, Semver{Parts: []int{0, 5, 0}}, '=')
48+
check(v{0, 5, 1}, Semver{Parts: []int{0, 5, 0}}, '>')
49+
50+
check(v{0, 5, 0}, Semver{Parts: []int{0, 5}}, '=')
51+
check(v{0, 5, 1}, Semver{Parts: []int{0, 5}}, '>')
52+
53+
check(v{1, 0, 0}, Semver{Parts: []int{1}}, '=')
54+
check(v{1, 1, 0}, Semver{Parts: []int{1}}, '>')
55+
check(v{1, 0, 1}, Semver{Parts: []int{1}}, '>')
56+
57+
check(v{1, 2, 0}, Semver{Parts: []int{1, 2}, PreRelease: "-pre"}, '>')
58+
check(v{1, 2, 1}, Semver{Parts: []int{1, 2}, PreRelease: "-pre"}, '>')
59+
check(v{1, 1, 0}, Semver{Parts: []int{1, 2}, PreRelease: "-pre"}, '<')
60+
61+
check(v{1, 2, 3}, Semver{Parts: []int{1, 2, 3}, PreRelease: "-pre"}, '>')
62+
check(v{1, 2, 2}, Semver{Parts: []int{1, 2, 3}, PreRelease: "-pre"}, '<')
63+
}

internal/compat/css_table.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ var cssTable = map[CSSFeature]map[Engine][]versionRange{
8585
}
8686

8787
// Return all features that are not available in at least one environment
88-
func UnsupportedCSSFeatures(constraints map[Engine][]int) (unsupported CSSFeature) {
88+
func UnsupportedCSSFeatures(constraints map[Engine]Semver) (unsupported CSSFeature) {
8989
for feature, engines := range cssTable {
9090
if feature == InlineStyle {
9191
continue // This is purely user-specified
@@ -275,7 +275,7 @@ var cssPrefixTable = map[css_ast.D][]prefixData{
275275
},
276276
}
277277

278-
func CSSPrefixData(constraints map[Engine][]int) (entries map[css_ast.D]CSSPrefix) {
278+
func CSSPrefixData(constraints map[Engine]Semver) (entries map[css_ast.D]CSSPrefix) {
279279
for property, items := range cssPrefixTable {
280280
prefixes := NoPrefix
281281
for engine, version := range constraints {

internal/compat/js_table.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{
767767
}
768768

769769
// Return all features that are not available in at least one environment
770-
func UnsupportedJSFeatures(constraints map[Engine][]int) (unsupported JSFeature) {
770+
func UnsupportedJSFeatures(constraints map[Engine]Semver) (unsupported JSFeature) {
771771
for feature, engines := range jsTable {
772772
if feature == InlineScript {
773773
continue // This is purely user-specified

internal/css_parser/css_parser_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ func expectPrintedLowerUnsupported(t *testing.T, unsupportedCSSFeatures compat.C
6060
func expectPrintedWithAllPrefixes(t *testing.T, contents string, expected string, expectedLog string) {
6161
t.Helper()
6262
expectPrintedCommon(t, contents+" [prefixed]", contents, expected, expectedLog, config.LoaderCSS, config.Options{
63-
CSSPrefixData: compat.CSSPrefixData(map[compat.Engine][]int{
64-
compat.Chrome: {0},
65-
compat.Edge: {0},
66-
compat.Firefox: {0},
67-
compat.IE: {0},
68-
compat.IOS: {0},
69-
compat.Opera: {0},
70-
compat.Safari: {0},
63+
CSSPrefixData: compat.CSSPrefixData(map[compat.Engine]compat.Semver{
64+
compat.Chrome: {Parts: []int{0}},
65+
compat.Edge: {Parts: []int{0}},
66+
compat.Firefox: {Parts: []int{0}},
67+
compat.IE: {Parts: []int{0}},
68+
compat.IOS: {Parts: []int{0}},
69+
compat.Opera: {Parts: []int{0}},
70+
compat.Safari: {Parts: []int{0}},
7171
}),
7272
})
7373
}

internal/js_parser/js_parser_test.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func expectParseError(t *testing.T, contents string, expected string) {
3939
func expectParseErrorTarget(t *testing.T, esVersion int, contents string, expected string) {
4040
t.Helper()
4141
expectParseErrorCommon(t, contents, expected, config.Options{
42-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
43-
compat.ES: {esVersion},
42+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
43+
compat.ES: {Parts: []int{esVersion}},
4444
}),
4545
})
4646
}
@@ -108,17 +108,17 @@ func expectPrintedNormalAndMangle(t *testing.T, contents string, normal string,
108108
func expectPrintedTarget(t *testing.T, esVersion int, contents string, expected string) {
109109
t.Helper()
110110
expectPrintedCommon(t, contents, expected, config.Options{
111-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
112-
compat.ES: {esVersion},
111+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
112+
compat.ES: {Parts: []int{esVersion}},
113113
}),
114114
})
115115
}
116116

117117
func expectPrintedMangleTarget(t *testing.T, esVersion int, contents string, expected string) {
118118
t.Helper()
119119
expectPrintedCommon(t, contents, expected, config.Options{
120-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
121-
compat.ES: {esVersion},
120+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
121+
compat.ES: {Parts: []int{esVersion}},
122122
}),
123123
MinifySyntax: true,
124124
})
@@ -134,8 +134,8 @@ func expectPrintedASCII(t *testing.T, contents string, expected string) {
134134
func expectPrintedTargetASCII(t *testing.T, esVersion int, contents string, expected string) {
135135
t.Helper()
136136
expectPrintedCommon(t, contents, expected, config.Options{
137-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
138-
compat.ES: {esVersion},
137+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
138+
compat.ES: {Parts: []int{esVersion}},
139139
}),
140140
ASCIIOnly: true,
141141
})
@@ -144,8 +144,8 @@ func expectPrintedTargetASCII(t *testing.T, esVersion int, contents string, expe
144144
func expectParseErrorTargetASCII(t *testing.T, esVersion int, contents string, expected string) {
145145
t.Helper()
146146
expectParseErrorCommon(t, contents, expected, config.Options{
147-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
148-
compat.ES: {esVersion},
147+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
148+
compat.ES: {Parts: []int{esVersion}},
149149
}),
150150
ASCIIOnly: true,
151151
})

internal/js_parser/ts_parser_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func expectParseErrorTargetTS(t *testing.T, esVersion int, contents string, expe
4444
TS: config.TSOptions{
4545
Parse: true,
4646
},
47-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
48-
compat.ES: {esVersion},
47+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
48+
compat.ES: {Parts: []int{esVersion}},
4949
}),
5050
})
5151
}
@@ -80,8 +80,8 @@ func expectPrintedAssignSemanticsTargetTS(t *testing.T, esVersion int, contents
8080
UseDefineForClassFields: config.False,
8181
},
8282
},
83-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
84-
compat.ES: {esVersion},
83+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
84+
compat.ES: {Parts: []int{esVersion}},
8585
}),
8686
})
8787
}
@@ -127,8 +127,8 @@ func expectPrintedTargetTS(t *testing.T, esVersion int, contents string, expecte
127127
TS: config.TSOptions{
128128
Parse: true,
129129
},
130-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
131-
compat.ES: {esVersion},
130+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
131+
compat.ES: {Parts: []int{esVersion}},
132132
}),
133133
})
134134
}
@@ -142,8 +142,8 @@ func expectPrintedTargetExperimentalDecoratorTS(t *testing.T, esVersion int, con
142142
ExperimentalDecorators: config.True,
143143
},
144144
},
145-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
146-
compat.ES: {esVersion},
145+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
146+
compat.ES: {Parts: []int{esVersion}},
147147
}),
148148
})
149149
}

internal/js_printer/js_printer_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,17 @@ func expectPrintedMinifyASCII(t *testing.T, contents string, expected string) {
9393
func expectPrintedTarget(t *testing.T, esVersion int, contents string, expected string) {
9494
t.Helper()
9595
expectPrintedCommon(t, contents, contents, expected, config.Options{
96-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
97-
compat.ES: {esVersion},
96+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
97+
compat.ES: {Parts: []int{esVersion}},
9898
}),
9999
})
100100
}
101101

102102
func expectPrintedTargetMinify(t *testing.T, esVersion int, contents string, expected string) {
103103
t.Helper()
104104
expectPrintedCommon(t, contents+" [minified]", contents, expected, config.Options{
105-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
106-
compat.ES: {esVersion},
105+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
106+
compat.ES: {Parts: []int{esVersion}},
107107
}),
108108
MinifyWhitespace: true,
109109
})
@@ -112,8 +112,8 @@ func expectPrintedTargetMinify(t *testing.T, esVersion int, contents string, exp
112112
func expectPrintedTargetMangle(t *testing.T, esVersion int, contents string, expected string) {
113113
t.Helper()
114114
expectPrintedCommon(t, contents+" [mangled]", contents, expected, config.Options{
115-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
116-
compat.ES: {esVersion},
115+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
116+
compat.ES: {Parts: []int{esVersion}},
117117
}),
118118
MinifySyntax: true,
119119
})
@@ -122,8 +122,8 @@ func expectPrintedTargetMangle(t *testing.T, esVersion int, contents string, exp
122122
func expectPrintedTargetASCII(t *testing.T, esVersion int, contents string, expected string) {
123123
t.Helper()
124124
expectPrintedCommon(t, contents+" [ascii]", contents, expected, config.Options{
125-
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine][]int{
126-
compat.ES: {esVersion},
125+
UnsupportedJSFeatures: compat.UnsupportedJSFeatures(map[compat.Engine]compat.Semver{
126+
compat.ES: {Parts: []int{esVersion}},
127127
}),
128128
ASCIIOnly: true,
129129
})

0 commit comments

Comments
 (0)