@peter.naydenov/morph 3.2.0 → 3.3.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.
@@ -0,0 +1,65 @@
1
+ import actionRender from './actionRender.js'
2
+ import actionData from './actionData.js'
3
+ import actionMix from './actionMix.js'
4
+ import actionExtendedRender from './actionExtendedRender.js'
5
+ import _defineDataType from "./_defineType.js"
6
+ import { actionSave, actionOverwrite } from './actionSave.js'
7
+
8
+
9
+
10
+ function executeActions({ nestedData, actSetup, helpers, original, dependencies, memory, dElement, createUseHelper }) {
11
+ let currentDElement = dElement;
12
+
13
+ for (let step of actSetup) {
14
+ let
15
+ { type, name, level } = step
16
+ , levelData = nestedData[level] || []
17
+ ;
18
+
19
+ // If levelData is undefined, we can't loop. But it should be [] from _defineData?
20
+ // nestedData[level] might be undefined?
21
+ // build.js said: `let levelData = nestedData[level] || []`
22
+
23
+ levelData.forEach((theData, iData) => {
24
+ const context = {
25
+ helpers
26
+ , original
27
+ , dependencies
28
+ , memory
29
+ , useHelper: createUseHelper
30
+ , nestedData
31
+ , level
32
+ , iData
33
+ , extendArguments: { dependencies, memory }
34
+ }
35
+
36
+ switch (type) {
37
+ case 'render':
38
+ actionRender(step, theData, context)
39
+ break
40
+ case 'data':
41
+ actionData(step, theData, context)
42
+ break
43
+ case 'mix':
44
+ actionMix(step, theData, context)
45
+ break
46
+ case 'save':
47
+ actionSave(step, theData, context)
48
+ break
49
+ case 'overwrite':
50
+ currentDElement = actionOverwrite(step, theData)
51
+ break
52
+ case 'route':
53
+ // Deprecated, removed as per refactoring plan
54
+ break
55
+ case 'extendedRender':
56
+ actionExtendedRender(step, theData, context)
57
+ break
58
+ }
59
+ })
60
+ }
61
+
62
+ return currentDElement
63
+ }
64
+
65
+ export default executeActions
@@ -0,0 +1,97 @@
1
+
2
+ /**
3
+ * Handles debug commands for template inspection.
4
+ *
5
+ * @param {string} d - Debug instruction
6
+ * @param {object} context - Context object
7
+ * @param {object} context.handshake - Example data
8
+ * @param {object} context.helpers - Helper functions
9
+ * @param {array} context.placeholders - Placeholder definitions
10
+ * @param {array} context.cuts - Chopped template parts
11
+ * @returns {any} Debug result or error message
12
+ */
13
+ function handleDebug(d, { handshake, helpers, placeholders, cuts }) {
14
+ switch (d) {
15
+ case 'raw':
16
+ return cuts.join('')
17
+ case 'demo':
18
+ if (!handshake) return `Error: No handshake data.`
19
+ return handshake // Caller handles rendering handshake
20
+ case 'handshake':
21
+ if (!handshake) return `Error: No handshake data.`
22
+ return structuredClone(handshake)
23
+ case 'helpers':
24
+ return Object.keys(helpers).join(', ')
25
+ case 'placeholders':
26
+ return placeholders.map(h => cuts[h.index]).join(', ')
27
+ case 'count':
28
+ return placeholders.length
29
+ default:
30
+ return `Error: Wrong instruction "${d}". Available instructions: raw, demo, handshake, helpers, placeholders, count.`
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Handles the 'set' command to modify template properties.
36
+ *
37
+ * @param {object} d - Modification data
38
+ * @param {object} context - Context object
39
+ * @param {object} context.helpers - Current helper functions
40
+ * @param {object} context.handshake - Current handshake data
41
+ * @param {array} context.placeholders - Current placeholders
42
+ * @param {array} context.chop - Current chopped template
43
+ * @param {function} context.build - Build function
44
+ * @param {object} context.buildDependencies - Build dependencies
45
+ * @returns {function} Modified template function
46
+ */
47
+ function handleSet(d, { helpers, handshake, placeholders, chop, build, buildDependencies }) {
48
+ if (typeof d !== 'object' || !d) return `Error: 'set' command requires an object with placeholders, helpers, handshake.`
49
+
50
+ const newHelpers = { ...helpers, ...(d.helpers || {}) }
51
+ const newHandshake = handshake ? { ...handshake, ...(d.handshake || {}) } : d.handshake || {}
52
+ const newChop = [...chop]
53
+
54
+ if (d.placeholders) {
55
+ Object.entries(d.placeholders).forEach(([k, v]) => {
56
+ if (!isNaN(k)) {
57
+ let index = placeholders[k].index;
58
+ newChop[index] = v
59
+ }
60
+ else {
61
+ let plx = placeholders.find(p => p.name === k)
62
+ newChop[plx.index] = v
63
+ }
64
+ })
65
+ }
66
+
67
+ const newTemplateStr = newChop.join('');
68
+ const newTpl = {
69
+ template: newTemplateStr,
70
+ helpers: newHelpers,
71
+ handshake: newHandshake
72
+ }
73
+
74
+ const result = build(newTpl, false, buildDependencies)
75
+ return typeof result === 'function' ? result : () => result
76
+ }
77
+
78
+ /**
79
+ * Handles snippets command to select specific placeholders.
80
+ *
81
+ * @param {string} command - Snippets command
82
+ * @param {object} snippets - Snippets mapping
83
+ * @returns {array|null} Selected placeholders or null
84
+ */
85
+ function handleSnippets(command, snippets) {
86
+ if (command.includes(':')) {
87
+ let snippetNames = command.split(':')
88
+ .slice(1)[0]
89
+ .trim()
90
+ .split(',')
91
+ .map(t => t.trim())
92
+ return snippetNames.map(item => snippets[item])
93
+ }
94
+ return null // Indicates 'all snippets' or logic handled by caller
95
+ }
96
+
97
+ export { handleDebug, handleSet, handleSnippets }
@@ -0,0 +1,155 @@
1
+ import _defineData from "./_defineData.js"
2
+ import _defineDataType from "./_defineType.js"
3
+ import _actionSupply from "./_actionSupply.js"
4
+ import _setupActions from "./_setupActions.js"
5
+ import executeActions from "./executeActions.js"
6
+ import render from "./render.js"
7
+
8
+ /**
9
+ * Processes placeholders in the template with provided data and context.
10
+ *
11
+ * @param {object} params - Parameters object
12
+ * @param {array} params.d - Data array to process
13
+ * @param {array} params.chop - Chopped template parts
14
+ * @param {array} params.placeholders - Placeholder definitions
15
+ * @param {any} params.original - Original data context
16
+ * @param {object} params.helpers - Helper functions
17
+ * @param {object} params.dependencies - Injected dependencies
18
+ * @param {object} params.memory - Internal memory state
19
+ * @param {array} params.args - Additional arguments
20
+ * @param {boolean} params.onlySnippets - Whether to process only snippets
21
+ * @returns {array} Processed template parts
22
+ */
23
+ function processPlaceholders ({ d, chop, placeholders, original, helpers, dependencies, memory, args, onlySnippets }) {
24
+ const endData = []
25
+ // We need a factory for useHelper.
26
+ // It depends on 'currentData'. But here we are iterating 'd'.
27
+
28
+ // Helper factory to share with executeActions
29
+ const createUseHelperFactory = (currentData) => (targetName, targetData) => {
30
+ let
31
+ targetFn = helpers[targetName]
32
+ , isCompiledTemplate = targetFn && typeof targetFn === 'function' && targetFn.length >= 2
33
+ ;
34
+ if (!targetFn) return `( Error: Helper '${targetName}' is not available )`
35
+
36
+ if (isCompiledTemplate) {
37
+ try {
38
+ return targetFn('render', targetData || currentData, dependencies)
39
+ }
40
+ catch (e) {
41
+ return render(targetData || currentData, targetName, helpers, original, dependencies, ...args)
42
+ }
43
+ }
44
+ return render(targetData || currentData, targetName, helpers, original, dependencies, ...args)
45
+ }
46
+
47
+
48
+ d.forEach(dElement => {
49
+ let
50
+ cuts = structuredClone(chop)
51
+ , currentDElement = dElement // Can be updated by overwrite
52
+ ;
53
+
54
+ placeholders.forEach(holder => {
55
+ const
56
+ { index, data, action } = holder
57
+ , dataOnly = !action
58
+ , mem = structuredClone(memory)
59
+ ;
60
+ let info = currentDElement;
61
+
62
+ // Data Resolution
63
+ if (data && data.includes('/')) {
64
+ if (info.hasOwnProperty(data)) {
65
+ info = info[data]
66
+ }
67
+ else {
68
+ data.split('/').forEach(d => {
69
+ if (info.hasOwnProperty(d)) info = info[d]
70
+ else info = [] // Bug in original? info = [] then info['next'] crashes?
71
+ // Original: else info = []. 256.
72
+ // If info is [], next iteration info['key'] is undefined.
73
+ // So safe.
74
+ })
75
+ }
76
+ }
77
+ else if (data === '@all' || data === null || data === '@root') info = currentDElement
78
+ else if (data) info = info[data]
79
+
80
+
81
+ if (dataOnly) {
82
+ const type = _defineDataType(info);
83
+ switch (type) {
84
+ case 'function':
85
+ cuts[index] = info()
86
+ break
87
+ case 'primitive':
88
+ cuts[index] = info
89
+ break
90
+ case 'array':
91
+ if (_defineDataType(info[0]) === 'primitive') cuts[index] = info[0]
92
+ break
93
+ case 'object':
94
+ if (info.text) cuts[index] = info.text
95
+ break
96
+ }
97
+ }
98
+ else {
99
+ // Logic for data processing via actions
100
+ let
101
+ { dataDeepLevel, nestedData } = _defineData(info, action)
102
+ , actSetup = _actionSupply(_setupActions(action, dataDeepLevel), dataDeepLevel)
103
+ ;
104
+
105
+ const createUseHelper = createUseHelperFactory(info); // use info as context?
106
+ // In build.js it was `d` (the item from levelData).
107
+ // `executeActions` passes `theData` to `createUseHelper`.
108
+
109
+ currentDElement = executeActions({
110
+ nestedData
111
+ , actSetup
112
+ , helpers
113
+ , original
114
+ , dependencies
115
+ , memory
116
+ , dElement: currentDElement
117
+ , createUseHelper: createUseHelperFactory
118
+ })
119
+
120
+ // Flatten Step
121
+ if (nestedData instanceof Array && nestedData.length === 1 && nestedData[0] instanceof Array) nestedData = nestedData[0]
122
+ if (nestedData[0] == null) return
123
+
124
+ let
125
+ accType = _defineDataType(nestedData[0])
126
+ , fineData = nestedData[0]
127
+ ;
128
+
129
+ switch (accType) {
130
+ case 'primitive':
131
+ if (fineData == null) return
132
+ cuts[index] = fineData
133
+ break
134
+ case 'object':
135
+ if (fineData['text'] == null) return
136
+ cuts[index] = fineData['text']
137
+ break
138
+ case 'array':
139
+ const aType = _defineDataType(fineData[0])
140
+ if (aType === 'object') cuts[index] = fineData.map(x => x.text).join('')
141
+ else cuts[index] = fineData.join('')
142
+ break
143
+ }
144
+ }
145
+ }) // placeholders loop
146
+
147
+ if (onlySnippets) endData.push(placeholders.map(x => cuts[x.index]).join('<~>'))
148
+ else endData.push(cuts.join(''))
149
+
150
+ }) // d.forEach
151
+
152
+ return endData
153
+ }
154
+
155
+ export default processPlaceholders
@@ -1,60 +1,34 @@
1
- import _renderHolder from "./_renderHolder.js"
1
+
2
+ import _renderHolder from './_renderHolder.js'
2
3
 
3
4
 
4
5
 
5
6
  /**
6
- * Executes rendering and returns the rendered result.
7
- *
8
- * Handles both function-based and template-based rendering. Normalizes data objects
9
- * by converting nested objects to their 'text' properties and arrays to their first elements.
10
- *
11
- * @param {object|string} theData - Data to be rendered. If string, becomes the 'text' property value.
12
- * @param {string} name - Name of the render helper/template to execute
13
- * @param {object} helpers - Object containing helper functions and templates
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
- * @example
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, or an error string if the helper is not available.
27
17
  */
28
- function render ( theData, name, helpers, original, dependencies, ...args ) {
29
- // *** Executes rendering and return the results
30
- if ( theData instanceof Object ) { // Make sure all properties are not objects
31
- Object.entries ( theData ).forEach ( ([key, value]) => {
32
- if ( value instanceof Object ) theData[key] = value['text']
33
- if ( value instanceof Array ) theData[key] = value[0]
34
- })
35
- }
36
-
37
- /**
38
- * Normalizes render data by wrapping strings in text objects.
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
-