@trustme24/flext 1.10.3 → 1.10.5
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/dist/engine.d.ts +66 -0
- package/dist/index.cjs +156 -156
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -54
- package/dist/index.js +20 -20
- package/dist/index.js.map +4 -4
- package/package.json +8 -8
- package/src/engine.ts +414 -0
- package/src/index.ts +4 -381
- package/src/lib/index.ts +2 -2
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trustme24/flext",
|
|
3
|
-
"version": "1.10.
|
|
4
|
-
"description": "A
|
|
5
|
-
"keywords": ["templates", "templating engine", "
|
|
3
|
+
"version": "1.10.5",
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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;
|