From 7b62342eb7cdf0529e9de11f99c138d7d7168c30 Mon Sep 17 00:00:00 2001 From: Maximilian Brune Date: Thu, 4 Aug 2022 19:09:05 +0200 Subject: [PATCH] Add minify implementation for golang --- go.mod | 3 ++ minify.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ minify_test.go | 72 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 go.mod create mode 100644 minify.go create mode 100644 minify_test.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..801193d --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/getify/JSON.minify/tree/go + +go 1.18 diff --git a/minify.go b/minify.go new file mode 100644 index 0000000..eb3f7f5 --- /dev/null +++ b/minify.go @@ -0,0 +1,92 @@ +package minify + +import ( + "strings" +) + +func JsonMinify(json string, stripSpace bool) string { + var out strings.Builder + indexMultilineComment := -1 + indexInlineComment := -1 + inJsonStr := false + skip := false + for i, r := range json { + if skip { + skip = false + continue + } + switch r { + case '/': + if !inJsonStr && indexMultilineComment == -1 && indexInlineComment == -1 { + // if currently not in a comment or json string, a slash + // could be the beginning of a new comment. look ahead and see + if len(json) > i+1 { + if json[i+1] == '/' { + indexInlineComment = i + // since we already looked ahead, we don't need + // to consider the next character + skip = true + } else if json[i+1] == '*' { + indexMultilineComment = i + // since we already looked ahead, we don't need + // to consider the next character + skip = true + } + } + } + case '*': + if indexMultilineComment != -1 { + // we are currently in a multiline comment. A star sign could mean + // the end of our multiline comment. look ahead and see + if len(json) > i+1 { + if json[i+1] == '/' { + indexMultilineComment = -1 + // since we already looked ahead, we don't need to consider + // the next character + skip = true + // a bit nasty but since we don't want to print the current + // character, we need to skip the rest + continue + } + } + } + case '"': + if inJsonStr { + inJsonStr = false + } else if !inJsonStr && indexMultilineComment == -1 && indexInlineComment == -1 { + // if we are currently not in a comment, a quote sign can only mean + // we got into a JSON string + inJsonStr = true + } + case '\\': + if len(json) > i+1 { + if json[i+1] == '"' { + // next quote sign is escaped and therefore looses it's meaning + // as start/end of JSON string. put into output and skip both + out.WriteRune('\\') + out.WriteRune('"') + skip = true + continue + } else if json[i+1] == '\\' { + // the next escape must loose it's power to escape stuff + // because it itself is escaped + out.WriteRune('\\') + skip = true + } + } + case '\n': + indexInlineComment = -1 + case '\r': + indexInlineComment = -1 + } + if indexMultilineComment == -1 && indexInlineComment == -1 { + // only consider in output if character is outside of any comment + if !stripSpace || inJsonStr || (r != '\n' && r != '\r' && r != '\t' && r != ' ') { + // everything considered a whitespace (line feed, carriage return, tab, whitespace) + // is only candidate for output if desired + out.WriteRune(r) + } + } + } + return out.String() +} diff --git a/minify_test.go b/minify_test.go new file mode 100644 index 0000000..145df16 --- /dev/null +++ b/minify_test.go @@ -0,0 +1,72 @@ +package minify + +import ( + "bytes" + "testing" +) + +type Test struct { + Before string; + Expected string; +} + +func TestRemoveComments(t *testing.T) { + tests := []Test { + { + Before: ` + // this is a JSON file with comments + { + "foo": "bar", // this is cool + "bar": [ + "baz", "bum", "zam" + ], + /* the rest of this document is just fluff + in case you are interested. */ + "something": 10, + "else": 20 + } + + /* NOTE: You can easily strip the whitespace and comments + from such a file with the JSON.minify() project hosted + here on github at http://github.com/getify/JSON.minify + */`, + Expected: `{"foo":"bar","bar":["baz","bum","zam"],"something":10,"else":20}`, + }, + { + Before: ` + + {"/*":"*/","//":"",/*"//"*/"/*/":// + "//"} + `, + Expected: `{"/*":"*/","//":"","/*/":"//"}`, + }, + { + Before: ` + /* + this is a + multi line comment */{ + + "foo" + : + "bar/*"// something + , "b\\\"az":/* + something else */"blah" + + }`, + Expected: `{"foo":"bar/*","b\\\"az":"blah"}`, + }, + { + Before: ` + {"foo": "ba\\\"r//", "bar\\\\": "b\\\\\\\"a/*z", + "baz\\\\\\\\": /* yay */ "fo\\\\\\\\\\\"*/o" + }`, + Expected: `{"foo":"ba\\\"r//","bar\\\\":"b\\\\\\\"a/*z","baz\\\\\\\\":"fo\\\\\\\\\\\"*/o"}`, + }}; + + for _, test := range tests { + after := JsonMinify(test.Before, true) + if !bytes.Equal([]byte(after), []byte(test.Expected)) { + t.Fatalf("Not the same:\nreal: %s\nExpected:%s\n", after, test.Expected) + } + } +}