Skip to content

Commit

Permalink
Merge pull request #15 from aminya/comments
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya authored Jul 7, 2021
2 parents 08bfaaa + 4f07309 commit cefda1e
Show file tree
Hide file tree
Showing 55 changed files with 203 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ dist
.dub
trace.*

test/fixtures/*-minified.json
test/fixtures/**/*-minified.json
32 changes: 26 additions & 6 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# minijson

Minify JSON files **blazing fast**! Written in D.
Minify JSON files **blazing fast**! Supports Comments. Written in D,

55 times faster than jsonminify!

Expand Down Expand Up @@ -30,14 +30,22 @@ dub build --config=executable --build=release-nobounds --compiler=ldc2

### CLI Usage

```
Usage: minify json
```shell
❯ minijson --help

minijson: minify json files with support for comments
minijson --file file1.json --file file2.json
minijson --file file1_with_comment.json --file file2_with_comment.json --comment

minijson --string '{"some_json": "string_here"}'
minijson --string '{"some_json": "string_here"} //comment' --comment

More information at https://github.com/aminya/minijson

--file an array of files to minify
--string a json string to minify
-h --help This help information.
--file an array of files to minify
--string a json string to minify
--comment a flag to support comments in json
-h --help This help information.
```

### Node API
Expand All @@ -48,8 +56,14 @@ import { minifyFiles, minifyString } from "minijson"
// minify the files in-place and in parallel
await minifyFiles(["file1.json", "file2.json"])

// supports comments by passing true as the second argument
await minifyFiles(["file1_with_comment.json", "file2_with_comment.json"], true)

// minify the given string
const minifiedString = minifyString(`{"some_json": "here"}`)

// supports comments by passing true as the second argument
const minifiedString2 = minifyString(`{"some_json": "here"}//comment`, true)
```

**Note**: in the Nodejs API, prefer `minifyFiles` over other functions, as it minifies the files in parallel with the least amount of resources.
Expand All @@ -62,8 +76,14 @@ import minijson: minifyString, minifyFiles;
// minify the given string
const minifiedString = minifyString(`{"some_json": "here"}`);

// supports comments by passing true as the second argument
const minifiedString2 = minifyString(`{"some_json": "here"}//comment`, true);

// minify the files in-place and in parallel
minifyFiles(["file1.json", "file2.json"]);

// supports comments by passing true as the second argument
minifyFiles(["file1.json", "file2.json"], true);
```

### Benchmarks
Expand Down
4 changes: 2 additions & 2 deletions benchmark/js-benchmark.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { readFile, writeFile } from "fs/promises"
import { performance } from "perf_hooks"
import jsonMinify from "jsonminify"

import { jsonFiles } from "../test/fixtures.mjs"
import { standardFiles } from "../test/fixtures.mjs"

// warmup
const tmp = await jsonMinify("{}")

const t1 = performance.now()

await Promise.all(
jsonFiles.map(async (jsonFile) => {
standardFiles.map(async (jsonFile) => {
const jsonString = await readFile(jsonFile, "utf8")
const data = await jsonMinify(jsonString)
return await writeFile(jsonFile, data)
Expand Down
4 changes: 2 additions & 2 deletions benchmark/native-benchmark.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { performance } from "perf_hooks"
import { minifyFiles } from "../dist/lib.js"

import { jsonFiles } from "../test/fixtures.mjs"
import { standardFiles } from "../test/fixtures.mjs"

const t1 = performance.now()

await minifyFiles(jsonFiles)
await minifyFiles(standardFiles)

const t2 = performance.now()
console.log(((t2 - t1) / 1000).toFixed(3), "seconds")
18 changes: 15 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@aminya/minijson",
"author": "Amin Yahyaabdi",
"version": "0.4.1",
"description": "Minify JSON files blazing fast! Written in D",
"description": "Minify JSON files blazing fast! Supports Comments. Written in D.",
"homepage": "https://github.com/aminya/minijson",
"license": "MIT",
"files": [
Expand All @@ -17,7 +17,7 @@
"test.format": "prettier . --check",
"lint": "eslint . --fix",
"test.lint": "eslint .",
"test": "jasmine ./test/index-test.mjs",
"test": "jasmine ./test/index-test.mjs || shx rm -rf ./test/fixtures/**/*-minified.json",
"clean": "shx rm -rf dist",
"build.native": "dub build --config=executable",
"build.native.release": "pnpm build.native -- --build release-nobounds --compiler=ldc2",
Expand All @@ -42,7 +42,8 @@
"parcel": "^2.0.0-beta.3.1",
"prettier-config-atomic": "^2.0.5",
"servor": "^4.0.2",
"shx": "0.3.3"
"shx": "0.3.3",
"strip-json-comments": "^3.1.1"
},
"targets": {
"browser": {
Expand All @@ -64,6 +65,17 @@
"mini",
"min",
"minifier",
"jsonc",
"comment",
"comments",
"remove",
"delete",
"multiline",
"strip",
"trim",
"parse",
"jsonc",
"json5",
"js",
"build",
"build-tool",
Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 10 additions & 4 deletions src/native/cli.d
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,25 @@ import std.getopt : getopt, defaultGetoptPrinter, GetoptResult;
/** Print help */
void printHelp(GetoptResult optResult) @trusted
{
return defaultGetoptPrinter(`Usage: minify json
return defaultGetoptPrinter(`minijson: minify json files with support for comments
minijson --file file1.json --file file2.json
minijson --file file1_with_comment.json --file file2_with_comment.json --comment
minijson --string '{"some_json": "string_here"}'
minijson --string '{"some_json": "string_here"} //comment' --comment
More information at https://github.com/aminya/minijson
`, optResult.options);
}

void main(string[] args) @trusted
{
string[] files;
string jsonString;
bool hasComment = false;

auto optResult = getopt(args, "file", "an array of files to minify", &files, "string",
"a json string to minify", &jsonString);
"a json string to minify", &jsonString, "comment", "a flag to support comments in json", &hasComment);

if (optResult.helpWanted || (!files && !jsonString))
{
Expand All @@ -29,14 +35,14 @@ void main(string[] args) @trusted
// minify the given files
if (files)
{
minifyFiles(files);
minifyFiles(files, hasComment);
}

// minify the given string and print to stdout
if (jsonString)
{
import std : write;

write(minifyString(jsonString));
write(minifyString(jsonString, hasComment));
}
}
38 changes: 24 additions & 14 deletions src/native/lib.d
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ const repeatingBackSlashRegex = ctRegex!(`(\\)*$`);
Params:
jsonString = the json string you want to minify
hasComments = a switch that indicates if the json string has comments. Pass `true` to support parsing comments. Default: `false`.
hasComment = a boolean to support comments in json. Default: `false`.
Return:
the minified json string
*/
string minifyString(string jsonString, bool hasComments = false) @trusted
string minifyString(string jsonString, bool hasComment = false) @trusted
{
auto in_string = false;
auto in_multiline_comment = false;
Expand All @@ -29,7 +29,7 @@ string minifyString(string jsonString, bool hasComments = false) @trusted
size_t from = 0;
auto rightContext = "";

const tokenizer = !hasComments ? tokenizerNoComment : tokenizerWithComment;
const tokenizer = !hasComment ? tokenizerNoComment : tokenizerWithComment;

auto match = jsonString.matchAll(tokenizer);

Expand All @@ -43,7 +43,8 @@ string minifyString(string jsonString, bool hasComments = false) @trusted
const prevFrom = from;
from = jsonString.length - rightContext.length; // lastIndex

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

if (noCommentOrNotInComment)
{
Expand All @@ -65,15 +66,11 @@ string minifyString(string jsonString, bool hasComments = false) @trusted
--from; // include " character in next catch
rightContext = jsonString[from .. $];
}
else if (matchFrontHit.matchFirst(spaceOrBreakRegex).empty())
{
new_str ~= matchFrontHit;
}
}
// comments
if (hasComments && !in_string)
if (hasComment && !in_string)
{
if (!in_multiline_comment && !in_singleline_comment)
if (notInComment)
{
if (matchFrontHit == "/*")
{
Expand All @@ -83,6 +80,10 @@ string minifyString(string jsonString, bool hasComments = false) @trusted
{
in_singleline_comment = true;
}
else if (notSlashAndNoSpaceOrBreak(matchFrontHit))
{
new_str ~= matchFrontHit;
}
}
else if (in_multiline_comment && !in_singleline_comment && matchFrontHit == "*/")
{
Expand All @@ -93,35 +94,44 @@ string minifyString(string jsonString, bool hasComments = false) @trusted
in_singleline_comment = false;
}
}

if (!hasComment && notSlashAndNoSpaceOrBreak(matchFrontHit))
{
new_str ~= matchFrontHit;
}
match.popFront();
}
new_str ~= rightContext;
return new_str.array().join("");
}

bool hasNoSlashOrEvenNumberOfSlashes(string leftContext) @safe
private bool hasNoSlashOrEvenNumberOfSlashes(string leftContext) @safe
{
auto leftContextMatch = leftContext.matchFirst(repeatingBackSlashRegex);
// if not matched the hit length will be 0 (== leftContextMatch.empty())
return leftContextMatch.hit().length % 2 == 0;
}

private bool notSlashAndNoSpaceOrBreak(string matchFrontHit)
{
return matchFrontHit != "\"" && matchFrontHit.matchFirst(spaceOrBreakRegex).empty();
}

/**
Minify the given files in place. It minifies the files in parallel.
Params:
files = the paths to the files.
hasComment = a boolean to support comments in json. Default: `false`.
*/
void minifyFiles(string[] files)
void minifyFiles(string[] files, bool hasComment = false)
{
import std.parallelism : parallel;
import std.file : readText, write;

foreach (ref file; files.parallel())
{
const string jsonString = readText(file);
const minifiedJsonString = minifyString(jsonString);
const minifiedJsonString = minifyString(jsonString, hasComment);
write(file, minifiedJsonString);
}
}
6 changes: 3 additions & 3 deletions src/native/libc.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ extern (C):
Params:
jsonString = the json string you want to minify
hasComments = a switch that indicates if the json string has comments. Pass `true` to support parsing comments. Default: `false`.
hasComment = a boolean to support comments in json. Default: `false`.
Return:
the minified json string
*/
auto minifyString(char* jsonCString, bool hasComments = false)
auto minifyString(char* jsonCString, bool hasComment = false)
{
import std : fromStringz, toStringz;

const string jsonString = fromStringz(jsonCString).idup;
const minifiedString = minifyString(jsonString, hasComments);
const minifiedString = minifyString(jsonString, hasComment);

return toStringz(minifiedString);
}
16 changes: 13 additions & 3 deletions src/node/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { join } from "path"
* Minify all the given JSON files in place. It minifies the files in parallel.
*
* @param files An array of paths to the files
* @param hasComment A boolean to support comments in json. Default `false`.
* @returns {Promise<void>} Returns a void promise that resolves when all the files are minified
* @throws {Promise<string | Error>} The promise is rejected with the reason for failure
*/
export async function minifyFiles(files: string[]): Promise<void> {
export async function minifyFiles(files: string[], hasComment = false): Promise<void> {
const filesNum = files.length
if (filesNum === 0) {
return Promise.resolve()
Expand All @@ -21,19 +22,28 @@ export async function minifyFiles(files: string[]): Promise<void> {
args.splice(iSplice, 0, "--file")
}

if (hasComment) {
args.push("--comment")
}

await spawnMinijson(args)
}

/**
* Minify the given JSON string
*
* @param jsonString The json string you want to minify
* @param hasComment A boolean to support comments in json. Default `false`.
* @returns {Promise<string>} The minified json string
* @throws {Promise<string | Error>} The promise is rejected with the reason for failure
*/
export async function minifyString(jsonString: string): Promise<string> {
export async function minifyString(jsonString: string, hasComment = false): Promise<string> {
const args = ["--string", jsonString]
if (hasComment) {
args.push("--comment")
}
// trim is needed due to using stdout for interop
return (await spawnMinijson(["--string", jsonString])).trim()
return (await spawnMinijson(args)).trim()
}

const exeExtention = process.platform === "win32" ? ".exe" : ""
Expand Down
7 changes: 5 additions & 2 deletions test/fixtures.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ import { join } from "path"
import { dirname as mjsDirname } from "mjs-dirname"
const __dirname = mjsDirname(import.meta.url)

const directory = join(__dirname, "fixtures")
export const jsonFiles = readdirSync(directory).map((file) => join(directory, file))
const standardDirectory = join(__dirname, "fixtures", "standard")
export const standardFiles = readdirSync(standardDirectory).map((file) => join(standardDirectory, file))

const withCommentDirectory = join(__dirname, "fixtures", "with-comment")
export const withCommentFiles = readdirSync(withCommentDirectory).map((file) => join(withCommentDirectory, file))
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit cefda1e

Please sign in to comment.