add option to gzip compress files
Jason Ish
7 years ago
7 | 7 | "path/filepath" |
8 | 8 | "runtime" |
9 | 9 | "strings" |
10 | "compress/gzip" | |
10 | 11 | ) |
11 | 12 | |
12 | 13 | // NewBox returns a Box that can be used to |
71 | 72 | return true |
72 | 73 | } |
73 | 74 | |
75 | func (b Box) decompress(bb []byte) []byte { | |
76 | reader, err := gzip.NewReader(bytes.NewReader(bb)) | |
77 | if err != nil { | |
78 | return bb | |
79 | } | |
80 | data, err := ioutil.ReadAll(reader) | |
81 | if err != nil { | |
82 | return bb | |
83 | } | |
84 | return data | |
85 | } | |
86 | ||
74 | 87 | func (b Box) find(name string) (File, error) { |
75 | 88 | name = strings.TrimPrefix(name, "/") |
76 | 89 | name = strings.Replace(name, "\\", "/", -1) |
77 | 90 | if _, ok := data[b.Path]; ok { |
78 | 91 | if bb, ok := data[b.Path][name]; ok { |
92 | bb = b.decompress(bb) | |
79 | 93 | return newVirtualFile(name, bb), nil |
80 | 94 | } |
81 | 95 | } |
7 | 7 | "strings" |
8 | 8 | |
9 | 9 | "github.com/pkg/errors" |
10 | "bytes" | |
11 | "compress/gzip" | |
10 | 12 | ) |
11 | 13 | |
12 | 14 | type box struct { |
14 | 16 | Files []file |
15 | 17 | } |
16 | 18 | |
17 | func (b *box) Walk(root string) error { | |
19 | func (b *box) Walk(root string, compress bool) error { | |
18 | 20 | return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { |
19 | 21 | if info == nil || info.IsDir() { |
20 | 22 | return nil |
29 | 31 | if err != nil { |
30 | 32 | return errors.WithStack(err) |
31 | 33 | } |
34 | if compress { | |
35 | bb, err = compressFile(bb) | |
36 | if err != nil { | |
37 | return errors.WithStack(err) | |
38 | } | |
39 | } | |
32 | 40 | bb, err = json.Marshal(bb) |
33 | 41 | if err != nil { |
34 | 42 | return errors.WithStack(err) |
39 | 47 | return nil |
40 | 48 | }) |
41 | 49 | } |
50 | ||
51 | func compressFile(bb []byte) ([]byte, error) { | |
52 | var buf bytes.Buffer | |
53 | writer := gzip.NewWriter(&buf) | |
54 | _, err := writer.Write(bb) | |
55 | if err != nil { | |
56 | return bb, errors.WithStack(err) | |
57 | } | |
58 | err = writer.Close() | |
59 | if err != nil { | |
60 | return bb, errors.WithStack(err) | |
61 | } | |
62 | return buf.Bytes(), nil | |
63 | }⏎ |
24 | 24 | type Builder struct { |
25 | 25 | context.Context |
26 | 26 | RootPath string |
27 | compress bool | |
27 | 28 | pkgs map[string]pkg |
28 | 29 | } |
29 | 30 | |
97 | 98 | Name: m[1], |
98 | 99 | Files: []file{}, |
99 | 100 | } |
100 | err = bx.Walk(filepath.Join(pk.Dir, bx.Name)) | |
101 | err = bx.Walk(filepath.Join(pk.Dir, bx.Name), b.compress) | |
101 | 102 | if err != nil { |
102 | 103 | return errors.WithStack(err) |
103 | 104 | } |
123 | 124 | } |
124 | 125 | |
125 | 126 | // New Builder with a given context and path |
126 | func New(ctx context.Context, path string) *Builder { | |
127 | func New(ctx context.Context, path string, compress bool) *Builder { | |
127 | 128 | return &Builder{ |
128 | 129 | Context: ctx, |
129 | 130 | RootPath: path, |
130 | 131 | pkgs: map[string]pkg{}, |
132 | compress: compress, | |
131 | 133 | } |
132 | 134 | } |
23 | 23 | fooPackr := filepath.Join(root, "foo", "foo-packr.go") |
24 | 24 | r.False(fileExists(fooPackr)) |
25 | 25 | |
26 | b := New(context.Background(), root) | |
26 | b := New(context.Background(), root, false) | |
27 | 27 | err := b.Run() |
28 | 28 | r.NoError(err) |
29 | 29 | |
42 | 42 | r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("../templates", "index.html", "\"PCFET0NUWVBFIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiIC8+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoIiAvPgogICAgPHRpdGxlPklOREVYPC90aXRsZT4KICAgIGxpbmsKICA8L2hlYWQ+CiAgPGJvZHk+CiAgICBib2R5CiAgPC9ib2R5Pgo8L2h0bWw+Cg==\"")`))) |
43 | 43 | } |
44 | 44 | |
45 | func Test_Builder_Run_Compress(t *testing.T) { | |
46 | r := require.New(t) | |
47 | ||
48 | root := filepath.Join("..", "example") | |
49 | defer Clean(root) | |
50 | ||
51 | exPackr := filepath.Join(root, "example-packr.go") | |
52 | r.False(fileExists(exPackr)) | |
53 | ||
54 | fooPackr := filepath.Join(root, "foo", "foo-packr.go") | |
55 | r.False(fileExists(fooPackr)) | |
56 | ||
57 | b := New(context.Background(), root, true) | |
58 | err := b.Run() | |
59 | r.NoError(err) | |
60 | ||
61 | r.True(fileExists(exPackr)) | |
62 | r.True(fileExists(fooPackr)) | |
63 | ||
64 | bb, err := ioutil.ReadFile(exPackr) | |
65 | r.NoError(err) | |
66 | r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("./assets", "app.css", "\"H4sIAAAAAAAA/0rKT6lUqOZSUEhKTM5OL8ovzUuxUihKTbHmquUCBAAA//8hHmttHAAAAA==\"`))) | |
67 | r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("./assets", "app.js", "\"H4sIAAAAAAAA/0rMSS0q0VDKSM3JyVdU0rTmAgQAAP//8IaimBEAAAA=\"")`))) | |
68 | ||
69 | bb, err = ioutil.ReadFile(fooPackr) | |
70 | r.NoError(err) | |
71 | r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("../assets", "app.css", "\"H4sIAAAAAAAA/0rKT6lUqOZSUEhKTM5OL8ovzUuxUihKTbHmquUCBAAA//8hHmttHAAAAA==\"")`))) | |
72 | r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("../assets", "app.js", "\"H4sIAAAAAAAA/0rMSS0q0VDKSM3JyVdU0rTmAgQAAP//8IaimBEAAAA=\"")`))) | |
73 | r.True(bytes.Contains(bb, []byte(`packr.PackJSONBytes("../templates", "index.html", "\"H4sIAAAAAAAA/0yOvQ7CMAyEd57CZK+yMjhdaAcWYGCAMSRGicgPKqYVb4+SCMHkO3866cP1cNieLscRHMfQr7AdAHSkbQkAGIk1GKenJ7ESL751GwHyHyYdSYnZ0/LIEwswOTElVmLxlp2yNHtDXS2/JXsO1O/2w3hG2UoFwad7MZBfBbxm+26spMraC2Xz/QQAAP//5yPZVscAAAA=\"")`))) | |
74 | } | |
75 | ||
45 | 76 | func Test_Binary_Builds(t *testing.T) { |
46 | 77 | r := require.New(t) |
47 | 78 | pwd, _ := os.Getwd() |
51 | 82 | defer Clean(root) |
52 | 83 | defer os.RemoveAll(filepath.Join(root, "bin")) |
53 | 84 | |
54 | b := New(context.Background(), root) | |
85 | b := New(context.Background(), root, false) | |
55 | 86 | err := b.Run() |
56 | 87 | r.NoError(err) |
57 | 88 |
8 | 8 | ) |
9 | 9 | |
10 | 10 | var input string |
11 | var compress bool | |
11 | 12 | |
12 | 13 | var rootCmd = &cobra.Command{ |
13 | 14 | Use: "packr", |
14 | 15 | Short: "compiles static files into Go files", |
15 | 16 | RunE: func(cmd *cobra.Command, args []string) error { |
16 | b := builder.New(context.Background(), input) | |
17 | b := builder.New(context.Background(), input, compress) | |
17 | 18 | return b.Run() |
18 | 19 | }, |
19 | 20 | } |
20 | 21 | |
21 | 22 | func init() { |
22 | 23 | rootCmd.Flags().StringVarP(&input, "input", "i", ".", "path to scan for packr Boxes") |
24 | rootCmd.Flags().BoolVarP(&compress, "compress", "z", false, "compress box contents") | |
23 | 25 | } |
24 | 26 | |
25 | 27 | // Execute the commands |
2 | 2 | import ( |
3 | 3 | "encoding/json" |
4 | 4 | "sync" |
5 | "bytes" | |
6 | "compress/gzip" | |
5 | 7 | ) |
6 | 8 | |
7 | 9 | var gil = &sync.Mutex{} |
17 | 19 | data[box][name] = bb |
18 | 20 | } |
19 | 21 | |
22 | // PackBytesGzip packets the gzipped compressed bytes into a box. | |
23 | func PackBytesGzip(box string, name string, bb []byte) error { | |
24 | var buf bytes.Buffer | |
25 | w := gzip.NewWriter(&buf) | |
26 | _, err := w.Write(bb) | |
27 | if err != nil { | |
28 | return err | |
29 | } | |
30 | err = w.Close() | |
31 | if err != nil { | |
32 | return err | |
33 | } | |
34 | PackBytes(box, name, buf.Bytes()) | |
35 | return nil | |
36 | } | |
37 | ||
20 | 38 | // PackJSONBytes packs JSON encoded bytes for a file into a box. |
21 | 39 | func PackJSONBytes(box string, name string, jbb string) error { |
22 | 40 | bb := []byte{} |