@peter.naydenov/morph 3.2.0 → 3.3.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.
- package/Changelog.md +5 -0
- package/README.md +32 -12
- package/dist/methods/build.d.ts +28 -2
- package/dist/methods/render.d.ts +9 -20
- package/package.json +1 -1
- package/src/methods/_renderHolder.js +13 -7
- package/src/methods/build.js +458 -376
- package/src/methods/render.js +23 -49
package/Changelog.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
### 3.3.0 (2025-12-13)
|
|
6
|
+
- [x] Use other helper from inside of helper functionsHelper functions attribute - useHelper;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
5
10
|
### 3.2.0 (2025-12-01)
|
|
6
11
|
- [x] Conditional actions are depricated. ( starting with `?` ). Too unconsistant with other actions. It's can be solved with normal helper functions by providing a list of possible templates. It's something that is possible even now;
|
|
7
12
|
- [x] Templates can have empty placeholders - just {{ }};
|
package/README.md
CHANGED
|
@@ -10,18 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
## What's new in version 3.x.x
|
|
14
|
-
|
|
15
|
-
In version 3 introduced `snippets`, you can choose to render only certain placeholders or groups of placeholders from the template. This lets you use templates as collections of reusable templates, so you can extract and use just the parts you need without having to render the whole template. This makes it easier to create and manage reusable template libraries for your project or rerender only the parts that need to be updated.
|
|
16
|
-
|
|
17
|
-
The `render` function now takes a command as its first argument. Available commands are: `render`, `debug`, `snippets`, `curry`, and `set`. Other arguments have no changes. Just shifted right. The second argument becomes the data, the third is dependencies, and the fourth is a list of post-processing functions.
|
|
18
|
-
|
|
19
|
-
In version 3.x.x, the data is always the second argument. It can be a string, as in version 2.x.x. The term "command" is no longer used for this argument; instead, it is called "instructions". Available instructions include: `raw`, `demo`, `handshake`, `placeholders`, and `count`.
|
|
20
|
-
|
|
21
|
-
How to migrate to version 3.x.x, please read the [Migration guide](./Migration.guide.md). Read more about `snippets` down below.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
13
|
## General Information
|
|
26
14
|
|
|
27
15
|
`Morph` has a logic-less template syntax. Placeholders are places surrounded by double curly braces `{{ }}` and they represents the pleces where the data will be inserted.
|
|
@@ -259,6 +247,38 @@ Helpers are templates and functions that are used by actions to decorate the dat
|
|
|
259
247
|
- `Extended render functions` will return a string like regular render functions, but will receive a deep branch of requested data;
|
|
260
248
|
- `Conditional render functions` could return null, that means: ignore this action. The result could be also a string: the name of other helper function that will render the data.
|
|
261
249
|
|
|
250
|
+
### Calling Helpers within Helpers
|
|
251
|
+
|
|
252
|
+
Starting from version 3.3.0, helper functions receive a `useHelper` function in their arguments object. This allows helpers to call other helpers programmatically.
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
const helpers = {
|
|
256
|
+
// Basic helper
|
|
257
|
+
format: ({ data }) => `[${data}]`,
|
|
258
|
+
|
|
259
|
+
// Helper using another helper
|
|
260
|
+
process: ({ data, useHelper }) => {
|
|
261
|
+
return useHelper('format') // Uses current data
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// Helper overriding data
|
|
265
|
+
customProcess: ({ data, useHelper }) => {
|
|
266
|
+
return useHelper('format', 'Override') // Uses provided data
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
// Helper calling a template string helper
|
|
270
|
+
linkToCheck: ({ data, useHelper }) => {
|
|
271
|
+
// 'link' is a string template helper defined elsewhere
|
|
272
|
+
if (data.url) return useHelper('link', { text: data.name, href: data.url })
|
|
273
|
+
return data.name
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`useHelper` signature: `useHelper(helperName, [dataOverride])`
|
|
279
|
+
- `helperName`: String name of the helper to call.
|
|
280
|
+
- `dataOverride`: Optional. Data to pass to the helper. If omitted, the current data context of the caller is used.
|
|
281
|
+
|
|
262
282
|
|
|
263
283
|
|
|
264
284
|
## Commands
|
package/dist/methods/build.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
export default build;
|
|
2
|
+
export type UseHelperFn = (name: string, data?: any) => any;
|
|
3
|
+
export type HelperFn = (args: {
|
|
4
|
+
data: any;
|
|
5
|
+
dependencies: object;
|
|
6
|
+
full?: any;
|
|
7
|
+
useHelper?: UseHelperFn;
|
|
8
|
+
memory?: object;
|
|
9
|
+
}) => any;
|
|
2
10
|
export type Template = {
|
|
3
11
|
/**
|
|
4
12
|
* - Data to be rendered;
|
|
@@ -7,17 +15,35 @@ export type Template = {
|
|
|
7
15
|
/**
|
|
8
16
|
* - Optional. Object with helper functions or simple templates for this template;
|
|
9
17
|
*/
|
|
10
|
-
helpers?:
|
|
18
|
+
helpers?: {
|
|
19
|
+
[x: string]: string | HelperFn;
|
|
20
|
+
};
|
|
11
21
|
/**
|
|
12
22
|
* - Optional. Example for data to be rendered with;
|
|
13
23
|
*/
|
|
14
24
|
handshake?: object;
|
|
15
25
|
};
|
|
16
26
|
export type tupleResult = any[];
|
|
27
|
+
/**
|
|
28
|
+
* @callback UseHelperFn
|
|
29
|
+
* @param {string} name - Name of the helper to call.
|
|
30
|
+
* @param {any} [data] - Optional data override.
|
|
31
|
+
* @returns {any} Result of the helper call.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* @callback HelperFn
|
|
35
|
+
* @param {object} args
|
|
36
|
+
* @param {any} args.data - The data context.
|
|
37
|
+
* @param {object} args.dependencies - Injected dependencies.
|
|
38
|
+
* @param {any} [args.full] - Full data context.
|
|
39
|
+
* @param {UseHelperFn} [args.useHelper] - Function to call other helpers.
|
|
40
|
+
* @param {object} [args.memory] - internal memory state.
|
|
41
|
+
* @returns {any} Rendered output.
|
|
42
|
+
*/
|
|
17
43
|
/**
|
|
18
44
|
* @typedef {Object} Template
|
|
19
45
|
* @property {string} template - Data to be rendered;
|
|
20
|
-
* @property {
|
|
46
|
+
* @property {Object.<string, HelperFn|string>} [helpers] - Optional. Object with helper functions or simple templates for this template;
|
|
21
47
|
* @property {object} [handshake] - Optional. Example for data to be rendered with;
|
|
22
48
|
*/
|
|
23
49
|
/**
|
package/dist/methods/render.d.ts
CHANGED
|
@@ -1,25 +1,14 @@
|
|
|
1
1
|
export default render;
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Renders a helper or template with the provided data and context.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* @param {any} theData - The data to process.
|
|
6
|
+
* @param {string} name - The name of the helper or template to render.
|
|
7
|
+
* @param {object} helpers - Dictionary of available helpers.
|
|
8
|
+
* @param {any} original - The full original data context.
|
|
9
|
+
* @param {object} dependencies - injected dependencies.
|
|
10
|
+
* @param {...any} args - Additional arguments.
|
|
7
11
|
*
|
|
8
|
-
* @
|
|
9
|
-
* @param {string} name - Name of the render helper/template to execute
|
|
10
|
-
* @param {object} helpers - Object containing helper functions and templates
|
|
11
|
-
* @param {object} original - Original data context for full data access
|
|
12
|
-
* @param {object} dependencies - External dependencies available to helpers
|
|
13
|
-
* @param {...any} args - Additional arguments passed to the render function
|
|
14
|
-
*
|
|
15
|
-
* @returns {string} Rendered string result
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* // Render with function helper
|
|
19
|
-
* const result = render(data, 'myHelper', helpers, originalData, deps);
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // Render with template
|
|
23
|
-
* const result = render(data, 'myTemplate', helpers, originalData, deps);
|
|
12
|
+
* @returns {any} The result of the rendering process.
|
|
24
13
|
*/
|
|
25
|
-
declare function render(theData:
|
|
14
|
+
declare function render(theData: any, name: string, helpers: object, original: any, dependencies: object, ...args: any[]): any;
|
package/package.json
CHANGED
|
@@ -26,17 +26,23 @@ function _renderHolder ( template, data ) {
|
|
|
26
26
|
chop = _chopTemplate (settings)( template )
|
|
27
27
|
, set = settings
|
|
28
28
|
;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
if ( typeof chop === 'string' ) return chop
|
|
30
|
+
chop.forEach ( ( item, i ) => {
|
|
31
|
+
const isPlaceholder = item.includes ( set.TG_PRX )
|
|
32
|
+
if ( isPlaceholder ) {
|
|
33
|
+
const field = item.replace(set.TG_PRX, '').replace(set.TG_SFX, '').trim();
|
|
34
|
+
if (data.hasOwnProperty(field) && data[field] != null) {
|
|
35
|
+
let val = data [ field ]
|
|
36
|
+
if ( typeof val === 'object' && val.text ) val = val.text
|
|
37
|
+
chop[i] = val
|
|
38
|
+
}
|
|
39
|
+
} // if isPlaceholder
|
|
40
|
+
}) // forEach chop
|
|
36
41
|
return chop.join ( '' )
|
|
37
42
|
} // _renderHolder func.
|
|
38
43
|
|
|
39
44
|
|
|
45
|
+
|
|
40
46
|
export default _renderHolder
|
|
41
47
|
|
|
42
48
|
|
package/src/methods/build.js
CHANGED
|
@@ -10,10 +10,28 @@ import render from './render.js'
|
|
|
10
10
|
|
|
11
11
|
import walk from '@peter.naydenov/walk'
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @callback UseHelperFn
|
|
15
|
+
* @param {string} name - Name of the helper to call.
|
|
16
|
+
* @param {any} [data] - Optional data override.
|
|
17
|
+
* @returns {any} Result of the helper call.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @callback HelperFn
|
|
22
|
+
* @param {object} args
|
|
23
|
+
* @param {any} args.data - The data context.
|
|
24
|
+
* @param {object} args.dependencies - Injected dependencies.
|
|
25
|
+
* @param {any} [args.full] - Full data context.
|
|
26
|
+
* @param {UseHelperFn} [args.useHelper] - Function to call other helpers.
|
|
27
|
+
* @param {object} [args.memory] - internal memory state.
|
|
28
|
+
* @returns {any} Rendered output.
|
|
29
|
+
*/
|
|
30
|
+
|
|
13
31
|
/**
|
|
14
32
|
* @typedef {Object} Template
|
|
15
33
|
* @property {string} template - Data to be rendered;
|
|
16
|
-
* @property {
|
|
34
|
+
* @property {Object.<string, HelperFn|string>} [helpers] - Optional. Object with helper functions or simple templates for this template;
|
|
17
35
|
* @property {object} [handshake] - Optional. Example for data to be rendered with;
|
|
18
36
|
*/
|
|
19
37
|
|
|
@@ -40,393 +58,457 @@ function build ( tpl, extra=false, buildDependencies={} ) {
|
|
|
40
58
|
return extra ? [ false, fail ] : fail
|
|
41
59
|
}
|
|
42
60
|
else { // If NO Error:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
61
|
+
const originalPlaceholders = structuredClone(placeholders);
|
|
62
|
+
// *** Template recognition complete. Start building the rendering function -->
|
|
63
|
+
/**
|
|
64
|
+
* Processes template rendering commands with provided data, dependencies, and optional post-processing functions.
|
|
65
|
+
*
|
|
66
|
+
* @function success
|
|
67
|
+
* @typedef { 'render' | 'debug' | 'snippets' | 'curry' | 'set' } Command
|
|
68
|
+
* @param {Command} [command='render'] - The command to execute. Supported commands: 'render', 'debug', 'snippets', 'curry', 'set', or 'snippets:<names>'.
|
|
69
|
+
* @param {Object|string} [d={}] - The data object to render, or a string instruction ('raw', 'demo', 'handshake', 'placeholders', 'count').
|
|
70
|
+
* @param {Object} [dependencies={}] - Additional dependencies to be merged with internal dependencies.
|
|
71
|
+
* @param {...any} args - Optional post-processing functions to apply to the rendered output.
|
|
72
|
+
* @returns {string|Array[]|function} The rendered template, snippets, data, or new function depending on the command and input.
|
|
73
|
+
*
|
|
74
|
+
* @throws {Error} If an unsupported command or instruction is provided.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // Render a template with data
|
|
78
|
+
* success('render', { name: 'Alice' }, { helperFn });
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // Get raw template
|
|
82
|
+
* success('debug', 'raw');
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Get count of remaining placeholders
|
|
86
|
+
* success('debug', 'count');
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* // Render only specific snippets
|
|
90
|
+
* success('snippets:header,footer', { ... });
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* // Curry partial data and get new render function
|
|
94
|
+
* const curried = success('curry', { partial: 'data' });
|
|
95
|
+
* curried('render', { more: 'data' });
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* // Set placeholders and get modified template
|
|
99
|
+
* const modified = success('set', { placeholders: { key: 'value' } });
|
|
100
|
+
*/
|
|
101
|
+
function success(command = 'render', d = {}, dependencies = {}, ...args) {
|
|
102
|
+
const cuts = structuredClone(chop)
|
|
103
|
+
let onlySnippets = false;
|
|
104
|
+
if (typeof command !== 'string') return `Error: Wrong command "${command}". Available commands: render, debug, snippets, set, curry.`
|
|
105
|
+
if (!['render', 'debug', 'snippets', 'set', 'curry'].includes(command) && !command.startsWith('snippets')) return `Error: Wrong command "${command}". Available commands: render, debug, snippets, set, curry.`
|
|
106
|
+
|
|
107
|
+
if (command.startsWith('snippets') && command.includes(':')) {
|
|
108
|
+
onlySnippets = true
|
|
109
|
+
let snippetNames = command.split(':')
|
|
110
|
+
.slice(1)[0]
|
|
111
|
+
.trim()
|
|
112
|
+
.split(',')
|
|
113
|
+
.map(t => t.trim())
|
|
114
|
+
placeholders = snippetNames.map(item => snippets[item])
|
|
115
|
+
}
|
|
116
|
+
else if (command === 'snippets') {
|
|
117
|
+
onlySnippets = true
|
|
118
|
+
}
|
|
119
|
+
else if (command === 'set') {
|
|
120
|
+
if (typeof d !== 'object' || !d) return `Error: 'set' command requires an object with placeholders, helpers, handshake.`
|
|
121
|
+
// Merge helpers
|
|
122
|
+
const newHelpers = { ...helpers, ...(d.helpers || {}) }
|
|
123
|
+
|
|
124
|
+
// Merge handshake
|
|
125
|
+
const newHandshake = handshake ? { ...handshake, ...(d.handshake || {}) } : d.handshake || {}
|
|
126
|
+
// Modify chop for placeholders
|
|
127
|
+
const newChop = [...chop]
|
|
128
|
+
if (d.placeholders) {
|
|
129
|
+
Object.entries(d.placeholders).forEach(([k, v]) => {
|
|
130
|
+
if (!isNaN(k)) { // If k is a number
|
|
131
|
+
let index = placeholders[k].index;
|
|
132
|
+
newChop[index] = v
|
|
133
|
+
} // if k is a number
|
|
134
|
+
else { // If k is a string
|
|
135
|
+
let plx = placeholders.find(p => p.name === k)
|
|
136
|
+
newChop[plx.index] = v
|
|
137
|
+
}
|
|
138
|
+
}) // placeholders
|
|
139
|
+
} // if d.placeholders
|
|
140
|
+
const newTemplateStr = newChop.join('');
|
|
141
|
+
const newTpl = {
|
|
142
|
+
template: newTemplateStr,
|
|
143
|
+
helpers: newHelpers,
|
|
144
|
+
handshake: newHandshake
|
|
145
|
+
}
|
|
146
|
+
const result = build(newTpl, false, buildDependencies)
|
|
147
|
+
return typeof result === 'function' ? result : () => result
|
|
148
|
+
} else if (command === 'curry') {
|
|
149
|
+
// Render the template with current data
|
|
150
|
+
const rendered = success('render', d, dependencies, ...args)
|
|
151
|
+
// Create new template with rendered as template, keep helpers and handshake
|
|
152
|
+
const newTpl = {
|
|
153
|
+
template: rendered,
|
|
154
|
+
helpers,
|
|
155
|
+
handshake
|
|
156
|
+
}
|
|
157
|
+
const newFn = build(newTpl, false, buildDependencies)
|
|
158
|
+
return newFn
|
|
159
|
+
} else placeholders = structuredClone(originalPlaceholders) // Reset placeholders if not snippets
|
|
160
|
+
|
|
161
|
+
if (typeof d === 'string') {
|
|
162
|
+
switch (d) {
|
|
163
|
+
case 'raw':
|
|
164
|
+
return cuts.join('') // Original template with placeholders
|
|
165
|
+
case 'demo':
|
|
166
|
+
if (!handshake) return `Error: No handshake data.`
|
|
167
|
+
d = handshake // Render with handshake object
|
|
168
|
+
break
|
|
169
|
+
case 'handshake':
|
|
170
|
+
if (!handshake) return `Error: No handshake data.`
|
|
171
|
+
return structuredClone(handshake) // return a copy of handshake object
|
|
172
|
+
case 'helpers':
|
|
173
|
+
return Object.keys(helpers).join(', ')
|
|
174
|
+
case 'placeholders':
|
|
175
|
+
return placeholders.map(h => cuts[h.index]).join(', ')
|
|
176
|
+
case 'count':
|
|
177
|
+
return placeholders.length
|
|
178
|
+
default:
|
|
179
|
+
return `Error: Wrong instruction "${d}". Available instructions: raw, demo, handshake, helpers, placeholders, count.`
|
|
180
|
+
}
|
|
181
|
+
} // if d is string
|
|
182
|
+
|
|
183
|
+
const endData = [];
|
|
184
|
+
const memory = {};
|
|
185
|
+
|
|
186
|
+
let topLevelType = _defineDataType(d);
|
|
187
|
+
let deps = { ...buildDependencies, ...dependencies }
|
|
188
|
+
d = walk({ data: d }) // Creates copy of data to avoid mutation of the original
|
|
189
|
+
let original = walk({ data: d }) // Creates copy of data to avoid mutation of the original
|
|
190
|
+
|
|
191
|
+
// useHelper definition for build.js
|
|
192
|
+
const useHelper = (targetName, targetData) => {
|
|
193
|
+
// If targetData provided, user that. If not, we can't easily know "current" data here outside loop.
|
|
194
|
+
// But each call site passes 'theData'.
|
|
195
|
+
// So useHelper actually needs to be a factory or passed with current data.
|
|
196
|
+
// Wait, in build.js we are iterating.
|
|
197
|
+
// We can define a generic useHelper here that takes (targetName, targetData).
|
|
198
|
+
// But if targetData is missing, it should default to the caller's data.
|
|
199
|
+
// The caller is the helper function.
|
|
200
|
+
// See implementation plan: "Implementation plan: useHelper(name, override). if override missing -> current data."
|
|
201
|
+
// In build.js loop:
|
|
202
|
+
// const paramUseHelper = (tName, tData) => render(tData || theData, tName, helpers, original, deps)
|
|
203
|
+
// We must define this INSIDE the loop or pass 'theData' to a factory.
|
|
204
|
+
// It's cleaner to define a factory here if possible, but 'theData' changes.
|
|
205
|
+
// So we will define a createUseHelper function here.
|
|
206
|
+
}
|
|
207
|
+
const createUseHelper = (currentData) => (targetName, targetData) => {
|
|
208
|
+
let
|
|
209
|
+
targetFn = helpers[targetName]
|
|
210
|
+
, isCompiledTemplate = targetFn && typeof targetFn === 'function' && targetFn.length >= 2 // success(command, d) has > 1 params
|
|
211
|
+
;
|
|
212
|
+
|
|
213
|
+
if (!targetFn) return `{{ Error: Helper '${targetName}' is not available}}`
|
|
214
|
+
|
|
215
|
+
if (isCompiledTemplate) {
|
|
216
|
+
// It's likely a compiled template (success function)
|
|
217
|
+
// Try calling it with 'render' command
|
|
218
|
+
try {
|
|
219
|
+
return targetFn('render', targetData || currentData, dependencies)
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
// Fallback if it wasn't a compiled template but just a function with arguments
|
|
223
|
+
return render(targetData || currentData, targetName, helpers, original, deps, ...args)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return render(targetData || currentData, targetName, helpers, original, deps, ...args)
|
|
227
|
+
}
|
|
228
|
+
if (topLevelType === 'null') return cuts.join('')
|
|
229
|
+
if (topLevelType !== 'array') d = [d]
|
|
230
|
+
|
|
231
|
+
// Handle null data case - just return the template without placeholders
|
|
232
|
+
if (topLevelType === 'null') return cuts.join('')
|
|
233
|
+
|
|
234
|
+
d.forEach(dElement => {
|
|
235
|
+
placeholders.forEach(holder => { // Placeholders
|
|
236
|
+
const
|
|
237
|
+
{ index, data, action } = holder // index - placeholder index, data - key of data, action - list of operations
|
|
238
|
+
, dataOnly = !action
|
|
239
|
+
, mem = structuredClone(memory)
|
|
240
|
+
// We cannot define extendArguments here with useHelper because we don't have 'theData' yet.
|
|
241
|
+
// It varies per dElement and deeper levels.
|
|
242
|
+
// Wait, 'info' is the data for this placeholder sort of.
|
|
243
|
+
// But 'extendArguments' is passed to helper.
|
|
244
|
+
// We need to inject 'useHelper' at the call site or update extendArguments inside the loops.
|
|
245
|
+
, extendArguments = { dependencies: deps, memory: mem }
|
|
246
|
+
;
|
|
247
|
+
let info = dElement;
|
|
248
|
+
|
|
249
|
+
if (data && data.includes('/')) {
|
|
250
|
+
if (info.hasOwnProperty(data)) {
|
|
251
|
+
info = info[data]
|
|
97
252
|
}
|
|
98
|
-
|
|
99
|
-
|
|
253
|
+
else {
|
|
254
|
+
data.split('/').forEach(d => {
|
|
255
|
+
if (info.hasOwnProperty(d)) info = info[d]
|
|
256
|
+
else info = []
|
|
257
|
+
})
|
|
100
258
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const newHelpers = { ...helpers, ...(d.helpers || {}) }
|
|
105
|
-
|
|
106
|
-
// Merge handshake
|
|
107
|
-
const newHandshake = handshake ? { ...handshake, ...(d.handshake || {}) } : d.handshake || {}
|
|
108
|
-
// Modify chop for placeholders
|
|
109
|
-
const newChop = [...chop]
|
|
110
|
-
if ( d.placeholders ) {
|
|
111
|
-
Object.entries ( d.placeholders ).forEach ( ([k,v]) => {
|
|
112
|
-
if ( !isNaN(k) ) { // If k is a number
|
|
113
|
-
let index = placeholders[k].index;
|
|
114
|
-
newChop[index] = v
|
|
115
|
-
} // if k is a number
|
|
116
|
-
else { // If k is a string
|
|
117
|
-
let plx = placeholders.find ( p => p.name === k )
|
|
118
|
-
newChop[ plx.index ] = v
|
|
119
|
-
}
|
|
120
|
-
}) // placeholders
|
|
121
|
-
} // if d.placeholders
|
|
122
|
-
const newTemplateStr = newChop.join ( '' );
|
|
123
|
-
const newTpl = {
|
|
124
|
-
template: newTemplateStr,
|
|
125
|
-
helpers: newHelpers,
|
|
126
|
-
handshake: newHandshake
|
|
127
|
-
}
|
|
128
|
-
const result = build ( newTpl, false, buildDependencies )
|
|
129
|
-
return typeof result === 'function' ? result : () => result
|
|
130
|
-
} else if ( command === 'curry' ) {
|
|
131
|
-
// Render the template with current data
|
|
132
|
-
const rendered = success('render', d, dependencies, ...args)
|
|
133
|
-
// Create new template with rendered as template, keep helpers and handshake
|
|
134
|
-
const newTpl = {
|
|
135
|
-
template: rendered,
|
|
136
|
-
helpers,
|
|
137
|
-
handshake
|
|
138
|
-
}
|
|
139
|
-
const newFn = build(newTpl, false, buildDependencies)
|
|
140
|
-
return newFn
|
|
141
|
-
} else placeholders = structuredClone ( originalPlaceholders ) // Reset placeholders if not snippets
|
|
142
|
-
|
|
143
|
-
if ( typeof d === 'string' ) {
|
|
144
|
-
switch ( d ) {
|
|
145
|
-
case 'raw':
|
|
146
|
-
return cuts.join ( '' ) // Original template with placeholders
|
|
147
|
-
case 'demo':
|
|
148
|
-
if ( !handshake ) return `Error: No handshake data.`
|
|
149
|
-
d = handshake // Render with handshake object
|
|
150
|
-
break
|
|
151
|
-
case 'handshake':
|
|
152
|
-
if ( !handshake ) return `Error: No handshake data.`
|
|
153
|
-
return structuredClone (handshake) // return a copy of handshake object
|
|
154
|
-
case 'helpers' :
|
|
155
|
-
return Object.keys ( helpers ).join ( ', ' )
|
|
156
|
-
case 'placeholders':
|
|
157
|
-
return placeholders.map ( h => cuts[h.index] ).join ( ', ')
|
|
158
|
-
case 'count':
|
|
159
|
-
return placeholders.length
|
|
160
|
-
default:
|
|
161
|
-
return `Error: Wrong instruction "${d}". Available instructions: raw, demo, handshake, helpers, placeholders, count.`
|
|
162
|
-
}
|
|
163
|
-
} // if d is string
|
|
164
|
-
|
|
165
|
-
const endData = [];
|
|
166
|
-
const memory = {};
|
|
167
|
-
|
|
168
|
-
let topLevelType = _defineDataType ( d );
|
|
169
|
-
let deps = { ...buildDependencies, ...dependencies }
|
|
170
|
-
d = walk ({data:d}) // Creates copy of data to avoid mutation of the original
|
|
171
|
-
let original = walk ({data:d}) // Creates copy of data to avoid mutation of the original
|
|
172
|
-
if ( topLevelType === 'null' ) return cuts.join ( '' )
|
|
173
|
-
if ( topLevelType !== 'array' ) d = [ d ]
|
|
174
|
-
|
|
175
|
-
// Handle null data case - just return the template without placeholders
|
|
176
|
-
if ( topLevelType === 'null' ) return cuts.join ( '' )
|
|
177
|
-
|
|
178
|
-
d.forEach ( dElement => {
|
|
179
|
-
placeholders.forEach ( holder => { // Placeholders
|
|
180
|
-
const
|
|
181
|
-
{ index, data, action } = holder // index - placeholder index, data - key of data, action - list of operations
|
|
182
|
-
, dataOnly = !action && data
|
|
183
|
-
, mem = structuredClone ( memory )
|
|
184
|
-
, extendArguments = { dependencies: deps, memory:mem }
|
|
185
|
-
;
|
|
186
|
-
let info = dElement;
|
|
187
|
-
|
|
188
|
-
if ( data && data.includes('/') ) {
|
|
189
|
-
if ( info.hasOwnProperty ( data )) {
|
|
190
|
-
info = info[data]
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
data.split('/').forEach ( d => {
|
|
194
|
-
if ( info.hasOwnProperty(d) ) info = info[d]
|
|
195
|
-
else info = []
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
} // If data contains '/'
|
|
199
|
-
else if ( data==='@all' || data===null || data==='@root' ) info = dElement
|
|
200
|
-
else if ( data ) info = info[data]
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if ( dataOnly ) {
|
|
205
|
-
const type = _defineDataType ( info );
|
|
206
|
-
switch ( type ) {
|
|
207
|
-
case 'function' :
|
|
208
|
-
cuts[index] = info ()
|
|
209
|
-
return
|
|
210
|
-
case 'primitive':
|
|
211
|
-
cuts[index] = info
|
|
212
|
-
return
|
|
213
|
-
case 'array':
|
|
214
|
-
if ( _defineDataType(info[0]) === 'primitive' ) cuts[index] = info[0]
|
|
215
|
-
return
|
|
216
|
-
case 'object':
|
|
217
|
-
if ( info.text ) cuts[index] = info.text
|
|
218
|
-
return
|
|
219
|
-
} // switch
|
|
220
|
-
} // dataOnly
|
|
221
|
-
else { // Data and Actions or only Actions
|
|
222
|
-
let
|
|
223
|
-
{ dataDeepLevel, nestedData } = _defineData ( info, action )
|
|
224
|
-
, actSetup = _actionSupply ( _setupActions ( action, dataDeepLevel ), dataDeepLevel )
|
|
225
|
-
;
|
|
226
|
-
|
|
227
|
-
for ( let step of actSetup ) {
|
|
228
|
-
let
|
|
229
|
-
{ type, name, level } = step
|
|
230
|
-
, levelData = nestedData[level] || []
|
|
231
|
-
;
|
|
259
|
+
} // If data contains '/'
|
|
260
|
+
else if (data === '@all' || data === null || data === '@root') info = dElement
|
|
261
|
+
else if (data) info = info[data]
|
|
232
262
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
if (dataOnly) {
|
|
266
|
+
const type = _defineDataType(info);
|
|
267
|
+
switch (type) {
|
|
268
|
+
case 'function':
|
|
269
|
+
cuts[index] = info()
|
|
270
|
+
return
|
|
271
|
+
case 'primitive':
|
|
272
|
+
cuts[index] = info
|
|
273
|
+
return
|
|
274
|
+
case 'array':
|
|
275
|
+
if (_defineDataType(info[0]) === 'primitive') cuts[index] = info[0]
|
|
276
|
+
return
|
|
277
|
+
case 'object':
|
|
278
|
+
if (info.text) cuts[index] = info.text
|
|
279
|
+
return
|
|
280
|
+
} // switch
|
|
281
|
+
} // dataOnly
|
|
282
|
+
else { // Data and Actions or only Actions
|
|
283
|
+
let
|
|
284
|
+
{ dataDeepLevel, nestedData } = _defineData(info, action)
|
|
285
|
+
, actSetup = _actionSupply(_setupActions(action, dataDeepLevel), dataDeepLevel)
|
|
286
|
+
;
|
|
287
|
+
|
|
288
|
+
for (let step of actSetup) {
|
|
289
|
+
let
|
|
290
|
+
{ type, name, level } = step
|
|
291
|
+
, levelData = nestedData[level] || []
|
|
292
|
+
;
|
|
293
|
+
|
|
294
|
+
levelData.forEach((theData, iData) => {
|
|
295
|
+
|
|
296
|
+
let dataType = _defineDataType(theData)
|
|
297
|
+
|
|
298
|
+
switch (type) { // Action type 'route','data', 'render', or mix -> different operations
|
|
299
|
+
case 'route': // DEPRICATED in version 3.2.0. This functionality can be replaced with normal render functions
|
|
300
|
+
switch (dataType) {
|
|
301
|
+
case 'array':
|
|
302
|
+
theData.forEach((d, i) => {
|
|
303
|
+
if (d == null) return
|
|
304
|
+
const dType = _defineDataType(d)
|
|
305
|
+
const useHelper = createUseHelper(d);
|
|
306
|
+
const routeName = helpers[name]({ data: d, ...extendArguments, full: d, useHelper });
|
|
307
|
+
if (routeName == null) return
|
|
308
|
+
if (dType === 'object') theData[i]['text'] = render(d, routeName, helpers, original, deps)
|
|
309
|
+
else theData[i] = render(d, routeName, helpers, original, deps)
|
|
310
|
+
})
|
|
311
|
+
break
|
|
312
|
+
case 'object':
|
|
313
|
+
theData['text'] = render(theData, name, helpers, original, deps)
|
|
314
|
+
break
|
|
315
|
+
}
|
|
316
|
+
break
|
|
317
|
+
case 'save':
|
|
318
|
+
memory[name] = structuredClone(theData)
|
|
319
|
+
break
|
|
320
|
+
case 'overwrite':
|
|
321
|
+
dElement = structuredClone(theData)
|
|
322
|
+
break
|
|
323
|
+
case 'data':
|
|
324
|
+
switch (dataType) {
|
|
325
|
+
case 'array':
|
|
326
|
+
theData.forEach((d, i) => {
|
|
327
|
+
const useHelper = createUseHelper(d);
|
|
328
|
+
theData[i] = (d instanceof Function) ? helpers[name]({ data: d(), ...extendArguments, full: d, useHelper }) : helpers[name]({ data: d, ...extendArguments, full: d, useHelper })
|
|
329
|
+
})
|
|
330
|
+
break
|
|
331
|
+
case 'object':
|
|
332
|
+
const uhObj = createUseHelper(theData);
|
|
333
|
+
nestedData[level] = [helpers[name]({ data: theData, ...extendArguments, full: d, useHelper: uhObj })]
|
|
334
|
+
break
|
|
335
|
+
case 'function':
|
|
336
|
+
const fnData = theData();
|
|
337
|
+
const uhFn = createUseHelper(fnData);
|
|
338
|
+
nestedData[level] = [helpers[name]({ data: fnData, ...extendArguments, full: d, useHelper: uhFn })]
|
|
339
|
+
break
|
|
340
|
+
case 'primitive':
|
|
341
|
+
const uhPrim = createUseHelper(theData);
|
|
342
|
+
nestedData[level] = helpers[name]({ data: theData, ...extendArguments, full: d, useHelper: uhPrim })
|
|
343
|
+
break
|
|
344
|
+
} // switch dataType
|
|
345
|
+
|
|
346
|
+
break
|
|
347
|
+
case 'render':
|
|
348
|
+
const isRenderFunction = typeof helpers[name] === 'function'; // Render could be a function and template.
|
|
349
|
+
switch (dataType) {
|
|
350
|
+
case 'array':
|
|
351
|
+
if (isRenderFunction) theData.forEach((d, i) => {
|
|
352
|
+
if (d == null) return
|
|
353
|
+
const dType = _defineDataType(d);
|
|
354
|
+
const useHelper = createUseHelper(d);
|
|
355
|
+
const text = helpers[name]({ data: d, ...extendArguments, full: original, useHelper });
|
|
356
|
+
|
|
357
|
+
if (text == null) theData[i] = null
|
|
358
|
+
if (dType === 'object') d['text'] = text
|
|
359
|
+
else theData[i] = text
|
|
360
|
+
})
|
|
361
|
+
else theData.forEach((d, i) => {
|
|
362
|
+
if (d == null) return
|
|
363
|
+
const
|
|
364
|
+
dType = _defineDataType(d)
|
|
365
|
+
, text = render(d, name, helpers, original, deps)
|
|
366
|
+
;
|
|
367
|
+
if (text == null) theData[i] = null
|
|
368
|
+
else if (dType === 'object') d['text'] = text
|
|
369
|
+
else theData[i] = text
|
|
370
|
+
})
|
|
371
|
+
break
|
|
372
|
+
case 'function':
|
|
373
|
+
const fD = theData();
|
|
374
|
+
const useHelperFn = createUseHelper(fD);
|
|
375
|
+
nestedData[level] = helpers[name]({ data: fD, ...extendArguments, full: d, useHelper: useHelperFn })
|
|
376
|
+
break
|
|
377
|
+
case 'primitive':
|
|
378
|
+
if (isRenderFunction) {
|
|
379
|
+
const uhPrim = createUseHelper(theData);
|
|
380
|
+
nestedData[level] = helpers[name]({ data: theData, ...extendArguments, full: d, useHelper: uhPrim })
|
|
381
|
+
}
|
|
382
|
+
else nestedData[level] = render(theData, name, helpers, original, deps)
|
|
383
|
+
break
|
|
384
|
+
case 'object':
|
|
385
|
+
if (isRenderFunction) {
|
|
386
|
+
const uhO = createUseHelper(theData);
|
|
387
|
+
nestedData[level][iData]['text'] = helpers[name]({ data: theData, ...extendArguments, full: d, useHelper: uhO })
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
theData['text'] = render(theData, name, helpers, original, deps)
|
|
391
|
+
}
|
|
392
|
+
break
|
|
393
|
+
} // switch renderDataType
|
|
394
|
+
break;
|
|
395
|
+
case 'extendedRender':
|
|
396
|
+
// TODO: Test extendedRender
|
|
397
|
+
const isValid = typeof helpers[name] === 'function'; // Render could be
|
|
398
|
+
if (isValid) {
|
|
399
|
+
nestedData[0].forEach((d, i) => {
|
|
400
|
+
const uh = createUseHelper(d);
|
|
401
|
+
nestedData[0][i] = helpers[name]({ data: d, ...extendArguments, full: d, useHelper: uh })
|
|
402
|
+
})
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
// TODO: Error...
|
|
406
|
+
}
|
|
407
|
+
break
|
|
408
|
+
case 'mix':
|
|
409
|
+
if (name === '') { // when is anonymous mixing action
|
|
410
|
+
switch (dataType) {
|
|
411
|
+
case 'object':
|
|
412
|
+
let kTest = Object.keys(theData).find(k => k.includes('/')); // Check if keys are breadcrumbs
|
|
413
|
+
if (kTest) Object.entries(theData).forEach(([k, v]) => nestedData[level][k] = v['text'])
|
|
414
|
+
else nestedData[level] = theData['text']
|
|
415
|
+
for (let i = level - 1; i >= 0; i--) {
|
|
416
|
+
nestedData[i] = walk({ data: nestedData[i], objectCallback: check })
|
|
417
|
+
}
|
|
418
|
+
function check({ value, breadcrumbs }) {
|
|
419
|
+
if (nestedData[level][breadcrumbs]) return nestedData[level][breadcrumbs]
|
|
420
|
+
return value
|
|
421
|
+
} // check
|
|
277
422
|
break
|
|
278
|
-
case '
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if ( text == null ) theData[i] = null
|
|
288
|
-
if ( dType === 'object' ) d['text'] = text
|
|
289
|
-
else theData[i] = text
|
|
290
|
-
})
|
|
291
|
-
else theData.forEach ( (d,i) => {
|
|
292
|
-
if ( d == null ) return
|
|
293
|
-
const
|
|
294
|
-
dType = _defineDataType ( d )
|
|
295
|
-
, text = render ( d, name, helpers, original, deps )
|
|
296
|
-
;
|
|
297
|
-
if ( text == null ) theData[i] = null
|
|
298
|
-
else if ( dType === 'object' ) d['text'] = text
|
|
299
|
-
else theData[i] = text
|
|
300
|
-
})
|
|
301
|
-
break
|
|
302
|
-
case 'function':
|
|
303
|
-
nestedData[level] = helpers[name]( {data:theData(), ...extendArguments, full: d} )
|
|
304
|
-
break
|
|
305
|
-
case 'primitive':
|
|
306
|
-
if ( isRenderFunction ) nestedData[level] = helpers[name]({ data:theData, ...extendArguments, full: d} )
|
|
307
|
-
else nestedData[level] = render ( theData, name, helpers, original, deps )
|
|
308
|
-
break
|
|
309
|
-
case 'object':
|
|
310
|
-
if ( isRenderFunction ) nestedData[level][iData]['text'] = helpers[name]({ data:theData, ...extendArguments, full: d })
|
|
311
|
-
else {
|
|
312
|
-
theData [ 'text' ] = render ( theData, name, helpers, original, deps )
|
|
313
|
-
}
|
|
314
|
-
break
|
|
315
|
-
} // switch renderDataType
|
|
316
|
-
break;
|
|
317
|
-
case 'extendedRender':
|
|
318
|
-
// TODO: Test extendedRender
|
|
319
|
-
const isValid = typeof helpers[name] === 'function'; // Render could be a function and template.
|
|
320
|
-
if ( isValid ) {
|
|
321
|
-
nestedData[0].forEach ( (d,i) => nestedData[0][i] = helpers[name]({ data:d, ...extendArguments, full: d }) )
|
|
423
|
+
case 'array':
|
|
424
|
+
theData.forEach((x, i) => {
|
|
425
|
+
if (i > 0) {
|
|
426
|
+
let xType = _defineDataType(x);
|
|
427
|
+
if (x == null) return
|
|
428
|
+
if (xType === 'object') theData[0] += `${x.text}`
|
|
429
|
+
else theData[0] += `${x}`
|
|
430
|
+
theData.toSpliced(i, 1)
|
|
322
431
|
}
|
|
323
|
-
|
|
324
|
-
|
|
432
|
+
else {
|
|
433
|
+
let xxType = _defineDataType(x);
|
|
434
|
+
theData[0] = ''
|
|
435
|
+
if (xxType === null) return
|
|
436
|
+
else if (xxType === 'object') theData[0] = `${x.text}`
|
|
437
|
+
else theData[0] = `${x}`
|
|
325
438
|
}
|
|
439
|
+
})
|
|
440
|
+
theData.length = 1
|
|
326
441
|
break
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
nestedData[i] = walk ({data:nestedData[i], objectCallback:check})
|
|
336
|
-
}
|
|
337
|
-
function check ({ value, breadcrumbs }) {
|
|
338
|
-
if ( nestedData[level][breadcrumbs] ) return nestedData[level][breadcrumbs]
|
|
339
|
-
return value
|
|
340
|
-
} // check
|
|
341
|
-
break
|
|
342
|
-
case 'array':
|
|
343
|
-
theData.forEach ( (x,i) => {
|
|
344
|
-
if ( i > 0 ) {
|
|
345
|
-
let xType = _defineDataType ( x );
|
|
346
|
-
if ( x == null ) return
|
|
347
|
-
if ( xType === 'object' ) theData[0] += `${x.text}`
|
|
348
|
-
else theData[0] += `${x}`
|
|
349
|
-
theData.toSpliced(i,1)
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
let xxType = _defineDataType ( x );
|
|
353
|
-
theData[0] = ''
|
|
354
|
-
if ( xxType === null ) return
|
|
355
|
-
else if ( xxType === 'object' ) theData[0] = `${x.text}`
|
|
356
|
-
else theData[0] = `${x}`
|
|
357
|
-
}
|
|
358
|
-
})
|
|
359
|
-
theData.length = 1
|
|
360
|
-
break
|
|
361
|
-
} // switch dataType
|
|
362
|
-
} // if name === ''
|
|
363
|
-
else {
|
|
364
|
-
let
|
|
365
|
-
val = helpers[name]({ data:theData, ...extendArguments, full: d })
|
|
366
|
-
, valType = _defineDataType ( val )
|
|
367
|
-
;
|
|
368
|
-
|
|
369
|
-
theData.forEach ( ( x, i ) => theData.splice ( i, 1) )
|
|
370
|
-
theData.length = 0
|
|
371
|
-
switch ( valType ) {
|
|
372
|
-
case 'primitive':
|
|
373
|
-
theData[0] = val
|
|
374
|
-
break
|
|
375
|
-
case 'array':
|
|
376
|
-
theData.push ( ...val )
|
|
377
|
-
break
|
|
378
|
-
} // switch valType
|
|
379
|
-
}
|
|
380
|
-
break
|
|
381
|
-
default:
|
|
382
|
-
break
|
|
383
|
-
}
|
|
384
|
-
}) // levelData
|
|
385
|
-
} // for step of actSetup
|
|
386
|
-
|
|
387
|
-
if ( nestedData instanceof Array &&
|
|
388
|
-
nestedData.length === 1 &&
|
|
389
|
-
nestedData[0] instanceof Array
|
|
390
|
-
) nestedData = nestedData[0]
|
|
391
|
-
if ( nestedData[0] == null ) return
|
|
392
|
-
|
|
393
|
-
let
|
|
394
|
-
accType = _defineDataType ( nestedData[0] )
|
|
395
|
-
, fineData = nestedData[0]
|
|
396
|
-
;
|
|
442
|
+
} // switch dataType
|
|
443
|
+
} // if name === ''
|
|
444
|
+
else {
|
|
445
|
+
const dType = _defineDataType(d);
|
|
446
|
+
const useHelper = createUseHelper(d);
|
|
447
|
+
let val = helpers[name]({ data: theData, ...extendArguments, full: d, useHelper });
|
|
448
|
+
let valType = _defineDataType(val)
|
|
449
|
+
;
|
|
397
450
|
|
|
398
|
-
|
|
451
|
+
theData.forEach((x, i) => theData.splice(i, 1))
|
|
452
|
+
theData.length = 0
|
|
453
|
+
switch (valType) {
|
|
399
454
|
case 'primitive':
|
|
400
|
-
|
|
401
|
-
cuts[index] = fineData
|
|
402
|
-
break
|
|
403
|
-
case 'object':
|
|
404
|
-
if (fineData['text'] == null ) return
|
|
405
|
-
cuts[index] = fineData['text']
|
|
455
|
+
theData[0] = val
|
|
406
456
|
break
|
|
407
457
|
case 'array':
|
|
408
|
-
|
|
409
|
-
if ( aType === 'object' ) cuts[index] = fineData.map ( x => x.text ).join ( '')
|
|
410
|
-
else cuts[index] = fineData.join ( '' )
|
|
458
|
+
theData.push(...val)
|
|
411
459
|
break
|
|
412
|
-
} // switch
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
460
|
+
} // switch valType
|
|
461
|
+
}
|
|
462
|
+
break
|
|
463
|
+
default:
|
|
464
|
+
break
|
|
465
|
+
}
|
|
466
|
+
}) // levelData
|
|
467
|
+
} // for step of actSetup
|
|
468
|
+
|
|
469
|
+
if (nestedData instanceof Array &&
|
|
470
|
+
nestedData.length === 1 &&
|
|
471
|
+
nestedData[0] instanceof Array
|
|
472
|
+
) nestedData = nestedData[0]
|
|
473
|
+
if (nestedData[0] == null) return
|
|
474
|
+
|
|
475
|
+
let
|
|
476
|
+
accType = _defineDataType(nestedData[0])
|
|
477
|
+
, fineData = nestedData[0]
|
|
478
|
+
;
|
|
479
|
+
|
|
480
|
+
switch (accType) {
|
|
481
|
+
case 'primitive':
|
|
482
|
+
if (fineData == null) return
|
|
483
|
+
cuts[index] = fineData
|
|
484
|
+
break
|
|
485
|
+
case 'object':
|
|
486
|
+
if (fineData['text'] == null) return
|
|
487
|
+
cuts[index] = fineData['text']
|
|
488
|
+
break
|
|
489
|
+
case 'array':
|
|
490
|
+
const aType = _defineDataType(fineData[0])
|
|
491
|
+
if (aType === 'object') cuts[index] = fineData.map(x => x.text).join('')
|
|
492
|
+
else cuts[index] = fineData.join('')
|
|
493
|
+
break
|
|
494
|
+
} // switch accType
|
|
495
|
+
} // else other
|
|
496
|
+
}) // forEach placeholders
|
|
497
|
+
|
|
498
|
+
if (onlySnippets) endData.push(placeholders.map(x => cuts[x.index]).join('<~>'))
|
|
499
|
+
else endData.push(cuts.join(''))
|
|
500
|
+
}) // forEach d
|
|
501
|
+
|
|
502
|
+
if (topLevelType === 'array') return endData
|
|
503
|
+
// Execute postprocess functions
|
|
504
|
+
if (args) return args.reduce((acc, fn) => {
|
|
505
|
+
if (typeof fn !== 'function') return acc
|
|
506
|
+
return fn(acc, deps)
|
|
507
|
+
}, endData.join(''))
|
|
508
|
+
else return endData.join('')
|
|
509
|
+
} // success func.
|
|
510
|
+
return extra ? [true, success] : success
|
|
511
|
+
} // if no error
|
|
430
512
|
} // build func.
|
|
431
513
|
|
|
432
514
|
|
package/src/methods/render.js
CHANGED
|
@@ -1,60 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import _renderHolder from './_renderHolder.js'
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {
|
|
14
|
-
* @param {object} original - Original data context for full data access
|
|
15
|
-
* @param {object} dependencies - External dependencies available to helpers
|
|
16
|
-
* @param {...any} args - Additional arguments passed to the render function
|
|
17
|
-
*
|
|
18
|
-
* @returns {string} Rendered string result
|
|
7
|
+
* Renders a helper or template with the provided data and context.
|
|
8
|
+
*
|
|
9
|
+
* @param {any} theData - The data to process.
|
|
10
|
+
* @param {string} name - The name of the helper or template to render.
|
|
11
|
+
* @param {object} helpers - Dictionary of available helpers.
|
|
12
|
+
* @param {any} original - The full original data context.
|
|
13
|
+
* @param {object} dependencies - injected dependencies.
|
|
14
|
+
* @param {...any} args - Additional arguments.
|
|
19
15
|
*
|
|
20
|
-
* @
|
|
21
|
-
* // Render with function helper
|
|
22
|
-
* const result = render(data, 'myHelper', helpers, originalData, deps);
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* // Render with template
|
|
26
|
-
* const result = render(data, 'myTemplate', helpers, originalData, deps);
|
|
16
|
+
* @returns {any} The result of the rendering process.
|
|
27
17
|
*/
|
|
28
|
-
function render ( theData, name, helpers, original, dependencies, ...args
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* @param {any} d - Data to normalize
|
|
41
|
-
* @returns {object} Returns { text: d } if d is string, otherwise returns d unchanged
|
|
42
|
-
*/
|
|
43
|
-
function setRenderData ( d={} ) {
|
|
44
|
-
if ( typeof d === 'string' ) return { text: d }
|
|
45
|
-
else return d
|
|
46
|
-
} // setRenderData func.
|
|
47
|
-
|
|
48
|
-
if (!helpers[name]) return `{{ Error: Helper '${name}' is not available}}`
|
|
49
|
-
const isRenderFunction = typeof helpers[name] === 'function'; // Render could be a function or template.
|
|
50
|
-
theData = setRenderData ( theData )
|
|
51
|
-
|
|
52
|
-
if ( isRenderFunction ) return helpers[name]( { theData, dependencies, full:original}, ...args )
|
|
53
|
-
else return _renderHolder ( helpers[name], theData )
|
|
18
|
+
function render ( theData, name, helpers, original, dependencies, ...args) {
|
|
19
|
+
const useHelper = ( targetName, targetData ) => render ( targetData || theData, targetName, helpers, original, dependencies, ...args )
|
|
20
|
+
if (!helpers[name]) return `( Error: Helper '${name}' is not available )`
|
|
21
|
+
|
|
22
|
+
const isRenderFunction = typeof helpers[name] === 'function';
|
|
23
|
+
|
|
24
|
+
if ( isRenderFunction ) return helpers[name]({ data: theData, dependencies, full: original, useHelper }, ...args)
|
|
25
|
+
else {
|
|
26
|
+
let dataForHolder = theData
|
|
27
|
+
if ( typeof theData !== 'object' || theData === null ) dataForHolder = { text: theData }
|
|
28
|
+
return _renderHolder ( helpers[name], dataForHolder )
|
|
29
|
+
}
|
|
54
30
|
} // render func.
|
|
55
31
|
|
|
56
32
|
|
|
57
33
|
|
|
58
34
|
export default render
|
|
59
|
-
|
|
60
|
-
|