Skip to content

Commit

Permalink
fix #3027: live reload SSE when outdir is /
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Apr 1, 2023
1 parent fa974c2 commit f780ad3
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
foo(1e3.x,0 .x,.1.x,1e-4.x,0xffff0000ffff0000.x);
```

* Fix server-sent events with live reload when writing to the file system root ([#3027](https://github.com/evanw/esbuild/issues/3027))

This release fixes a bug where esbuild previously failed to emit server-sent events for live reload when `outdir` was the file system root, such as `/`. This happened because `/` is the only path on Unix that cannot have a trailing slash trimmed from it, which was fixed by improved path handling.

## 0.17.14

* Allow the TypeScript 5.0 `const` modifier in object type declarations ([#3021](https://github.com/evanw/esbuild/issues/3021))
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ check-go-version:
# variable like this: "ESBUILD_RACE=-race make test". Or you can permanently
# enable it by adding "export ESBUILD_RACE=-race" to your shell profile.
test-go:
go test $(ESBUILD_RACE) ./internal/...
go test $(ESBUILD_RACE) ./internal/... ./pkg/...

vet-go:
go vet ./cmd/... ./internal/... ./pkg/...
Expand Down
35 changes: 35 additions & 0 deletions pkg/api/api_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2445,3 +2445,38 @@ func summarizeOutputFiles(outputFiles []OutputFile) buildSummary {
}
return summary
}

func stripDirPrefix(path string, prefix string, allowedSlashes string) (string, bool) {
if strings.HasPrefix(path, prefix) {
pathLen := len(path)
prefixLen := len(prefix)

// Just return the path if there is no prefix
if prefixLen == 0 {
return path, true
}

// Return the empty string if the path equals the prefix
if pathLen == prefixLen {
return "", true
}

if strings.IndexByte(allowedSlashes, prefix[prefixLen-1]) >= 0 {
// Return the suffix if the prefix ends in a slash. Examples:
//
// stripDirPrefix(`/foo`, `/`, `/`) => `foo`
// stripDirPrefix(`C:\foo`, `C:\`, `\/`) => `foo`
//
return path[prefixLen:], true
} else if strings.IndexByte(allowedSlashes, path[prefixLen]) >= 0 {
// Return the suffix if there's a slash after the prefix. Examples:
//
// stripDirPrefix(`/foo/bar`, `/foo`, `/`) => `bar`
// stripDirPrefix(`C:\foo\bar`, `C:\foo`, `\/`) => `bar`
//
return path[prefixLen+1:], true
}
}

return "", false
}
57 changes: 57 additions & 0 deletions pkg/api/api_impl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package api

import (
"fmt"
"testing"

"github.com/evanw/esbuild/internal/test"
)

func TestStripDirPrefix(t *testing.T) {
expectSuccess := func(path string, prefix string, allowedSlashes string, expected string) {
t.Helper()
t.Run(fmt.Sprintf("path=%s prefix=%s slashes=%s", path, prefix, allowedSlashes), func(t *testing.T) {
t.Helper()
observed, ok := stripDirPrefix(path, prefix, allowedSlashes)
if !ok {
t.Fatalf("Unexpected failure")
}
test.AssertEqualWithDiff(t, observed, expected)
})
}

expectFailure := func(path string, prefix string, allowedSlashes string) {
t.Helper()
t.Run(fmt.Sprintf("path=%s prefix=%s slashes=%s", path, prefix, allowedSlashes), func(t *testing.T) {
t.Helper()
_, ok := stripDirPrefix(path, prefix, allowedSlashes)
if ok {
t.Fatalf("Unexpected success")
}
})
}

// Note: People sometimes set "outdir" to "/" and expect that to work:
// https://github.com/evanw/esbuild/issues/3027

expectSuccess(`/foo/bar/baz`, ``, `/`, `/foo/bar/baz`)
expectSuccess(`/foo/bar/baz`, `/`, `/`, `foo/bar/baz`)
expectSuccess(`/foo/bar/baz`, `/foo`, `/`, `bar/baz`)
expectSuccess(`/foo/bar/baz`, `/foo/bar`, `/`, `baz`)
expectSuccess(`/foo/bar/baz`, `/foo/bar/baz`, `/`, ``)
expectSuccess(`/foo/bar//baz`, `/foo/bar`, `/`, `/baz`)
expectSuccess(`C:\foo\bar\baz`, ``, `\/`, `C:\foo\bar\baz`)
expectSuccess(`C:\foo\bar\baz`, `C:`, `\/`, `foo\bar\baz`)
expectSuccess(`C:\foo\bar\baz`, `C:\`, `\/`, `foo\bar\baz`)
expectSuccess(`C:\foo\bar\baz`, `C:\foo`, `\/`, `bar\baz`)
expectSuccess(`C:\foo\bar\baz`, `C:\foo\bar`, `\/`, `baz`)
expectSuccess(`C:\foo\bar\baz`, `C:\foo\bar\baz`, `\/`, ``)
expectSuccess(`C:\foo\bar\\baz`, `C:\foo\bar`, `\/`, `\baz`)
expectSuccess(`C:\foo\bar/baz`, `C:\foo\bar`, `\/`, `baz`)