@peter.naydenov/morph 3.1.4 → 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.
Files changed (61) hide show
  1. package/.mocharc.json +1 -0
  2. package/.opencode/command/speckit.analyze.md +184 -0
  3. package/.opencode/command/speckit.checklist.md +294 -0
  4. package/.opencode/command/speckit.clarify.md +181 -0
  5. package/.opencode/command/speckit.constitution.md +82 -0
  6. package/.opencode/command/speckit.implement.md +135 -0
  7. package/.opencode/command/speckit.plan.md +89 -0
  8. package/.opencode/command/speckit.specify.md +257 -0
  9. package/.opencode/command/speckit.tasks.md +137 -0
  10. package/.opencode/command/speckit.taskstoissues.md +28 -0
  11. package/.specify/memory/constitution.md +43 -0
  12. package/.specify/scripts/bash/check-prerequisites.sh +166 -0
  13. package/.specify/scripts/bash/common.sh +156 -0
  14. package/.specify/scripts/bash/create-new-feature.sh +305 -0
  15. package/.specify/scripts/bash/setup-plan.sh +61 -0
  16. package/.specify/scripts/bash/update-agent-context.sh +790 -0
  17. package/.specify/templates/agent-file-template.md +28 -0
  18. package/.specify/templates/checklist-template.md +40 -0
  19. package/.specify/templates/plan-template.md +104 -0
  20. package/.specify/templates/spec-template.md +115 -0
  21. package/.specify/templates/tasks-template.md +251 -0
  22. package/Changelog.md +17 -0
  23. package/README.md +154 -3
  24. package/dist/main.d.ts +100 -0
  25. package/dist/methods/_actionSupply.d.ts +18 -0
  26. package/dist/methods/_chopTemplates.d.ts +22 -0
  27. package/dist/methods/_defineData.d.ts +25 -0
  28. package/dist/methods/_defineType.d.ts +15 -0
  29. package/dist/methods/_readTemplate.d.ts +24 -0
  30. package/dist/methods/_renderHolder.d.ts +17 -0
  31. package/dist/methods/_setupActions.d.ts +19 -0
  32. package/dist/methods/build.d.ts +35 -0
  33. package/dist/methods/render.d.ts +25 -0
  34. package/dist/methods/settings.d.ts +7 -0
  35. package/dist/morph.cjs +1 -1
  36. package/dist/morph.esm.mjs +1 -1
  37. package/dist/morph.umd.js +1 -1
  38. package/morph.png +0 -0
  39. package/package.json +10 -7
  40. package/simple.js +17 -0
  41. package/specs/001-extend-templates/checklists/requirements.md +35 -0
  42. package/specs/001-extend-templates/contracts/set-method.json +39 -0
  43. package/specs/001-extend-templates/data-model.md +76 -0
  44. package/specs/001-extend-templates/plan.md +90 -0
  45. package/specs/001-extend-templates/quickstart.md +56 -0
  46. package/specs/001-extend-templates/research.md +18 -0
  47. package/specs/001-extend-templates/spec.md +139 -0
  48. package/specs/001-extend-templates/tasks.md +255 -0
  49. package/src/main.js +70 -32
  50. package/src/methods/_actionSupply.js +16 -0
  51. package/src/methods/_chopTemplates.js +22 -1
  52. package/src/methods/_defineData.js +33 -1
  53. package/src/methods/_defineType.js +13 -0
  54. package/src/methods/_readTemplate.js +46 -46
  55. package/src/methods/_renderHolder.js +15 -0
  56. package/src/methods/_setupActions.js +18 -8
  57. package/src/methods/build.js +98 -43
  58. package/src/methods/render.js +32 -11
  59. package/src/methods/settings.js +11 -0
  60. package/tsconfig.json +27 -0
  61. package/types/index.d.ts +0 -60
@@ -0,0 +1,255 @@
1
+ ---
2
+
3
+ description: "Task list template for feature implementation"
4
+ ---
5
+
6
+ # Tasks: Extend Templates
7
+
8
+ **Input**: Design documents from `/specs/001-extend-templates/`
9
+ **Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
10
+
11
+ **Tests**: Tests are required per feature requirements (FR-007, FR-008, FR-009)
12
+
13
+ **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
14
+
15
+ ## Format: `[ID] [P?] [Story] Description`
16
+
17
+ - **[P]**: Can run in parallel (different files, no dependencies)
18
+ - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
19
+ - Include exact file paths in descriptions
20
+
21
+ ## Path Conventions
22
+
23
+ - **Single project**: `src/`, `test/` at repository root
24
+ - Paths shown below assume single project - adjust based on plan.md structure
25
+
26
+ ## Phase 1: Setup (Shared Infrastructure)
27
+
28
+ **Purpose**: Project initialization and basic structure
29
+
30
+ - [X] T001 [P] Review existing code structure in src/ and test/
31
+ - [ ] T002 [P] Identify files to modify for 'set' command support
32
+
33
+ ---
34
+
35
+ ## Phase 2: Foundational (Blocking Prerequisites)
36
+
37
+ **Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented
38
+
39
+ **⚠️ CRITICAL**: No user story work can begin until this phase is complete
40
+
41
+ - [ ] T003 Add 'set' command support to src/methods/render.js
42
+ - [ ] T004 Update src/methods/build.js to handle template extensions
43
+ - [ ] T005 Implement placeholder replacement logic in src/methods/_chopTemplates.js
44
+ - [ ] T006 Implement helper merging logic in src/methods/_defineData.js
45
+ - [ ] T007 Implement handshake merging logic in src/methods/_defineData.js
46
+
47
+ **Checkpoint**: Foundation ready - user story implementation can now begin in parallel
48
+
49
+ ---
50
+
51
+ ## Phase 3: User Story 1 - Create Template Variants (Priority: P1) 🎯 MVP
52
+
53
+ **Goal**: Enable modification of placeholder definitions in existing templates to create variants
54
+
55
+ **Independent Test**: Create a base template, use 'set' to modify placeholders, render and verify changes
56
+
57
+ ### Tests for User Story 1 ⚠️
58
+
59
+ > **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
60
+
61
+ - [ ] T008 [P] [US1] Contract test for 'set' command in test/08-set-contract.test.js
62
+ - [ ] T009 [P] [US1] Integration test for placeholder replacement in test/09-placeholder-integration.test.js
63
+
64
+ ### Implementation for User Story 1
65
+
66
+ - [ ] T010 [US1] Implement placeholder identification by position/name in src/methods/_chopTemplates.js
67
+ - [ ] T011 [US1] Implement placeholder replacement in 'set' command in src/methods/render.js
68
+ - [ ] T012 [US1] Add validation for placeholder definitions in src/methods/_chopTemplates.js
69
+
70
+ **Checkpoint**: At this point, User Story 1 should be fully functional and testable independently
71
+
72
+ ---
73
+
74
+ ## Phase 4: User Story 2 - Add/Overwrite Helpers (Priority: P1)
75
+
76
+ **Goal**: Allow adding new helpers or overwriting existing ones in templates dynamically
77
+
78
+ **Independent Test**: Extend a template with new helpers, render and verify helper execution
79
+
80
+ ### Tests for User Story 2 ⚠️
81
+
82
+ - [ ] T013 [P] [US2] Contract test for helper merging in test/10-helper-contract.test.js
83
+ - [ ] T014 [P] [US2] Integration test for helper overwriting in test/11-helper-integration.test.js
84
+
85
+ ### Implementation for User Story 2
86
+
87
+ - [ ] T015 [US2] Implement helper merging logic in src/methods/_defineData.js
88
+ - [ ] T016 [US2] Ensure new helpers overwrite existing ones in src/methods/build.js
89
+ - [ ] T017 [US2] Preserve original helpers in extended templates in src/methods/build.js
90
+
91
+ **Checkpoint**: At this point, User Stories 1 AND 2 should both work independently
92
+
93
+ ---
94
+
95
+ ## Phase 5: User Story 3 - Provide Alternative Handshake Data (Priority: P2)
96
+
97
+ **Goal**: Enable providing alternative demo data for template testing
98
+
99
+ **Independent Test**: Extend template with new handshake, render in demo mode with new data
100
+
101
+ ### Tests for User Story 3 ⚠️
102
+
103
+ - [ ] T018 [P] [US3] Contract test for handshake merging in test/12-handshake-contract.test.js
104
+ - [ ] T019 [P] [US3] Integration test for demo rendering with new handshake in test/13-handshake-integration.test.js
105
+
106
+ ### Implementation for User Story 3
107
+
108
+ - [ ] T020 [US3] Implement handshake merging logic in src/methods/_defineData.js
109
+ - [ ] T021 [US3] Update demo rendering to use merged handshake in src/methods/render.js
110
+
111
+ **Checkpoint**: At this point, User Stories 1, 2 AND 3 should all work independently
112
+
113
+ ---
114
+
115
+ ## Phase 6: User Story 4 - Handle Missing Helpers Gracefully (Priority: P2)
116
+
117
+ **Goal**: Provide clear error messages when rendering with missing helpers
118
+
119
+ **Independent Test**: Attempt to render template with missing helper, verify error message
120
+
121
+ ### Tests for User Story 4 ⚠️
122
+
123
+ - [ ] T022 [P] [US4] Contract test for error handling in test/14-error-contract.test.js
124
+ - [ ] T023 [P] [US4] Integration test for missing helper errors in test/15-error-integration.test.js
125
+
126
+ ### Implementation for User Story 4
127
+
128
+ - [ ] T024 [US4] Implement missing helper detection in src/methods/_actionSupply.js
129
+ - [ ] T025 [US4] Add error message formatting "Helper '<name>' is not available" in src/methods/render.js
130
+ - [ ] T026 [US4] Support listing multiple missing helpers in error messages in src/methods/render.js
131
+
132
+ **Checkpoint**: At this point, User Stories 1-4 should all work independently
133
+
134
+ ---
135
+
136
+ ## Phase 7: User Story 5 - Remove Routing Actions (Priority: P3)
137
+
138
+ **Goal**: Remove inconsistent routing actions from the action system
139
+
140
+ **Independent Test**: Verify routing actions are no longer available, alternatives work
141
+
142
+ ### Tests for User Story 5 ⚠️
143
+
144
+ - [ ] T027 [P] [US5] Contract test for removed routing actions in test/16-routing-contract.test.js
145
+ - [ ] T028 [P] [US5] Integration test for alternative action patterns in test/17-routing-integration.test.js
146
+
147
+ ### Implementation for User Story 5
148
+
149
+ - [ ] T029 [US5] Remove routing action implementations from src/methods/_setupActions.js
150
+ - [ ] T030 [US5] Update action validation to reject routing actions in src/methods/_setupActions.js
151
+
152
+ **Checkpoint**: All user stories should now be independently functional
153
+
154
+ ---
155
+
156
+ ## Phase 8: Polish & Cross-Cutting Concerns
157
+
158
+ **Purpose**: Improvements that affect multiple user stories
159
+
160
+ - [ ] T031 [P] Update JSDoc comments in src/methods/*.js
161
+ - [ ] T032 [P] Update TypeScript definitions in morph.d.ts
162
+ - [ ] T033 [P] Update tests for new features in test/*.js
163
+ - [ ] T034 [P] Update README.md with 'set' command documentation
164
+ - [ ] T035 Run npm test to validate all changes
165
+ - [ ] T036 Run npm run lint to check code quality
166
+
167
+ ---
168
+
169
+ ## Dependencies & Execution Order
170
+
171
+ ### Phase Dependencies
172
+
173
+ - **Setup (Phase 1)**: No dependencies - can start immediately
174
+ - **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories
175
+ - **User Stories (Phase 3-7)**: All depend on Foundational phase completion
176
+ - User stories can then proceed in parallel (if staffed)
177
+ - Or sequentially in priority order (P1 → P2 → P3)
178
+ - **Polish (Phase 8)**: Depends on all user stories being complete
179
+
180
+ ### User Story Dependencies
181
+
182
+ - **User Story 1 (P1)**: Can start after Foundational (Phase 2) - No dependencies on other stories
183
+ - **User Story 2 (P1)**: Can start after Foundational (Phase 2) - No dependencies on other stories
184
+ - **User Story 3 (P2)**: Can start after Foundational (Phase 2) - No dependencies on other stories
185
+ - **User Story 4 (P2)**: Can start after Foundational (Phase 2) - No dependencies on other stories
186
+ - **User Story 5 (P3)**: Can start after Foundational (Phase 2) - No dependencies on other stories
187
+
188
+ ### Within Each User Story
189
+
190
+ - Tests (if included) MUST be written and FAIL before implementation
191
+ - Core implementation before integration
192
+ - Story complete before moving to next priority
193
+
194
+ ### Parallel Opportunities
195
+
196
+ - All Setup tasks marked [P] can run in parallel
197
+ - All Foundational tasks marked [P] can run in parallel (within Phase 2)
198
+ - Once Foundational phase completes, all user stories can start in parallel (if team capacity allows)
199
+ - All tests for a user story marked [P] can run in parallel
200
+ - Different user stories can be worked on in parallel by different team members
201
+
202
+ ---
203
+
204
+ ## Parallel Example: User Story 1
205
+
206
+ ```bash
207
+ # Launch all tests for User Story 1 together:
208
+ Task: "Contract test for 'set' command in test/08-set-contract.test.js"
209
+ Task: "Integration test for placeholder replacement in test/09-placeholder-integration.test.js"
210
+ ```
211
+
212
+ ---
213
+
214
+ ## Implementation Strategy
215
+
216
+ ### MVP First (User Story 1 Only)
217
+
218
+ 1. Complete Phase 1: Setup
219
+ 2. Complete Phase 2: Foundational (CRITICAL - blocks all stories)
220
+ 3. Complete Phase 3: User Story 1
221
+ 4. **STOP and VALIDATE**: Test User Story 1 independently
222
+ 5. Deploy/demo if ready
223
+
224
+ ### Incremental Delivery
225
+
226
+ 1. Complete Setup + Foundational → Foundation ready
227
+ 2. Add User Story 1 → Test independently → Deploy/Demo (MVP!)
228
+ 3. Add User Story 2 → Test independently → Deploy/Demo
229
+ 4. Add User Story 3 → Test independently → Deploy/Demo
230
+ 5. Add User Story 4 → Test independently → Deploy/Demo
231
+ 6. Add User Story 5 → Test independently → Deploy/Demo
232
+ 7. Each story adds value without breaking previous stories
233
+
234
+ ### Parallel Team Strategy
235
+
236
+ With multiple developers:
237
+
238
+ 1. Team completes Setup + Foundational together
239
+ 2. Once Foundational is done:
240
+ - Developer A: User Stories 1 & 2
241
+ - Developer B: User Stories 3 & 4
242
+ - Developer C: User Story 5
243
+ 3. Stories complete and integrate independently
244
+
245
+ ---
246
+
247
+ ## Notes
248
+
249
+ - [P] tasks = different files, no dependencies
250
+ - [Story] label maps task to specific user story for traceability
251
+ - Each user story should be independently completable and testable
252
+ - Verify tests fail before implementing
253
+ - Commit after each task or logical group
254
+ - Stop at any checkpoint to validate story independently
255
+ - Avoid: vague tasks, same file conflicts, cross-story dependencies that break independence
package/src/main.js CHANGED
@@ -24,15 +24,22 @@ const storage = ( () => ({default: {}}) ) ();
24
24
 
25
25
 
26
26
  /**
27
- * Get a template from a storage.
27
+ * Retrieves a template from storage.
28
28
  *
29
- * @param {string[]} location - The location of the template. Array of two elements.
30
- * The first element is the name of the template. The second element
31
- * is optional and is the name of the storage. Defaults to 'default'.
29
+ * @param {string[]} location - The location of the template. Array of two elements:
30
+ * - First element: The name of the template
31
+ * - Second element (optional): The name of the storage. Defaults to 'default'
32
32
  *
33
- * @returns {function } The template (a render function) if it exists in the storage.
34
- * An function that returns an error message if
35
- * either the storage or template does not exist.
33
+ * @returns {Function} The template (render function) if found, or an error function that returns
34
+ * an error message if the storage or template doesn't exist.
35
+ *
36
+ * @example
37
+ * // Get template from default storage
38
+ * const template = get(['myTemplate']);
39
+ *
40
+ * @example
41
+ * // Get template from custom storage
42
+ * const template = get(['myTemplate', 'customStorage']);
36
43
  */
37
44
  function get ( location ) {
38
45
  if ( !(location instanceof Array) ) {
@@ -61,20 +68,28 @@ function get ( location ) {
61
68
 
62
69
 
63
70
  /**
64
- * Add a template to a storage.
65
- *
66
- * If the template is already a function, it is added to the storage.
67
- * If the template is a template description (an object), it is built and
68
- * added to the storage.
69
- * If the template is broken, an error message is printed in the console
70
- * and the template is not added to the storage.
71
- *
72
- * @param {string[]} location - The location to add the template to. Array of two elements.
73
- * The first element is the name of the template. The second element
74
- * is optional and is the name of the storage. Defaults to 'default'.
75
- * @param {object|function|null} tplfn - The template description or the already built template function.
76
- * @param {...any} args - Additional arguments to be passed to the build function.
77
- * Only used if the first argument is a template description.
71
+ * Adds a template to storage.
72
+ *
73
+ * If the template is already a function, it's added directly to storage.
74
+ * If it's a template description object, it's built first and then added.
75
+ * If the template is null or broken, a warning/error is logged and it's not added.
76
+ *
77
+ * @param {string[]} location - The location to add the template to. Array of two elements:
78
+ * - First element: The name of the template
79
+ * - Second element (optional): The name of the storage. Defaults to 'default'
80
+ * @param {object|function|null} tplfn - The template description object, pre-built template function, or null
81
+ * @param {...any} args - Additional arguments passed to the build function (only used when tplfn is a template description)
82
+ *
83
+ * @example
84
+ * // Add a pre-built template function
85
+ * add(['myTemplate'], templateFunction);
86
+ *
87
+ * @example
88
+ * // Add and build a template description
89
+ * add(['myTemplate'], {
90
+ * template: 'Hello {{name}}!',
91
+ * helpers: { name: (data) => data.data.name }
92
+ * });
78
93
  */
79
94
  function add ( location, tplfn, ...args ) {
80
95
  const [ name, strName='default'] = location
@@ -98,11 +113,20 @@ function add ( location, tplfn, ...args ) {
98
113
 
99
114
 
100
115
  /**
101
- * Returns an array of all the names of the templates in the given storages.
102
- * No arguments - will return the list of templates in the 'default' storage.
116
+ * Returns an array of template names from specified storages.
117
+ *
118
+ * @param {string[]} [storageNames=['default']] - Array of storage names to retrieve template names from.
119
+ * Defaults to ['default'] if not provided.
103
120
  *
104
- * @param {string[]} [storageNames=['default']] - The names of the storages to retrieve template names from;
105
- * @returns {string[]} An array of all the template names in the given storages;
121
+ * @returns {string[]} Array of all template names from the specified storages.
122
+ *
123
+ * @example
124
+ * // List templates from default storage
125
+ * const templates = list();
126
+ *
127
+ * @example
128
+ * // List templates from multiple storages
129
+ * const templates = list(['default', 'customStorage']);
106
130
  */
107
131
  function list ( storageNames=['default'] ) {
108
132
  let r = storageNames.map ( strName => {
@@ -115,8 +139,13 @@ function list ( storageNames=['default'] ) {
115
139
 
116
140
 
117
141
  /**
118
- * Clears all templates from the storages.
119
- * This function deletes all storages. Storage 'default' will be reset to an empty object.
142
+ * Clears all templates from all storages.
143
+ *
144
+ * Deletes all custom storages and resets the 'default' storage to an empty object.
145
+ *
146
+ * @example
147
+ * // Clear all templates
148
+ * clear();
120
149
  */
121
150
  function clear ( ) {
122
151
  const keys = Object.keys ( storage )
@@ -130,13 +159,22 @@ function clear ( ) {
130
159
 
131
160
 
132
161
  /**
133
- * Removes a template from the storage.
162
+ * Removes a template from storage.
163
+ *
164
+ * @param {string[]} location - The location of the template to remove. Array of two elements:
165
+ * - First element: The name of the template
166
+ * - Second element (optional): The name of the storage. Defaults to 'default'
167
+ *
168
+ * @returns {void|string} Returns an error message if the storage or template doesn't exist,
169
+ * otherwise returns undefined.
134
170
  *
135
- * @param {string[]} location - The location to remove the template from. Array of two elements.
136
- * The first element is the name of the template. The second element
137
- * is optional and is the name of the storage. Defaults to 'default'.
171
+ * @example
172
+ * // Remove template from default storage
173
+ * remove(['myTemplate']);
138
174
  *
139
- * @returns {void|string} An error message if the storage or template does not exist.
175
+ * @example
176
+ * // Remove template from custom storage
177
+ * remove(['myTemplate', 'customStorage']);
140
178
  */
141
179
  function remove ( location ) {
142
180
  const [name, strName='default'] = location;
@@ -2,6 +2,22 @@ import stack from "@peter.naydenov/stack"
2
2
 
3
3
 
4
4
 
5
+ /**
6
+ * Generator function that supplies actions in a controlled sequence using a stack.
7
+ *
8
+ * Manages the flow of actions through different processing levels, allowing for
9
+ * dynamic action insertion during processing.
10
+ *
11
+ * @param {Object} act - Object containing action arrays organized by level
12
+ * @param {number} level - Maximum processing level
13
+ * @returns {Generator} Generator that yields action objects in sequence
14
+ *
15
+ * @example
16
+ * const generator = _actionSupply(actionSetup, 2);
17
+ * for (const action of generator) {
18
+ * // Process each action
19
+ * }
20
+ */
5
21
  function* _actionSupply ( act, level ) {
6
22
  let action = stack ({ type:'LIFO' });
7
23
  for ( let i=0; i<=level; i++ ) {
@@ -1,3 +1,18 @@
1
+ /**
2
+ * Creates a template chopping function that splits text into parts and placeholders.
3
+ *
4
+ * @param {object} settings - Configuration object containing template delimiters
5
+ * @param {string} settings.TG_PRX - Opening delimiter (e.g., '{{')
6
+ * @param {string} settings.TG_SFX - Closing delimiter (e.g., '}}')
7
+ * @param {number} settings.TG_SIZE_P - Length of opening delimiter
8
+ * @param {number} settings.TG_SIZE_S - Length of closing delimiter
9
+ *
10
+ * @returns {Function} Function that chops template text into array parts
11
+ *
12
+ * @example
13
+ * const chop = _chopTemplate(settings);
14
+ * const result = chop('Hello {{name}}!');
15
+ */
1
16
  function _chopTemplate ( settings ) {
2
17
  return function _chopTemplate ( text ) {
3
18
  const { TG_PRX, TG_SFX, TG_SIZE_P, TG_SIZE_S } = settings;
@@ -38,7 +53,13 @@ function _chopTemplate ( settings ) {
38
53
 
39
54
 
40
55
 
41
- function showError (type) {
56
+ /**
57
+ * Returns error messages for template parsing failures.
58
+ *
59
+ * @param {string} type - Error type identifier
60
+ * @returns {string} Human-readable error message
61
+ */
62
+ function showError (type) {
42
63
  switch (type) {
43
64
  case 'notAString':
44
65
  return `Error: Template is not a string.`
@@ -2,6 +2,29 @@ import walk from '@peter.naydenov/walk'
2
2
 
3
3
 
4
4
 
5
+ /**
6
+ * Processes data source based on action requirements and creates nested data structure.
7
+ *
8
+ * Analyzes the data source and action to determine if nested data processing is needed.
9
+ * If action contains '#', it walks through the data structure to collect nested objects.
10
+ *
11
+ * @param {any} dSource - The data source to process (function, null, string, or object)
12
+ * @param {string} action - Action string that may contain '#' indicating nested processing
13
+ *
14
+ * @returns {object} Data processing result containing:
15
+ * - dataDeepLevel: Maximum nesting level found
16
+ * - nestedData: Array of nested data arrays organized by level
17
+ *
18
+ * @example
19
+ * // Simple data without nesting
20
+ * const result = _defineData('hello', 'render');
21
+ * // Returns: { dataDeepLevel: 0, nestedData: [['hello']] }
22
+ *
23
+ * @example
24
+ * // Nested data with '#' action
25
+ * const result = _defineData({ user: { name: 'John' } }, 'render:#');
26
+ * // Returns: { dataDeepLevel: 1, nestedData: [[{ name: 'John' }]] }
27
+ */
5
28
  function _defineData ( dSource, action ) {
6
29
  const nestedData = [];
7
30
  let dataDeepLevel = 0;
@@ -18,7 +41,16 @@ function _defineData ( dSource, action ) {
18
41
  return { dataDeepLevel:0, nestedData }
19
42
  }
20
43
 
21
- function findObjects ({key, value, breadcrumbs}) {
44
+ /**
45
+ * Callback function for walking through data structure to collect nested objects.
46
+ *
47
+ * @param {object} params - Walk parameters
48
+ * @param {string} params.key - Current key being processed
49
+ * @param {any} params.value - Current value being processed
50
+ * @param {string} params.breadcrumbs - Path to current value as breadcrumb string
51
+ * @returns {any} Returns the value unchanged
52
+ */
53
+ function findObjects ({key, value, breadcrumbs}) {
22
54
  if ( key === breadcrumbs ) {
23
55
  nestedData[0] = [ value ]
24
56
  return value
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Determines the data type of a value for template processing.
3
+ *
4
+ * @param {any} data - Value to type-check
5
+ * @returns {string|undefined} Returns one of: 'null', 'primitive', 'function', 'array', 'object', or undefined
6
+ *
7
+ * @example
8
+ * _defineDataType(null); // 'null'
9
+ * _defineDataType('hello'); // 'primitive'
10
+ * _defineDataType([1,2,3]); // 'array'
11
+ * _defineDataType({}); // 'object'
12
+ * _defineDataType(() => {}); // 'function'
13
+ */
1
14
  function _defineDataType ( data ) {
2
15
 
3
16
  if ( data == null ) return 'null'
@@ -3,12 +3,36 @@ import _chopTemplate from "./_chopTemplates.js"
3
3
 
4
4
 
5
5
 
6
+ /**
7
+ * Normalizes field data by returning null for falsy values.
8
+ *
9
+ * @param {any} field - Field value to normalize
10
+ * @returns {any|null} Returns the field if truthy, null otherwise
11
+ */
6
12
  function readData ( field ) {
7
13
  return field ? field : null
8
14
  } // readData func.
9
15
 
10
16
 
11
17
 
18
+ /**
19
+ * Parses and validates a template description object.
20
+ *
21
+ * Extracts placeholders, validates helpers, and prepares template for building.
22
+ *
23
+ * @param {object} tpl - Template description object
24
+ * @param {string} tpl.template - Template string with placeholders
25
+ * @param {object} [tpl.helpers={}] - Optional helper functions
26
+ * @param {object} [tpl.handshake] - Optional example data
27
+ *
28
+ * @returns {object} Template parsing result containing:
29
+ * - hasError: Error message or null
30
+ * - placeholders: Array of placeholder objects
31
+ * - chop: Array of template parts
32
+ * - helpers: Helper functions object
33
+ * - handshake: Example data object
34
+ * - snippets: Object mapping snippet names to placeholders
35
+ */
12
36
  function _readTemplate ( tpl ) {
13
37
  const
14
38
  { template, helpers={}, handshake } = tpl
@@ -28,52 +52,28 @@ function _readTemplate ( tpl ) {
28
52
 
29
53
  if ( typeof chop === 'string' ) hasError = chop
30
54
  else {
31
- chop.forEach ( (item,i) => {
32
- const
33
- // Placeholder contains: Opening tag(TG_PRX), dataName, delimiter(:), list of operations, placeholder's name, closing tag(TG_SFX)
34
- finding = RegExp ( TG_PRX + '\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?' + TG_SFX, 'g' )
35
- , isPlaceholder = item.includes( TG_PRX )
36
- ;
37
-
38
- if ( isPlaceholder ) {
39
- const x = finding.exec ( item )
40
- if ( !x ) return
41
- let holder = {
42
- index: i
43
- , data : readData ( x[1] )
44
- , action : x[2] ? x[2].split(',').map ( x => x.trim()) : null
45
- , name : x[3] ? x[3].trim() : null
46
- }
47
- placeholders.push ( holder )
48
- snippets[placeholders.length-1] = holder
49
- if ( holder.name ) snippets[holder.name] = holder
50
- } // if isPlaceholder
51
- }) // forEach chop
52
- } // else error
53
-
54
-
55
-
56
- // Check helpers - sanity check
57
- placeholders.forEach ( holder => {
58
- if ( !holder.action ) return
59
- holder.action.every ( act => {
60
- if ( act === '#' ) return true
61
- if ( act === '^^' ) return true
62
- if ( act.startsWith('^') && act !== '^^' ) return true
63
- if ( act.startsWith ( '?' )) act = act.replace ( '?', '' )
64
- if ( act.startsWith ( '+' )) act = act.replace ( '+', '' )
65
- if ( act.startsWith ( '[]' )) act = act.replace ( '[]', '' )
66
- if ( act.startsWith ( '>' )) act = act.replace ( '>', '' )
67
- if ( act === '' ) return true
68
- if ( helpers[act] ) return true
69
- else {
70
- hasError = `Error: Missing helper: ${act}`
71
- return false
72
- }
73
- }) // every action
74
- }) // forEach placeholders
75
-
76
-
55
+ chop.forEach ( (item,i) => {
56
+ const
57
+ // Placeholder contains: Opening tag(TG_PRX), dataName, delimiter(:), list of operations, placeholder's name, closing tag(TG_SFX)
58
+ finding = RegExp ( TG_PRX + '\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?' + TG_SFX, 'g' )
59
+ , isPlaceholder = item.includes( TG_PRX )
60
+ ;
61
+
62
+ if ( isPlaceholder ) {
63
+ const x = finding.exec ( item )
64
+ if ( !x ) return
65
+ let holder = {
66
+ index: i
67
+ , data : readData ( x[1] )
68
+ , action : x[2] ? x[2].split(',').map ( x => x.trim()) : null
69
+ , name : x[3] ? x[3].trim() : null
70
+ }
71
+ placeholders.push ( holder )
72
+ snippets[placeholders.length-1] = holder
73
+ if ( holder.name ) snippets[holder.name] = holder
74
+ } // if isPlaceholder
75
+ }) // forEach chop
76
+ } // else error
77
77
 
78
78
  return {
79
79
  hasError
@@ -3,6 +3,21 @@ import settings from './settings.js'
3
3
 
4
4
 
5
5
 
6
+ /**
7
+ * Renders a simple template by replacing placeholders with data values.
8
+ *
9
+ * Processes templates with basic placeholder substitution without actions or helpers.
10
+ * Only supports direct field replacement from the data object.
11
+ *
12
+ * @param {string} template - Template string with placeholders
13
+ * @param {object} data - Data object containing values for placeholder replacement
14
+ *
15
+ * @returns {string|null} Rendered template string or null if data is null
16
+ *
17
+ * @example
18
+ * const result = _renderHolder('Hello {{name}}!', { name: 'World' });
19
+ * // Returns: 'Hello World!'
20
+ */
6
21
  function _renderHolder ( template, data ) {
7
22
  // Data should be an object. No array, no string.
8
23
  if ( data == null ) return null