@trustme24/flext 1.10.6 → 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.
- package/README.md +17 -17
- package/bin/flext.mjs +24 -0
- package/dist/dialects/index.d.ts +2 -0
- package/dist/dialects/latest.d.ts +6 -0
- package/dist/dialects/legacy-1-0-beta4.d.ts +11 -0
- package/dist/index.cjs +259 -253
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +10 -7
- package/dist/index.js +29 -29
- package/dist/index.js.map +4 -4
- package/dist/types.d.ts +3 -74
- package/package.json +13 -11
- package/src/dialects/index.ts +2 -0
- package/src/dialects/latest.ts +8 -0
- package/src/dialects/legacy-1-0-beta4.ts +75 -0
- package/src/index.ts +82 -16
- package/src/tsconfig.json +3 -3
- package/src/types.ts +2 -102
- package/dist/engine.d.ts +0 -66
- package/dist/errors.d.ts +0 -25
- package/dist/index.css +0 -2
- package/dist/lib/index.d.ts +0 -70
- package/dist/modules/array/index.d.ts +0 -5
- package/dist/modules/cond/index.d.ts +0 -10
- package/dist/modules/date/index.d.ts +0 -16
- package/dist/modules/index.d.ts +0 -9
- package/dist/modules/match/index.d.ts +0 -5
- package/dist/modules/math/index.d.ts +0 -17
- package/dist/modules/media/index.d.ts +0 -3
- package/dist/modules/number/index.d.ts +0 -8
- package/dist/modules/put/index.d.ts +0 -7
- package/dist/modules/string/index.d.ts +0 -5
- package/src/engine.ts +0 -414
- package/src/env.d.ts +0 -4
- package/src/errors.ts +0 -51
- package/src/index.css +0 -1
- package/src/lib/index.ts +0 -931
- package/src/lib/unocssShadowDomHack.css +0 -133
- package/src/modules/array/index.ts +0 -68
- package/src/modules/cond/index.ts +0 -123
- package/src/modules/date/index.ts +0 -254
- package/src/modules/index.ts +0 -9
- package/src/modules/match/index.ts +0 -54
- package/src/modules/math/index.ts +0 -198
- package/src/modules/media/index.ts +0 -27
- package/src/modules/number/index.ts +0 -73
- package/src/modules/number/localeNames.json +0 -16
- package/src/modules/number/locales/kkKz.json +0 -98
- package/src/modules/put/index.ts +0 -61
- 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 (isset(year) && isset(month) && isset(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
|
-
}
|