@peter.naydenov/morph 3.1.5 → 3.2.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/.mocharc.json +1 -0
- package/.opencode/command/speckit.analyze.md +184 -0
- package/.opencode/command/speckit.checklist.md +294 -0
- package/.opencode/command/speckit.clarify.md +181 -0
- package/.opencode/command/speckit.constitution.md +82 -0
- package/.opencode/command/speckit.implement.md +135 -0
- package/.opencode/command/speckit.plan.md +89 -0
- package/.opencode/command/speckit.specify.md +257 -0
- package/.opencode/command/speckit.tasks.md +137 -0
- package/.opencode/command/speckit.taskstoissues.md +28 -0
- package/.specify/memory/constitution.md +43 -0
- package/.specify/scripts/bash/check-prerequisites.sh +166 -0
- package/.specify/scripts/bash/common.sh +156 -0
- package/.specify/scripts/bash/create-new-feature.sh +305 -0
- package/.specify/scripts/bash/setup-plan.sh +61 -0
- package/.specify/scripts/bash/update-agent-context.sh +790 -0
- package/.specify/templates/agent-file-template.md +28 -0
- package/.specify/templates/checklist-template.md +40 -0
- package/.specify/templates/plan-template.md +104 -0
- package/.specify/templates/spec-template.md +115 -0
- package/.specify/templates/tasks-template.md +251 -0
- package/Changelog.md +12 -0
- package/README.md +154 -3
- package/dist/morph.cjs +1 -1
- package/dist/morph.esm.mjs +1 -1
- package/dist/morph.umd.js +1 -1
- package/morph.png +0 -0
- package/package.json +3 -2
- package/simple.js +17 -0
- package/specs/001-extend-templates/checklists/requirements.md +35 -0
- package/specs/001-extend-templates/contracts/set-method.json +39 -0
- package/specs/001-extend-templates/data-model.md +76 -0
- package/specs/001-extend-templates/plan.md +90 -0
- package/specs/001-extend-templates/quickstart.md +56 -0
- package/specs/001-extend-templates/research.md +18 -0
- package/specs/001-extend-templates/spec.md +139 -0
- package/specs/001-extend-templates/tasks.md +255 -0
- package/src/methods/_readTemplate.js +22 -46
- package/src/methods/_setupActions.js +1 -8
- package/src/methods/build.js +98 -43
- package/src/methods/render.js +14 -11
package/src/methods/build.js
CHANGED
|
@@ -34,8 +34,6 @@ import walk from '@peter.naydenov/walk'
|
|
|
34
34
|
*/
|
|
35
35
|
function build ( tpl, extra=false, buildDependencies={} ) {
|
|
36
36
|
let { hasError, placeholders, chop, helpers, handshake, snippets } = _readTemplate ( tpl );
|
|
37
|
-
|
|
38
|
-
|
|
39
37
|
|
|
40
38
|
if ( hasError ) {
|
|
41
39
|
function fail () { return hasError }
|
|
@@ -44,36 +42,49 @@ function build ( tpl, extra=false, buildDependencies={} ) {
|
|
|
44
42
|
else { // If NO Error:
|
|
45
43
|
const originalPlaceholders = structuredClone ( placeholders );
|
|
46
44
|
// *** Template recognition complete. Start building the rendering function -->
|
|
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
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Processes template rendering commands with provided data, dependencies, and optional post-processing functions.
|
|
47
|
+
*
|
|
48
|
+
* @function success
|
|
49
|
+
* @typedef { 'render' | 'debug' | 'snippets' | 'curry' | 'set' } Command
|
|
50
|
+
* @param {Command} [command='render'] - The command to execute. Supported commands: 'render', 'debug', 'snippets', 'curry', 'set', or 'snippets:<names>'.
|
|
51
|
+
* @param {Object|string} [d={}] - The data object to render, or a string instruction ('raw', 'demo', 'handshake', 'placeholders', 'count').
|
|
52
|
+
* @param {Object} [dependencies={}] - Additional dependencies to be merged with internal dependencies.
|
|
53
|
+
* @param {...any} args - Optional post-processing functions to apply to the rendered output.
|
|
54
|
+
* @returns {string|Array[]|function} The rendered template, snippets, data, or new function depending on the command and input.
|
|
55
|
+
*
|
|
56
|
+
* @throws {Error} If an unsupported command or instruction is provided.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Render a template with data
|
|
60
|
+
* success('render', { name: 'Alice' }, { helperFn });
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Get raw template
|
|
64
|
+
* success('debug', 'raw');
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* // Get count of remaining placeholders
|
|
68
|
+
* success('debug', 'count');
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // Render only specific snippets
|
|
72
|
+
* success('snippets:header,footer', { ... });
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* // Curry partial data and get new render function
|
|
76
|
+
* const curried = success('curry', { partial: 'data' });
|
|
77
|
+
* curried('render', { more: 'data' });
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* // Set placeholders and get modified template
|
|
81
|
+
* const modified = success('set', { placeholders: { key: 'value' } });
|
|
82
|
+
*/
|
|
83
|
+
function success ( command='render', d={}, dependencies={}, ...args ) {
|
|
84
|
+
const cuts = structuredClone ( chop )
|
|
85
|
+
let onlySnippets = false;
|
|
86
|
+
if ( typeof command !== 'string' ) return `Error: Wrong command "${command}". Available commands: render, debug, snippets, set, curry.`
|
|
87
|
+
if ( ![ 'render', 'debug', 'snippets', 'set', 'curry'].includes ( command ) && !command.startsWith('snippets') ) return `Error: Wrong command "${command}". Available commands: render, debug, snippets, set, curry.`
|
|
77
88
|
|
|
78
89
|
if ( command.startsWith ( 'snippets') && command.includes ( ':' ) ) {
|
|
79
90
|
onlySnippets = true
|
|
@@ -87,9 +98,49 @@ function build ( tpl, extra=false, buildDependencies={} ) {
|
|
|
87
98
|
else if ( command === 'snippets' ) {
|
|
88
99
|
onlySnippets = true
|
|
89
100
|
}
|
|
90
|
-
|
|
101
|
+
else if ( command === 'set' ) {
|
|
102
|
+
if ( typeof d !== 'object' || !d ) return `Error: 'set' command requires an object with placeholders, helpers, handshake.`
|
|
103
|
+
// Merge helpers
|
|
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
|
|
91
142
|
|
|
92
|
-
|
|
143
|
+
if ( typeof d === 'string' ) {
|
|
93
144
|
switch ( d ) {
|
|
94
145
|
case 'raw':
|
|
95
146
|
return cuts.join ( '' ) // Original template with placeholders
|
|
@@ -100,13 +151,17 @@ function build ( tpl, extra=false, buildDependencies={} ) {
|
|
|
100
151
|
case 'handshake':
|
|
101
152
|
if ( !handshake ) return `Error: No handshake data.`
|
|
102
153
|
return structuredClone (handshake) // return a copy of handshake object
|
|
103
|
-
case '
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
|
|
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.`
|
|
107
162
|
}
|
|
108
163
|
} // if d is string
|
|
109
|
-
|
|
164
|
+
|
|
110
165
|
const endData = [];
|
|
111
166
|
const memory = {};
|
|
112
167
|
|
|
@@ -177,10 +232,10 @@ function build ( tpl, extra=false, buildDependencies={} ) {
|
|
|
177
232
|
|
|
178
233
|
levelData.forEach ( (theData, iData ) => {
|
|
179
234
|
|
|
180
|
-
let dataType = _defineDataType ( theData )
|
|
181
|
-
|
|
235
|
+
let dataType = _defineDataType ( theData )
|
|
236
|
+
|
|
182
237
|
switch ( type ) { // Action type 'route','data', 'render', or mix -> different operations
|
|
183
|
-
case 'route':
|
|
238
|
+
case 'route': // DEPRICATED in version 3.2.0. This functionality can be replaced with normal render functions
|
|
184
239
|
switch ( dataType ) {
|
|
185
240
|
case 'array':
|
|
186
241
|
theData.forEach ( (d,i) => {
|
|
@@ -270,7 +325,7 @@ function build ( tpl, extra=false, buildDependencies={} ) {
|
|
|
270
325
|
}
|
|
271
326
|
break
|
|
272
327
|
case 'mix':
|
|
273
|
-
if ( name === '' ) { // when is anonymous mixing
|
|
328
|
+
if ( name === '' ) { // when is anonymous mixing action
|
|
274
329
|
switch ( dataType ) {
|
|
275
330
|
case 'object':
|
|
276
331
|
let kTest = Object.keys ( theData ).find ( k => k.includes ( '/' ) ); // Check if keys are breadcrumbs
|
package/src/methods/render.js
CHANGED
|
@@ -33,21 +33,24 @@ function render ( theData, name, helpers, original, dependencies, ...args ) {
|
|
|
33
33
|
if ( value instanceof Array ) theData[key] = value[0]
|
|
34
34
|
})
|
|
35
35
|
}
|
|
36
|
+
|
|
36
37
|
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
function setRenderData ( d={} ) {
|
|
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={} ) {
|
|
43
44
|
if ( typeof d === 'string' ) return { text: d }
|
|
44
45
|
else return d
|
|
45
46
|
} // setRenderData func.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 )
|
|
51
54
|
} // render func.
|
|
52
55
|
|
|
53
56
|
|