handlebars-i18n-cli 1.0.5 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,151 +1,43 @@
1
- /*********************************************************************
1
+ /****************************************
2
2
  * i18n-collect.js
3
- *
4
3
  * @author: Florian Walzel
5
- * @date: 2022-10
6
- *
7
- * Usage:
8
- * $ i18n-collect <source> <target> <options...>
9
- *
10
- * valid options:
11
- * --alphabetical || -a
12
- * --dryRun || -dr
13
- * --empty || -e
14
- * --lng=de,fr,es,etc…
15
- * --log || -l
16
- * --separateLngFiles || -sf
17
- * --translFunc=yourCustomFunctionName
18
- * --update || -u*
19
- *
20
- * Copyright (c) 2020 Florian Walzel, MIT LICENSE
21
- *
22
- * Permission is hereby granted, free of charge, to any person
23
- * obtaininga copy of this software and associated documentation
24
- * files (the "Software"), to deal in the Software without restriction,
25
- * including without limitation the rights to use, copy, modify, merge,
26
- * publish, distribute, sublicense, and/or sell copies of the Software,
27
- * and to permit persons to whom the Software is furnished to do so,
28
- * subject to the following conditions:
29
- *
30
- * The above copyright notice and this permission notice shall be
31
- * included in all copies or substantial portions of the Software.
32
- *
33
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
35
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
36
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
37
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
38
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
39
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40
- * THE SOFTWARE.
41
4
  *
42
- *********************************************************************/
43
-
5
+ ****************************************/
44
6
 
45
- 'use strict';
46
7
 
47
8
  /****************************************
48
- * REQUIRE & DEFINE
9
+ * IMPORT
49
10
  ****************************************/
50
11
 
51
- const fs = require('fs')
52
- const { promisify } = require('util')
53
- const readFileAsync = promisify(fs.readFile)
54
- const writeFileAsync = promisify(fs.writeFile)
55
-
12
+ import fst from 'async-file-tried';
13
+ import {glob} from 'glob';
56
14
 
57
15
  /****************************************
58
- * FUNCTIONS
16
+ * PRIVATE FUNCTIONS
59
17
  ****************************************/
60
18
 
61
19
  /**
62
- * Asynchronously read file
63
- *
64
- * @param file
65
- * @returns {Promise<*>}
66
- */
67
- async function readFile (file) {
68
- try {
69
- const data = await readFileAsync(file, 'utf8')
70
- return data
71
- }
72
- catch (e) {
73
- console.log('\x1b[31m%s\x1b[0m', `Error. Could not read ${file}`)
74
- console.error(e)
75
- return false
76
- }
77
- }
78
-
79
- /**
80
- * Asynchronously write file in utf8 encoding
81
- *
82
- * @param file
83
- * @param data
84
- * @returns {Promise<boolean>}
85
- */
86
- async function writeFile(file, data) {
87
- try {
88
- await writeFileAsync(file, data, 'utf8')
89
- return true
90
- }
91
- catch (e) {
92
- console.log('\x1b[31m%s\x1b[0m', `Error. Could not write ${file}`)
93
- console.error(e)
94
- return false
95
- }
96
- }
97
-
98
- /**
99
- * Simple object check.
20
+ * Simple object check
100
21
  *
101
22
  * @param item
102
23
  * @returns {boolean}
103
24
  */
104
25
  function isObject(item) {
105
- return (item && typeof item === 'object' && ! Array.isArray(item))
26
+ return (item && typeof item === 'object' && !Array.isArray(item))
106
27
  }
107
28
 
108
29
  /**
109
30
  * Conditionally removes a substring (file extension)
110
- * from end of given string.
31
+ * from end of given string
111
32
  *
112
33
  * @param str
113
34
  * @param ending
114
35
  * @returns {*|string[]}
115
36
  */
116
- function sanitizeFileExt(str, ending='.json') {
37
+ function sanitizeFileExt(str, ending = '.json') {
117
38
  return str.toLowerCase().endsWith(ending) ? str.slice(0, ending.length * (-1)) : str
118
39
  }
119
40
 
120
- /**
121
- * Log the help information to console
122
- * @returns {boolean}
123
- */
124
- function logHelp() {
125
- console.log('\x1b[2m%s', 'Usage:')
126
- console.log('i18n-collect <source> <target> <options...>')
127
- console.log('')
128
- console.log('<source> path to handlebars.js template file(s), glob pattern allowed')
129
- console.log('<target> json file(s) to write result to')
130
- console.log('')
131
- console.log('<options>')
132
- console.log('--alphabetical or -a will order the keys to the translation strings alphabetically in the json')
133
- console.log(' default: keys in order of appearance as within the template(s)')
134
- console.log('--dryRun or -dr will log the result(s) but not write out json file(s)')
135
- console.log('--empty or -e will create empty value strings for the translations in the json')
136
- console.log(' default: value strings contain current language and key name')
137
- console.log('--lng=en,fr,es,… the languages you want to be generated')
138
- console.log(' default: en')
139
- console.log('--log or -l log final results to console')
140
- console.log('--separateLngFiles write each language in a separate json file')
141
- console.log(' or -sf default: all languages are written as arrays in one json file')
142
- console.log('--translFunc=customName a custom name of the translation function used in the templates')
143
- console.log(' default: __ like handlebars-i18n notation: {{__ keyToTranslate}}')
144
- console.log('--update or -u updates existing json files(s) after changes made in template file(s)')
145
- console.log('\x1b[0m')
146
- return true
147
- }
148
-
149
41
  /**
150
42
  * A collection of functions to extract and handle the
151
43
  * strings between mustaches {{ ... }}
@@ -157,8 +49,8 @@ function logHelp() {
157
49
  * getAllResults: mustacheBetweens.getAllResults}}
158
50
  */
159
51
  const mustacheBetweens = {
160
- results : [ ],
161
- str : '',
52
+ results: [],
53
+ str: '',
162
54
  /**
163
55
  * Returns a substring between an opening and
164
56
  * a closing string sequence in a given string
@@ -167,51 +59,49 @@ const mustacheBetweens = {
167
59
  * @param sub2
168
60
  * @returns {string|boolean}
169
61
  */
170
- getFromBetween : function(sub1, sub2) {
171
- if (this.str.indexOf(sub1) < 0 || this.str.indexOf(sub2) < 0)
172
- return false
62
+ getFromBetween: function (sub1, sub2) {
173
63
  let SP = this.str.indexOf(sub1) + sub1.length,
174
- string1 = this.str.substr(0, SP),
175
- string2 = this.str.substr(SP),
176
- TP = string1.length + string2.indexOf(sub2)
64
+ string1 = this.str.slice(0, SP),
65
+ string2 = this.str.slice(SP),
66
+ TP = string1.length + string2.indexOf(sub2);
67
+
177
68
  return this.str.substring(SP, TP)
178
69
  },
179
70
 
180
71
  /**
181
72
  * Removes a found sequence between an opening and
182
- * a closing string from a a given string
73
+ * a closing string from a given string
183
74
  *
184
75
  * @param sub1
185
76
  * @param sub2
186
77
  * @returns {boolean}
187
78
  */
188
- removeFromBetween : function(sub1, sub2) {
189
- if (this.str.indexOf(sub1) < 0 || this.str.indexOf(sub2) < 0)
190
- return false
191
- let removal = sub1 + this.getFromBetween(sub1, sub2) + sub2
192
- this.str = this.str.replace(removal,'')
79
+ removeFromBetween: function (sub1, sub2) {
80
+ let removal = sub1 + this.getFromBetween(sub1, sub2) + sub2;
81
+ this.str = this.str.replace(removal, '');
82
+ return true
193
83
  },
194
84
 
195
85
  /**
196
86
  * gets all substrings of a string that are between an opening character sequence (sub1)
197
- * and a closing sequence (sub2). The Result is stored in parent results array.
87
+ * and a closing sequence (sub2). The Result is stored in parent results array
198
88
  *
199
89
  * @param sub1
200
90
  * @param sub2
201
91
  */
202
- getAllResults : function(sub1, sub2) {
92
+ getAllResults: function (sub1, sub2) {
203
93
  // first check to see if we do have both substrings
204
94
  if (this.str.indexOf(sub1) < 0 || this.str.indexOf(sub2) < 0)
205
- return false
95
+ return false;
206
96
  // find first result
207
- let result = this.getFromBetween(sub1, sub2)
97
+ let result = this.getFromBetween(sub1, sub2);
208
98
  // replace multiple spaces by a single one, then trim and push it to the results array
209
- this.results.push(result.replace(/ +(?= )/g,'').trim())
99
+ this.results.push(result.replace(/ +(?= )/g, '').trim());
210
100
  // remove the most recently found one from the string
211
- this.removeFromBetween(sub1, sub2)
101
+ this.removeFromBetween(sub1, sub2);
212
102
  // recursion in case there are more substrings
213
103
  if (this.str.indexOf(sub1) > -1 && this.str.indexOf(sub2) > -1)
214
- this.getAllResults(sub1, sub2)
104
+ this.getAllResults(sub1, sub2);
215
105
  },
216
106
 
217
107
  /**
@@ -221,34 +111,34 @@ const mustacheBetweens = {
221
111
  * @param sub2
222
112
  * @returns {*}
223
113
  */
224
- getSorted : function(string, translFuncName, sub1='{{', sub2='}}') {
225
- this.str = string
226
- this.getAllResults(sub1, sub2)
114
+ getSorted: function (string, translFuncName, sub1 = '{{', sub2 = '}}') {
115
+ this.str = string;
116
+ this.getAllResults(sub1, sub2);
227
117
  this.results =
228
118
  this.results.filter(
229
- (el) => {
230
- return typeof el === 'string' && el.startsWith(`${translFuncName} `)
231
- })
232
- .map(
233
119
  (el) => {
234
- // remove leading translation function and explode string by space
235
- let splited = el.replace(`${translFuncName} `, '').split(' ')
236
- // remove quotation marks around key name in element 0 of array
237
- splited[0] = splited[0]
238
- .replace(/"/g, '')
239
- .replace(/'/g, '')
240
- // split remaining string in first element of array by dot (.) to get separate keys of a dot-notated object
241
- let keys = splited[0].split('.')
242
- // transformed is a container object for key
243
- let transformed = { }
244
- transformed.keys = keys
245
- transformed.replacementVars = [ ]
246
- // split following elements by '=' and preserve first element of split
247
- for (let i = 1; i < splited.length; i++)
248
- transformed.replacementVars[i-1] = splited[i].split('=')[0]
249
-
250
- return transformed
251
- })
120
+ return typeof el === 'string' && el.startsWith(`${translFuncName} `)
121
+ })
122
+ .map(
123
+ (el) => {
124
+ // remove leading translation function and explode string by space
125
+ let splited = el.replace(`${translFuncName} `, '').split(' ');
126
+ // remove quotation marks around key name in element 0 of array
127
+ splited[0] = splited[0]
128
+ .replace(/"/g, '')
129
+ .replace(/'/g, '');
130
+ // split remaining string in first element of array by dot (.) to get separate keys of a dot-notated object
131
+ let keys = splited[0].split('.');
132
+ // transformed is a container object for key
133
+ let transformed = {};
134
+ transformed.keys = keys;
135
+ transformed.replacementVars = [];
136
+ // split following elements by '=' and preserve first element of split
137
+ for (let i = 1; i < splited.length; i++)
138
+ transformed.replacementVars[i - 1] = splited[i].split('=')[0];
139
+
140
+ return transformed
141
+ });
252
142
  return this.results
253
143
  }
254
144
  };
@@ -266,22 +156,22 @@ const mustacheBetweens = {
266
156
  * @returns {*}
267
157
  */
268
158
  const arrRmvDuplicateValues = (arr) => {
269
- let seen = { }
159
+ let seen = {};
270
160
  return arr.filter((item) => {
271
161
  return seen.hasOwnProperty(item.keys) ? false : seen[item.keys] = true
272
162
  })
273
- }
163
+ };
274
164
 
275
165
 
276
166
  /**
277
- * Builds a nested object with key-value-pairs from a three-dimensional array.
167
+ * Builds a nested object with key-value-pairs from a three-dimensional array
278
168
  *
279
169
  * @param arr
280
170
  * @param lang
281
171
  * @param empty
282
172
  * @returns {{}}
283
173
  */
284
- function objectify (arr, lang='en', empty = false) {
174
+ function objectify(arr, lang = 'en', empty = false) {
285
175
 
286
176
  /**
287
177
  *
@@ -290,16 +180,14 @@ function objectify (arr, lang='en', empty = false) {
290
180
  * @param arr
291
181
  * @param pos
292
182
  */
293
- function __iterateArr (obj, val, arr, pos) {
294
- if (! obj.hasOwnProperty(arr[pos])) {
183
+ function __iterateArr(obj, val, arr, pos) {
184
+ if (!obj.hasOwnProperty(arr[pos])) {
295
185
  if (pos + 1 < arr.length) {
296
- obj[arr[pos]] = { }
297
- __iterateArr(obj[arr[pos]], val, arr, pos + 1)
298
- }
299
- else
186
+ obj[arr[pos]] = {};
187
+ __iterateArr(obj[arr[pos]], val, arr, pos + 1);
188
+ } else
300
189
  obj[arr[pos]] = val;
301
- }
302
- else if (pos+1 < arr.length)
190
+ } else if (pos + 1 < arr.length)
303
191
  __iterateArr(obj[arr[pos]], val, arr, pos + 1);
304
192
  }
305
193
 
@@ -311,22 +199,22 @@ function objectify (arr, lang='en', empty = false) {
311
199
  * @param textBefore
312
200
  * @returns {string}
313
201
  */
314
- function __listTranslVariables(arr, textBefore= '') {
202
+ function __listTranslVariables(arr, textBefore = '') {
315
203
  let str = '';
316
204
  if (arr.length === 0)
317
205
  return str;
318
206
  for (let elem of arr)
319
207
  str += `{{${elem}}} `;
320
- return textBefore + str.slice(0, -1);
208
+ return textBefore + str.slice(0, -1)
321
209
  }
322
210
 
323
- let obj = { }
211
+ let obj = {}
324
212
  arr.forEach((el) => {
325
213
  let prop;
326
214
  if (empty)
327
215
  prop = __listTranslVariables(el.replacementVars);
328
216
  else
329
- prop = `${lang} of ${el.keys.join('.') + __listTranslVariables(el.replacementVars, ' with variables ')}`
217
+ prop = `${lang} of ${el.keys.join('.') + __listTranslVariables(el.replacementVars, ' with variables ')}`;
330
218
  __iterateArr(obj, prop, el.keys, 0)
331
219
  })
332
220
 
@@ -335,27 +223,29 @@ function objectify (arr, lang='en', empty = false) {
335
223
 
336
224
  /**
337
225
  * Deep merge two objects whereby all properties of
338
- * sources are kept and the target properties are added.
226
+ * sources are kept and the target properties are added
339
227
  *
340
228
  * @param target
341
229
  * @param ...sources
342
230
  */
343
231
  function mergeDeep(target, ...sources) {
344
- if (! sources.length) return target;
232
+ if (!sources.length)
233
+ return target;
234
+
345
235
  const source = sources.shift();
346
236
 
347
237
  if (isObject(target) && isObject(source)) {
348
238
  for (const key in source) {
349
239
  if (isObject(source[key])) {
350
- if (! target[key])
351
- Object.assign(target, {[key]: { }});
240
+ if (!target[key])
241
+ Object.assign(target, {[key]: {}});
352
242
  mergeDeep(target[key], source[key]);
353
243
  } else
354
244
  Object.assign(target, {[key]: source[key]});
355
245
  }
356
246
  }
357
247
 
358
- return mergeDeep(target, ...sources);
248
+ return mergeDeep(target, ...sources)
359
249
  }
360
250
 
361
251
  /**
@@ -370,23 +260,23 @@ function mergeDeep(target, ...sources) {
370
260
  * @returns arr
371
261
  */
372
262
  function deepSort(arr) {
373
- // determine the longest array in keys properties
374
263
  let depth = 0;
264
+ // determine the longest array in keys properties
375
265
  for (let inst of arr)
376
266
  if (inst.keys.length > depth)
377
267
  depth = inst.keys.length;
378
268
 
379
269
  // iterate from longest to shortest and sort
380
- for (let i = depth-1; i>=0; i--) {
270
+ for (let i = depth - 1; i >= 0; i--) {
381
271
  arr = arr.sort((a, b) => {
382
- if ( a.keys[i] !== undefined && b.keys[i] !== undefined )
272
+ if (a.keys[i] !== undefined && b.keys[i] !== undefined)
383
273
  return a.keys[i] > b.keys[i] ? -1 : 1;
384
274
  else
385
- return a.keys[i] !== undefined ? -1 : 1
275
+ return a.keys[i] !== undefined ? -1 : 1;
386
276
  });
387
277
  }
388
278
  // we get a descending array, so we invert it
389
- return arr.reverse();
279
+ return arr.reverse()
390
280
  }
391
281
 
392
282
 
@@ -394,130 +284,106 @@ function deepSort(arr) {
394
284
  * EXPORT PUBLIC INTERFACE
395
285
  ****************************************/
396
286
 
397
- exports.cli = async (argv) => {
287
+ async function i18nCollect(source, target, options) {
398
288
 
399
- // take in cli arguments from process argv
400
- let args = [ ]
401
- for (let i = 2; i < argv.length; i++)
402
- args.push(argv[i])
289
+ if (typeof source !== 'string')
290
+ throw new Error(`First argument SOURCE must be of type string. Please specify a valid SOURCE.`);
403
291
 
404
- // validation: error when no argument was given
405
- if (args.length === 0)
406
- throw new Error(`No arguments given. Please specify SOURCE and TARGET.
407
- Call argument --help for further Information.`)
292
+ if (typeof target !== 'string')
293
+ throw new Error(`Second argument TARGET must be of type string. Please specify a valid TARGET.`);
408
294
 
409
- // only one argument was given
410
- if (args.length === 1) {
411
- // answer to argument 'help'
412
- if (['help', '--help', '-h'].includes(args[0]))
413
- return logHelp()
414
- // error the missing second argument
415
- else
416
- throw new Error(`Missing second argument for TARGET.
417
- Call argument --help for further Information.`)
418
- }
295
+ options = options || {};
296
+
297
+ if (typeof options !== 'object' && !Array.isArray(options) && Object.prototype.toString.call(options))
298
+ throw new Error(`Third argument OPTIONS must be of type object. Please specify a valid OPTION.`);
419
299
 
420
300
  // register vars
421
- let hndlbrKeys = [ ],
422
- sources,
423
- targetFileName,
424
- targetFileNameSeparated,
425
- translationFuncName,
426
- pos = -1,
427
- languages,
301
+ let hndlbrKeys = [],
428
302
  translObj,
429
303
  outputObj;
430
304
 
431
- // create an array "sources" by filtering out all options from args
432
- sources = args.filter(
433
- (el) => ! (el === 'help' || el.startsWith('--') || el.startsWith('-'))
434
- )
305
+ // get glob from source
306
+ const templateFiles = await glob(source, {ignore: 'node_modules/**'})
435
307
 
436
- // then removing last element as being the <target>
437
- targetFileName = sources.pop()
438
-
439
- // check for argument '--translFunc=someName' => a custom function name was given
440
- args.forEach((el, key) => {
441
- if (el.startsWith('--translFunc=')) return pos = key
442
- })
443
- translationFuncName = (pos >= 0) ? args[pos].split('=')[1] : '__'
444
-
445
- // read in file(s) and join contents keeping only unique key names
446
- for (let file of sources) {
447
- console.log(`Now processing ${file}`)
448
- let content = await readFile(file)
308
+ // extract translations keys from file(s) in array hndlbrKeys
309
+ for (let file of templateFiles) {
310
+ console.log(`Now processing ${file}`);
311
+ let [content, err] = await fst.readFile(file, 'utf8');
312
+ if (err)
313
+ throw (err);
314
+ //console.log(content)
449
315
  hndlbrKeys = hndlbrKeys.concat(
450
- mustacheBetweens.getSorted(content, translationFuncName)
451
- )
316
+ mustacheBetweens.getSorted(content, options.translFunc || '__')
317
+ );
452
318
  }
453
319
 
454
320
  // break if no strings for translation where found
455
321
  if (hndlbrKeys.length === 0)
456
- return console.log('No strings for translation found, no files written.')
322
+ return console.log('No strings for translation found, no files written.');
457
323
 
458
324
  // remove all duplicate value entries in position 'keys' of array hndlbrKeys
459
- hndlbrKeys = arrRmvDuplicateValues(hndlbrKeys)
325
+ hndlbrKeys = arrRmvDuplicateValues(hndlbrKeys);
460
326
 
461
- // evaluate argument '--alphabetical' for sorting
462
- if (args.includes('--alphabetical') || args.includes('-a'))
327
+ // evaluate argument 'alphabetical' for sorting
328
+ if (options.alphabetical)
463
329
  hndlbrKeys = deepSort(hndlbrKeys)
464
330
 
465
- // form an array of languages from argument '--lng='
466
- languages = args.filter((el) => {
467
- return el.startsWith('--lng=')
468
- }).map((el) => {
469
- return el.split('=')[1].split(',')
470
- })[0]
471
-
472
- // if no language parameter is passed set 'en' as default language
473
- if (typeof languages === 'undefined') languages = ['en']
331
+ // form an array of languages from argument 'lng
332
+ const languages = (Array.isArray(options.lng) && options.lng.length > 0)
333
+ ? options.lng
334
+ : ['en'];
474
335
 
475
336
 
476
337
  // WRITE TO ONE FILE PER LANGUAGE
477
338
  // ------------------------------------------------
478
339
 
479
- // evaluate argument '--separateLngFiles' to output each language in a separate file
480
- if (args.includes('--separateLngFiles') || args.includes('-sf')) {
340
+ // evaluate argument 'separateLngFiles' to output each language in a separate file
341
+ if (options.separateLngFiles) {
481
342
 
482
343
  // if user entered argument for target ending with .json, remove it
483
- targetFileName = sanitizeFileExt(targetFileName)
344
+ let targetFileName = sanitizeFileExt(target)
484
345
 
485
346
  for (let lng of languages) {
347
+
486
348
  // join file name per language such as myfile.de.json, myfile.en.json, ...
487
- targetFileNameSeparated =
488
- (targetFileName.startsWith('/') ? targetFileName.substring(1) : targetFileName) + '.' + lng + '.json'
349
+ let targetFileNameSeparated = (targetFileName.startsWith('/')
350
+ ? targetFileName.substring(1)
351
+ : targetFileName)
352
+ + '.' + lng + '.json';
489
353
 
490
354
  // create output object per language and add keys in nested object form
491
- outputObj = { }
492
- outputObj[lng] = objectify(hndlbrKeys, lng, args.includes('--empty') || args.includes('-e'))
355
+ outputObj = {}
356
+ outputObj[lng] = objectify(hndlbrKeys, lng, options.empty)
493
357
 
494
- // if argument '--update' was given, existing files per language are read in, parsed,
358
+ // if option 'update' was given, existing files per language are read in, parsed,
495
359
  // and the new translation Object is merged onto the existing translation
496
- if (args.includes('--update') || args.includes('-u')) {
497
- let existingTransl = await readFile(targetFileNameSeparated)
498
- existingTransl = JSON.parse(existingTransl)
499
- outputObj = mergeDeep(outputObj[lng], existingTransl)
360
+ if (options.update) {
361
+ let [res, err] = await fst.readJson(targetFileNameSeparated);
362
+ if (err)
363
+ throw (err);
364
+ outputObj = mergeDeep(outputObj[lng], res)
500
365
  }
501
366
 
502
367
  // convert output object to json with linebreaks and indenting of 2 spaces
503
368
  const fileOutputJson = JSON.stringify(outputObj, null, 2)
504
369
 
505
370
  // log output per language
506
- if (args.includes('--log') || args.includes('-l')
507
- || args.includes('--dryRun') || args.includes('-dr'))
371
+ if (options.log || options.dryRun)
508
372
  console.log(fileOutputJson)
509
373
 
374
+ let [write, e] = [undefined, undefined];
375
+
510
376
  // write files only if no --dryRun option was set
511
- if (! args.includes('--dryRun') && ! args.includes('-dr'))
377
+ if (!options.dryRun)
512
378
  // write out the json to target file per language
513
- if (await writeFile(targetFileNameSeparated, fileOutputJson))
514
- console.log('\x1b[34m%s\x1b[0m', `Wrote language keys for '${lng}' to ${targetFileNameSeparated}`)
379
+ [write, e] = await fst.writeFile(targetFileNameSeparated, fileOutputJson);
380
+ if (e)
381
+ throw (e);
382
+ console.log('\x1b[34m%s\x1b[0m', `Wrote language keys for '${lng}' to ${targetFileNameSeparated}`)
515
383
  }
516
384
 
517
- return (args.includes('--dryRun') || args.includes('-dr')) ?
518
- console.log('\x1b[36m%s\x1b[0m', 'This was a dry run. No files witten.')
519
- :
520
- console.log('\x1b[32m%s\x1b[0m', `You’re good. All Done.`)
385
+ if (options.dryRun)
386
+ console.log('\x1b[36m%s\x1b[0m', 'This was a dry run. No files witten.');
521
387
  }
522
388
 
523
389
  // WRITE SINGLE FILE CONTAINING ALL LANGUAGES
@@ -525,37 +391,44 @@ exports.cli = async (argv) => {
525
391
  else {
526
392
  // create object to hold the translations and create a key for every language
527
393
  // add all handlebars translation keys to each language key as nested objects
528
- translObj = {translations: { }}
394
+ translObj = {translations: {}}
395
+
529
396
  languages.forEach((lng) => {
530
- translObj.translations[lng] = objectify(hndlbrKeys, lng, args.includes('--empty') || args.includes('-e'))
397
+ translObj.translations[lng] = objectify(hndlbrKeys, lng, options.empty)
531
398
  })
532
399
 
533
- // if argument '--update' was given, an existing file is read in, parsed,
400
+ // if argument 'update' was given, an existing file is read in, parsed,
534
401
  // and the new translation Object is merged onto the existing translations
535
- if (args.includes('--update') || args.includes('-u')) {
536
- let existingTransl = await readFile(targetFileName)
537
- existingTransl = JSON.parse(existingTransl)
538
- outputObj = mergeDeep(translObj, existingTransl)
539
- }
540
- else
541
- outputObj = translObj
402
+ if (options.update) {
403
+ let [res, err] = await fst.readJson(target);
404
+ if (err)
405
+ throw (err);
406
+ outputObj = mergeDeep(translObj, res)
407
+ } else
408
+ outputObj = translObj;
542
409
 
543
410
  // convert output object to json with linebreaks and indenting of 2 spaces
544
- const fileOutputJson = JSON.stringify(outputObj,null, 2)
411
+ const fileOutputJson = JSON.stringify(outputObj, null, 2)
545
412
 
546
- // log the final object to console if option '--log' or '--dryRun' was set
547
- if (args.includes('--log') || args.includes('-l')
548
- || args.includes('--dryRun') || args.includes('-dr'))
549
- console.log(fileOutputJson)
413
+ // log the final object to console if option 'log' or 'dryRun' was set
414
+ if (options.log || options.dryRun)
415
+ console.log(fileOutputJson);
550
416
 
551
- // exit if option '--dryRun' was set
552
- if (args.includes('--dryRun') || args.includes('-dr')) {
417
+ // exit if option 'dryRun' was set
418
+ if (options.dryRun) {
553
419
  console.log('\x1b[36m%s\x1b[0m', 'This was a dry run. No file witten.')
554
420
  process.exit(0)
555
421
  }
556
422
 
557
423
  // write out the json to target file
558
- if (await writeFile(targetFileName, fileOutputJson))
559
- return console.log('\x1b[32m%s\x1b[0m', `Done and Ready! Your output was written to ${targetFileName}`)
424
+ let [res, err] = await fst.writeFile(target, fileOutputJson);
425
+ if (err)
426
+ throw (err);
560
427
  }
561
- }
428
+ return true;
429
+ }
430
+
431
+ // Export the function
432
+ export {
433
+ i18nCollect
434
+ }