Skip to content

Commit 861ca34

Browse files
authored
Merge pull request #25 from aminya/node-update [skip ci]
2 parents d477365 + b5f68d2 commit 861ca34

File tree

7 files changed

+177
-95
lines changed

7 files changed

+177
-95
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"cSpell.words": ["jsonminify", "minijson"]
3+
}

Readme.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ dub build --config=executable --build=release-nobounds --compiler=ldc2
2424
```
2525

2626
- Download Native Binaries from
27-
https://github.com/aminya/minijson/releases/latest
28-
27+
https://github.com/aminya/minijson/releases/latest
2928

3029
### CLI Usage
3130

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"format.d": "dub run --build=release --quiet dfmt -- --soft_max_line_length 110 --indent_size 2 --inplace ./src ./benchmark",
2626
"lint": "eslint . --fix",
2727
"prepublishOnly": "shx rm -rf ./dist/tsconfig.tsbuildinfo ./dist/*.zip ./dist/build.* && chmod +x ./dist/*/minijson",
28+
"start.benchmark.js": "node ./benchmark/js-benchmark.mjs",
2829
"start.benchmark.node": "node ./benchmark/native-benchmark.mjs",
2930
"start.browser": "servor ./dist/ --browse --reload",
3031
"start.node": "node ./dist/node/cli.js",

src/native/cli.d

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,24 @@ module minijson.cli;
33
import minijson.lib : minifyFiles, minifyStrings;
44
import argparse;
55

6-
@(Command("minijson")
7-
.Description(`minijson: minify json files with support for comments
6+
@(Command("minijson").Description(`minijson: minify json files with support for comments
87
98
# Minify the specified files
109
minijson ./dist/**/*.json ./build/a.json
10+
minijson file1_with_comment.json file2_with_comment.json
1111
12-
# Minify the specified files (supports comments)
13-
minijson --comment file1_with_comment.json file2_with_comment.json
12+
# Minify the specified files and disable comment support for faster minification
13+
minijson --comment=false file1_no_comment.json file2_no_comment.json
1414
1515
# Minify the specified json string
1616
minijson --str '{"some_json": "string_here"}'
17-
18-
# Minify the specified json string (supports comments)
19-
minijson --comment --str '{"some_json": "string_here"} //comment'
17+
minijson --str '{"some_json": "string_here"} //comment'
2018
2119
More information at https://github.com/aminya/minijson
22-
`)
23-
)
20+
`))
2421
struct Options
2522
{
26-
bool comment = false;
23+
bool comment = true;
2724
string[] str;
2825
// (Deprecated) A list of files to minify (for backwards compatiblitity with getopt)
2926
string[] file;

src/native/lib.d

Lines changed: 119 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,66 @@ const tokenizerNoComment = ctRegex!(`[\n\r"[]]`, "g");
1212
1313
Params:
1414
jsonString = the json string you want to minify
15-
hasComment = a boolean to support comments in json. Default: `false`.
15+
hasComment = a boolean to support comments in json. Default: `true`.
1616
1717
Return:
1818
the minified json string
1919
*/
20-
string minifyString(in string jsonString, in bool hasComment = false) @trusted
20+
string minifyString(in string jsonString, in bool hasComment = true) @trusted
21+
{
22+
return hasComment ? minifyStringWithComments(jsonString) : minifyStringNoComments(jsonString);
23+
}
24+
25+
string minifyStringNoComments(in string jsonString) @trusted
26+
{
27+
auto in_string = false;
28+
string result;
29+
size_t from = 0;
30+
auto rightContext = "";
31+
32+
auto match = jsonString.matchAll(tokenizerNoComment);
33+
34+
while (!match.empty())
35+
{
36+
const matchFrontHit = match.front().hit();
37+
38+
rightContext = match.post();
39+
40+
// update from for the next iteration
41+
const prevFrom = from;
42+
from = jsonString.length - rightContext.length; // lastIndex
43+
44+
auto leftContextSubstr = match.pre()[prevFrom .. $];
45+
const noLeftContext = leftContextSubstr.length == 0;
46+
if (!noLeftContext)
47+
{
48+
if (!in_string)
49+
{
50+
leftContextSubstr = remove_spaces(leftContextSubstr);
51+
}
52+
result ~= leftContextSubstr;
53+
}
54+
if (matchFrontHit == "\"")
55+
{
56+
if (!in_string || noLeftContext || hasNoSlashOrEvenNumberOfSlashes(leftContextSubstr))
57+
{
58+
// start of string with ", or unescaped " character found to end string
59+
in_string = !in_string;
60+
}
61+
--from; // include " character in next catch
62+
rightContext = jsonString[from .. $];
63+
}
64+
else if (notSlashAndNoSpaceOrBreak(matchFrontHit))
65+
{
66+
result ~= matchFrontHit;
67+
}
68+
match.popFront();
69+
}
70+
result ~= rightContext;
71+
return result;
72+
}
73+
74+
string minifyStringWithComments(in string jsonString) @trusted
2175
{
2276
auto in_string = false;
2377
auto in_multiline_comment = false;
@@ -26,9 +80,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
2680
size_t from = 0;
2781
auto rightContext = "";
2882

29-
const tokenizer = !hasComment ? tokenizerNoComment : tokenizerWithComment;
30-
31-
auto match = jsonString.matchAll(tokenizer);
83+
auto match = jsonString.matchAll(tokenizerWithComment);
3284

3385
while (!match.empty())
3486
{
@@ -40,10 +92,9 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
4092
const prevFrom = from;
4193
from = jsonString.length - rightContext.length; // lastIndex
4294

43-
const notInComment = (!in_multiline_comment && !in_singleline_comment);
44-
const noCommentOrNotInComment = !hasComment || notInComment;
95+
const notInComment = !in_multiline_comment && !in_singleline_comment;
4596

46-
if (noCommentOrNotInComment)
97+
if (notInComment)
4798
{
4899
auto leftContextSubstr = match.pre()[prevFrom .. $];
49100
const noLeftContext = leftContextSubstr.length == 0;
@@ -67,7 +118,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
67118
}
68119
}
69120
// comments
70-
if (hasComment && !in_string)
121+
if (!in_string)
71122
{
72123
if (notInComment)
73124
{
@@ -93,7 +144,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
93144
in_singleline_comment = false;
94145
}
95146
}
96-
else if (!hasComment && notSlashAndNoSpaceOrBreak(matchFrontHit))
147+
else if (notSlashAndNoSpaceOrBreak(matchFrontHit))
97148
{
98149
result ~= matchFrontHit;
99150
}
@@ -176,7 +227,7 @@ private bool hasNoSpace(const ref string str) @trusted
176227
Return:
177228
the minified json strings
178229
*/
179-
string[] minifyStrings(in string[] jsonStrings, in bool hasComment = false) @trusted
230+
string[] minifyStrings(in string[] jsonStrings, in bool hasComment = true) @trusted
180231
{
181232
import std.algorithm : map;
182233
import std.array : array;
@@ -191,7 +242,7 @@ string[] minifyStrings(in string[] jsonStrings, in bool hasComment = false) @tru
191242
paths = the paths to the files. It could be glob patterns.
192243
hasComment = a boolean to support comments in json. Default: `false`.
193244
*/
194-
void minifyFiles(in string[] paths, in bool hasComment = false) @trusted
245+
void minifyFiles(in string[] paths, in bool hasComment = true) @trusted
195246
{
196247
import std.parallelism : parallel;
197248
import std.algorithm;
@@ -202,33 +253,70 @@ void minifyFiles(in string[] paths, in bool hasComment = false) @trusted
202253
import std.stdio : writeln;
203254

204255
// get the files from the given paths (resolve glob patterns)
205-
auto files = paths
206-
.map!((path) {
207-
if (path.exists)
256+
auto files = paths.map!((path) {
257+
if (path.exists)
258+
{
259+
if (path.isFile)
208260
{
209-
if (path.isFile)
210-
{
211-
return [path];
212-
}
213-
else if (path.isDir)
214-
{
215-
return glob(path ~ "/**/*.json");
216-
}
217-
else
218-
{
219-
throw new Exception("The given path is not a file or a directory: " ~ path);
220-
}
261+
return [path];
262+
}
263+
else if (path.isDir)
264+
{
265+
return glob(path ~ "/**/*.json");
221266
}
222267
else
223268
{
224-
return glob(path);
269+
throw new Exception("The given path is not a file or a directory: " ~ path);
225270
}
226-
})
227-
.joiner()
228-
.array();
271+
}
272+
else
273+
{
274+
return glob(path);
275+
}
276+
}).joiner().array();
277+
278+
if (files.empty)
279+
{
280+
writeln("No files found.");
281+
return;
282+
}
283+
284+
if (!confirmExtension(files))
285+
{
286+
return;
287+
}
229288

230289
foreach (file; files.parallel())
231290
{
232291
write(file, minifyString(readText(file), hasComment));
233292
}
234293
}
294+
295+
bool confirmExtension(string[] files) @trusted
296+
{
297+
auto confirmExtension = false;
298+
import std.path : extension;
299+
300+
foreach (file; files)
301+
{
302+
// if the file extension is not json, jsonc, or json5, confirm before minifying
303+
auto fileExtension = file.extension();
304+
if (fileExtension != ".json" && fileExtension != ".jsonc" && fileExtension != ".json5")
305+
{
306+
if (!confirmExtension)
307+
{
308+
import std.stdio : readln, writeln;
309+
310+
writeln("The file ", file, " doesn't have a json extension. Do you want to minify it? (y/n)");
311+
auto input = readln();
312+
confirmExtension = input == "y";
313+
if (!confirmExtension)
314+
{
315+
return false;
316+
}
317+
}
318+
}
319+
}
320+
321+
return true;
322+
}

src/native/libc.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import minijson.lib : minifyString;
77
88
Params:
99
jsonString = the json string you want to minify
10-
hasComment = a boolean to support comments in json. Default: `false`.
10+
hasComment = a boolean to support comments in json. Default: `true`.
1111
1212
Return:
1313
the minified json string
1414
*/
15-
extern (C) auto c_minifyString(char* jsonCString, bool hasComment = false)
15+
extern (C) auto c_minifyString(char* jsonCString, bool hasComment = true)
1616
{
1717
import std : fromStringz, toStringz;
1818

0 commit comments

Comments
 (0)