Detailed changes
@@ -131,9 +131,12 @@
[[projects]]
branch = "master"
- digest = "1:7ca2584fa7da0520cd2d1136a10194fe5a5b220bdb215074ab6f7b5ad91115f4"
+ digest = "1:9d29b003dc5f98647a5dd6754d65c07171fcd35761102ea56ecd3d6993adee7f"
name = "github.com/shurcooL/httpfs"
- packages = ["vfsutil"]
+ packages = [
+ "filter",
+ "vfsutil",
+ ]
pruneopts = "UT"
revision = "809beceb23714880abc4a382a00c05f89d13b1cc"
@@ -216,6 +219,7 @@
"github.com/jroimartin/gocui",
"github.com/phayes/freeport",
"github.com/pkg/errors",
+ "github.com/shurcooL/httpfs/filter",
"github.com/shurcooL/vfsgen",
"github.com/skratchdot/open-golang/open",
"github.com/spf13/cobra",
@@ -0,0 +1,133 @@
+// Package filter offers an http.FileSystem wrapper with the ability to keep or skip files.
+package filter
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ pathpkg "path"
+ "time"
+)
+
+// Func is a selection function which is provided two arguments,
+// its '/'-separated cleaned rooted absolute path (i.e., it always begins with "/"),
+// and the os.FileInfo of the considered file.
+//
+// The path is cleaned via pathpkg.Clean("/" + path).
+//
+// For example, if the considered file is named "a" and it's inside a directory "dir",
+// then the value of path will be "/dir/a".
+type Func func(path string, fi os.FileInfo) bool
+
+// Keep returns a filesystem that contains only those entries in source for which
+// keep returns true.
+func Keep(source http.FileSystem, keep Func) http.FileSystem {
+ return &filterFS{source: source, keep: keep}
+}
+
+// Skip returns a filesystem that contains everything in source, except entries
+// for which skip returns true.
+func Skip(source http.FileSystem, skip Func) http.FileSystem {
+ keep := func(path string, fi os.FileInfo) bool {
+ return !skip(path, fi)
+ }
+ return &filterFS{source: source, keep: keep}
+}
+
+type filterFS struct {
+ source http.FileSystem
+ keep Func // Keep entries that keep returns true for.
+}
+
+func (fs *filterFS) Open(path string) (http.File, error) {
+ f, err := fs.source.Open(path)
+ if err != nil {
+ return nil, err
+ }
+
+ fi, err := f.Stat()
+ if err != nil {
+ f.Close()
+ return nil, err
+ }
+
+ if !fs.keep(clean(path), fi) {
+ // Skip.
+ f.Close()
+ return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
+ }
+
+ if !fi.IsDir() {
+ return f, nil
+ }
+ defer f.Close()
+
+ fis, err := f.Readdir(0)
+ if err != nil {
+ return nil, err
+ }
+
+ var entries []os.FileInfo
+ for _, fi := range fis {
+ if !fs.keep(clean(pathpkg.Join(path, fi.Name())), fi) {
+ // Skip.
+ continue
+ }
+ entries = append(entries, fi)
+ }
+
+ return &dir{
+ name: fi.Name(),
+ entries: entries,
+ modTime: fi.ModTime(),
+ }, nil
+}
+
+// clean turns a potentially relative path into an absolute one.
+//
+// This is needed to normalize path parameter for selection function.
+func clean(path string) string {
+ return pathpkg.Clean("/" + path)
+}
+
+// dir is an opened dir instance.
+type dir struct {
+ name string
+ modTime time.Time
+ entries []os.FileInfo
+ pos int // Position within entries for Seek and Readdir.
+}
+
+func (d *dir) Read([]byte) (int, error) {
+ return 0, fmt.Errorf("cannot Read from directory %s", d.name)
+}
+func (d *dir) Close() error { return nil }
+func (d *dir) Stat() (os.FileInfo, error) { return d, nil }
+
+func (d *dir) Name() string { return d.name }
+func (d *dir) Size() int64 { return 0 }
+func (d *dir) Mode() os.FileMode { return 0755 | os.ModeDir }
+func (d *dir) ModTime() time.Time { return d.modTime }
+func (d *dir) IsDir() bool { return true }
+func (d *dir) Sys() interface{} { return nil }
+
+func (d *dir) Seek(offset int64, whence int) (int64, error) {
+ if offset == 0 && whence == io.SeekStart {
+ d.pos = 0
+ return 0, nil
+ }
+ return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
+}
+
+func (d *dir) Readdir(count int) ([]os.FileInfo, error) {
+ if d.pos >= len(d.entries) && count > 0 {
+ return nil, io.EOF
+ }
+ if count <= 0 || count > len(d.entries)-d.pos {
+ count = len(d.entries) - d.pos
+ }
+ e := d.entries[d.pos : d.pos+count]
+ d.pos += count
+ return e, nil
+}
@@ -0,0 +1,26 @@
+package filter
+
+import (
+ "os"
+ pathpkg "path"
+)
+
+// FilesWithExtensions returns a filter func that selects files (but not directories)
+// that have any of the given extensions. For example:
+//
+// filter.FilesWithExtensions(".go", ".html")
+//
+// Would select both .go and .html files. It would not select any directories.
+func FilesWithExtensions(exts ...string) Func {
+ return func(path string, fi os.FileInfo) bool {
+ if fi.IsDir() {
+ return false
+ }
+ for _, ext := range exts {
+ if pathpkg.Ext(path) == ext {
+ return true
+ }
+ }
+ return false
+ }
+}
@@ -9,13 +9,19 @@ import (
"os"
"path/filepath"
+ "github.com/shurcooL/httpfs/filter"
"github.com/shurcooL/vfsgen"
)
func main() {
var cwd, _ = os.Getwd()
- webUIAssets := http.Dir(filepath.Join(cwd, "webui/build"))
+ webUIAssets := filter.Skip(
+ http.Dir(filepath.Join(cwd, "webui/build")),
+ func(path string, fi os.FileInfo) bool {
+ return filter.FilesWithExtensions(".map")(path, fi)
+ },
+ )
fmt.Println("Packing Web UI files ...")
@@ -21,64 +21,57 @@ var WebUIAssets = func() http.FileSystem {
fs := vfsgen۰FS{
"/": &vfsgen۰DirInfo{
name: "/",
- modTime: time.Date(2018, 8, 8, 17, 0, 5, 812171708, time.UTC),
+ modTime: time.Date(2018, 8, 13, 23, 54, 21, 858686490, time.UTC),
},
"/asset-manifest.json": &vfsgen۰CompressedFileInfo{
name: "asset-manifest.json",
- modTime: time.Date(2018, 8, 8, 17, 0, 5, 608173974, time.UTC),
+ modTime: time.Date(2018, 8, 13, 23, 54, 21, 758684604, time.UTC),
uncompressedSize: 96,
- compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xe6\x52\x50\x50\xca\x4d\xcc\xcc\xd3\xcb\x2a\x56\xb2\x52\x50\x2a\x2e\x49\x2c\xc9\x4c\xd6\xcf\x2a\xd6\x07\x0b\x9a\x58\x24\x1b\xa5\x1a\x58\x9a\x82\x64\x75\x90\x94\xea\xe5\x26\x16\xe0\x57\x0e\x56\xc1\x55\x0b\x08\x00\x00\xff\xff\xf7\xc2\x31\xef\x60\x00\x00\x00"),
+ compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xe6\x52\x50\x50\xca\x4d\xcc\xcc\xd3\xcb\x2a\x56\xb2\x52\x50\x2a\x2e\x49\x2c\xc9\x4c\xd6\xcf\x2a\xd6\x07\x0b\x5a\x24\xa5\x98\x1a\x98\x59\x26\x83\x64\x75\x90\x94\xea\xe5\x26\x16\xe0\x57\x0e\x56\xc1\x55\x0b\x08\x00\x00\xff\xff\x0d\xb2\xb0\x73\x60\x00\x00\x00"),
},
"/favicon.ico": &vfsgen۰CompressedFileInfo{
name: "favicon.ico",
- modTime: time.Date(2018, 8, 8, 16, 59, 55, 448286873, time.UTC),
+ modTime: time.Date(2018, 8, 13, 23, 54, 14, 218542451, time.UTC),
uncompressedSize: 3870,