lang-json 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. package/babel.config.js +8 -0
  2. package/coverage/clover.xml +6 -0
  3. package/coverage/coverage-final.json +1 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +101 -0
  8. package/coverage/lcov-report/prettify.css +1 -0
  9. package/coverage/lcov-report/prettify.js +2 -0
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +196 -0
  12. package/dist/esm/src/index.d.ts +18 -0
  13. package/dist/esm/src/index.js +415 -0
  14. package/dist/esm/src/index.js.map +1 -0
  15. package/dist/esm/src/modules/is-this/index.d.ts +136 -0
  16. package/dist/esm/src/modules/is-this/index.js +484 -0
  17. package/dist/esm/src/modules/is-this/index.js.map +1 -0
  18. package/dist/esm/tests/helpers.test.d.ts +1 -0
  19. package/dist/esm/tests/helpers.test.js +220 -0
  20. package/dist/esm/tests/helpers.test.js.map +1 -0
  21. package/dist/esm/tests/index.test.d.ts +1 -0
  22. package/dist/esm/tests/index.test.js +537 -0
  23. package/dist/esm/tests/index.test.js.map +1 -0
  24. package/jest.config.ts +212 -0
  25. package/package.json +19 -9
  26. package/src/index.ts +450 -296
  27. package/src/modules/is-this/index.ts +682 -0
  28. package/tests/helpers.test.ts +259 -0
  29. package/tests/index.test.ts +681 -0
  30. package/tsconfig.json +15 -16
  31. package/dist/esm/dump.js +0 -2
  32. package/dist/esm/dump.js.map +0 -1
  33. package/dist/esm/example.d.ts +0 -13
  34. package/dist/esm/example.js +0 -93
  35. package/dist/esm/example.js.map +0 -1
  36. package/dist/esm/index.d.ts +0 -36
  37. package/dist/esm/index.js +0 -326
  38. package/dist/esm/index.js.map +0 -1
  39. package/src/example.ts +0 -116
  40. /package/{dist/esm/dump.d.ts → coverage/lcov.info} +0 -0
package/src/index.ts CHANGED
@@ -1,44 +1,228 @@
1
- type HelperFunction = (context: any, data: any, innerTemplate: any) => any;
2
- export class JsonTemplateEngine {
3
- helpers: Record<string, HelperFunction> = {};
1
+ // import isThis from "@devanshdeveloper/is-this";
2
+ import isThis from "./modules/is-this/index";
3
+
4
+ type HelperFunction = (args: any, data: any, innerTemplate: any) => any;
5
+
6
+ class MemoryAddresses {
7
+ private storage: { [key: string]: any } = {};
8
+
9
+ // Helper function to generate a random string key
10
+ private generateKey(length: number = 4): string {
11
+ const chars =
12
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
13
+ let key = "";
14
+ for (let i = 0; i < length; i++) {
15
+ key += chars.charAt(Math.floor(Math.random() * chars.length));
16
+ }
17
+ return key;
18
+ }
19
+
20
+ // Store data with a random key
21
+ storeData(data: any): string {
22
+ const key = this.generateKey();
23
+ this.storage[key] = data;
24
+ return key;
25
+ }
26
+
27
+ getData(key: string): any | null {
28
+ return this.storage[key] !== undefined ? this.storage[key] : null;
29
+ }
30
+
31
+ removeData(key: string): void {
32
+ if (this.storage[key]) {
33
+ delete this.storage[key];
34
+ }
35
+ }
36
+ }
4
37
 
38
+ export default class LangJSON {
39
+ helpers: Record<string, HelperFunction> = {};
40
+ private memory: { [key: string]: any } = {};
5
41
  constructor() {
6
- this.registerHelper("each", this.eachHelper.bind(this));
7
- this.registerHelper("loop", this.loopHelper.bind(this));
8
- this.registerHelper("if", this.ifHelper.bind(this));
9
- this.registerHelper("with", this.withHelper.bind(this));
10
- this.registerHelper("set", this.setHelper.bind(this));
11
- this.registerHelper("unless", this.unlessHelper.bind(this));
12
- this.registerHelper("log", this.logHelper.bind(this));
13
- this.registerHelper("concat", this.concatHelper.bind(this));
14
- this.registerHelper("length", this.lengthHelper.bind(this));
15
- this.registerHelper("math", this.mathHelper.bind(this));
16
- this.registerHelper("var", this.varHelper.bind(this));
17
- this.registerHelper("compare", this.compareHelper.bind(this));
18
- this.registerHelper("upperCase", this.upperCaseHelper.bind(this));
19
- this.registerHelper("lowerCase", this.lowerCaseHelper.bind(this));
20
- this.registerHelper("jsonStringify", this.jsonStringifyHelper.bind(this));
21
- this.registerHelper("jsonParse", this.jsonParseHelper.bind(this));
22
- this.registerHelper("date", this.dateHelper.bind(this));
23
- this.registerHelper("repeat", this.repeatHelper.bind(this));
24
- this.registerHelper("slice", this.sliceHelper.bind(this));
25
- this.registerHelper("join", this.joinHelper.bind(this));
26
- this.registerHelper("find", this.findHelper.bind(this));
27
- this.registerHelper("map", this.mapHelper.bind(this));
28
- this.registerHelper("unique", this.uniqueHelper.bind(this));
29
- this.registerHelper("random", this.randomHelper.bind(this));
30
- this.registerHelper("reverse", this.reverseHelper.bind(this));
31
- this.registerHelper("exists", this.existsHelper.bind(this));
42
+ // Registering Default Helpers
43
+
44
+ this.registerHelpers({
45
+ var: ([string]: any, data: any): any => {
46
+ return this.lookup(string, data);
47
+ },
48
+
49
+ // manupilating helpers
50
+ each: ([arr], data, innerTemplate): any => {
51
+ const result = [];
52
+ for (let i = 0; i < arr.length; i++) {
53
+ const element = arr[i];
54
+ result.push(
55
+ this.applyTemplate(innerTemplate, {
56
+ ...data,
57
+ item: element,
58
+ index: i,
59
+ })
60
+ );
61
+ }
62
+ return result;
63
+ },
64
+
65
+ loop: (number, data, innerTemplate): any => {
66
+ const result = [];
67
+ for (let i = 0; i < number; i++) {
68
+ result.push(
69
+ this.applyTemplate(innerTemplate, {
70
+ ...data,
71
+ index: i,
72
+ })
73
+ );
74
+ }
75
+ return result;
76
+ },
77
+
78
+ // String Helpers
79
+ uppercase: ([str]): any => str.toUpperCase(),
80
+ lowercase: ([str]): any => str.toLowerCase(),
81
+ capitalize: ([str]): any => str.charAt(0).toUpperCase() + str.slice(1),
82
+ trim: ([str]): any => str.trim(),
83
+ substring: ([str, start, length]): any =>
84
+ str.substring(start, start + length),
85
+ concat: ([...args]: any[]): any => args.join(""),
86
+ replace: ([str, search, replacement]): any =>
87
+ str.replace(new RegExp(search, "g"), replacement),
88
+ split: ([str, separator]): any => str.split(separator),
89
+ join: ([arr, separator]): any => arr.join(separator),
90
+ contains: ([str, substring]): any => str.includes(substring),
91
+ length: ([str]): any => str.length,
92
+ startsWith: ([str, prefix]): any => str.startsWith(prefix),
93
+ endsWith: ([str, suffix]): any => str.endsWith(suffix),
94
+ reverseString: ([str]): any => str.split("").reverse().join(""),
95
+ isEmpty: ([str]): any => !str || str.length === 0,
96
+ formatPhoneNumber: ([number]): any =>
97
+ number.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3"),
98
+ toTitleCase: ([str]): any =>
99
+ str.toLowerCase().replace(/\b\w/g, (char: any) => char.toUpperCase()),
100
+ repeat: ([str, times]) => str.repeat(times),
101
+ // Mathematical Helpers
102
+ add: ([a, b]): any => a + b,
103
+ subtract: ([a, b]): any => a - b,
104
+ multiply: ([a, b]): any => a * b,
105
+ divide: ([a, b]): any => a / b,
106
+ modulo: ([a, b]): any => a % b,
107
+ max: ([...args]: any[]): any => Math.max(...args),
108
+ min: ([...args]: any[]): any => Math.min(...args),
109
+ round: ([num]): any => Math.round(num),
110
+ floor: ([num]): any => Math.floor(num),
111
+ ceil: ([num]): any => Math.ceil(num),
112
+
113
+ // Logical Helpers
114
+ if: ([condition, trueValue, falseValue]): any =>
115
+ condition ? trueValue : falseValue,
116
+ and: ([...conditions]: any[]): any => conditions.every(Boolean),
117
+ or: ([...conditions]: any[]): any => conditions.some(Boolean),
118
+ not: ([condition]: any): any => !condition,
119
+
120
+ // Date and Time Helpers
121
+ getCurrentDate: (): any => new Date().toISOString().split("T")[0],
122
+ getCurrentTime: (): any => new Date().toLocaleTimeString(),
123
+
124
+ // Array Helpers
125
+ arrayLength: ([arr]): any => arr.length,
126
+ arrayIncludes: ([arr, item]): any => arr.includes(item),
127
+ arrayJoin: ([arr, separator]): any => arr.join(separator),
128
+ arrayMap: ([arr, fn]): any => arr.map(fn),
129
+ arrayFilter: ([arr, fn]): any => arr.filter(fn),
130
+ arrayReduce: ([arr, fn, initial]): any => arr.reduce(fn, initial),
131
+ uniqueArray: ([arr]): any => [...new Set(arr)],
132
+ flattenArray: ([arr]): any => arr.flat(),
133
+ arraySort: ([arr, compareFn]): any => arr.sort(compareFn),
134
+ arrayFind: ([arr, fn]): any => arr.find(fn),
135
+ arrayEvery: ([arr, fn]): any => arr.every(fn),
136
+ arraySome: ([arr, fn]): any => arr.some(fn),
137
+
138
+ // Object Helpers
139
+ objectKeys: ([obj]): any => Object.keys(obj),
140
+ objectValues: ([obj]): any => Object.values(obj),
141
+ objectEntries: ([obj]): any => Object.entries(obj),
142
+ objectHasKey: ([obj, key]): any =>
143
+ Object.prototype.hasOwnProperty.call(obj, key),
144
+ mergeObjects: ([obj1, obj2]): any => ({ ...obj1, ...obj2 }),
145
+ deepClone: ([obj]): any => JSON.parse(JSON.stringify(obj)),
146
+ objectFreeze: ([obj]): any => Object.freeze(obj),
147
+ objectMergeDeep: ([obj1, obj2]): any => {
148
+ function mergeDeep(obj1: any, obj2: any): any {
149
+ const result = { ...obj1 };
150
+ for (const key in obj2) {
151
+ if (obj2[key] instanceof Object && !Array.isArray(obj2[key])) {
152
+ result[key] = mergeDeep(obj1[key], obj2[key]); // Use the named function 'mergeDeep'
153
+ } else {
154
+ result[key] = obj2[key];
155
+ }
156
+ }
157
+ return result;
158
+ }
159
+ return mergeDeep(obj1, obj2);
160
+ },
161
+
162
+ // Random Helpers
163
+ randomNumber: ([min, max]): any =>
164
+ Math.floor(Math.random() * (max - min + 1)) + min,
165
+ randomElement: ([arr]): any =>
166
+ arr[Math.floor(Math.random() * arr.length)],
167
+
168
+ // Formatting Helpers
169
+ currency: ([amount, currencySymbol]): any =>
170
+ `${currencySymbol}${amount.toFixed(2)}`,
171
+ percent: ([num]): any => `${(num * 100).toFixed(2)}%`,
172
+
173
+ // Miscellaneous Helpers
174
+ jsonStringify: ([obj]): any => JSON.stringify(obj),
175
+ jsonParse: ([str]): any => JSON.parse(str),
176
+ delay: ([ms]): any => new Promise((resolve) => setTimeout(resolve, ms)),
177
+ noop: (): any => {}, // No operation function
178
+ deepEqual: ([obj1, obj2]): any =>
179
+ JSON.stringify(obj1) === JSON.stringify(obj2), // Deep equality check
180
+
181
+ // Additional String Manipulation Helpers
182
+ snakeToCamel: ([str]): any =>
183
+ str.replace(/([-_]\w)/g, (g: any) => g[1].toUpperCase()),
184
+ camelToSnake: ([str]): any =>
185
+ str.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase(),
186
+
187
+ // Additional Array Helpers
188
+ chunkArray: ([arr, size]): any =>
189
+ Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
190
+ arr.slice(i * size, i * size + size)
191
+ ),
192
+ shuffleArray: ([arr]): any => arr.sort(() => Math.random() - 0.5),
193
+ removeDuplicates: ([arr]): any =>
194
+ arr.filter((item: any, index: any) => arr.indexOf(item) === index),
195
+
196
+ // Additional Object Helpers
197
+ objectMap: ([obj, fn]): any =>
198
+ Object.fromEntries(
199
+ Object.entries(obj).map(([key, value]) => [key, fn(value)])
200
+ ),
201
+ });
202
+
203
+ this.memory = new MemoryAddresses();
32
204
  }
33
205
 
34
- // Register a custom helper
206
+ // Managing Helpers
35
207
  registerHelper(name: string, fn: HelperFunction) {
36
208
  this.helpers[name] = fn;
37
209
  }
38
210
 
39
- applyTemplate(template: any, data: any): any {
211
+ registerHelpers(helpers: { [key: string]: HelperFunction }) {
212
+ for (const [name, fn] of Object.entries(helpers)) {
213
+ this.registerHelper(name, fn);
214
+ }
215
+ }
216
+
217
+ getHelper(name: string): HelperFunction | undefined {
218
+ return this.lookup(name, this.helpers);
219
+ }
220
+
221
+ applyTemplate(template: any, data: any, innerTemplate?: any): any {
40
222
  if (typeof template === "string") {
41
- return this.proccessHelper(template, data);
223
+ const stringResult = this.processHelper(template, data);
224
+ // console.log({ stringResult });
225
+ return stringResult;
42
226
  } else if (Array.isArray(template)) {
43
227
  return template.map((item, index) =>
44
228
  this.applyTemplate(item, { ...data, currentItem: item, index })
@@ -49,22 +233,17 @@ export class JsonTemplateEngine {
49
233
  if (Object.prototype.hasOwnProperty.call(template, key)) {
50
234
  const value = template[key];
51
235
 
52
- const currentKeyResult = this.proccessHelper(key, data, value);
236
+ const currentKeyResult = this.processHelper(key, data, value);
237
+ // console.log({ currentKeyResult });
53
238
 
54
- if (typeof currentKeyResult === "string") {
239
+ if (isThis.isArrayOrObject(currentKeyResult)) {
240
+ result = this.applyTemplate(currentKeyResult, data);
241
+ } else {
55
242
  if (value) {
56
- result[currentKeyResult] = this.applyTemplate(value, data);
243
+ result[`${currentKeyResult}`] = this.applyTemplate(value, data);
57
244
  } else {
58
245
  result = currentKeyResult;
59
246
  }
60
- } else if (Array.isArray(currentKeyResult)) {
61
- result = this.applyTemplate(currentKeyResult, data);
62
- } else if (typeof currentKeyResult === "object") {
63
- result = this.applyTemplate(currentKeyResult, data);
64
- } else if (typeof currentKeyResult === "boolean") {
65
- if (currentKeyResult) {
66
- result = this.applyTemplate(value, data);
67
- }
68
247
  }
69
248
  }
70
249
  }
@@ -73,286 +252,261 @@ export class JsonTemplateEngine {
73
252
  return template;
74
253
  }
75
254
 
76
- lookup(varPath: string, data: any): any {
255
+ lookup(varPath: string | number, data: any): any {
77
256
  if (typeof varPath !== "string") return varPath;
78
257
  let normalizedPath = varPath.replace(/\[(\d+)\]/g, ".$1");
79
258
  if (normalizedPath.startsWith(".")) {
80
259
  normalizedPath = normalizedPath.slice(1);
81
260
  }
82
- const returnValue = normalizedPath.split(".").reduce((acc, part) => {
261
+
262
+ let path = normalizedPath.split(".");
263
+ const returnValue = path.reduce((acc, part) => {
83
264
  return acc?.[part];
84
265
  }, data);
85
- if (returnValue) {
86
- return returnValue;
87
- } else {
88
- return varPath;
89
- }
266
+ return returnValue;
90
267
  }
91
268
 
92
- proccessHelper(string: string, data: any, innerTemplate?: any) {
93
- const helperMatch = string.match(/{{#(\w+)\s+(.*?)}}/);
94
- if (helperMatch) {
95
- const [helperString, helperName, helperArg] = helperMatch;
96
- const helperFn = this.helpers[helperName];
97
- if (helperFn) {
98
- // Check if there's an inner expression (e.g., compare user.age 30)
99
- const innerExpressionMatch = helperArg.match(/\((.*?)\)/);
100
- if (innerExpressionMatch) {
101
- const innerExpression = innerExpressionMatch[1]; // e.g., 'compare user.age 30'
102
- const [innerHelperName, ...innerArgs] = innerExpression.split(" "); // ['compare', 'user.age', '30']
103
- const innerHelperFn = this.helpers[innerHelperName];
104
-
105
- if (innerHelperFn) {
106
- // Look up values for arguments, treat them as variables
107
- const evaluatedArgs = innerArgs.map(
108
- (arg) => this.lookup(arg, data) || arg
109
- );
110
- console.log(evaluatedArgs);
111
-
112
- // Ensure innerHelperFn can handle an array directly or accept rest parameters
113
- const innerResult = innerHelperFn(
114
- evaluatedArgs.join(" "),
115
- data,
116
- innerTemplate
117
- );
118
-
119
- // Apply the outer helper (e.g., ifHelper) with the result of the inner expression
120
- const outerResult = helperFn(innerResult, data, innerTemplate);
121
-
122
- if (
123
- typeof outerResult === "string" ||
124
- typeof outerResult === "number"
125
- ) {
126
- return string.replace(helperString, `${outerResult}`);
127
- } else {
128
- return outerResult;
129
- }
130
- }
131
- } else {
132
- // Handle the normal case where there is no inner expression
133
- const result = helperFn(helperArg, data, innerTemplate);
134
- if (typeof result === "string" || typeof result === "number") {
135
- return string.replace(helperString, `${result}`);
136
- } else {
137
- return result;
138
- }
139
- }
140
- }
141
- } else {
142
- return string;
269
+ sanitizeArg(arg: any, helperName: any, data: any) {
270
+ if (helperName === "var") return arg;
271
+ if (arg.match(/^(['"])([\s\S]*?)\1$/g)) {
272
+ return arg.replace(
273
+ /^(['"])([\s\S]*?)\1$/g,
274
+ (_: any, __: any, innerText: any) => innerText
275
+ );
143
276
  }
144
- }
145
-
146
- // helpers
147
- logHelper(varPath: string, data: any, innerTemplate: any) {
148
- const value = this.lookup(varPath, data);
149
- console.log(value);
150
-
151
- return innerTemplate;
152
- }
153
-
154
- varHelper(string: any, data: any): any {
155
- return this.lookup(string, data);
156
- }
157
- lengthHelper(varPath: string, data: any) {
158
- const value = this.lookup(varPath, data);
159
- return Array.isArray(value) || typeof value === "string" ? value.length : 0;
160
- }
161
-
162
- eachHelper(string: any, data: any, innerTemplate: any) {
163
- const array = this.lookup(string, data);
164
- if (!Array.isArray(array)) {
165
- throw new Error(`${data.helperArg} is not an array`);
277
+ if (isThis.isNumberString(arg)) {
278
+ return Number(arg);
166
279
  }
280
+ if (isThis.isBooleanString(arg)) {
281
+ return arg === "true";
282
+ }
283
+ const lookUpValue = this.lookup(arg, data);
284
+ const memoryValue = this.memory.getData(arg);
285
+ // console.log({ lookUpValue, memoryValue, arg });
167
286
 
168
- return array.map((item: any) =>
169
- this.applyTemplate(innerTemplate, { ...data, item })
170
- );
171
- }
172
- loopHelper(string: any, data: any, innerTemplate: any) {
173
- const count = this.lookup(string, data);
174
-
175
- return Array(+count)
176
- .fill(null)
177
- .map((item: any) => this.applyTemplate(innerTemplate, { ...data, item }));
178
- }
179
-
180
- ifHelper(condition: string, data: any, innerTemplate: any) {
181
- console.log(condition, data);
182
-
183
- return !!this.lookup(condition, data);
184
- }
185
- unlessHelper(condition: string, data: any, innerTemplate: any) {
186
- return !!this.lookup(condition, data);
287
+ if (!isThis.isNullOrUndefined(memoryValue)) {
288
+ return memoryValue;
289
+ }
290
+ if (!isThis.isUndefined(lookUpValue)) {
291
+ return lookUpValue;
292
+ }
293
+ return arg;
187
294
  }
188
295
 
189
- withHelper(scopePath: string, data: any, innerTemplate: any) {
190
- const newContext = this.lookup(scopePath, data);
191
- return this.applyTemplate(innerTemplate, { ...data, ...newContext });
192
- }
193
- setHelper(varName: string, data: any, innerTemplate: any) {
194
- const [key, valuePath] = varName.split(" ");
296
+ handleSpitStringArgs(stringArg: string) {
297
+ let currentQuote: string | null = null;
298
+ let result: string[] = [];
299
+ let currentPart = "";
300
+ for (let i = 0; i < stringArg.length; i++) {
301
+ const char = stringArg[i];
302
+
303
+ if (char === " " && currentQuote === null) {
304
+ if (currentPart) {
305
+ result.push(currentPart);
306
+ currentPart = "";
307
+ }
308
+ continue;
309
+ }
195
310
 
196
- const value = this.lookup(valuePath, data);
311
+ if (char === '"' || char === "'") {
312
+ if (currentQuote === char) {
313
+ currentQuote = null;
314
+ } else if (currentQuote === null) {
315
+ currentQuote = char;
316
+ }
317
+ continue;
318
+ }
197
319
 
198
- return this.applyTemplate(innerTemplate, { ...data, [key]: value });
199
- }
200
- concatHelper(args: string, data: any) {
201
- const values = args.split(" ").map((arg) => this.lookup(arg, data) || arg);
202
- return values.join(" ");
203
- }
204
- mathHelper(expression: string, data: any) {
205
- const [left, operator, right] = expression.split(" ");
206
- const leftValue = parseFloat(this.lookup(left, data));
207
- const rightValue = parseFloat(this.lookup(right, data));
208
- switch (operator) {
209
- case "+":
210
- return leftValue + rightValue;
211
- case "-":
212
- return leftValue - rightValue;
213
- case "*":
214
- return leftValue * rightValue;
215
- case "/":
216
- return leftValue / rightValue;
217
- default:
218
- return 0;
219
- }
220
- }
221
- compareHelper(expression: string, data: any) {
222
- const [left, operator, right] = expression.split(" ");
223
- const leftValue = this.lookup(left, data);
224
- const rightValue = this.lookup(right, data);
225
-
226
- switch (operator) {
227
- case "==":
228
- return leftValue == rightValue;
229
- case "===":
230
- return leftValue === rightValue;
231
- case "!=":
232
- return leftValue != rightValue;
233
- case "!==":
234
- return leftValue !== rightValue;
235
- case ">":
236
- return leftValue > rightValue;
237
- case "<":
238
- return leftValue < rightValue;
239
- case ">=":
240
- return leftValue >= rightValue;
241
- case "<=":
242
- return leftValue <= rightValue;
243
- default:
244
- throw new Error(`Unknown operator: ${operator}`);
245
- }
246
- }
247
- upperCaseHelper(varPath: string, data: any) {
248
- const value = this.lookup(varPath, data);
249
- return typeof value === "string" ? value.toUpperCase() : value;
250
- }
251
- lowerCaseHelper(varPath: string, data: any) {
252
- const value = this.lookup(varPath, data);
253
- return typeof value === "string" ? value.toLowerCase() : value;
254
- }
255
- jsonStringifyHelper(varPath: string, data: any) {
256
- const value = this.lookup(varPath, data);
257
- return JSON.stringify(value);
258
- }
259
- jsonParseHelper(varPath: string, data: any) {
260
- const value = this.lookup(varPath, data);
261
- return typeof value === "string" ? JSON.parse(value) : value;
262
- }
263
- dateHelper(varPath: string, data: any) {
264
- const dateString = varPath.split(" ");
265
- const date = new Date(dateString[0]);
266
- return date.toISOString(); // Default format YYYY-MM-DD
267
- }
268
- repeatHelper(args: string, data: any) {
269
- const [str, count] = args.split(" ");
270
- const repeatData = this.lookup(str, data) || str;
271
- const repeatCount = parseInt(count);
272
- console.log(repeatCount, repeatData);
273
-
274
- if (typeof repeatData === "string") {
275
- return repeatData.repeat(repeatCount);
276
- } else if (typeof repeatData === "object") {
277
- return Array(repeatCount).fill(repeatData);
320
+ currentPart += char;
278
321
  }
279
- }
280
- sliceHelper(args: string, data: any) {
281
- console.log(args);
282
-
283
- const [varPath, start, end] = args
284
- .split(" ")
285
- .map((arg) => this.lookup(arg, data));
286
- const value = this.lookup(varPath, data);
287
- console.log(start, end, value);
288
-
289
- if (Array.isArray(value)) {
290
- return value.slice(start, end);
291
- } else if (typeof value === "string") {
292
- return value.slice(start, end);
293
- }
294
- return value;
295
- }
296
- joinHelper(args: string, data: any) {
297
- const [varPath, separator] = args.split(" ");
298
- const value = this.lookup(varPath, data);
299
322
 
300
- if (Array.isArray(value)) {
301
- return value.join(separator);
323
+ if (currentPart) {
324
+ result.push(currentPart);
302
325
  }
303
- return value;
326
+ return result;
304
327
  }
305
- findHelper(args: string, data: any) {
306
- const [arrayPath, property, value] = args.split(" ");
307
- const array = this.lookup(arrayPath, data);
308
328
 
309
- if (Array.isArray(array)) {
310
- return array.find((item) => this.lookup(property, item) === value);
329
+ processHelperArgs(helperArgs: string, data: any, innerTemplate: any): any {
330
+ const helperArgsMatches = [...helperArgs.matchAll(/\(([^()]+)\)/g)]?.[0];
331
+
332
+ if (helperArgsMatches) {
333
+ const innerString = helperArgsMatches[1];
334
+ const splitedInnerArgs = this.handleSpitStringArgs(innerString);
335
+ const helperName = splitedInnerArgs[0];
336
+ const helperArgsArray = splitedInnerArgs.slice(1);
337
+ const helper = this.getHelper(helperName);
338
+ if (!helper) {
339
+ throw new Error("Missing helper: " + helperName);
340
+ }
341
+ const splittedArgs = helperArgsArray.map((e: any) =>
342
+ this.sanitizeArg(e, helperName, data)
343
+ ) as any[];
344
+ let helperResult = helper(splittedArgs, data, innerTemplate);
345
+ // console.log({ helperName, helperArgs, splitedInnerArgs, helperResult });
346
+
347
+ if (isThis.isArrayOrObject(helperResult)) {
348
+ helperResult = this.memory.storeData(helperResult);
349
+ }
350
+ const returnValue = helperArgs.replace(
351
+ helperArgsMatches[0],
352
+ `"${helperResult}"`
353
+ );
354
+ return this.processHelperArgs(returnValue, data, innerTemplate);
355
+ } else {
356
+ return helperArgs;
311
357
  }
312
- return null;
313
358
  }
314
- mapHelper(args: string, data: any, innerTemplate: any) {
315
- const [arrayPath] = args.split(" ");
316
- const array = this.lookup(arrayPath, data);
317
359
 
318
- if (!Array.isArray(array)) {
319
- throw new Error(`${arrayPath} is not an array`);
320
- }
321
- console.log(innerTemplate);
360
+ processHelper(
361
+ string: string,
362
+ data: any,
363
+ innerTemplate?: any,
364
+ stringMatches?: any
365
+ ): any {
366
+ stringMatches = stringMatches
367
+ ? stringMatches
368
+ : [...string.matchAll(/{{#(\w+)\s*([^}]*)}}/g)]?.[0];
369
+
370
+ if (stringMatches) {
371
+ const capturedString = stringMatches[0];
372
+ const helperName = stringMatches[1];
373
+ const helperArgs = stringMatches[2];
374
+ const helper = this.getHelper(helperName);
375
+ if (!helper) {
376
+ throw new Error("Missing helper : " + helperName);
377
+ }
378
+ let splitedArgs = [];
379
+ if (!isThis.isEmptyString(helperArgs)) {
380
+ const processedHelperArgs = this.processHelperArgs(
381
+ helperArgs,
382
+ data,
383
+ innerTemplate
384
+ );
385
+
386
+ splitedArgs = this.handleSpitStringArgs(processedHelperArgs).map(
387
+ (e: any) => this.sanitizeArg(e, helperName, data)
388
+ ) as any[];
389
+ // console.log({
390
+ // string,
391
+ // helperName,
392
+ // helperArgs,
393
+ // processedHelperArgs,
394
+ // splitedArgs,
395
+ // });
396
+ }
322
397
 
323
- return array.map((item) =>
324
- this.applyTemplate(innerTemplate, { ...data, item })
325
- );
326
- }
327
- uniqueHelper(varPath: string, data: any) {
328
- const value = this.lookup(varPath, data);
398
+ const helperResult = helper(splitedArgs, data, innerTemplate);
399
+ // console.log({ helperResult });
329
400
 
330
- if (Array.isArray(value)) {
331
- return Array.from(new Set(value));
332
- }
333
- return value;
334
- }
335
- randomHelper(varPath: string, data: any) {
336
- const array = this.lookup(varPath, data);
337
-
338
- if (Array.isArray(array)) {
339
- const randomIndex = Math.floor(Math.random() * array.length);
340
- return array[randomIndex];
341
- }
342
- return null;
343
- }
344
- reverseHelper(varPath: string, data: any) {
345
- const value = this.lookup(varPath, data);
401
+ if (isThis.isArrayOrObject(helperResult)) {
402
+ return helperResult;
403
+ }
346
404
 
347
- if (Array.isArray(value)) {
348
- return value.reverse();
349
- } else if (typeof value === "string") {
350
- return value.split("").reverse().join("");
405
+ const resultString = string.replace(capturedString, helperResult);
406
+ stringMatches = [...resultString.matchAll(/{{#(\w+)\s*([^}]*)}}/g)]?.[0];
407
+ // console.log({ resultString, stringMatches });
408
+
409
+ if (stringMatches) {
410
+ return this.processHelper(
411
+ resultString,
412
+ data,
413
+ innerTemplate,
414
+ stringMatches
415
+ );
416
+ } else {
417
+ if (
418
+ isThis.isBooleanString(resultString) &&
419
+ isThis.isBoolean(helperResult)
420
+ ) {
421
+ return helperResult;
422
+ }
423
+ if (isThis.isNullString(resultString) && isThis.isNull(helperResult)) {
424
+ return null;
425
+ }
426
+ if (
427
+ isThis.isUndefinedString(resultString) ||
428
+ isThis.isUndefined(helperResult)
429
+ ) {
430
+ return undefined;
431
+ }
432
+ if (
433
+ (isThis.isNumberString(resultString) ||
434
+ isThis.isNumber(helperResult)) &&
435
+ helperResult === parseFloat(resultString)
436
+ ) {
437
+ return helperResult;
438
+ }
439
+ return resultString;
440
+ }
441
+ } else {
442
+ return string;
351
443
  }
352
- return value;
353
- }
354
- existsHelper(varPath: string, data: any) {
355
- const value = this.lookup(varPath, data);
356
- return value !== undefined && value !== null;
357
444
  }
358
445
  }
446
+ // const langJson = new LangJSON();
447
+
448
+ // langJson.registerHelpers({
449
+ // upper: ([str]) => str.toUpperCase(),
450
+ // lower: ([str]) => str.toLowerCase(),
451
+ // isTrue: ([arg]) => arg === "true",
452
+ // getLength: ([arr]) => arr.length,
453
+ // });
454
+
455
+ // // Define a complex data structure
456
+ // const data = {
457
+ // user: {
458
+ // name: "Alice",
459
+ // age: 30,
460
+ // roles: ["admin", "editor"],
461
+ // address: {
462
+ // city: "Wonderland",
463
+ // zip: "12345",
464
+ // coordinates: { lat: 51.5074, long: -0.1278 },
465
+ // },
466
+ // preferences: {
467
+ // notifications: { email: true, sms: false },
468
+ // theme: "dark",
469
+ // },
470
+ // },
471
+ // isActive: "true",
472
+ // items: [
473
+ // { id: 1, name: "Item1", description: "First Item" },
474
+ // { id: 2, name: "Item2", description: "Second Item" },
475
+ // { id: 3, name: "Item3", description: "Third Item" },
476
+ // ],
477
+ // };
478
+
479
+ // // // Define a complex template
480
+ // const template = {
481
+ // "{{#upper user.name}}": {
482
+ // age: "{{#var user.age}}",
483
+ // active: "{{#isTrue isActive}}",
484
+ // address: {
485
+ // city: "{{#var user.address.city}}",
486
+ // zip: "{{#var user.address.zip}}",
487
+ // coordinates:
488
+ // "{{#var user.address.coordinates.lat}}, {{#var user.address.coordinates.long}}",
489
+ // },
490
+ // roles: {
491
+ // "{{#arrayJoin (var user.roles) ', '}}": {
492
+ // roleCount: "{{#getLength user.roles}}",
493
+ // roleList: "{{#var user.roles[0]}} and {{#var user.roles[1]}}",
494
+ // },
495
+ // },
496
+ // items: "{{#repeat (concat 'Item: ' (var items[0].name) ' ') 3}}",
497
+ // preferences: {
498
+ // theme: "{{#var user.preferences.theme}}",
499
+ // notificationStatus: {
500
+ // email: "{{#var user.preferences.notifications.email}}",
501
+ // sms: "{{#var user.preferences.notifications.sms}}",
502
+ // },
503
+ // },
504
+ // },
505
+ // };
506
+
507
+ // const result = langJson.applyTemplate(template, data);
508
+
509
+ // const returnValue = {
510
+ // 1: result,
511
+ // };
512
+ // console.log(JSON.stringify({ returnValue }, null, 2));