@@ -12,12 +12,66 @@ const tokenizerNoComment = ctRegex!(`[\n\r"[]]`, "g");
12
12
13
13
Params:
14
14
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 `.
16
16
17
17
Return:
18
18
the minified json string
19
19
*/
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
21
75
{
22
76
auto in_string = false ;
23
77
auto in_multiline_comment = false ;
@@ -26,9 +80,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
26
80
size_t from = 0 ;
27
81
auto rightContext = " " ;
28
82
29
- const tokenizer = ! hasComment ? tokenizerNoComment : tokenizerWithComment;
30
-
31
- auto match = jsonString.matchAll(tokenizer);
83
+ auto match = jsonString.matchAll(tokenizerWithComment);
32
84
33
85
while (! match.empty())
34
86
{
@@ -40,10 +92,9 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
40
92
const prevFrom = from;
41
93
from = jsonString.length - rightContext.length; // lastIndex
42
94
43
- const notInComment = (! in_multiline_comment && ! in_singleline_comment);
44
- const noCommentOrNotInComment = ! hasComment || notInComment;
95
+ const notInComment = ! in_multiline_comment && ! in_singleline_comment;
45
96
46
- if (noCommentOrNotInComment )
97
+ if (notInComment )
47
98
{
48
99
auto leftContextSubstr = match.pre()[prevFrom .. $];
49
100
const noLeftContext = leftContextSubstr.length == 0 ;
@@ -67,7 +118,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
67
118
}
68
119
}
69
120
// comments
70
- if (hasComment && ! in_string)
121
+ if (! in_string)
71
122
{
72
123
if (notInComment)
73
124
{
@@ -93,7 +144,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted
93
144
in_singleline_comment = false ;
94
145
}
95
146
}
96
- else if (! hasComment && notSlashAndNoSpaceOrBreak(matchFrontHit))
147
+ else if (notSlashAndNoSpaceOrBreak(matchFrontHit))
97
148
{
98
149
result ~= matchFrontHit;
99
150
}
@@ -176,7 +227,7 @@ private bool hasNoSpace(const ref string str) @trusted
176
227
Return:
177
228
the minified json strings
178
229
*/
179
- string [] minifyStrings (in string [] jsonStrings, in bool hasComment = false ) @trusted
230
+ string [] minifyStrings (in string [] jsonStrings, in bool hasComment = true ) @trusted
180
231
{
181
232
import std.algorithm : map;
182
233
import std.array : array;
@@ -191,7 +242,7 @@ string[] minifyStrings(in string[] jsonStrings, in bool hasComment = false) @tru
191
242
paths = the paths to the files. It could be glob patterns.
192
243
hasComment = a boolean to support comments in json. Default: `false`.
193
244
*/
194
- void minifyFiles (in string [] paths, in bool hasComment = false ) @trusted
245
+ void minifyFiles (in string [] paths, in bool hasComment = true ) @trusted
195
246
{
196
247
import std.parallelism : parallel;
197
248
import std.algorithm ;
@@ -202,33 +253,70 @@ void minifyFiles(in string[] paths, in bool hasComment = false) @trusted
202
253
import std.stdio : writeln;
203
254
204
255
// 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)
208
260
{
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" );
221
266
}
222
267
else
223
268
{
224
- return glob ( path);
269
+ throw new Exception ( " The given path is not a file or a directory: " ~ path);
225
270
}
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
+ }
229
288
230
289
foreach (file; files.parallel())
231
290
{
232
291
write(file, minifyString (readText(file), hasComment));
233
292
}
234
293
}
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
+ }
0 commit comments