@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.
- package/Changelog.md +5 -0
- package/README.md +32 -12
- package/build.architecture.png +0 -0
- package/dist/methods/actionData.d.ts +10 -0
- package/dist/methods/actionExtendedRender.d.ts +10 -0
- package/dist/methods/actionMix.d.ts +10 -0
- package/dist/methods/actionRender.d.ts +13 -0
- package/dist/methods/actionSave.d.ts +8 -0
- package/dist/methods/build.d.ts +28 -2
- package/dist/methods/executeActions.d.ts +11 -0
- package/dist/methods/processCommands.d.ts +46 -0
- package/dist/methods/processPlaceholders.d.ts +27 -0
- package/dist/methods/render.d.ts +9 -20
- package/package.json +1 -1
- package/src/methods/_renderHolder.js +13 -7
- package/src/methods/actionData.js +30 -0
- package/src/methods/actionExtendedRender.js +28 -0
- package/src/methods/actionMix.js +66 -0
- package/src/methods/actionRender.js +86 -0
- package/src/methods/actionSave.js +9 -0
- package/src/methods/build.js +123 -404
- package/src/methods/executeActions.js +65 -0
- package/src/methods/processCommands.js +97 -0
- package/src/methods/processPlaceholders.js +155 -0
- package/src/methods/render.js +23 -49
|
@@ -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
|
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, or an error string if the helper is not available.
|
|
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
|
-
|