@trustme24/flext 1.10.3 → 1.10.4

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.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@trustme24/flext",
3
- "version": "1.10.3",
4
- "description": "A Powerful Templating Engine",
5
- "keywords": ["templates", "templating engine", "modular templates", "handlebars"],
3
+ "version": "1.10.4",
4
+ "description": "A technology for building reliable document templates",
5
+ "keywords": ["templates", "templating engine", "documents", "document engine", "handlebars"],
6
6
  "repository": "https://github.com/TrustMe-kz/flext.git",
7
7
  "type": "module",
8
8
  "main": "dist/index.js",
@@ -35,24 +35,24 @@
35
35
  "devDependencies": {
36
36
  "@babel/cli": "^7.28.6",
37
37
  "@babel/core": "^7.29.0",
38
- "@babel/preset-env": "^7.29.0",
38
+ "@babel/preset-env": "^7.29.2",
39
39
  "@babel/preset-typescript": "^7.28.5",
40
40
  "@handlebars/parser": "^2.2.2",
41
- "@tailwindcss/cli": "^4.2.1",
41
+ "@tailwindcss/cli": "^4.2.2",
42
42
  "@types/node": "^25.5.0",
43
43
  "esbuild": "^0.27.4",
44
44
  "esbuild-plugin-import-glob": "^0.1.1",
45
- "tailwindcss": "^4.2.1",
45
+ "tailwindcss": "^4.2.2",
46
46
  "tsc-alias": "^1.8.16",
47
47
  "typescript": "^5.9.3",
48
48
  "vitest": "^4.1.0"
49
49
  },
50
50
  "dependencies": {
51
- "@unocss/preset-wind4": "^66.6.6",
51
+ "@unocss/preset-wind4": "^66.6.7",
52
52
  "handlebars": "^4.7.8",
53
53
  "luxon": "^3.7.2",
54
54
  "striptags": "^3.2.0",
55
- "unocss": "^66.6.6",
55
+ "unocss": "^66.6.7",
56
56
  "written-number": "^0.11.1"
57
57
  }
58
58
  }
package/src/engine.ts ADDED
@@ -0,0 +1,414 @@
1
+ import { AST } from '@handlebars/parser';
2
+ import * as types from '@/types';
3
+ import * as lib from '@/lib';
4
+ import * as errors from '@/errors';
5
+ import * as modules from './modules';
6
+
7
+
8
+ // Constants
9
+
10
+ export const DEFAULT_HELPER_NAME = '__default';
11
+
12
+ export const DEFAULT_MODEL_DEPTH = 10;
13
+
14
+
15
+ // Types
16
+
17
+ export type MacrosData = {
18
+ version?: string|null,
19
+ lang?: string|null,
20
+ timeZone?: string|null,
21
+ title?: string|null,
22
+ moduleNames?: string[] | null,
23
+ lineHeight?: string|null,
24
+ fields?: types.Field[] | null,
25
+ };
26
+
27
+
28
+ // Classes
29
+
30
+ export class SimpleFlext {
31
+ declare public ast: AST.Program;
32
+ declare public data: types.Obj;
33
+ declare public helpers: types.Obj;
34
+ public onGetProcessed: types.GetProcessedTemplateHandler = defaultGetProcessed;
35
+ public onGetAst: types.GetTemplateAstHandler = lib.getAst;
36
+
37
+ constructor(val: string|null = null, data: types.Obj = {}, helpers: types.Obj = {}) {
38
+ if (val) this.setTemplate(val);
39
+ this.setData({ ...this.data, ...data });
40
+ this.setHelpers({ ...this.helpers, ...helpers});
41
+ }
42
+
43
+ public setTemplate(val: string): this {
44
+
45
+ // Clearing the data
46
+
47
+ this.data = {};
48
+ this.helpers = {};
49
+
50
+
51
+ // Getting the AST
52
+
53
+ const template = this.onGetProcessed(val);
54
+
55
+ this.ast = this.onGetAst(template);
56
+
57
+
58
+ return this;
59
+ }
60
+
61
+ public setData(val: types.Obj): this {
62
+ this.data = val;
63
+ return this;
64
+ }
65
+
66
+ public setHelpers(val: types.Obj): this {
67
+ this.helpers = val;
68
+ return this;
69
+ }
70
+
71
+ public addHelper(name: string, val: any): this {
72
+ this.helpers[name] = val;
73
+ return this;
74
+ }
75
+
76
+ public setOnGetProcessed(val: types.GetProcessedTemplateHandler): this {
77
+ this.onGetProcessed = val;
78
+ return this;
79
+ }
80
+
81
+ public setOnGetAst(val: types.GetTemplateAstHandler): this {
82
+ this.onGetAst = val;
83
+ return this;
84
+ }
85
+
86
+ public getHtml(data?: types.Obj | null, helpers?: types.Obj | null): string {
87
+ const template = lib.getTemplate(this.ast);
88
+
89
+
90
+ // Doing some checks
91
+
92
+ if (!template)
93
+ throw new errors.BaseError('Flext: Unable to get HTML: No template');
94
+
95
+
96
+ return lib.getHtml(
97
+ template,
98
+ data ?? this.data,
99
+ helpers ?? this.helpers,
100
+ );
101
+ }
102
+
103
+ public async getCss(data?: types.Obj | null, options: types.Obj = {}): Promise<string> {
104
+ const template = lib.getTemplate(this.ast);
105
+ const helpersObj = options?.helpers ?? {};
106
+ const helpers = { ...this.helpers, ...helpersObj };
107
+
108
+
109
+ // Doing some checks
110
+
111
+ if (!template)
112
+ throw new errors.BaseError('Flext: Unable to get CSS: No template');
113
+
114
+
115
+ return await lib.getCss(
116
+ template,
117
+ data ?? this.data,
118
+ { ...options, helpers },
119
+ );
120
+ }
121
+
122
+ public get html(): string {
123
+ return this.getHtml();
124
+ }
125
+ }
126
+
127
+ export class Flext extends SimpleFlext {
128
+ declare public version: string;
129
+ declare public lang: string;
130
+ declare public title: string;
131
+ declare public timeZone: string;
132
+ declare public lineHeight: number;
133
+ declare public assets: types.Obj<Blob>;
134
+ declare public fields: types.Field[];
135
+ public onGetTitle: types.GetTemplateTitleHandler = lib.getHtmlH1;
136
+ public onGetMacro: types.GetTemplateMacroHandler = lib.getMacros;
137
+
138
+ constructor(val: string|null = null, data: types.Obj = {}, helpers: types.Obj = {}) {
139
+ super(null, data, helpers);
140
+
141
+ if (val) this.setTemplate(val);
142
+ this.setData({ ...this.data, ...data });
143
+ this.setHelpers({ ...this.helpers, ...helpers});
144
+ }
145
+
146
+ public useModule(...val: string[]): this {
147
+ for (const name of val)
148
+ this.addModule(name, modules[name]);
149
+
150
+ return this;
151
+ }
152
+
153
+ public setTemplate(val: string): this {
154
+
155
+ // Setting the template
156
+
157
+ super.setTemplate(val);
158
+
159
+
160
+ // Defining the variables
161
+
162
+ const [ titleStr ] = this.onGetTitle(this.ast);
163
+
164
+ const macros = this.onGetMacro(this.ast);
165
+
166
+
167
+ // Getting the data
168
+
169
+ const { version, lang, timeZone, title, moduleNames, lineHeight, fields } = macrosToData(macros);
170
+
171
+
172
+ // Setting the data
173
+
174
+ if (version)
175
+ this.setVersion(version);
176
+
177
+ if (lang)
178
+ this.setLang(lang);
179
+
180
+ if (timeZone)
181
+ this.setTimeZone(timeZone);
182
+
183
+ if (title || titleStr)
184
+ this.setTitle(title ?? lib.ensureTitle(titleStr));
185
+
186
+ if (lineHeight)
187
+ this.setLineHeight(Number(lineHeight));
188
+
189
+ if (fields && fields?.length)
190
+ this.setFields(fields);
191
+
192
+
193
+ // Using the modules
194
+
195
+ this.useModule(...moduleNames);
196
+
197
+
198
+ return this;
199
+ }
200
+
201
+ public setVersion(val: string): this {
202
+ this.version = val;
203
+ return this;
204
+ }
205
+
206
+ public setLang(val: string): this {
207
+ this.lang = val;
208
+ return this;
209
+ }
210
+
211
+ public setTitle(val: string): this {
212
+ this.title = val;
213
+ return this;
214
+ }
215
+
216
+ public setTimeZone(val: string): this {
217
+ this.timeZone = val;
218
+ return this;
219
+ }
220
+
221
+ public setLineHeight(val: number): this {
222
+ this.lineHeight = val;
223
+ return this;
224
+ }
225
+
226
+ public setAssets(val: types.Obj<Blob>): this {
227
+ this.assets = val;
228
+ return this;
229
+ }
230
+
231
+ public addAsset(name: string, val: Blob): this {
232
+ this.assets[name] = val;
233
+ return this;
234
+ }
235
+
236
+ public setFields(val: types.Field[]): this {
237
+ this.fields = val;
238
+ return this;
239
+ }
240
+
241
+ public addModule(name: string, val: any): this {
242
+ const helpers = val?.helpers ?? {};
243
+
244
+
245
+ // Iterating for each helper
246
+
247
+ for (const helperName in helpers) {
248
+ if (!lib.has(helpers, helperName)) continue;
249
+
250
+
251
+ // Getting the data
252
+
253
+ const handle = helpers[helperName];
254
+
255
+ const isDefault = helperName === DEFAULT_HELPER_NAME;
256
+
257
+
258
+ // Adding the helper
259
+
260
+ const flext = this;
261
+
262
+ const helper = function (..._args: any[]): any {
263
+ const args = _args?.slice(0, -1) ?? [];
264
+ const options = _args[_args.length - 1] ?? {};
265
+ const namedArgs = options?.hash ?? {};
266
+ // @ts-ignore
267
+ const self = this;
268
+ const getContent = () => options?.fn(self) ?? null;
269
+
270
+ return handle({ flext, args, options, namedArgs, self, getContent });
271
+ }
272
+
273
+ if (isDefault)
274
+ this.addHelper(name, helper);
275
+ else
276
+ this.addHelper(name + ':' + helperName, helper);
277
+ }
278
+
279
+
280
+ return this;
281
+ }
282
+
283
+ public setOnGetTitle(val: types.GetTemplateTitleHandler): this {
284
+ this.onGetTitle = val;
285
+ return this;
286
+ }
287
+
288
+ public setOnGetMacro(val: types.GetTemplateMacroHandler): this {
289
+ this.onGetMacro = val;
290
+ return this;
291
+ }
292
+
293
+ public getDataModel(depth: number = DEFAULT_MODEL_DEPTH): types.MetadataModelNode[] {
294
+
295
+ // Defining the functions
296
+
297
+ /**
298
+ * TODO: kr: Costyl: Detects if it is a helper call (like 'put:noColor')
299
+ */
300
+ const isHelper = (node: types.DataModelNode): boolean => {
301
+ for (const helperName in this.helpers) {
302
+ if (!lib.has(this.helpers, helperName))
303
+ continue;
304
+ else if (node?.name === helperName)
305
+ return true;
306
+ }
307
+
308
+ return false;
309
+ }
310
+
311
+ /**
312
+ * TODO: kr: Costyl: Filters the helper calls (like 'put:noColor')
313
+ */
314
+ const isValid = (node: types.DataModelNode): boolean => !isHelper(node);
315
+
316
+ const dataModelNodeToMetadata = (node: types.DataModelNode): types.MetadataModelNode => lib.dataModelNodeToMetadata(node, this.fields, {}, depth);
317
+
318
+
319
+ // Getting the nodes
320
+
321
+ const model = lib.getDataModel(this.ast);
322
+ const nodes: types.DataModelNode[] = model?.$ ?? [];
323
+
324
+
325
+ return nodes.filter(isValid).map(dataModelNodeToMetadata);
326
+ }
327
+
328
+ public getValidationErrors(data?: types.Obj | null, depth: number = DEFAULT_MODEL_DEPTH): errors.TemplateDataValidationError[] {
329
+ return lib.getTemplateValidationErrorsByMetadata(data ?? this.data, this.model, depth);
330
+ }
331
+
332
+ public getIsValid(data?: types.Obj | null, depth: number = DEFAULT_MODEL_DEPTH): boolean {
333
+ const errors = this.getValidationErrors(data ?? this.data, depth);
334
+ return !errors?.length;
335
+ }
336
+
337
+ public get model(): types.MetadataModelNode[] {
338
+ return this.getDataModel();
339
+ }
340
+
341
+ public get validationErrors(): errors.TemplateDataValidationError[] {
342
+ return this.getValidationErrors();
343
+ }
344
+
345
+ public get errors(): errors.BaseError[] {
346
+ return this.validationErrors;
347
+ }
348
+
349
+ public get isValid(): boolean {
350
+ return this.getIsValid();
351
+ }
352
+ }
353
+
354
+
355
+ // Functions
356
+
357
+ export function macrosToData(macros: types.Macro[]): MacrosData {
358
+
359
+ // Defining the functions
360
+
361
+ const getAll = (..._val: string[]): types.Macro[] | null => macros?.filter(m => lib.inarr(m?.name, ..._val)) ?? null;
362
+
363
+ const get = (_val: string): string | null => {
364
+ const [macro] = getAll(_val);
365
+ const [param] = macro?.params ?? [];
366
+
367
+ return param?.value ?? null;
368
+ };
369
+
370
+
371
+ // Getting the data
372
+
373
+ const version = get('v');
374
+ const lang = get('lang');
375
+ const title = get('title');
376
+ const timeZone = get('timeZone');
377
+ const modulesMacros = getAll('use');
378
+ const lineHeight = get('lineHeight');
379
+ const optionMacros = getAll('option');
380
+ const fieldMacros = getAll('group', 'field');
381
+
382
+
383
+ // Getting the fields
384
+
385
+ const fieldValueOptions = optionMacros?.map(lib.macroToFieldValueOption) ?? null;
386
+ const fields = fieldMacros?.map(lib.macroToField) ?? [];
387
+
388
+ lib.applyValueOptionsToFields(fieldValueOptions, fields);
389
+
390
+ lib.applyAbsoluteOrderToFields(fields);
391
+
392
+
393
+ // Getting the field groups
394
+
395
+ const fieldGroups = fields.filter(f => f?.extra?.macroName === 'group');
396
+
397
+ for (const fieldGroup of fieldGroups)
398
+ fieldGroup.type = 'object';
399
+
400
+
401
+ // Getting the modules
402
+
403
+ const moduleNames = modulesMacros.map(lib.macroToModuleNames).flat();
404
+
405
+
406
+ return { version, lang, timeZone, title, moduleNames, lineHeight, fields };
407
+ }
408
+
409
+ export function defaultGetProcessed(val: string): string {
410
+ return val;
411
+ }
412
+
413
+
414
+ export default Flext;