Skip to content

Commit

Permalink
Merge pull request #25 from aminya/node-update [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya authored Sep 10, 2024
2 parents d477365 + b5f68d2 commit 861ca34
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 95 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cSpell.words": ["jsonminify", "minijson"]
}
3 changes: 1 addition & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ dub build --config=executable --build=release-nobounds --compiler=ldc2
```

- Download Native Binaries from
https://github.com/aminya/minijson/releases/latest

https://github.com/aminya/minijson/releases/latest

### CLI Usage

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"format.d": "dub run --build=release --quiet dfmt -- --soft_max_line_length 110 --indent_size 2 --inplace ./src ./benchmark",
"lint": "eslint . --fix",
"prepublishOnly": "shx rm -rf ./dist/tsconfig.tsbuildinfo ./dist/*.zip ./dist/build.* && chmod +x ./dist/*/minijson",
"start.benchmark.js": "node ./benchmark/js-benchmark.mjs",
"start.benchmark.node": "node ./benchmark/native-benchmark.mjs",
"start.browser": "servor ./dist/ --browse --reload",
"start.node": "node ./dist/node/cli.js",
Expand Down
17 changes: 7 additions & 10 deletions src/native/cli.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,24 @@ module minijson.cli;
import minijson.lib : minifyFiles, minifyStrings;
import argparse;

@(Command("minijson")
.Description(`minijson: minify json files with support for comments
@(Command("minijson").Description(`minijson: minify json files with support for comments
# Minify the specified files
minijson ./dist/**/*.json ./build/a.json
minijson file1_with_comment.json file2_with_comment.json
# Minify the specified files (supports comments)
minijson --comment file1_with_comment.json file2_with_comment.json
# Minify the specified files and disable comment support for faster minification
minijson --comment=false file1_no_comment.json file2_no_comment.json
# Minify the specified json string
minijson --str '{"some_json": "string_here"}'
# Minify the specified json string (supports comments)
minijson --comment --str '{"some_json": "string_here"} //comment'
minijson --str '{"some_json": "string_here"} //comment'
More information at https://github.com/aminya/minijson
`)
)
`))
struct Options
{
bool comment = false;
bool comment = true;
string[] str;
// (Deprecated) A list of files to minify (for backwards compatiblitity with getopt)
string[] file;
Expand Down
150 changes: 119 additions & 31 deletions src/native/lib.d
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,66 @@ const tokenizerNoComment = ctRegex!(`[\n\r"[]]`, "g");
Params:
jsonString = the json string you want to minify
hasComment = a boolean to support comments in json. Default: `false`.
hasComment = a boolean to support comments in json. Default: `true`.
Return:
the minified json string
*/
string minifyString(in string jsonString, in bool hasComment = false) @trusted
string minifyString(in string jsonString, in bool hasComment = true) @trusted
{
return hasComment ? minifyStringWithComments(jsonString) : minifyStringNoComments(jsonString);
}

string minifyStringNoComments(in string jsonString) @trusted
{
auto in_string = false;
string result;
size_t from = 0;
auto rightContext = "";

auto match = jsonString.matchAll(tokenizerNoComment);

while (!match.empty())
{
const matchFrontHit = match.front().hit();

rightContext = match.post();

// update from for the next iteration
const prevFrom = from;
from = jsonString.length - rightContext.length; // lastIndex

auto leftContextSubstr = match.pre()[prevFrom .. $];
const noLeftContext = leftContextSubstr.length == 0;
if (!noLeftContext)
{
if (!in_string)
{
leftContextSubstr = remove_spaces(leftContextSubstr);
}
result ~= leftContextSubstr;
}
if (matchFrontHit == "\"")
{
if (!in_string || noLeftContext || hasNoSlashOrEvenNumberOfSlashes(leftContextSubstr))
{
// start of string with ", or unescaped " character found to end string
in_string = !in_string;
}
--from; // include " character in next catch
rightContext = jsonString[from .. $];
}
else if (notSlashAndNoSpaceOrBreak(matchFrontHit))
{
result ~= matchFrontHit;
}
match.popFront();
}
result ~= rightContext;
return result;
}

string minifyStringWithComments(in string jsonString) @trusted
{
auto in_string = false;
auto in_multiline_comment = false;
Expand All @@ -26,9 +80,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
size_t from = 0;
auto rightContext = "";

const tokenizer = !hasComment ? tokenizerNoComment : tokenizerWithComment;

auto match = jsonString.matchAll(tokenizer);
auto match = jsonString.matchAll(tokenizerWithComment);

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

const notInComment = (!in_multiline_comment && !in_singleline_comment);
const noCommentOrNotInComment = !hasComment || notInComment;
const notInComment = !in_multiline_comment && !in_singleline_comment;

if (noCommentOrNotInComment)
if (notInComment)
{
auto leftContextSubstr = match.pre()[prevFrom .. $];
const noLeftContext = leftContextSubstr.length == 0;
Expand All @@ -67,7 +118,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
}
}
// comments
if (hasComment && !in_string)
if (!in_string)
{
if (notInComment)
{
Expand All @@ -93,7 +144,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
in_singleline_comment = false;
}
}
else if (!hasComment && notSlashAndNoSpaceOrBreak(matchFrontHit))
else if (notSlashAndNoSpaceOrBreak(matchFrontHit))
{
result ~= matchFrontHit;
}
Expand Down Expand Up @@ -176,7 +227,7 @@ private bool hasNoSpace(const ref string str) @trusted
Return:
the minified json strings
*/
string[] minifyStrings(in string[] jsonStrings, in bool hasComment = false) @trusted
string[] minifyStrings(in string[] jsonStrings, in bool hasComment = true) @trusted
{
import std.algorithm : map;
import std.array : array;
Expand All @@ -191,7 +242,7 @@ string[] minifyStrings(in string[] jsonStrings, in bool hasComment = false) @tru
paths = the paths to the files. It could be glob patterns.
hasComment = a boolean to support comments in json. Default: `false`.
*/
void minifyFiles(in string[] paths, in bool hasComment = false) @trusted
void minifyFiles(in string[] paths, in bool hasComment = true) @trusted
{
import std.parallelism : parallel;
import std.algorithm;
Expand All @@ -202,33 +253,70 @@ void minifyFiles(in string[] paths, in bool hasComment = false) @trusted
import std.stdio : writeln;

// get the files from the given paths (resolve glob patterns)
auto files = paths
.map!((path) {
if (path.exists)
auto files = paths.map!((path) {
if (path.exists)
{
if (path.isFile)
{
if (path.isFile)
{
return [path];
}
else if (path.isDir)
{
return glob(path ~ "/**/*.json");
}
else
{
throw new Exception("The given path is not a file or a directory: " ~ path);
}
return [path];
}
else if (path.isDir)
{
return glob(path ~ "/**/*.json");
}
else
{
return glob(path);
throw new Exception("The given path is not a file or a directory: " ~ path);
}
})
.joiner()
.array();
}
else
{
return glob(path);
}
}).joiner().array();

if (files.empty)
{
writeln("No files found.");
return;
}

if (!confirmExtension(files))
{
return;
}

foreach (file; files.parallel())
{
write(file, minifyString(readText(file), hasComment));
}
}

bool confirmExtension(string[] files) @trusted
{
auto confirmExtension = false;
import std.path : extension;

foreach (file; files)
{
// if the file extension is not json, jsonc, or json5, confirm before minifying
auto fileExtension = file.extension();
if (fileExtension != ".json" && fileExtension != ".jsonc" && fileExtension != ".json5")
{
if (!confirmExtension)
{
import std.stdio : readln, writeln;

writeln("The file ", file, " doesn't have a json extension. Do you want to minify it? (y/n)");
auto input = readln();
confirmExtension = input == "y";
if (!confirmExtension)
{
return false;
}
}
}
}

return true;
}
4 changes: 2 additions & 2 deletions src/native/libc.d
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import minijson.lib : minifyString;
Params:
jsonString = the json string you want to minify
hasComment = a boolean to support comments in json. Default: `false`.
hasComment = a boolean to support comments in json. Default: `true`.
Return:
the minified json string
*/
extern (C) auto c_minifyString(char* jsonCString, bool hasComment = false)
extern (C) auto c_minifyString(char* jsonCString, bool hasComment = true)
{
import std : fromStringz, toStringz;

Expand Down
Loading

0 comments on commit 861ca34

Please sign in to comment.