@trustme24/flext 1.10.5 → 2.0.0

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.
Files changed (52) hide show
  1. package/README.md +4 -4
  2. package/bin/flext.mjs +24 -0
  3. package/dist/dialects/index.d.ts +3 -0
  4. package/dist/dialects/latest.d.ts +6 -0
  5. package/dist/dialects/legacy-1-0-beta3.d.ts +5 -0
  6. package/dist/dialects/legacy-1-0-beta4.d.ts +5 -0
  7. package/dist/index.cjs +257 -253
  8. package/dist/index.cjs.map +4 -4
  9. package/dist/index.d.ts +8 -7
  10. package/dist/index.js +29 -29
  11. package/dist/index.js.map +4 -4
  12. package/dist/types.d.ts +3 -74
  13. package/package.json +13 -11
  14. package/src/dialects/index.ts +3 -0
  15. package/src/dialects/latest.ts +8 -0
  16. package/src/dialects/legacy-1-0-beta3.ts +7 -0
  17. package/src/dialects/legacy-1-0-beta4.ts +7 -0
  18. package/src/index.ts +75 -16
  19. package/src/tsconfig.json +3 -3
  20. package/src/types.ts +2 -102
  21. package/dist/engine.d.ts +0 -66
  22. package/dist/errors.d.ts +0 -25
  23. package/dist/index.css +0 -2
  24. package/dist/lib/index.d.ts +0 -70
  25. package/dist/modules/array/index.d.ts +0 -5
  26. package/dist/modules/cond/index.d.ts +0 -10
  27. package/dist/modules/date/index.d.ts +0 -16
  28. package/dist/modules/index.d.ts +0 -9
  29. package/dist/modules/match/index.d.ts +0 -5
  30. package/dist/modules/math/index.d.ts +0 -17
  31. package/dist/modules/media/index.d.ts +0 -3
  32. package/dist/modules/number/index.d.ts +0 -8
  33. package/dist/modules/put/index.d.ts +0 -6
  34. package/dist/modules/string/index.d.ts +0 -5
  35. package/src/engine.ts +0 -414
  36. package/src/env.d.ts +0 -4
  37. package/src/errors.ts +0 -51
  38. package/src/index.css +0 -1
  39. package/src/lib/index.ts +0 -931
  40. package/src/lib/unocssShadowDomHack.css +0 -133
  41. package/src/modules/array/index.ts +0 -68
  42. package/src/modules/cond/index.ts +0 -123
  43. package/src/modules/date/index.ts +0 -254
  44. package/src/modules/index.ts +0 -9
  45. package/src/modules/match/index.ts +0 -54
  46. package/src/modules/math/index.ts +0 -198
  47. package/src/modules/media/index.ts +0 -27
  48. package/src/modules/number/index.ts +0 -73
  49. package/src/modules/number/localeNames.json +0 -16
  50. package/src/modules/number/locales/kkKz.json +0 -98
  51. package/src/modules/put/index.ts +0 -41
  52. package/src/modules/string/index.ts +0 -45
package/src/lib/index.ts DELETED
@@ -1,931 +0,0 @@
1
- import { DateTime } from 'luxon';
2
- import { AST } from '@handlebars/parser';
3
- import { createGenerator, presetTypography } from 'unocss';
4
- import { presetWind4 } from '@unocss/preset-wind4';
5
- import { BaseError, BaseWarning, PotentialLoopError, TemplateDataValidationError } from '@/errors';
6
- import striptags from 'striptags';
7
- import unocssShadowDomHack from './unocssShadowDomHack.css';
8
- import Handlebars, { TemplateDelegate } from 'handlebars';
9
- import * as types from '@/types';
10
-
11
-
12
- // Third-parties
13
-
14
- export const uno = createGenerator({
15
- presets: [
16
- presetWind4(),
17
- presetTypography(),
18
- ],
19
- preflights: [
20
- // @remarks NOTE: kr: UnoCSS needs a hack to make Shadow DOM work in Tailwind v4
21
- // @see https://github.com/tailwindlabs/tailwindcss/discussions/15556
22
- { getCSS: () => unocssShadowDomHack },
23
- ],
24
- theme: {},
25
- });
26
-
27
-
28
- // Constants
29
-
30
- export const DEFAULT_MODEL_DEPTH = 10;
31
-
32
- export const DEFAULT_FIELD_TYPE: types.FieldType = 'string';
33
-
34
-
35
- // Variables
36
-
37
- export const stripHtml = striptags;
38
-
39
-
40
- // Classes
41
-
42
- export class HandlebarsCollector<T = any> extends Handlebars.Visitor {
43
- public data: T[] = [];
44
- public match: types.CollectorFilterHandler<T> = () => true;
45
-
46
- constructor(filter?: types.CollectorFilterHandler<T>) {
47
- super();
48
- if (filter) this.setMatchHandler(filter);
49
- }
50
-
51
- public onCollect(val: T): void {
52
- if (this.match(val))
53
- this.data.push(val);
54
- }
55
-
56
- public setMatchHandler(val: types.CollectorFilterHandler<T>): this {
57
- this.match = val;
58
- return this;
59
- }
60
-
61
- public setAst(ast: AST.Program): this {
62
- this.accept(ast);
63
- return this;
64
- }
65
-
66
- public collect(ast: AST.Program): T[] {
67
- this.data = [];
68
-
69
- this.setAst(ast);
70
-
71
- return this.data;
72
- }
73
- }
74
-
75
- class HandlebarsCommentCollector extends HandlebarsCollector<string> {
76
- public CommentStatement(node) {
77
- this.onCollect(node.value);
78
- return super.CommentStatement(node);
79
- }
80
- }
81
-
82
- class HandlebarsContentCollector extends HandlebarsCollector<string> {
83
- public ContentStatement(node) {
84
- this.onCollect(node.value);
85
- return super.ContentStatement(node);
86
- }
87
- }
88
-
89
- class HandlebarsPathCollector extends HandlebarsCollector<string> {
90
- public PathExpression(node) {
91
- const path = node.original;
92
-
93
-
94
- // Defining the functions
95
-
96
- const test = (val: string): boolean => path === val;
97
-
98
-
99
- // TODO: kr: Costyl to skip '{{#if}}' paths in AST
100
-
101
- if (test('if') || test('unless') || test('each') || test('with'))
102
- return super.PathExpression(node);
103
-
104
-
105
- // Collecting the path
106
-
107
- this.onCollect(node.original);
108
-
109
-
110
- return super.PathExpression(node);
111
- }
112
- }
113
-
114
- class FlextMacroCollector extends HandlebarsCommentCollector {
115
- public match = FilterHelper.macro;
116
- }
117
-
118
- class FlextH1SomewhereContentCollector extends HandlebarsContentCollector {
119
- public match = FilterHelper.htmlH1Somewhere;
120
- }
121
-
122
-
123
- // Checking Functions
124
-
125
- export function inarr<T extends any, A extends any[]>(val: T, ...arr: A): types.Inarr<T, A> {
126
- return arr.includes(val) as types.Inarr<T, A>;
127
- }
128
-
129
- export function has<T extends types.Obj, K extends keyof T>(obj: T, key: K): types.Has<T, K> {
130
- return obj.hasOwnProperty(key) as types.Has<T, K>;
131
- }
132
-
133
- export function isset<T extends any>(val: T): types.Isset<T> {
134
- return !inarr(val, null, undefined) as types.Isset<T>;
135
- }
136
-
137
- export function isNumber<T extends any>(val: T): types.IsNumber<T> {
138
- return (isset(val) && !isNaN(Number(val))) as types.IsNumber<T>;
139
- }
140
-
141
- export function isObject<T extends any>(val: T): types.IsObject<T> {
142
- return (typeof val === 'object' && val !== null) as types.IsObject<T>;
143
- }
144
-
145
-
146
- // System Functions
147
-
148
- export function audit(val: any): string {
149
- if (isObject(val))
150
- return JSON.stringify(val);
151
-
152
- else if (typeof val === 'string')
153
- return `'${val}'`;
154
-
155
- else
156
- return String(val);
157
- }
158
-
159
-
160
- // Handlebars Functions
161
-
162
- export function getAst(val: string): AST.Program {
163
- return Handlebars.parse(val);
164
- }
165
-
166
- export function getTemplate(val: string | AST.Program): TemplateDelegate {
167
- return Handlebars.compile(val);
168
- }
169
-
170
- export function getHtml(template: TemplateDelegate, data: types.Obj = {}, helpers: types.Obj = {}): string {
171
- return template(data, { helpers });
172
- }
173
-
174
- export async function getCss(template: TemplateDelegate, data: types.Obj = {}, options: types.Obj = {}): Promise<string> {
175
- const helpers = options?.helpers ?? {};
176
- const doGenerateGlobalStyles = Boolean(options?.doGenerateGlobalStyles ?? true);
177
-
178
-
179
- // Getting the CSS
180
-
181
- const generator = await uno;
182
- const html = getHtml(template, data, helpers);
183
- const { css } = await generator.generate(html, { preflights: doGenerateGlobalStyles });
184
-
185
-
186
- return css;
187
- }
188
-
189
-
190
- // Analyze Functions
191
-
192
- export function unique<T = any>(arr: T[]): T[] {
193
- return [ ...new Set(arr) ];
194
- }
195
-
196
- export function getPaths(ast: AST.Program): string[] {
197
- const paths = new HandlebarsPathCollector().collect(ast);
198
- return unique(paths);
199
- }
200
-
201
- export function pathToDataModelNode(path: string, depth: number = DEFAULT_MODEL_DEPTH): types.DataModelNode {
202
-
203
- // Doing some checks
204
-
205
- if (depth <= 0)
206
- throw new PotentialLoopError('Flext: Unable to get the model: The model is too deep');
207
-
208
-
209
- // Getting the root node
210
-
211
- const [ name, ...items ] = path?.split('.') ?? [];
212
-
213
- const result: types.DataModelNode = { name };
214
-
215
-
216
- // If the node has children
217
-
218
- if (items?.length > 0)
219
- result.$ = [ pathToDataModelNode(items?.join('.') || '', depth - 1) ];
220
-
221
-
222
- return result;
223
- }
224
-
225
- export function pathToDataModel(path: string, depth: number = DEFAULT_MODEL_DEPTH): types.DataModel {
226
- const node: any = pathToDataModelNode(path, depth);
227
- const dataModel: any = { name: 'root', $: [ node ] };
228
-
229
-
230
- // Defining the methods
231
-
232
- dataModel.addPath = (_path: string, _depth: number = DEFAULT_MODEL_DEPTH): void => {
233
- const newNode = pathToDataModelNode(_path, _depth);
234
- let cursorRef: types.DataModelNode = dataModel;
235
- let cursor: types.DataModelNode | null = newNode;
236
-
237
-
238
- // Iterating for each new node
239
-
240
- for (let i = 0; i < 99; i++) {
241
-
242
- // Doing some checks
243
-
244
- if (!cursor) return;
245
-
246
-
247
- // Trying to match the model node
248
-
249
- const nodesRef = cursorRef?.$ ?? [];
250
- let nextCursorRef = null;
251
-
252
- for (const nodeRef of nodesRef)
253
- if (cursor?.name === nodeRef?.name)
254
- nextCursorRef = nodeRef;
255
-
256
-
257
- // If the model node does not exist
258
-
259
- if (!nextCursorRef) {
260
- cursorRef.$ = [ ...nodesRef, cursor ];
261
- return;
262
- }
263
-
264
-
265
- // Setting the next cursor
266
-
267
- const [ nextCursor ] = cursor.$ ?? [];
268
-
269
- cursorRef = nextCursorRef;
270
- cursor = nextCursor ?? null;
271
- }
272
- };
273
-
274
-
275
- return dataModel;
276
- }
277
-
278
- export function getDataModel(ast: AST.Program): types.DataModel {
279
- const [ first, ...paths ] = getPaths(ast);
280
- const result: types.DataModel = pathToDataModel(first);
281
-
282
-
283
- // Iterating for each path
284
-
285
- for (const path of paths) {
286
- const test = (val: string): boolean => path.startsWith(val);
287
-
288
- if (test('.') || test('/') || test('@') || test('this'))
289
- continue; // TODO: kr: Costyl to skip 'this*' paths in AST
290
- else
291
- result.addPath(path);
292
- }
293
-
294
-
295
- return result;
296
- }
297
-
298
- export function dataModelNodeToMetadata(node: types.DataModelNode, fields: types.Field[], _options: types.Obj = {}, depth: number = DEFAULT_MODEL_DEPTH): types.MetadataModelNode {
299
-
300
- // Doing some checks
301
-
302
- if (depth <= 0)
303
- throw new PotentialLoopError('Flext: Unable to get data model: The data model is too deep');
304
-
305
-
306
- // Defining the functions
307
-
308
- const getField = (_fieldName: string): types.Field | null => fields?.find(f => f?.name === _fieldName) ?? null;
309
-
310
-
311
- // Getting the data
312
-
313
- const fieldName = _options?.fieldName ?? node?.name ?? null;
314
- const field = getField(fieldName);
315
- const order = field?.order ?? null;
316
- const nodes = node?.$ ?? [];
317
- const type = nodes?.length ? 'object' : field?.type ?? DEFAULT_FIELD_TYPE;
318
- const name = node?.name ?? null;
319
- const label = field?.label ?? null;
320
- const hint = field?.hint ?? null;
321
- const min = field?.min ?? null;
322
- const max = field?.max ?? null;
323
- const minLength = field?.minLength ?? null;
324
- const maxLength = field?.maxLength ?? null;
325
- const options = field?.options ?? null;
326
- const isRequiredBool = field?.isRequired ?? null;
327
-
328
-
329
- // Getting the sub-nodes
330
-
331
- const newNodes: types.MetadataModelNode[] = [];
332
-
333
- for (const node of nodes) {
334
- const nodeName = node?.name ?? null;
335
-
336
- newNodes.push(dataModelNodeToMetadata(node, fields, {
337
- fieldName: fieldName + '.' + nodeName,
338
- }, depth - 1));
339
- }
340
-
341
-
342
- // Getting the ordered sub-nodes
343
-
344
- const $ = newNodes.sort((node, nodeRef) => {
345
- const nodeFieldName = node?.extra?.fieldName ?? null;
346
- const nodeField: types.Obj = getField(nodeFieldName) ?? {};
347
- const nodeFieldOrder = nodeField?.order ?? null;
348
- const nodeFieldAbsoluteOrder = nodeField?.extra?.absoluteOrder ?? null;
349
- const nodeFieldNameRef = nodeRef?.extra?.fieldName ?? null;
350
- const nodeFieldRef: types.Obj = getField(nodeFieldNameRef) ?? {};
351
- const nodeFieldOrderRef = nodeFieldRef?.order ?? null;
352
- const nodeFieldAbsoluteOrderRef = nodeFieldRef?.extra?.absoluteOrder ?? null;
353
-
354
- if (compare(nodeFieldOrder, nodeFieldOrderRef) !== 0)
355
- return compare(nodeFieldOrder, nodeFieldOrderRef);
356
- else
357
- return compare(nodeFieldAbsoluteOrder, nodeFieldAbsoluteOrderRef);
358
- });
359
-
360
-
361
- // Getting the required
362
-
363
- const isAllChildrenRequired = newNodes?.length && newNodes.every(n => n?.isRequired);
364
-
365
- const isRequired = isset(isRequiredBool) ? isRequiredBool : isAllChildrenRequired;
366
-
367
-
368
- // Getting the extra
369
-
370
- const extra = { fieldName };
371
-
372
-
373
- return {
374
- type,
375
- name,
376
- label,
377
- hint,
378
- min,
379
- max,
380
- minLength,
381
- maxLength,
382
- order,
383
- options,
384
- isRequired,
385
- extra,
386
- $,
387
- };
388
- }
389
-
390
- export function getMacroParam(val: string): types.MacroParam | null {
391
-
392
- // Defining the functions
393
-
394
- const match = (regex: RegExp): any => val?.match(regex) ?? null;
395
-
396
- const get = (val: any): types.MacroParam => {
397
- const value = val?.groups?.value ?? null;
398
- const name = val?.groups?.name ?? value;
399
-
400
- return { name, value };
401
- }
402
-
403
-
404
- // Guessing the param type
405
-
406
- const param = match(RegexHelper.macroParam);
407
- const namedParam = match(RegexHelper.macroNamedParam);
408
- const simpleParam = match(RegexHelper.macroSimpleParam)
409
-
410
-
411
- // If the param type is known
412
-
413
- if (param)
414
- return get(param);
415
-
416
- if (namedParam)
417
- return get(namedParam);
418
-
419
- if (simpleParam)
420
- return get(simpleParam);
421
-
422
-
423
- return null;
424
- }
425
-
426
- export function getMacroParams(val: string, doWarn: boolean = true): types.MacroParam[] {
427
- const matches = val?.match(RegexHelper.macroParams) ?? [];
428
- const result: types.MacroParam[] = [];
429
-
430
-
431
- // Iterating for each token
432
-
433
- for (const token of matches) {
434
- const macro = getMacroParam(token);
435
-
436
- if (macro)
437
- result.push(macro);
438
- else if (doWarn)
439
- throw new BaseWarning('Flext: Unable to parse the macros: Bad token: ' + audit(token));
440
- }
441
-
442
-
443
- return result;
444
- }
445
-
446
- export function getMacros(ast: AST.Program, doWarn: boolean = true): types.Macro[] {
447
- const macroArr = new FlextMacroCollector().collect(ast);
448
- const result: types.Macro[] = [];
449
-
450
-
451
- // Iterating for each macro string
452
-
453
- for (const macroStr of macroArr) {
454
- const matches = macroStr?.trim()?.match(RegexHelper.macro) ?? null;
455
-
456
-
457
- // Doing some checks
458
-
459
- if (!matches) {
460
- if (doWarn)
461
- throw new BaseWarning('Flext: Unable to parse the macros: Bad macro: ' + audit(macroStr));
462
- else
463
- return null;
464
- }
465
-
466
-
467
- // Getting the data
468
-
469
- const name = matches?.groups?.name ?? null;
470
- const paramsStr = matches?.groups?.params ?? null;
471
- const params = getMacroParams(paramsStr, doWarn);
472
-
473
-
474
- result.push({ name, params });
475
- }
476
-
477
-
478
- return result;
479
- }
480
-
481
- export function getHtmlH1(ast: AST.Program, doWarn: boolean = true): string[] {
482
- const titleArr = new FlextH1SomewhereContentCollector().collect(ast);
483
- const result: string[] = [];
484
-
485
-
486
- // Iterating for each macro string
487
-
488
- for (const titleStr of titleArr) {
489
- const matches = titleStr?.trim()?.match(RegexHelper.htmlH1Somewhere) ?? null;
490
-
491
-
492
- // Doing some checks
493
-
494
- if (!matches) {
495
- if (doWarn)
496
- throw new BaseWarning('Flext: Unable to parse H1: Bad HTML: ' + audit(titleStr));
497
- else
498
- return null;
499
- }
500
-
501
-
502
- // Iterating for each match
503
-
504
- for (const match of matches)
505
- result.push(String(match));
506
- }
507
-
508
-
509
- return result;
510
- }
511
-
512
- export function getTemplateValidationErrorsByMetadata(data: types.Obj, model: types.MetadataModelNode[], depth: number = DEFAULT_MODEL_DEPTH): TemplateDataValidationError[] {
513
-
514
- // Doing some checks
515
-
516
- if (depth <= 0)
517
- throw new PotentialLoopError('Flext: Unable to verify the data: The data model is too deep');
518
-
519
-
520
- // Getting the data
521
-
522
- const result: TemplateDataValidationError[] = [];
523
-
524
-
525
- // Defining the functions
526
-
527
- const isval = (val: any): boolean => !inarr(val, '', null, undefined);
528
-
529
- const len = (val: any): number => String(val).length;
530
-
531
- const err = (message: string, fieldName?: string|null): void => { result.push(new TemplateDataValidationError(message, fieldName)); };
532
-
533
-
534
- // Iterating for each child node
535
-
536
- for (const node of model) {
537
-
538
- // Getting the data
539
-
540
- const fieldNameStr = node?.extra?.fieldName ?? null;
541
- const fieldName = fieldNameStr ?? node?.name ?? 'Unknown';
542
- const fieldValue: types.Obj = data[node.name] ?? null;
543
-
544
-
545
- // If the value is required
546
-
547
- if (!isval(fieldValue) && node?.isRequired) {
548
- err(`Field '${fieldName}' is required (${audit(fieldValue)} is passed)`, fieldNameStr);
549
- continue;
550
- }
551
-
552
-
553
- // If the value has value range (e.g. 10 < value < 20)
554
-
555
- if (inarr(node?.type, 'number', 'date') && isval(node?.min) && isval(fieldValue) && node?.min > fieldValue) {
556
- err(`'${fieldName}' field value is less than the range (${audit(fieldValue)} is passed, the minimum is ${audit(node?.min)})`, fieldNameStr);
557
- continue;
558
- }
559
-
560
- if (inarr(node?.type, 'number', 'date') && isval(node?.max) && isval(fieldValue) && node?.max < fieldValue) {
561
- err(`'${fieldName}' field value is greater than the range (${audit(fieldValue)} is passed, the maximum is ${audit(node?.max)})`, fieldNameStr);
562
- continue;
563
- }
564
-
565
-
566
- // If the value has length range (e.g. 100 < length < 200)
567
-
568
- if (!inarr(node?.type, 'object', 'array', 'mixed') && isval(node?.minLength) && isval(fieldValue) && node?.minLength > len(fieldValue)) {
569
- err(`'${fieldName}' field value is shorter than the range (${audit(fieldValue)} is passed, the minimum is ${audit(node?.minLength)})`, fieldNameStr);
570
- continue;
571
- }
572
-
573
- if (!inarr(node?.type, 'object', 'array', 'mixed') && isval(node?.maxLength) && isval(fieldValue) && node?.maxLength < len(fieldValue)) {
574
- err(`'${fieldName}' field value is longer than the range (${audit(fieldValue)} is passed, the maximum is ${audit(node?.maxLength)})`, fieldNameStr);
575
- continue;
576
- }
577
-
578
- if (inarr(node?.type, 'string') && isNumber(node?.min) && isval(fieldValue) && Number(node?.min) > len(fieldValue)) {
579
- err(`'${fieldName}' field value is shorter than the range (${audit(fieldValue)} is passed, the minimum is ${audit(node?.min)})`, fieldNameStr);
580
- continue;
581
- }
582
-
583
- if (inarr(node?.type, 'string') && isNumber(node?.max) && isval(fieldValue) && Number(node?.max) < len(fieldValue)) {
584
- err(`'${fieldName}' field value is longer than the range (${audit(fieldValue)} is passed, the maximum is ${audit(node?.max)})`, fieldNameStr);
585
- continue;
586
- }
587
-
588
-
589
- result.push(...getTemplateValidationErrorsByMetadata(fieldValue ?? {}, node.$ as types.MetadataModelNode[], depth - 1));
590
- }
591
-
592
-
593
- return result;
594
- }
595
-
596
-
597
- // Framework Functions
598
-
599
- export function ensureString(val: any): string {
600
- return String(val ?? '');
601
- }
602
-
603
- export function ensureNullableString(val: any): string|null {
604
- if (inarr(val, null, undefined))
605
- return null;
606
- else
607
- return ensureString(val);
608
- }
609
-
610
- export function ensureDate<T extends boolean = true>(val: Date | string | number, doWarn: T = true as T): T extends true ? Date : Date | null {
611
- const isDateObj = isObject(val) && val instanceof Date;
612
- const isDbDate = typeof val === 'string' && RegexHelper.dbDateStr.test(val);
613
-
614
-
615
- // Defining the functions
616
-
617
- const unixDate = <R extends boolean = true>(_val: string|number, _doWarn: R = true as R): R extends true ? Date : Date | null => {
618
- const date = new Date(_val);
619
-
620
- if (isNaN(date.getTime()) && _doWarn)
621
- throw new BaseWarning('Flext: Unable to get date: The date is invalid: ' + audit(_val));
622
- else if (isNaN(date.getTime()))
623
- return null;
624
- else
625
- return date;
626
- }
627
-
628
- const dbDate = <R extends boolean = true>(_val: string, _doWarn: R = true as R): R extends true ? Date : Date | null => {
629
- const [ year, month, day ] = _val?.split('-')?.map(Number) ?? [];
630
-
631
- if (year && month && day)
632
- return DateTime.fromObject({ year, month, day }).toJSDate();
633
- else if (_doWarn)
634
- throw new BaseError('Flext: Unable to get date: The date is invalid: ' + audit(_val));
635
- else
636
- return null;
637
- }
638
-
639
- const isoDate = <R extends boolean = true>(_val: string, _doWarn: R = true as R): R extends true ? Date : Date | null => {
640
- const date = DateTime.fromISO(_val);
641
-
642
- if (date.isValid)
643
- return date.toJSDate();
644
- else if (_doWarn)
645
- throw new BaseWarning('Flext: Unable to get date: The date is invalid: ' + audit(_val));
646
- else
647
- return null;
648
- }
649
-
650
-
651
- if (isDateObj)
652
- return val as Date;
653
-
654
- if (isNumber(val))
655
- return unixDate(val as number, doWarn);
656
-
657
- else if (isDbDate)
658
- return dbDate(val as string, doWarn);
659
-
660
- else
661
- return isoDate(val as string, doWarn);
662
- }
663
-
664
- export function ensureTitle(val: string|number): string {
665
- let title: string|null = stripHtml(String(val)).trim();
666
-
667
-
668
- // Defining the functions
669
-
670
- const filter = (search: string | RegExp, val: string = '') => title = title.replace(search, val);
671
-
672
-
673
- // Getting the title
674
-
675
- filter('\n', ' ');
676
- filter(/\s{2,}/mg, ' ');
677
- filter(/[^\p{L}\d\s]/mgu);
678
-
679
-
680
- return title.trim();
681
- }
682
-
683
- export function ensureFieldName(val: string): string {
684
- let pathItem = val;
685
-
686
-
687
- // Defining the functions
688
-
689
- const filter = (search: string, val: string = '') => pathItem = pathItem.replace(search, val);
690
-
691
-
692
- // Getting the path item
693
-
694
- filter('['); // Filtering the '[n]' case
695
- filter(']'); // Filtering the '[n]' case
696
-
697
-
698
- return pathItem.trim();
699
- }
700
-
701
- export function ensureNullableFieldMinMax(val: any): Date | number | null {
702
- if (isNumber(val))
703
- return val;
704
- else if (isObject(val) && val instanceof Date)
705
- return val;
706
- else
707
- return null;
708
- }
709
-
710
- export function ensureNullableFieldMinMaxLength(val: any): number|null {
711
- if (isNumber(val))
712
- return val;
713
- else
714
- return null;
715
- }
716
-
717
- export function ensureNullableFieldOrder(val: any): number|null {
718
- return isset(val) ? Number(val) : null;
719
- }
720
-
721
- export function ensureFieldValue(val: any): types.FieldValue {
722
-
723
- // If the value is a string
724
-
725
- if (typeof val !== 'string') try {
726
- return JSON.parse(val);
727
- } catch (e) {
728
- return val ?? null;
729
- }
730
-
731
-
732
- // If the value is other
733
-
734
- else return val ?? null;
735
- }
736
-
737
- export function sum(...args: number[]): number {
738
- return args.reduce((acc, val) => Number(acc) + Number(val), 0);
739
- }
740
-
741
- export function matches(regex: RegExp, val: string|number): boolean {
742
- return !!String(val).trim().match(regex);
743
- }
744
-
745
- export function compare(val: number|null|undefined, valRef: number|null|undefined): number {
746
- if (!isset(val) && !isset(valRef))
747
- return 0;
748
- else if (!isset(val))
749
- return 1;
750
- else if (!isset(valRef))
751
- return -1;
752
- else
753
- return val - valRef;
754
- }
755
-
756
- export function macroToModuleNames(val: types.Macro): string[] {
757
- const params = val?.params ?? [];
758
- return params.map(p => p?.value ?? null);
759
- }
760
-
761
- export function macroToField(val: types.Macro): types.Field {
762
- const macroName = val?.name ?? null;
763
- const params = val?.params ?? [];
764
- const [ nameParam, ...args ] = params;
765
-
766
-
767
- // Defining the functions
768
-
769
- const get = (_val: string): any => {
770
- const arg = args?.find(a => a?.name === _val) ?? null;
771
-
772
- if (arg && arg?.value)
773
- return arg?.value ?? null;
774
- else if (arg && arg?.name)
775
- return true;
776
- else
777
- return null;
778
- }
779
-
780
-
781
- // Getting the data
782
-
783
- const type = ensureString(get('type') ?? DEFAULT_FIELD_TYPE) as types.FieldType;
784
- const nameStr = ensureNullableString(nameParam?.value);
785
- const label = ensureNullableString(get('label'));
786
- const descr = ensureNullableString(get('descr'));
787
- const hint = ensureNullableString(get('hint'));
788
- const min = ensureNullableFieldMinMax(get('min'));
789
- const max = ensureNullableFieldMinMax(get('max'));
790
- const minLength = ensureNullableFieldMinMaxLength(get('minLength'));
791
- const maxLength = ensureNullableFieldMinMaxLength(get('maxLength'));
792
- const order = ensureNullableFieldOrder(get('order'));
793
- const value = ensureFieldValue(get('value'));
794
- const isRequired = !!get('required');
795
-
796
-
797
- // Doing some checks
798
-
799
- if (!nameStr)
800
- throw new BaseError(`Unable to get field: The 'name' param is not set: ` + audit(nameStr));
801
-
802
-
803
- // Getting the name
804
-
805
- const name = ensureFieldName(nameStr);
806
-
807
-
808
- // Gettign the extra
809
-
810
- const extra = { macroName };
811
-
812
-
813
- return {
814
- type,
815
- name,
816
- label,
817
- descr,
818
- hint,
819
- min,
820
- max,
821
- minLength,
822
- maxLength,
823
- order,
824
- value,
825
- isRequired,
826
- extra,
827
- };
828
- }
829
-
830
- export function macroToFieldValueOption(val: types.Macro): types.FieldValueOption {
831
- const params = val?.params ?? [];
832
- const [ nameParam, ...args ] = params;
833
-
834
-
835
- // Defining the functions
836
-
837
- const get = (_val: string): any => {
838
- const arg = args?.find(a => a?.name === _val) ?? null;
839
-
840
- if (arg && arg?.value)
841
- return arg?.value ?? null;
842
- else if (arg && arg?.name)
843
- return true;
844
- else
845
- return null;
846
- }
847
-
848
-
849
- // Getting the data
850
-
851
- const type = ensureString(get('type') ?? DEFAULT_FIELD_TYPE);
852
- const name = nameParam?.value ?? null;
853
- const fieldName = get('for') ?? null;
854
- const label = get('label') ?? null;
855
- const descr = get('descr') ?? null;
856
- const value = ensureFieldValue(get('value') ?? name);
857
- const isDisabled = !!get('disabled');
858
-
859
-
860
- // Doing some checks
861
-
862
- if (!name)
863
- throw new BaseError(`Unable to get field option: The 'name' param is not set: ` + audit(name));
864
-
865
- if (!fieldName)
866
- throw new BaseError(`Unable to get field option '${name}': The 'for' param is not set: ` + audit(name));
867
-
868
-
869
- return {
870
- type,
871
- name,
872
- fieldName,
873
- label,
874
- descr,
875
- value,
876
- isDisabled,
877
- };
878
- }
879
-
880
- export function applyValueOptionsToFields(options: types.FieldValueOption[], fields: types.Field[]): void {
881
-
882
- // Defining the functions
883
-
884
- const get = (fieldName: string): types.FieldValueOption[] => {
885
- return options?.filter(o => o?.fieldName === fieldName) ?? [];
886
- };
887
-
888
-
889
- // Iterating for each field
890
-
891
- for (const field of fields)
892
- if (get(field.name)?.length)
893
- field.options = get(field.name);
894
- }
895
-
896
- export function applyAbsoluteOrderToFields(fields: types.Field[]): void {
897
- for (const [ i, field ] of fields.entries()) {
898
- if (field?.extra)
899
- field.extra.absoluteOrder = i;
900
- else
901
- field.extra = { absoluteOrder: i };
902
- }
903
- }
904
-
905
- export function defineModule(options: any = {}): any {
906
- const helpers = options?.helpers ?? null;
907
- return { helpers };
908
- }
909
-
910
-
911
- // Helpers
912
-
913
- export class RegexHelper {
914
- public static dbDateStr = /^\d+-\d+-\d+$/;
915
- public static macro = /^@(?<name>.+?) (?<params>.+)$/;
916
- public static macroParams = /(?<param>".+?")|(?<namedParam>[a-zA-Z0-9]+=".+?")|(?<simplaeParam>[a-zA-Z0-9]+)/gm;
917
- public static macroParam = /^"(?<value>.+)"$/;
918
- public static macroNamedParam = /^(?<name>.+)="(?<value>.+)"$/;
919
- public static macroSimpleParam = /^(?<name>[a-zA-Z]+)$/;
920
- public static htmlH1Somewhere = /\<h1.*?\>(?<value>.*)\<\/h1.*?\>/gs;
921
- }
922
-
923
- export class FilterHelper {
924
- public static macro(val: string): boolean {
925
- return matches(RegexHelper.macro, val);
926
- }
927
-
928
- public static htmlH1Somewhere(val: string): boolean {
929
- return matches(RegexHelper.htmlH1Somewhere, val);
930
- }
931
- }