@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.
- 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 +17 -0
- package/README.md +154 -3
- package/dist/main.d.ts +100 -0
- package/dist/methods/_actionSupply.d.ts +18 -0
- package/dist/methods/_chopTemplates.d.ts +22 -0
- package/dist/methods/_defineData.d.ts +25 -0
- package/dist/methods/_defineType.d.ts +15 -0
- package/dist/methods/_readTemplate.d.ts +24 -0
- package/dist/methods/_renderHolder.d.ts +17 -0
- package/dist/methods/_setupActions.d.ts +19 -0
- package/dist/methods/build.d.ts +35 -0
- package/dist/methods/render.d.ts +25 -0
- package/dist/methods/settings.d.ts +7 -0
- 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 +10 -7
- 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/main.js +70 -32
- package/src/methods/_actionSupply.js +16 -0
- package/src/methods/_chopTemplates.js +22 -1
- package/src/methods/_defineData.js +33 -1
- package/src/methods/_defineType.js +13 -0
- package/src/methods/_readTemplate.js +46 -46
- package/src/methods/_renderHolder.js +15 -0
- package/src/methods/_setupActions.js +18 -8
- package/src/methods/build.js +98 -43
- package/src/methods/render.js +32 -11
- package/src/methods/settings.js +11 -0
- package/tsconfig.json +27 -0
- 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
|
-
*
|
|
27
|
+
* Retrieves a template from storage.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
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
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
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
|
-
*
|
|
102
|
-
*
|
|
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
|
-
*
|
|
105
|
-
*
|
|
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
|
|
119
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
171
|
+
* @example
|
|
172
|
+
* // Remove template from default storage
|
|
173
|
+
* remove(['myTemplate']);
|
|
138
174
|
*
|
|
139
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|