@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.
Files changed (41) 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 +12 -0
  23. package/README.md +154 -3
  24. package/dist/morph.cjs +1 -1
  25. package/dist/morph.esm.mjs +1 -1
  26. package/dist/morph.umd.js +1 -1
  27. package/morph.png +0 -0
  28. package/package.json +3 -2
  29. package/simple.js +17 -0
  30. package/specs/001-extend-templates/checklists/requirements.md +35 -0
  31. package/specs/001-extend-templates/contracts/set-method.json +39 -0
  32. package/specs/001-extend-templates/data-model.md +76 -0
  33. package/specs/001-extend-templates/plan.md +90 -0
  34. package/specs/001-extend-templates/quickstart.md +56 -0
  35. package/specs/001-extend-templates/research.md +18 -0
  36. package/specs/001-extend-templates/spec.md +139 -0
  37. package/specs/001-extend-templates/tasks.md +255 -0
  38. package/src/methods/_readTemplate.js +22 -46
  39. package/src/methods/_setupActions.js +1 -8
  40. package/src/methods/build.js +98 -43
  41. package/src/methods/render.js +14 -11
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ <img src="morph.png" alt="morph header image">
2
+
1
3
  # Morph (@peter.naydenov/morph)
2
4
 
3
5
  ![version](https://img.shields.io/github/package-json/v/peterNaydenov/morph)
@@ -12,9 +14,9 @@
12
14
 
13
15
  In version 3 introduced `snippets`, you can choose to render only certain placeholders or groups of placeholders from the template. This lets you use templates as collections of reusable templates, so you can extract and use just the parts you need without having to render the whole template. This makes it easier to create and manage reusable template libraries for your project or rerender only the parts that need to be updated.
14
16
 
15
- The `render` function now takes a command as its first argument. Available commands are: `render`, `debug`, and `snippets`. Other arguments have no changes. Just shifted right. The second argument becomes the data, the third is dependencies, and the fourth is a list of post-processing functions.
17
+ The `render` function now takes a command as its first argument. Available commands are: `render`, `debug`, `snippets`, `curry`, and `set`. Other arguments have no changes. Just shifted right. The second argument becomes the data, the third is dependencies, and the fourth is a list of post-processing functions.
16
18
 
17
- In version 3.x.x, the data is always the second argument. It can be a string, as in version 2.x.x. The term "command" is no longer used for this argument; instead, it is called "instructions". Available instructions include: `raw`, `demo`, `handshake`, and `placeholders`.
19
+ In version 3.x.x, the data is always the second argument. It can be a string, as in version 2.x.x. The term "command" is no longer used for this argument; instead, it is called "instructions". Available instructions include: `raw`, `demo`, `handshake`, `placeholders`, and `count`.
18
20
 
19
21
  How to migrate to version 3.x.x, please read the [Migration guide](./Migration.guide.md). Read more about `snippets` down below.
20
22
 
@@ -260,7 +262,24 @@ Helpers are templates and functions that are used by actions to decorate the dat
260
262
 
261
263
 
262
264
  ## Commands
263
- The first argument of the render function is the command. Available commands are: `render`, `debug`, and `snippets`. Default command is `render` so if template doesn't need external information we can call the function without arguments.
265
+ The first argument of the render function is the command. Available commands are: `render`, `debug`, `snippets`, `curry`, and `set`. Default command is `render` so if template doesn't need external information we can call the function without arguments.
266
+
267
+
268
+ ## Debug
269
+ The `debug` command provides information about the template. The second argument is an instruction. Available instructions: `raw`, `demo`, `handshake`, `placeholders`, `count`.
270
+
271
+ - `raw`: Returns the original template string.
272
+ - `demo`: Renders the template with handshake data.
273
+ - `handshake`: Returns the handshake object.
274
+ - `placeholders`: Returns a string of placeholder names separated by commas.
275
+ - `count`: Returns the number of unresolved placeholders in the current template state.
276
+
277
+ ```js
278
+ const fn = morph.build(template);
279
+
280
+ let raw = fn('debug', 'raw'); // Original template
281
+ let count = fn('debug', 'count'); // Number of unresolved placeholders
282
+ ```
264
283
 
265
284
 
266
285
  ## Snippets
@@ -308,9 +327,141 @@ let res4 = fn ( 'snippets:2,3', 'demo' )
308
327
  ```
309
328
 
310
329
 
330
+ ## Curry
331
+ The `curry` command performs partial rendering with the provided data and returns a new render function. The new function uses the rendered output as the new template, while preserving the original helpers and handshake.
332
+
333
+ ```js
334
+ const template = morph.build({
335
+ template: 'Hello {{name}}! Welcome to {{place}}.',
336
+ helpers: { format: ({data}) => data.toUpperCase() },
337
+ handshake: { name: 'World', place: 'Earth' }
338
+ });
339
+
340
+ const curried = template('curry', { name: 'Alice' });
341
+ // curried is a new function with template 'Hello Alice! Welcome to {{place}}.'
342
+
343
+ const result = curried('render', { place: 'Mars' });
344
+ // result: 'Hello Alice! Welcome to Mars.'
345
+ ```
346
+
347
+ This allows chaining partial renders or completing with defaults using `('render', 'demo')`.
348
+
349
+
350
+ ## Set
351
+ The `set` command modifies the template by merging new helpers, handshake, or replacing placeholders, then returns a new render function with the changes applied.
352
+
353
+ ```js
354
+ const template = morph.build({
355
+ template: 'Hello {{name}}!',
356
+ helpers: { format: ({data}) => data.toLowerCase() },
357
+ handshake: { name: 'World' }
358
+ });
359
+
360
+ const modified = template('set', {
361
+ helpers: { format: ({data}) => data.toUpperCase() },
362
+ placeholders: { 0: '{{greeting}}' } // Replace first placeholder
363
+ });
364
+ // modified has updated helpers and template 'Hello {{greeting}}!'
365
+
366
+ const result = modified('render', { greeting: 'Hi' });
367
+ // result: 'HELLO HI!' (note: format applied to 'Hi')
368
+ ```
369
+
370
+
371
+ ## Experimentals
372
+
373
+ ### `.morph` File Extension
374
+
375
+ Describe Morph templates within `.morph` file extensions. Available after version 3.5.x. This allows you to create the template files with HTML-like syntax, CSS modules, and JavaScript helpers. During the build process, vite plugin will extract the template, helpers, and handshake data from the file, will compile it to function and will save as ES module, ready to import from other files. CSS support comes as extension of what Morph can do. Writing a Morph file that contains only CSS will be converted to CSS file. In morph files that contain mix of HTML, CSS, and JavaScript, the CSS will be converted to CSS modules.
376
+
377
+ A `.morph` file contains four separate sections.
378
+
379
+ The four sections are:
380
+ - **Template (HTML)** - The main template with Morph syntax
381
+ - **Script (JavaScript)** - Helper functions and logic
382
+ - **Style (CSS)** - CSS with automatic module scoping
383
+ - **Handshake (JSON)** - Demo data for testing
384
+
385
+ Example `.morph` file structure:
386
+ ```html
387
+ <!-- Template (HTML) -->
388
+ <div class="card">
389
+ <h2>{{ title : formatTitle }}</h2>
390
+ <p>{{ description : truncate }}</p>
391
+ <h3>Items</h3>
392
+ {{ items : ul, [], renderItem }}
393
+ <button data-click="save">Save</button>
394
+ </div>
395
+
396
+
397
+
398
+ <script>
399
+ // Place for helpers
400
+ // Function definition
401
+ function formatTitle ({ data }) {
402
+ return data.toUpperCase();
403
+ }
404
+
405
+ function truncate ({data}) {
406
+ const length = 100;
407
+ return data.length > length ? data.substring(0, length) + '...' : data;
408
+ }
409
+
410
+ // Template definition
411
+ let renderItem = `<li>{{name}}</li>`;
412
+ let ul = `<ul>{{text}}</ul>`
413
+ </script>
414
+
415
+ <style>
416
+ .card {
417
+ background: var(--card-bg, #fff);
418
+ padding: 1rem;
419
+ border-radius: 8px;
420
+ }
421
+ </style>
422
+
423
+ <script type="application/json">
424
+ // Script with type: application/json
425
+ // Handshake - Place for demo data
426
+ {
427
+ "title": "Card Title",
428
+ "description": "Card description",
429
+ "items": [
430
+ { "name": "Item 1" },
431
+ { "name": "Item 2" },
432
+ { "name": "Item 3" }
433
+ ]
434
+ }
435
+ </script>
436
+ ```
437
+
438
+ Get started with the official Vite plugin:
439
+ **GitHub Repository:** [vite-plugin-morph](https://github.com/PeterNaydenov/vite-plugin-morph)
440
+
441
+ Once configured, you can directly import `.morph` files in your code:
442
+ ```js
443
+ import myTemplate from './templates/my-template.morph'
444
+
445
+ // The imported template is ready to use
446
+ const result = myTemplate ( 'render', { name: 'Peter' })
447
+ ```
448
+
449
+
450
+
451
+ ### VSCode Extension
452
+
453
+ For better development experience with `.morph` files, you can install the official VSCode extension that provides syntax highlighting:
454
+
455
+ **Extension Name:** [Morph Template Syntax Highlighting](https://marketplace.visualstudio.com/items?itemName=PeterNaydenov.morph-template-syntax-highlighting)
456
+
457
+ Install directly from the VSCode Marketplace or search for "Morph Template Syntax Highlighting" in your VSCode extensions panel.
458
+
459
+
311
460
  ## Links
312
461
  - [Release history](Changelog.md)
313
462
  - [ Migration guide ](https://github.com/PeterNaydenov/morph/blob/master/Migration.guide.md)
463
+ - [ Vite Plugin ](https://github.com/PeterNaydenov/vite-plugin-morph)
464
+ - [ VSCode Extension ](https://marketplace.visualstudio.com/items?itemName=PeterNaydenov.morph-template-syntax-highlighting)
314
465
 
315
466
 
316
467
 
package/dist/morph.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("@peter.naydenov/walk"),t=require("@peter.naydenov/stack");function r(e){return function t(r){const{TG_PRX:a,TG_SFX:o,TG_SIZE_P:s,TG_SIZE_S:l}=e;let i,c,u,f=[];if("string"!=typeof r)return n("notAString");if(0==r.length)return[];if(i=r.indexOf(a),0<i&&f.push(r.slice(0,i)),-1==i)return f.push(r),f;{if(u=r.indexOf(a,i+s),c=r.indexOf(o),-1==c)return n("missingClosing");if(c<i)return n("closedBeforeOpened");if(c+=l,-1!=u&&u<c)return n("newBeforeClosed");f.push(r.slice(i,c));let e=t(r.slice(c));return f.concat(e)}}}function n(e){switch(e){case"notAString":return"Error: Template is not a string.";case"missingClosing":return"Error: Placeholder with missing closing tag.";case"closedBeforeOpened":return"Error: Placeholder closing tag without starting one.";case"newBeforeClosed":return"Error: Nested placeholders. Close placeholder before open new one.";default:return"Error: Unknown template error."}}var a={TG_PRX:"{{",TG_SFX:"}}",TG_SIZE_P:2,TG_SIZE_S:2};function o(e){return null==e?"null":"string"==typeof e||"number"==typeof e||"boolean"==typeof e?"primitive":"function"==typeof e?"function":e instanceof Array?"array":e instanceof Object?"object":void 0}function s(e,t,n,o,s,...l){e instanceof Object&&Object.entries(e).forEach((([t,r])=>{r instanceof Object&&(e[t]=r.text),r instanceof Array&&(e[t]=r[0])}));const i="function"==typeof n[t];return e=function(e={}){return"string"==typeof e?{text:e}:e}(e),i?n[t]({theData:e,dependencies:s,full:o},...l):function(e,t){if(null==t)return null;const n=r(a)(e),o=a;return n.forEach(((e,r)=>{if(e.includes(o.TG_PRX)){const a=e.replace(o.TG_PRX,"").replace(o.TG_SFX,"").trim();t.hasOwnProperty(a)&&null!=t[a]&&(n[r]=t[a])}})),n.join("")}(n[t],e)}function l(n,l=!1,i={}){let{hasError:c,placeholders:u,chop:f,helpers:d,handshake:p,snippets:h}=function(e){const{template:t,helpers:n={},handshake:o}=e,{TG_PRX:s,TG_SFX:l}=a,i=[],c={},u="string"==typeof t?t.replace(/<!--[\s\S]*?-->/g,"").replace(/\s{2,}/g," "):t;let f=null;const d=r(a)(u);return"string"==typeof d?f=d:d.forEach(((e,t)=>{const r=RegExp(s+"\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?"+l,"g");if(e.includes(s)){const a=r.exec(e);if(!a)return;let o={index:t,data:(n=a[1],n||null),action:a[2]?a[2].split(",").map((e=>e.trim())):null,name:a[3]?a[3].trim():null};i.push(o),c[i.length-1]=o,o.name&&(c[o.name]=o)}var n})),i.forEach((e=>{e.action&&e.action.every((e=>"#"===e||"^^"===e||!(!e.startsWith("^")||"^^"===e)||(e.startsWith("?")&&(e=e.replace("?","")),e.startsWith("+")&&(e=e.replace("+","")),e.startsWith("[]")&&(e=e.replace("[]","")),e.startsWith(">")&&(e=e.replace(">","")),""===e||!!n[e]||(f=`Error: Missing helper: ${e}`,!1))))})),{hasError:f,placeholders:i,chop:d,helpers:n,handshake:o,snippets:c}}(n);if(c){function m(){return c}return l?[!1,m]:m}{const b=structuredClone(u);function y(r="render",n={},a={},...l){const c=structuredClone(f);let m=!1;if(!["render","debug","snippets"].includes(r)&&!r.startsWith("snippets"))return`Error: Wrong command "${r}". Available commands: render, debug, snippets.`;if(r.startsWith("snippets")&&r.includes(":")){m=!0;let e=r.split(":").slice(1)[0].trim().split(",").map((e=>e.trim()));u=e.map((e=>h[e]))}else"snippets"===r?m=!0:u=structuredClone(b);if("string"==typeof n)switch(n){case"raw":return c.join("");case"demo":if(!p)return"Error: No handshake data.";n=p;break;case"handshake":return p?structuredClone(p):"Error: No handshake data.";case"placeholders":return u.map((e=>c[e.index])).join(", ");default:return`Error: Wrong instruction "${n}". Available instructions: raw, demo, handshake, placeholders.`}const y=[],g={};let v=o(n),E={...i,...a};n=e({data:n});let k=e({data:n});return"null"===v?c.join(""):("array"!==v&&(n=[n]),"null"===v?c.join(""):(n.forEach((r=>{u.forEach((a=>{const{index:l,data:i,action:u}=a,f=!u&&i,p=structuredClone(g),h={dependencies:E,memory:p};let m=r;if(i&&i.includes("/")?m.hasOwnProperty(i)?m=m[i]:i.split("/").forEach((e=>{m=m.hasOwnProperty(e)?m[e]:[]})):"@all"===i||null===i||"@root"===i?m=r:i&&(m=m[i]),f){switch(o(m)){case"function":return void(c[l]=m());case"primitive":return void(c[l]=m);case"array":return void("primitive"===o(m[0])&&(c[l]=m[0]));case"object":return void(m.text&&(c[l]=m.text))}}else{let{dataDeepLevel:a,nestedData:i}=function(t,r){const n=[];let a=0;if(t instanceof Function)return{dataDeepLevel:0,nestedData:[[t()]]};if(null==t)return{dataDeepLevel:0,nestedData:[null]};if("string"==typeof t)return{dataDeepLevel:0,nestedData:[[t]]};const o=structuredClone(t);return r.includes("#")?(e({data:o,objectCallback:function({key:e,value:t,breadcrumbs:r}){return e===r?(n[0]=[t],t):(a=r.split("/").length-1,n[a]||(n[a]=[]),n[a].push(t),t)}}),{dataDeepLevel:a,nestedData:n}):(n.push([o]),{dataDeepLevel:0,nestedData:n})}(m,u),f=function*(e,r){let n=t({type:"LIFO"});for(let t=0;t<=r;t++)n.push(e[t]);for(;n&&!n.isEmpty();){let e=yield n.pull();e&&n.push(e)}}(function(e,t=10){let r={},n=[...e],a=0,o=0,s=0;n.forEach((e=>{"#"===e&&s++})),s<t&&console.error(`Error: Not enough level markers (#) for data with depth level ${t}. Found ${s} level markers in actions: ${e.join(", ")}`);do{r[o]=[],o++}while(o<=t);return n.every((e=>"#"===e?(a++,!(a>t)):e.startsWith("?")?(r[a].push({type:"route",name:e.replace("?",""),level:a}),!0):e.startsWith("^")&&"^^"!==e?(r[a].push({type:"save",name:e.replace("^",""),level:a}),!0):"^^"===e?(r[a].push({type:"overwrite",name:"none",level:a}),!0):e.startsWith("+")?(r[a].push({type:"extendedRender",name:e.replace("+",""),level:a}),!0):e.startsWith("[]")?(r[a].push({type:"mix",name:e.replace("[]",""),level:a}),!0):e.startsWith(">")?(r[a].push({type:"data",name:e.replace(">",""),level:a}),!0):(""===e||r[a].push({type:"render",name:e,level:a}),!0))),r}(u,a),a);for(let t of f){let{type:a,name:l,level:c}=t;(i[c]||[]).forEach(((t,u)=>{let f=o(t);switch(a){case"route":switch(f){case"array":t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=d[l]({data:e,...h,full:e});null!=a&&("object"===n?t[r].text=s(e,a,d,k,E):t[r]=s(e,a,d,k,E))}));break;case"object":t.text=s(t,l,d,k,E)}break;case"save":g[l]=structuredClone(t);break;case"overwrite":r=structuredClone(t);break;case"data":switch(f){case"array":t.forEach(((e,r)=>t[r]=e instanceof Function?d[l]({data:e(),...h,full:e}):d[l]({data:e,...h,full:e})));break;case"object":i[c]=[d[l]({data:t,...h,full:n})];break;case"function":i[c]=[d[l]({data:t(),...h,full:n})];break;case"primitive":i[c]=d[l]({data:t,...h,full:n})}break;case"render":const p="function"==typeof d[l];switch(f){case"array":p?t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=d[l]({data:e,...h,full:k});null==a&&(t[r]=null),"object"===n?e.text=a:t[r]=a})):t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=s(e,l,d,k,E);null==a?t[r]=null:"object"===n?e.text=a:t[r]=a}));break;case"function":i[c]=d[l]({data:t(),...h,full:n});break;case"primitive":i[c]=p?d[l]({data:t,...h,full:n}):s(t,l,d,k,E);break;case"object":p?i[c][u].text=d[l]({data:t,...h,full:n}):t.text=s(t,l,d,k,E)}break;case"extendedRender":"function"==typeof d[l]&&i[0].forEach(((e,t)=>i[0][t]=d[l]({data:e,...h,full:e})));break;case"mix":if(""===l)switch(f){case"object":Object.keys(t).find((e=>e.includes("/")))?Object.entries(t).forEach((([e,t])=>i[c][e]=t.text)):i[c]=t.text;for(let b=c-1;b>=0;b--)i[b]=e({data:i[b],objectCallback:m});function m({value:e,breadcrumbs:t}){return i[c][t]?i[c][t]:e}break;case"array":t.forEach(((e,r)=>{if(r>0){let n=o(e);if(null==e)return;t[0]+="object"===n?`${e.text}`:`${e}`,t.toSpliced(r,1)}else{let r=o(e);if(t[0]="",null===r)return;t[0]="object"===r?`${e.text}`:`${e}`}})),t.length=1}else{let y=d[l]({data:t,...h,full:n}),v=o(y);switch(t.forEach(((e,r)=>t.splice(r,1))),t.length=0,v){case"primitive":t[0]=y;break;case"array":t.push(...y)}}}}))}if(i instanceof Array&&1===i.length&&i[0]instanceof Array&&(i=i[0]),null==i[0])return;let p=o(i[0]),b=i[0];switch(p){case"primitive":if(null==b)return;c[l]=b;break;case"object":if(null==b.text)return;c[l]=b.text;break;case"array":const e=o(b[0]);c[l]="object"===e?b.map((e=>e.text)).join(""):b.join("")}}})),m?y.push(u.map((e=>c[e.index])).join("<~>")):y.push(c.join(""))})),"array"===v?y:l?l.reduce(((e,t)=>"function"!=typeof t?e:t(e,E)),y.join("")):y.join("")))}return l?[!0,y]:y}}const i={default:{}};const c={build:l,get:function(e){if(!(e instanceof Array))return function(){return'Error: Argument "location" is a string. Should be an array. E.g. ["templateName", "storageName"].'};const[t,r="default"]=e;return i[r]?i[r][t]?i[r][t]:function(){return`Error: Template "${t}" does not exist in storage "${r}".`}:function(){return`Error: Storage "${r}" does not exist.`}},add:function(e,t,...r){const[n,a="default"]=e;if(null==t)return void console.warn(`Warning: Template ${a}/${n} is not added to storage. The template is null.`);let o=t,s=!0;if(i[a]||(i[a]={}),"function"!=typeof t){let e=l(t,!0,...r);s=e[0],o=e[1]}s?i[a][n]=o:console.error(`Error: Template "${n}" looks broken and is not added to storage.`)},list:function(e=["default"]){return e.map((e=>i[e]?Object.keys(i[e]):[])).flat()},clear:function(){Object.keys(i).forEach((e=>{"default"!=e?delete i[e]:i.default={}}))},remove:function(e){const[t,r="default"]=e;return i[r]?i[r][t]?void delete i[r][t]:`Error: Template "${t}" does not exist in storage "${r}".`:`Error: Storage "${r}" does not exist.`}};module.exports=c;
1
+ "use strict";var e=require("@peter.naydenov/walk"),t=require("@peter.naydenov/stack");function r(e){return function t(r){const{TG_PRX:a,TG_SFX:o,TG_SIZE_P:s,TG_SIZE_S:l}=e;let i,c,u,d=[];if("string"!=typeof r)return n("notAString");if(0==r.length)return[];if(i=r.indexOf(a),0<i&&d.push(r.slice(0,i)),-1==i)return d.push(r),d;{if(u=r.indexOf(a,i+s),c=r.indexOf(o),-1==c)return n("missingClosing");if(c<i)return n("closedBeforeOpened");if(c+=l,-1!=u&&u<c)return n("newBeforeClosed");d.push(r.slice(i,c));let e=t(r.slice(c));return d.concat(e)}}}function n(e){switch(e){case"notAString":return"Error: Template is not a string.";case"missingClosing":return"Error: Placeholder with missing closing tag.";case"closedBeforeOpened":return"Error: Placeholder closing tag without starting one.";case"newBeforeClosed":return"Error: Nested placeholders. Close placeholder before open new one.";default:return"Error: Unknown template error."}}var a={TG_PRX:"{{",TG_SFX:"}}",TG_SIZE_P:2,TG_SIZE_S:2};function o(e){return null==e?"null":"string"==typeof e||"number"==typeof e||"boolean"==typeof e?"primitive":"function"==typeof e?"function":e instanceof Array?"array":e instanceof Object?"object":void 0}function s(e,t,n,o,s,...l){if(e instanceof Object&&Object.entries(e).forEach((([t,r])=>{r instanceof Object&&(e[t]=r.text),r instanceof Array&&(e[t]=r[0])})),!n[t])return`{{ Error: Helper '${t}' is not available}}`;const i="function"==typeof n[t];return e=function(e={}){return"string"==typeof e?{text:e}:e}(e),i?n[t]({theData:e,dependencies:s,full:o},...l):function(e,t){if(null==t)return null;const n=r(a)(e),o=a;return n.forEach(((e,r)=>{if(e.includes(o.TG_PRX)){const a=e.replace(o.TG_PRX,"").replace(o.TG_SFX,"").trim();t.hasOwnProperty(a)&&null!=t[a]&&(n[r]=t[a])}})),n.join("")}(n[t],e)}function l(n,i=!1,c={}){let{hasError:u,placeholders:d,chop:f,helpers:p,handshake:h,snippets:m}=function(e){const{template:t,helpers:n={},handshake:o}=e,{TG_PRX:s,TG_SFX:l}=a,i=[],c={},u="string"==typeof t?t.replace(/<!--[\s\S]*?-->/g,"").replace(/\s{2,}/g," "):t;let d=null;const f=r(a)(u);return"string"==typeof f?d=f:f.forEach(((e,t)=>{const r=RegExp(s+"\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?"+l,"g");if(e.includes(s)){const a=r.exec(e);if(!a)return;let o={index:t,data:(n=a[1],n||null),action:a[2]?a[2].split(",").map((e=>e.trim())):null,name:a[3]?a[3].trim():null};i.push(o),c[i.length-1]=o,o.name&&(c[o.name]=o)}var n})),{hasError:d,placeholders:i,chop:f,helpers:n,handshake:o,snippets:c}}(n);if(u){function b(){return u}return i?[!1,b]:b}{const y=structuredClone(d);function g(r="render",n={},a={},...i){const u=structuredClone(f);let b=!1;if("string"!=typeof r)return`Error: Wrong command "${r}". Available commands: render, debug, snippets, set, curry.`;if(!["render","debug","snippets","set","curry"].includes(r)&&!r.startsWith("snippets"))return`Error: Wrong command "${r}". Available commands: render, debug, snippets, set, curry.`;if(r.startsWith("snippets")&&r.includes(":")){b=!0;let e=r.split(":").slice(1)[0].trim().split(",").map((e=>e.trim()));d=e.map((e=>m[e]))}else if("snippets"===r)b=!0;else{if("set"===r){if("object"!=typeof n||!n)return"Error: 'set' command requires an object with placeholders, helpers, handshake.";const e={...p,...n.helpers||{}},t=h?{...h,...n.handshake||{}}:n.handshake||{},r=[...f];n.placeholders&&Object.entries(n.placeholders).forEach((([e,t])=>{if(isNaN(e)){let n=d.find((t=>t.name===e));r[n.index]=t}else{let n=d[e].index;r[n]=t}}));const a=l({template:r.join(""),helpers:e,handshake:t},!1,c);return"function"==typeof a?a:()=>a}if("curry"===r){return l({template:g("render",n,a,...i),helpers:p,handshake:h},!1,c)}d=structuredClone(y)}if("string"==typeof n)switch(n){case"raw":return u.join("");case"demo":if(!h)return"Error: No handshake data.";n=h;break;case"handshake":return h?structuredClone(h):"Error: No handshake data.";case"helpers":return Object.keys(p).join(", ");case"placeholders":return d.map((e=>u[e.index])).join(", ");case"count":return d.length;default:return`Error: Wrong instruction "${n}". Available instructions: raw, demo, handshake, helpers, placeholders, count.`}const v=[],E={};let k=o(n),j={...c,...a};n=e({data:n});let x=e({data:n});return"null"===k?u.join(""):("array"!==k&&(n=[n]),"null"===k?u.join(""):(n.forEach((r=>{d.forEach((a=>{const{index:l,data:i,action:c}=a,d=!c&&i,f=structuredClone(E),h={dependencies:j,memory:f};let m=r;if(i&&i.includes("/")?m.hasOwnProperty(i)?m=m[i]:i.split("/").forEach((e=>{m=m.hasOwnProperty(e)?m[e]:[]})):"@all"===i||null===i||"@root"===i?m=r:i&&(m=m[i]),d){switch(o(m)){case"function":return void(u[l]=m());case"primitive":return void(u[l]=m);case"array":return void("primitive"===o(m[0])&&(u[l]=m[0]));case"object":return void(m.text&&(u[l]=m.text))}}else{let{dataDeepLevel:a,nestedData:i}=function(t,r){const n=[];let a=0;if(t instanceof Function)return{dataDeepLevel:0,nestedData:[[t()]]};if(null==t)return{dataDeepLevel:0,nestedData:[null]};if("string"==typeof t)return{dataDeepLevel:0,nestedData:[[t]]};const o=structuredClone(t);return r.includes("#")?(e({data:o,objectCallback:function({key:e,value:t,breadcrumbs:r}){return e===r?(n[0]=[t],t):(a=r.split("/").length-1,n[a]||(n[a]=[]),n[a].push(t),t)}}),{dataDeepLevel:a,nestedData:n}):(n.push([o]),{dataDeepLevel:0,nestedData:n})}(m,c),d=function*(e,r){let n=t({type:"LIFO"});for(let t=0;t<=r;t++)n.push(e[t]);for(;n&&!n.isEmpty();){let e=yield n.pull();e&&n.push(e)}}(function(e,t=10){let r={},n=[...e],a=0,o=0,s=0;n.forEach((e=>{"#"===e&&s++})),s<t&&console.error(`Error: Not enough level markers (#) for data with depth level ${t}. Found ${s} level markers in actions: ${e.join(", ")}`);do{r[o]=[],o++}while(o<=t);return n.every((e=>"#"===e?(a++,!(a>t)):e.startsWith("^")&&"^^"!==e?(r[a].push({type:"save",name:e.replace("^",""),level:a}),!0):"^^"===e?(r[a].push({type:"overwrite",name:"none",level:a}),!0):e.startsWith("+")?(r[a].push({type:"extendedRender",name:e.replace("+",""),level:a}),!0):e.startsWith("[]")?(r[a].push({type:"mix",name:e.replace("[]",""),level:a}),!0):e.startsWith(">")?(r[a].push({type:"data",name:e.replace(">",""),level:a}),!0):(""===e||r[a].push({type:"render",name:e,level:a}),!0))),r}(c,a),a);for(let t of d){let{type:a,name:l,level:c}=t;(i[c]||[]).forEach(((t,u)=>{let d=o(t);switch(a){case"route":switch(d){case"array":t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=p[l]({data:e,...h,full:e});null!=a&&("object"===n?t[r].text=s(e,a,p,x,j):t[r]=s(e,a,p,x,j))}));break;case"object":t.text=s(t,l,p,x,j)}break;case"save":E[l]=structuredClone(t);break;case"overwrite":r=structuredClone(t);break;case"data":switch(d){case"array":t.forEach(((e,r)=>t[r]=e instanceof Function?p[l]({data:e(),...h,full:e}):p[l]({data:e,...h,full:e})));break;case"object":i[c]=[p[l]({data:t,...h,full:n})];break;case"function":i[c]=[p[l]({data:t(),...h,full:n})];break;case"primitive":i[c]=p[l]({data:t,...h,full:n})}break;case"render":const f="function"==typeof p[l];switch(d){case"array":f?t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=p[l]({data:e,...h,full:x});null==a&&(t[r]=null),"object"===n?e.text=a:t[r]=a})):t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=s(e,l,p,x,j);null==a?t[r]=null:"object"===n?e.text=a:t[r]=a}));break;case"function":i[c]=p[l]({data:t(),...h,full:n});break;case"primitive":i[c]=f?p[l]({data:t,...h,full:n}):s(t,l,p,x,j);break;case"object":f?i[c][u].text=p[l]({data:t,...h,full:n}):t.text=s(t,l,p,x,j)}break;case"extendedRender":"function"==typeof p[l]&&i[0].forEach(((e,t)=>i[0][t]=p[l]({data:e,...h,full:e})));break;case"mix":if(""===l)switch(d){case"object":Object.keys(t).find((e=>e.includes("/")))?Object.entries(t).forEach((([e,t])=>i[c][e]=t.text)):i[c]=t.text;for(let b=c-1;b>=0;b--)i[b]=e({data:i[b],objectCallback:m});function m({value:e,breadcrumbs:t}){return i[c][t]?i[c][t]:e}break;case"array":t.forEach(((e,r)=>{if(r>0){let n=o(e);if(null==e)return;t[0]+="object"===n?`${e.text}`:`${e}`,t.toSpliced(r,1)}else{let r=o(e);if(t[0]="",null===r)return;t[0]="object"===r?`${e.text}`:`${e}`}})),t.length=1}else{let y=p[l]({data:t,...h,full:n}),g=o(y);switch(t.forEach(((e,r)=>t.splice(r,1))),t.length=0,g){case"primitive":t[0]=y;break;case"array":t.push(...y)}}}}))}if(i instanceof Array&&1===i.length&&i[0]instanceof Array&&(i=i[0]),null==i[0])return;let f=o(i[0]),b=i[0];switch(f){case"primitive":if(null==b)return;u[l]=b;break;case"object":if(null==b.text)return;u[l]=b.text;break;case"array":const e=o(b[0]);u[l]="object"===e?b.map((e=>e.text)).join(""):b.join("")}}})),b?v.push(d.map((e=>u[e.index])).join("<~>")):v.push(u.join(""))})),"array"===k?v:i?i.reduce(((e,t)=>"function"!=typeof t?e:t(e,j)),v.join("")):v.join("")))}return i?[!0,g]:g}}const i={default:{}};const c={build:l,get:function(e){if(!(e instanceof Array))return function(){return'Error: Argument "location" is a string. Should be an array. E.g. ["templateName", "storageName"].'};const[t,r="default"]=e;return i[r]?i[r][t]?i[r][t]:function(){return`Error: Template "${t}" does not exist in storage "${r}".`}:function(){return`Error: Storage "${r}" does not exist.`}},add:function(e,t,...r){const[n,a="default"]=e;if(null==t)return void console.warn(`Warning: Template ${a}/${n} is not added to storage. The template is null.`);let o=t,s=!0;if(i[a]||(i[a]={}),"function"!=typeof t){let e=l(t,!0,...r);s=e[0],o=e[1]}s?i[a][n]=o:console.error(`Error: Template "${n}" looks broken and is not added to storage.`)},list:function(e=["default"]){return e.map((e=>i[e]?Object.keys(i[e]):[])).flat()},clear:function(){Object.keys(i).forEach((e=>{"default"!=e?delete i[e]:i.default={}}))},remove:function(e){const[t,r="default"]=e;return i[r]?i[r][t]?void delete i[r][t]:`Error: Template "${t}" does not exist in storage "${r}".`:`Error: Storage "${r}" does not exist.`}};module.exports=c;
@@ -1 +1 @@
1
- import e from"@peter.naydenov/walk";import t from"@peter.naydenov/stack";function r(e){return function t(r){const{TG_PRX:a,TG_SFX:o,TG_SIZE_P:s,TG_SIZE_S:l}=e;let i,c,u,f=[];if("string"!=typeof r)return n("notAString");if(0==r.length)return[];if(i=r.indexOf(a),0<i&&f.push(r.slice(0,i)),-1==i)return f.push(r),f;{if(u=r.indexOf(a,i+s),c=r.indexOf(o),-1==c)return n("missingClosing");if(c<i)return n("closedBeforeOpened");if(c+=l,-1!=u&&u<c)return n("newBeforeClosed");f.push(r.slice(i,c));let e=t(r.slice(c));return f.concat(e)}}}function n(e){switch(e){case"notAString":return"Error: Template is not a string.";case"missingClosing":return"Error: Placeholder with missing closing tag.";case"closedBeforeOpened":return"Error: Placeholder closing tag without starting one.";case"newBeforeClosed":return"Error: Nested placeholders. Close placeholder before open new one.";default:return"Error: Unknown template error."}}var a={TG_PRX:"{{",TG_SFX:"}}",TG_SIZE_P:2,TG_SIZE_S:2};function o(e){return null==e?"null":"string"==typeof e||"number"==typeof e||"boolean"==typeof e?"primitive":"function"==typeof e?"function":e instanceof Array?"array":e instanceof Object?"object":void 0}function s(e,t,n,o,s,...l){e instanceof Object&&Object.entries(e).forEach((([t,r])=>{r instanceof Object&&(e[t]=r.text),r instanceof Array&&(e[t]=r[0])}));const i="function"==typeof n[t];return e=function(e={}){return"string"==typeof e?{text:e}:e}(e),i?n[t]({theData:e,dependencies:s,full:o},...l):function(e,t){if(null==t)return null;const n=r(a)(e),o=a;return n.forEach(((e,r)=>{if(e.includes(o.TG_PRX)){const a=e.replace(o.TG_PRX,"").replace(o.TG_SFX,"").trim();t.hasOwnProperty(a)&&null!=t[a]&&(n[r]=t[a])}})),n.join("")}(n[t],e)}function l(n,l=!1,i={}){let{hasError:c,placeholders:u,chop:f,helpers:d,handshake:p,snippets:h}=function(e){const{template:t,helpers:n={},handshake:o}=e,{TG_PRX:s,TG_SFX:l}=a,i=[],c={},u="string"==typeof t?t.replace(/<!--[\s\S]*?-->/g,"").replace(/\s{2,}/g," "):t;let f=null;const d=r(a)(u);return"string"==typeof d?f=d:d.forEach(((e,t)=>{const r=RegExp(s+"\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?"+l,"g");if(e.includes(s)){const a=r.exec(e);if(!a)return;let o={index:t,data:(n=a[1],n||null),action:a[2]?a[2].split(",").map((e=>e.trim())):null,name:a[3]?a[3].trim():null};i.push(o),c[i.length-1]=o,o.name&&(c[o.name]=o)}var n})),i.forEach((e=>{e.action&&e.action.every((e=>"#"===e||"^^"===e||!(!e.startsWith("^")||"^^"===e)||(e.startsWith("?")&&(e=e.replace("?","")),e.startsWith("+")&&(e=e.replace("+","")),e.startsWith("[]")&&(e=e.replace("[]","")),e.startsWith(">")&&(e=e.replace(">","")),""===e||!!n[e]||(f=`Error: Missing helper: ${e}`,!1))))})),{hasError:f,placeholders:i,chop:d,helpers:n,handshake:o,snippets:c}}(n);if(c){function m(){return c}return l?[!1,m]:m}{const b=structuredClone(u);function y(r="render",n={},a={},...l){const c=structuredClone(f);let m=!1;if(!["render","debug","snippets"].includes(r)&&!r.startsWith("snippets"))return`Error: Wrong command "${r}". Available commands: render, debug, snippets.`;if(r.startsWith("snippets")&&r.includes(":")){m=!0;let e=r.split(":").slice(1)[0].trim().split(",").map((e=>e.trim()));u=e.map((e=>h[e]))}else"snippets"===r?m=!0:u=structuredClone(b);if("string"==typeof n)switch(n){case"raw":return c.join("");case"demo":if(!p)return"Error: No handshake data.";n=p;break;case"handshake":return p?structuredClone(p):"Error: No handshake data.";case"placeholders":return u.map((e=>c[e.index])).join(", ");default:return`Error: Wrong instruction "${n}". Available instructions: raw, demo, handshake, placeholders.`}const y=[],g={};let v=o(n),E={...i,...a};n=e({data:n});let k=e({data:n});return"null"===v?c.join(""):("array"!==v&&(n=[n]),"null"===v?c.join(""):(n.forEach((r=>{u.forEach((a=>{const{index:l,data:i,action:u}=a,f=!u&&i,p=structuredClone(g),h={dependencies:E,memory:p};let m=r;if(i&&i.includes("/")?m.hasOwnProperty(i)?m=m[i]:i.split("/").forEach((e=>{m=m.hasOwnProperty(e)?m[e]:[]})):"@all"===i||null===i||"@root"===i?m=r:i&&(m=m[i]),f){switch(o(m)){case"function":return void(c[l]=m());case"primitive":return void(c[l]=m);case"array":return void("primitive"===o(m[0])&&(c[l]=m[0]));case"object":return void(m.text&&(c[l]=m.text))}}else{let{dataDeepLevel:a,nestedData:i}=function(t,r){const n=[];let a=0;if(t instanceof Function)return{dataDeepLevel:0,nestedData:[[t()]]};if(null==t)return{dataDeepLevel:0,nestedData:[null]};if("string"==typeof t)return{dataDeepLevel:0,nestedData:[[t]]};const o=structuredClone(t);return r.includes("#")?(e({data:o,objectCallback:function({key:e,value:t,breadcrumbs:r}){return e===r?(n[0]=[t],t):(a=r.split("/").length-1,n[a]||(n[a]=[]),n[a].push(t),t)}}),{dataDeepLevel:a,nestedData:n}):(n.push([o]),{dataDeepLevel:0,nestedData:n})}(m,u),f=function*(e,r){let n=t({type:"LIFO"});for(let t=0;t<=r;t++)n.push(e[t]);for(;n&&!n.isEmpty();){let e=yield n.pull();e&&n.push(e)}}(function(e,t=10){let r={},n=[...e],a=0,o=0,s=0;n.forEach((e=>{"#"===e&&s++})),s<t&&console.error(`Error: Not enough level markers (#) for data with depth level ${t}. Found ${s} level markers in actions: ${e.join(", ")}`);do{r[o]=[],o++}while(o<=t);return n.every((e=>"#"===e?(a++,!(a>t)):e.startsWith("?")?(r[a].push({type:"route",name:e.replace("?",""),level:a}),!0):e.startsWith("^")&&"^^"!==e?(r[a].push({type:"save",name:e.replace("^",""),level:a}),!0):"^^"===e?(r[a].push({type:"overwrite",name:"none",level:a}),!0):e.startsWith("+")?(r[a].push({type:"extendedRender",name:e.replace("+",""),level:a}),!0):e.startsWith("[]")?(r[a].push({type:"mix",name:e.replace("[]",""),level:a}),!0):e.startsWith(">")?(r[a].push({type:"data",name:e.replace(">",""),level:a}),!0):(""===e||r[a].push({type:"render",name:e,level:a}),!0))),r}(u,a),a);for(let t of f){let{type:a,name:l,level:c}=t;(i[c]||[]).forEach(((t,u)=>{let f=o(t);switch(a){case"route":switch(f){case"array":t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=d[l]({data:e,...h,full:e});null!=a&&("object"===n?t[r].text=s(e,a,d,k,E):t[r]=s(e,a,d,k,E))}));break;case"object":t.text=s(t,l,d,k,E)}break;case"save":g[l]=structuredClone(t);break;case"overwrite":r=structuredClone(t);break;case"data":switch(f){case"array":t.forEach(((e,r)=>t[r]=e instanceof Function?d[l]({data:e(),...h,full:e}):d[l]({data:e,...h,full:e})));break;case"object":i[c]=[d[l]({data:t,...h,full:n})];break;case"function":i[c]=[d[l]({data:t(),...h,full:n})];break;case"primitive":i[c]=d[l]({data:t,...h,full:n})}break;case"render":const p="function"==typeof d[l];switch(f){case"array":p?t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=d[l]({data:e,...h,full:k});null==a&&(t[r]=null),"object"===n?e.text=a:t[r]=a})):t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=s(e,l,d,k,E);null==a?t[r]=null:"object"===n?e.text=a:t[r]=a}));break;case"function":i[c]=d[l]({data:t(),...h,full:n});break;case"primitive":i[c]=p?d[l]({data:t,...h,full:n}):s(t,l,d,k,E);break;case"object":p?i[c][u].text=d[l]({data:t,...h,full:n}):t.text=s(t,l,d,k,E)}break;case"extendedRender":"function"==typeof d[l]&&i[0].forEach(((e,t)=>i[0][t]=d[l]({data:e,...h,full:e})));break;case"mix":if(""===l)switch(f){case"object":Object.keys(t).find((e=>e.includes("/")))?Object.entries(t).forEach((([e,t])=>i[c][e]=t.text)):i[c]=t.text;for(let b=c-1;b>=0;b--)i[b]=e({data:i[b],objectCallback:m});function m({value:e,breadcrumbs:t}){return i[c][t]?i[c][t]:e}break;case"array":t.forEach(((e,r)=>{if(r>0){let n=o(e);if(null==e)return;t[0]+="object"===n?`${e.text}`:`${e}`,t.toSpliced(r,1)}else{let r=o(e);if(t[0]="",null===r)return;t[0]="object"===r?`${e.text}`:`${e}`}})),t.length=1}else{let y=d[l]({data:t,...h,full:n}),v=o(y);switch(t.forEach(((e,r)=>t.splice(r,1))),t.length=0,v){case"primitive":t[0]=y;break;case"array":t.push(...y)}}}}))}if(i instanceof Array&&1===i.length&&i[0]instanceof Array&&(i=i[0]),null==i[0])return;let p=o(i[0]),b=i[0];switch(p){case"primitive":if(null==b)return;c[l]=b;break;case"object":if(null==b.text)return;c[l]=b.text;break;case"array":const e=o(b[0]);c[l]="object"===e?b.map((e=>e.text)).join(""):b.join("")}}})),m?y.push(u.map((e=>c[e.index])).join("<~>")):y.push(c.join(""))})),"array"===v?y:l?l.reduce(((e,t)=>"function"!=typeof t?e:t(e,E)),y.join("")):y.join("")))}return l?[!0,y]:y}}const i={default:{}};const c={build:l,get:function(e){if(!(e instanceof Array))return function(){return'Error: Argument "location" is a string. Should be an array. E.g. ["templateName", "storageName"].'};const[t,r="default"]=e;return i[r]?i[r][t]?i[r][t]:function(){return`Error: Template "${t}" does not exist in storage "${r}".`}:function(){return`Error: Storage "${r}" does not exist.`}},add:function(e,t,...r){const[n,a="default"]=e;if(null==t)return void console.warn(`Warning: Template ${a}/${n} is not added to storage. The template is null.`);let o=t,s=!0;if(i[a]||(i[a]={}),"function"!=typeof t){let e=l(t,!0,...r);s=e[0],o=e[1]}s?i[a][n]=o:console.error(`Error: Template "${n}" looks broken and is not added to storage.`)},list:function(e=["default"]){return e.map((e=>i[e]?Object.keys(i[e]):[])).flat()},clear:function(){Object.keys(i).forEach((e=>{"default"!=e?delete i[e]:i.default={}}))},remove:function(e){const[t,r="default"]=e;return i[r]?i[r][t]?void delete i[r][t]:`Error: Template "${t}" does not exist in storage "${r}".`:`Error: Storage "${r}" does not exist.`}};export{c as default};
1
+ import e from"@peter.naydenov/walk";import t from"@peter.naydenov/stack";function r(e){return function t(r){const{TG_PRX:a,TG_SFX:o,TG_SIZE_P:s,TG_SIZE_S:l}=e;let i,c,u,d=[];if("string"!=typeof r)return n("notAString");if(0==r.length)return[];if(i=r.indexOf(a),0<i&&d.push(r.slice(0,i)),-1==i)return d.push(r),d;{if(u=r.indexOf(a,i+s),c=r.indexOf(o),-1==c)return n("missingClosing");if(c<i)return n("closedBeforeOpened");if(c+=l,-1!=u&&u<c)return n("newBeforeClosed");d.push(r.slice(i,c));let e=t(r.slice(c));return d.concat(e)}}}function n(e){switch(e){case"notAString":return"Error: Template is not a string.";case"missingClosing":return"Error: Placeholder with missing closing tag.";case"closedBeforeOpened":return"Error: Placeholder closing tag without starting one.";case"newBeforeClosed":return"Error: Nested placeholders. Close placeholder before open new one.";default:return"Error: Unknown template error."}}var a={TG_PRX:"{{",TG_SFX:"}}",TG_SIZE_P:2,TG_SIZE_S:2};function o(e){return null==e?"null":"string"==typeof e||"number"==typeof e||"boolean"==typeof e?"primitive":"function"==typeof e?"function":e instanceof Array?"array":e instanceof Object?"object":void 0}function s(e,t,n,o,s,...l){if(e instanceof Object&&Object.entries(e).forEach((([t,r])=>{r instanceof Object&&(e[t]=r.text),r instanceof Array&&(e[t]=r[0])})),!n[t])return`{{ Error: Helper '${t}' is not available}}`;const i="function"==typeof n[t];return e=function(e={}){return"string"==typeof e?{text:e}:e}(e),i?n[t]({theData:e,dependencies:s,full:o},...l):function(e,t){if(null==t)return null;const n=r(a)(e),o=a;return n.forEach(((e,r)=>{if(e.includes(o.TG_PRX)){const a=e.replace(o.TG_PRX,"").replace(o.TG_SFX,"").trim();t.hasOwnProperty(a)&&null!=t[a]&&(n[r]=t[a])}})),n.join("")}(n[t],e)}function l(n,i=!1,c={}){let{hasError:u,placeholders:d,chop:f,helpers:p,handshake:h,snippets:m}=function(e){const{template:t,helpers:n={},handshake:o}=e,{TG_PRX:s,TG_SFX:l}=a,i=[],c={},u="string"==typeof t?t.replace(/<!--[\s\S]*?-->/g,"").replace(/\s{2,}/g," "):t;let d=null;const f=r(a)(u);return"string"==typeof f?d=f:f.forEach(((e,t)=>{const r=RegExp(s+"\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?"+l,"g");if(e.includes(s)){const a=r.exec(e);if(!a)return;let o={index:t,data:(n=a[1],n||null),action:a[2]?a[2].split(",").map((e=>e.trim())):null,name:a[3]?a[3].trim():null};i.push(o),c[i.length-1]=o,o.name&&(c[o.name]=o)}var n})),{hasError:d,placeholders:i,chop:f,helpers:n,handshake:o,snippets:c}}(n);if(u){function b(){return u}return i?[!1,b]:b}{const y=structuredClone(d);function g(r="render",n={},a={},...i){const u=structuredClone(f);let b=!1;if("string"!=typeof r)return`Error: Wrong command "${r}". Available commands: render, debug, snippets, set, curry.`;if(!["render","debug","snippets","set","curry"].includes(r)&&!r.startsWith("snippets"))return`Error: Wrong command "${r}". Available commands: render, debug, snippets, set, curry.`;if(r.startsWith("snippets")&&r.includes(":")){b=!0;let e=r.split(":").slice(1)[0].trim().split(",").map((e=>e.trim()));d=e.map((e=>m[e]))}else if("snippets"===r)b=!0;else{if("set"===r){if("object"!=typeof n||!n)return"Error: 'set' command requires an object with placeholders, helpers, handshake.";const e={...p,...n.helpers||{}},t=h?{...h,...n.handshake||{}}:n.handshake||{},r=[...f];n.placeholders&&Object.entries(n.placeholders).forEach((([e,t])=>{if(isNaN(e)){let n=d.find((t=>t.name===e));r[n.index]=t}else{let n=d[e].index;r[n]=t}}));const a=l({template:r.join(""),helpers:e,handshake:t},!1,c);return"function"==typeof a?a:()=>a}if("curry"===r){return l({template:g("render",n,a,...i),helpers:p,handshake:h},!1,c)}d=structuredClone(y)}if("string"==typeof n)switch(n){case"raw":return u.join("");case"demo":if(!h)return"Error: No handshake data.";n=h;break;case"handshake":return h?structuredClone(h):"Error: No handshake data.";case"helpers":return Object.keys(p).join(", ");case"placeholders":return d.map((e=>u[e.index])).join(", ");case"count":return d.length;default:return`Error: Wrong instruction "${n}". Available instructions: raw, demo, handshake, helpers, placeholders, count.`}const v=[],E={};let k=o(n),j={...c,...a};n=e({data:n});let x=e({data:n});return"null"===k?u.join(""):("array"!==k&&(n=[n]),"null"===k?u.join(""):(n.forEach((r=>{d.forEach((a=>{const{index:l,data:i,action:c}=a,d=!c&&i,f=structuredClone(E),h={dependencies:j,memory:f};let m=r;if(i&&i.includes("/")?m.hasOwnProperty(i)?m=m[i]:i.split("/").forEach((e=>{m=m.hasOwnProperty(e)?m[e]:[]})):"@all"===i||null===i||"@root"===i?m=r:i&&(m=m[i]),d){switch(o(m)){case"function":return void(u[l]=m());case"primitive":return void(u[l]=m);case"array":return void("primitive"===o(m[0])&&(u[l]=m[0]));case"object":return void(m.text&&(u[l]=m.text))}}else{let{dataDeepLevel:a,nestedData:i}=function(t,r){const n=[];let a=0;if(t instanceof Function)return{dataDeepLevel:0,nestedData:[[t()]]};if(null==t)return{dataDeepLevel:0,nestedData:[null]};if("string"==typeof t)return{dataDeepLevel:0,nestedData:[[t]]};const o=structuredClone(t);return r.includes("#")?(e({data:o,objectCallback:function({key:e,value:t,breadcrumbs:r}){return e===r?(n[0]=[t],t):(a=r.split("/").length-1,n[a]||(n[a]=[]),n[a].push(t),t)}}),{dataDeepLevel:a,nestedData:n}):(n.push([o]),{dataDeepLevel:0,nestedData:n})}(m,c),d=function*(e,r){let n=t({type:"LIFO"});for(let t=0;t<=r;t++)n.push(e[t]);for(;n&&!n.isEmpty();){let e=yield n.pull();e&&n.push(e)}}(function(e,t=10){let r={},n=[...e],a=0,o=0,s=0;n.forEach((e=>{"#"===e&&s++})),s<t&&console.error(`Error: Not enough level markers (#) for data with depth level ${t}. Found ${s} level markers in actions: ${e.join(", ")}`);do{r[o]=[],o++}while(o<=t);return n.every((e=>"#"===e?(a++,!(a>t)):e.startsWith("^")&&"^^"!==e?(r[a].push({type:"save",name:e.replace("^",""),level:a}),!0):"^^"===e?(r[a].push({type:"overwrite",name:"none",level:a}),!0):e.startsWith("+")?(r[a].push({type:"extendedRender",name:e.replace("+",""),level:a}),!0):e.startsWith("[]")?(r[a].push({type:"mix",name:e.replace("[]",""),level:a}),!0):e.startsWith(">")?(r[a].push({type:"data",name:e.replace(">",""),level:a}),!0):(""===e||r[a].push({type:"render",name:e,level:a}),!0))),r}(c,a),a);for(let t of d){let{type:a,name:l,level:c}=t;(i[c]||[]).forEach(((t,u)=>{let d=o(t);switch(a){case"route":switch(d){case"array":t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=p[l]({data:e,...h,full:e});null!=a&&("object"===n?t[r].text=s(e,a,p,x,j):t[r]=s(e,a,p,x,j))}));break;case"object":t.text=s(t,l,p,x,j)}break;case"save":E[l]=structuredClone(t);break;case"overwrite":r=structuredClone(t);break;case"data":switch(d){case"array":t.forEach(((e,r)=>t[r]=e instanceof Function?p[l]({data:e(),...h,full:e}):p[l]({data:e,...h,full:e})));break;case"object":i[c]=[p[l]({data:t,...h,full:n})];break;case"function":i[c]=[p[l]({data:t(),...h,full:n})];break;case"primitive":i[c]=p[l]({data:t,...h,full:n})}break;case"render":const f="function"==typeof p[l];switch(d){case"array":f?t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=p[l]({data:e,...h,full:x});null==a&&(t[r]=null),"object"===n?e.text=a:t[r]=a})):t.forEach(((e,r)=>{if(null==e)return;const n=o(e),a=s(e,l,p,x,j);null==a?t[r]=null:"object"===n?e.text=a:t[r]=a}));break;case"function":i[c]=p[l]({data:t(),...h,full:n});break;case"primitive":i[c]=f?p[l]({data:t,...h,full:n}):s(t,l,p,x,j);break;case"object":f?i[c][u].text=p[l]({data:t,...h,full:n}):t.text=s(t,l,p,x,j)}break;case"extendedRender":"function"==typeof p[l]&&i[0].forEach(((e,t)=>i[0][t]=p[l]({data:e,...h,full:e})));break;case"mix":if(""===l)switch(d){case"object":Object.keys(t).find((e=>e.includes("/")))?Object.entries(t).forEach((([e,t])=>i[c][e]=t.text)):i[c]=t.text;for(let b=c-1;b>=0;b--)i[b]=e({data:i[b],objectCallback:m});function m({value:e,breadcrumbs:t}){return i[c][t]?i[c][t]:e}break;case"array":t.forEach(((e,r)=>{if(r>0){let n=o(e);if(null==e)return;t[0]+="object"===n?`${e.text}`:`${e}`,t.toSpliced(r,1)}else{let r=o(e);if(t[0]="",null===r)return;t[0]="object"===r?`${e.text}`:`${e}`}})),t.length=1}else{let y=p[l]({data:t,...h,full:n}),g=o(y);switch(t.forEach(((e,r)=>t.splice(r,1))),t.length=0,g){case"primitive":t[0]=y;break;case"array":t.push(...y)}}}}))}if(i instanceof Array&&1===i.length&&i[0]instanceof Array&&(i=i[0]),null==i[0])return;let f=o(i[0]),b=i[0];switch(f){case"primitive":if(null==b)return;u[l]=b;break;case"object":if(null==b.text)return;u[l]=b.text;break;case"array":const e=o(b[0]);u[l]="object"===e?b.map((e=>e.text)).join(""):b.join("")}}})),b?v.push(d.map((e=>u[e.index])).join("<~>")):v.push(u.join(""))})),"array"===k?v:i?i.reduce(((e,t)=>"function"!=typeof t?e:t(e,j)),v.join("")):v.join("")))}return i?[!0,g]:g}}const i={default:{}};const c={build:l,get:function(e){if(!(e instanceof Array))return function(){return'Error: Argument "location" is a string. Should be an array. E.g. ["templateName", "storageName"].'};const[t,r="default"]=e;return i[r]?i[r][t]?i[r][t]:function(){return`Error: Template "${t}" does not exist in storage "${r}".`}:function(){return`Error: Storage "${r}" does not exist.`}},add:function(e,t,...r){const[n,a="default"]=e;if(null==t)return void console.warn(`Warning: Template ${a}/${n} is not added to storage. The template is null.`);let o=t,s=!0;if(i[a]||(i[a]={}),"function"!=typeof t){let e=l(t,!0,...r);s=e[0],o=e[1]}s?i[a][n]=o:console.error(`Error: Template "${n}" looks broken and is not added to storage.`)},list:function(e=["default"]){return e.map((e=>i[e]?Object.keys(i[e]):[])).flat()},clear:function(){Object.keys(i).forEach((e=>{"default"!=e?delete i[e]:i.default={}}))},remove:function(e){const[t,r="default"]=e;return i[r]?i[r][t]?void delete i[r][t]:`Error: Template "${t}" does not exist in storage "${r}".`:`Error: Storage "${r}" does not exist.`}};export{c as default};
package/dist/morph.umd.js CHANGED
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@peter.naydenov/walk")):"function"==typeof define&&define.amd?define(["@peter.naydenov/walk"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).morph=t(e.walk)}(this,(function(e){"use strict";function t(e){return function t(n){const{TG_PRX:a,TG_SFX:o,TG_SIZE_P:l,TG_SIZE_S:s}=e;let i,c,u,f=[];if("string"!=typeof n)return r("notAString");if(0==n.length)return[];if(i=n.indexOf(a),0<i&&f.push(n.slice(0,i)),-1==i)return f.push(n),f;{if(u=n.indexOf(a,i+l),c=n.indexOf(o),-1==c)return r("missingClosing");if(c<i)return r("closedBeforeOpened");if(c+=s,-1!=u&&u<c)return r("newBeforeClosed");f.push(n.slice(i,c));let e=t(n.slice(c));return f.concat(e)}}}function r(e){switch(e){case"notAString":return"Error: Template is not a string.";case"missingClosing":return"Error: Placeholder with missing closing tag.";case"closedBeforeOpened":return"Error: Placeholder closing tag without starting one.";case"newBeforeClosed":return"Error: Nested placeholders. Close placeholder before open new one.";default:return"Error: Unknown template error."}}function*n(e,t){let r=function(e={}){let t=[],{type:r,limit:n,onLimit:a}=Object.assign({},{type:"FIFO",limit:!1,onLimit:"update"},e),o="LIFO"===r.toUpperCase(),l=!1;function s(e=1,r=0){let n=[];return r>0&&Array.from({length:r}).map((()=>t.pop())),1==e?t.pop():(Array.from({length:e}).map((()=>{let e=t.pop();null!=e&&n.push(e)})),n)}function i(e=1,r=0){let n=[],a=t.length-r;return e>1&&Array.from({length:e}).map((()=>{null!=t[a-1]&&n.push(t[a-1]),a--})),1==e?t[t.length-1]:n}function c(){}return c.prototype={pull:s,pullReverse:function(e=1,t=0){let r=s(e,t);return r instanceof Array?r.reverse():r},peek:i,peekReverse:function(e=1,t=0){const r=i(e,t);return r instanceof Array?r.reverse():r},getSize:()=>t.length,isEmpty:()=>0==t.length,reset:()=>(t=[],!0),debug:()=>[...t]},c.prototype.push=o?function(e){const r=e instanceof Array,o=r?e.length:1;let i=!1;if("full"!==a||!l){if(n&&r&&o>n&&(e=e.slice(0,-o+n)),n){const o=(r?e.length:1)+t.length;o>=n&&"full"===a&&(e=e.slice(0,-(o-n))),o>=n&&"update"===a&&(i=s(o-n))}return t=e instanceof Array?t.concat(e):t.concat([e]),l=!!n&&t.length===n,i||void 0}}:function(e){const r=e instanceof Array,o=r?e.length:1;let i=!1;if("full"!==a||!l){if(n&&r&&o>n&&(e=e.slice(o-n)),n){const o=(r?e.length:1)+t.length;o>=n&&"full"===a&&(e=e.slice(0,-(o-n))),o>=n&&"update"===a&&(i=s(o-n))}return t=r?e.reduce(((e,t)=>[t,...e]),t):[e].reduce(((e,t)=>[t,...e]),t),l=!!n&&t.length===n,i||void 0}},new c}({type:"LIFO"});for(let n=0;n<=t;n++)r.push(e[n]);for(;r&&!r.isEmpty();){let e=yield r.pull();e&&r.push(e)}}var a={TG_PRX:"{{",TG_SFX:"}}",TG_SIZE_P:2,TG_SIZE_S:2};function o(e){return null==e?"null":"string"==typeof e||"number"==typeof e||"boolean"==typeof e?"primitive":"function"==typeof e?"function":e instanceof Array?"array":e instanceof Object?"object":void 0}function l(e,r,n,o,l,...s){e instanceof Object&&Object.entries(e).forEach((([t,r])=>{r instanceof Object&&(e[t]=r.text),r instanceof Array&&(e[t]=r[0])}));const i="function"==typeof n[r];return e=function(e={}){return"string"==typeof e?{text:e}:e}(e),i?n[r]({theData:e,dependencies:l,full:o},...s):function(e,r){if(null==r)return null;const n=t(a)(e),o=a;return n.forEach(((e,t)=>{if(e.includes(o.TG_PRX)){const a=e.replace(o.TG_PRX,"").replace(o.TG_SFX,"").trim();r.hasOwnProperty(a)&&null!=r[a]&&(n[t]=r[a])}})),n.join("")}(n[r],e)}function s(r,s=!1,i={}){let{hasError:c,placeholders:u,chop:f,helpers:p,handshake:d,snippets:h}=function(e){const{template:r,helpers:n={},handshake:o}=e,{TG_PRX:l,TG_SFX:s}=a,i=[],c={},u="string"==typeof r?r.replace(/<!--[\s\S]*?-->/g,"").replace(/\s{2,}/g," "):r;let f=null;const p=t(a)(u);return"string"==typeof p?f=p:p.forEach(((e,t)=>{const r=RegExp(l+"\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?"+s,"g");if(e.includes(l)){const a=r.exec(e);if(!a)return;let o={index:t,data:(n=a[1],n||null),action:a[2]?a[2].split(",").map((e=>e.trim())):null,name:a[3]?a[3].trim():null};i.push(o),c[i.length-1]=o,o.name&&(c[o.name]=o)}var n})),i.forEach((e=>{e.action&&e.action.every((e=>"#"===e||"^^"===e||!(!e.startsWith("^")||"^^"===e)||(e.startsWith("?")&&(e=e.replace("?","")),e.startsWith("+")&&(e=e.replace("+","")),e.startsWith("[]")&&(e=e.replace("[]","")),e.startsWith(">")&&(e=e.replace(">","")),""===e||!!n[e]||(f=`Error: Missing helper: ${e}`,!1))))})),{hasError:f,placeholders:i,chop:p,helpers:n,handshake:o,snippets:c}}(r);if(c){function m(){return c}return s?[!1,m]:m}{const y=structuredClone(u);function g(t="render",r={},a={},...s){const c=structuredClone(f);let m=!1;if(!["render","debug","snippets"].includes(t)&&!t.startsWith("snippets"))return`Error: Wrong command "${t}". Available commands: render, debug, snippets.`;if(t.startsWith("snippets")&&t.includes(":")){m=!0;let e=t.split(":").slice(1)[0].trim().split(",").map((e=>e.trim()));u=e.map((e=>h[e]))}else"snippets"===t?m=!0:u=structuredClone(y);if("string"==typeof r)switch(r){case"raw":return c.join("");case"demo":if(!d)return"Error: No handshake data.";r=d;break;case"handshake":return d?structuredClone(d):"Error: No handshake data.";case"placeholders":return u.map((e=>c[e.index])).join(", ");default:return`Error: Wrong instruction "${r}". Available instructions: raw, demo, handshake, placeholders.`}const g=[],b={};let v=o(r),E={...i,...a};r=e({data:r});let k=e({data:r});return"null"===v?c.join(""):("array"!==v&&(r=[r]),"null"===v?c.join(""):(r.forEach((t=>{u.forEach((a=>{const{index:s,data:i,action:u}=a,f=!u&&i,d=structuredClone(b),h={dependencies:E,memory:d};let m=t;if(i&&i.includes("/")?m.hasOwnProperty(i)?m=m[i]:i.split("/").forEach((e=>{m=m.hasOwnProperty(e)?m[e]:[]})):"@all"===i||null===i||"@root"===i?m=t:i&&(m=m[i]),f){switch(o(m)){case"function":return void(c[s]=m());case"primitive":return void(c[s]=m);case"array":return void("primitive"===o(m[0])&&(c[s]=m[0]));case"object":return void(m.text&&(c[s]=m.text))}}else{let{dataDeepLevel:a,nestedData:i}=function(t,r){const n=[];let a=0;if(t instanceof Function)return{dataDeepLevel:0,nestedData:[[t()]]};if(null==t)return{dataDeepLevel:0,nestedData:[null]};if("string"==typeof t)return{dataDeepLevel:0,nestedData:[[t]]};const o=structuredClone(t);return r.includes("#")?(e({data:o,objectCallback:function({key:e,value:t,breadcrumbs:r}){return e===r?(n[0]=[t],t):(a=r.split("/").length-1,n[a]||(n[a]=[]),n[a].push(t),t)}}),{dataDeepLevel:a,nestedData:n}):(n.push([o]),{dataDeepLevel:0,nestedData:n})}(m,u),f=n(function(e,t=10){let r={},n=[...e],a=0,o=0,l=0;n.forEach((e=>{"#"===e&&l++})),l<t&&console.error(`Error: Not enough level markers (#) for data with depth level ${t}. Found ${l} level markers in actions: ${e.join(", ")}`);do{r[o]=[],o++}while(o<=t);return n.every((e=>"#"===e?(a++,!(a>t)):e.startsWith("?")?(r[a].push({type:"route",name:e.replace("?",""),level:a}),!0):e.startsWith("^")&&"^^"!==e?(r[a].push({type:"save",name:e.replace("^",""),level:a}),!0):"^^"===e?(r[a].push({type:"overwrite",name:"none",level:a}),!0):e.startsWith("+")?(r[a].push({type:"extendedRender",name:e.replace("+",""),level:a}),!0):e.startsWith("[]")?(r[a].push({type:"mix",name:e.replace("[]",""),level:a}),!0):e.startsWith(">")?(r[a].push({type:"data",name:e.replace(">",""),level:a}),!0):(""===e||r[a].push({type:"render",name:e,level:a}),!0))),r}(u,a),a);for(let n of f){let{type:a,name:s,level:c}=n;(i[c]||[]).forEach(((n,u)=>{let f=o(n);switch(a){case"route":switch(f){case"array":n.forEach(((e,t)=>{if(null==e)return;const r=o(e),a=p[s]({data:e,...h,full:e});null!=a&&("object"===r?n[t].text=l(e,a,p,k,E):n[t]=l(e,a,p,k,E))}));break;case"object":n.text=l(n,s,p,k,E)}break;case"save":b[s]=structuredClone(n);break;case"overwrite":t=structuredClone(n);break;case"data":switch(f){case"array":n.forEach(((e,t)=>n[t]=e instanceof Function?p[s]({data:e(),...h,full:e}):p[s]({data:e,...h,full:e})));break;case"object":i[c]=[p[s]({data:n,...h,full:r})];break;case"function":i[c]=[p[s]({data:n(),...h,full:r})];break;case"primitive":i[c]=p[s]({data:n,...h,full:r})}break;case"render":const d="function"==typeof p[s];switch(f){case"array":d?n.forEach(((e,t)=>{if(null==e)return;const r=o(e),a=p[s]({data:e,...h,full:k});null==a&&(n[t]=null),"object"===r?e.text=a:n[t]=a})):n.forEach(((e,t)=>{if(null==e)return;const r=o(e),a=l(e,s,p,k,E);null==a?n[t]=null:"object"===r?e.text=a:n[t]=a}));break;case"function":i[c]=p[s]({data:n(),...h,full:r});break;case"primitive":i[c]=d?p[s]({data:n,...h,full:r}):l(n,s,p,k,E);break;case"object":d?i[c][u].text=p[s]({data:n,...h,full:r}):n.text=l(n,s,p,k,E)}break;case"extendedRender":"function"==typeof p[s]&&i[0].forEach(((e,t)=>i[0][t]=p[s]({data:e,...h,full:e})));break;case"mix":if(""===s)switch(f){case"object":Object.keys(n).find((e=>e.includes("/")))?Object.entries(n).forEach((([e,t])=>i[c][e]=t.text)):i[c]=n.text;for(let y=c-1;y>=0;y--)i[y]=e({data:i[y],objectCallback:m});function m({value:e,breadcrumbs:t}){return i[c][t]?i[c][t]:e}break;case"array":n.forEach(((e,t)=>{if(t>0){let r=o(e);if(null==e)return;n[0]+="object"===r?`${e.text}`:`${e}`,n.toSpliced(t,1)}else{let t=o(e);if(n[0]="",null===t)return;n[0]="object"===t?`${e.text}`:`${e}`}})),n.length=1}else{let g=p[s]({data:n,...h,full:r}),v=o(g);switch(n.forEach(((e,t)=>n.splice(t,1))),n.length=0,v){case"primitive":n[0]=g;break;case"array":n.push(...g)}}}}))}if(i instanceof Array&&1===i.length&&i[0]instanceof Array&&(i=i[0]),null==i[0])return;let d=o(i[0]),y=i[0];switch(d){case"primitive":if(null==y)return;c[s]=y;break;case"object":if(null==y.text)return;c[s]=y.text;break;case"array":const e=o(y[0]);c[s]="object"===e?y.map((e=>e.text)).join(""):y.join("")}}})),m?g.push(u.map((e=>c[e.index])).join("<~>")):g.push(c.join(""))})),"array"===v?g:s?s.reduce(((e,t)=>"function"!=typeof t?e:t(e,E)),g.join("")):g.join("")))}return s?[!0,g]:g}}const i={default:{}};return{build:s,get:function(e){if(!(e instanceof Array))return function(){return'Error: Argument "location" is a string. Should be an array. E.g. ["templateName", "storageName"].'};const[t,r="default"]=e;return i[r]?i[r][t]?i[r][t]:function(){return`Error: Template "${t}" does not exist in storage "${r}".`}:function(){return`Error: Storage "${r}" does not exist.`}},add:function(e,t,...r){const[n,a="default"]=e;if(null==t)return void console.warn(`Warning: Template ${a}/${n} is not added to storage. The template is null.`);let o=t,l=!0;if(i[a]||(i[a]={}),"function"!=typeof t){let e=s(t,!0,...r);l=e[0],o=e[1]}l?i[a][n]=o:console.error(`Error: Template "${n}" looks broken and is not added to storage.`)},list:function(e=["default"]){return e.map((e=>i[e]?Object.keys(i[e]):[])).flat()},clear:function(){Object.keys(i).forEach((e=>{"default"!=e?delete i[e]:i.default={}}))},remove:function(e){const[t,r="default"]=e;return i[r]?i[r][t]?void delete i[r][t]:`Error: Template "${t}" does not exist in storage "${r}".`:`Error: Storage "${r}" does not exist.`}}}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@peter.naydenov/walk")):"function"==typeof define&&define.amd?define(["@peter.naydenov/walk"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).morph=t(e.walk)}(this,(function(e){"use strict";function t(e){return function t(n){const{TG_PRX:a,TG_SFX:o,TG_SIZE_P:l,TG_SIZE_S:s}=e;let i,c,u,f=[];if("string"!=typeof n)return r("notAString");if(0==n.length)return[];if(i=n.indexOf(a),0<i&&f.push(n.slice(0,i)),-1==i)return f.push(n),f;{if(u=n.indexOf(a,i+l),c=n.indexOf(o),-1==c)return r("missingClosing");if(c<i)return r("closedBeforeOpened");if(c+=s,-1!=u&&u<c)return r("newBeforeClosed");f.push(n.slice(i,c));let e=t(n.slice(c));return f.concat(e)}}}function r(e){switch(e){case"notAString":return"Error: Template is not a string.";case"missingClosing":return"Error: Placeholder with missing closing tag.";case"closedBeforeOpened":return"Error: Placeholder closing tag without starting one.";case"newBeforeClosed":return"Error: Nested placeholders. Close placeholder before open new one.";default:return"Error: Unknown template error."}}function*n(e,t){let r=function(e={}){let t=[],{type:r,limit:n,onLimit:a}=Object.assign({},{type:"FIFO",limit:!1,onLimit:"update"},e),o="LIFO"===r.toUpperCase(),l=!1;function s(e=1,r=0){let n=[];return r>0&&Array.from({length:r}).map((()=>t.pop())),1==e?t.pop():(Array.from({length:e}).map((()=>{let e=t.pop();null!=e&&n.push(e)})),n)}function i(e=1,r=0){let n=[],a=t.length-r;return e>1&&Array.from({length:e}).map((()=>{null!=t[a-1]&&n.push(t[a-1]),a--})),1==e?t[t.length-1]:n}function c(){}return c.prototype={pull:s,pullReverse:function(e=1,t=0){let r=s(e,t);return r instanceof Array?r.reverse():r},peek:i,peekReverse:function(e=1,t=0){const r=i(e,t);return r instanceof Array?r.reverse():r},getSize:()=>t.length,isEmpty:()=>0==t.length,reset:()=>(t=[],!0),debug:()=>[...t]},c.prototype.push=o?function(e){const r=e instanceof Array,o=r?e.length:1;let i=!1;if("full"!==a||!l){if(n&&r&&o>n&&(e=e.slice(0,-o+n)),n){const o=(r?e.length:1)+t.length;o>=n&&"full"===a&&(e=e.slice(0,-(o-n))),o>=n&&"update"===a&&(i=s(o-n))}return t=e instanceof Array?t.concat(e):t.concat([e]),l=!!n&&t.length===n,i||void 0}}:function(e){const r=e instanceof Array,o=r?e.length:1;let i=!1;if("full"!==a||!l){if(n&&r&&o>n&&(e=e.slice(o-n)),n){const o=(r?e.length:1)+t.length;o>=n&&"full"===a&&(e=e.slice(0,-(o-n))),o>=n&&"update"===a&&(i=s(o-n))}return t=r?e.reduce(((e,t)=>[t,...e]),t):[e].reduce(((e,t)=>[t,...e]),t),l=!!n&&t.length===n,i||void 0}},new c}({type:"LIFO"});for(let n=0;n<=t;n++)r.push(e[n]);for(;r&&!r.isEmpty();){let e=yield r.pull();e&&r.push(e)}}var a={TG_PRX:"{{",TG_SFX:"}}",TG_SIZE_P:2,TG_SIZE_S:2};function o(e){return null==e?"null":"string"==typeof e||"number"==typeof e||"boolean"==typeof e?"primitive":"function"==typeof e?"function":e instanceof Array?"array":e instanceof Object?"object":void 0}function l(e,r,n,o,l,...s){if(e instanceof Object&&Object.entries(e).forEach((([t,r])=>{r instanceof Object&&(e[t]=r.text),r instanceof Array&&(e[t]=r[0])})),!n[r])return`{{ Error: Helper '${r}' is not available}}`;const i="function"==typeof n[r];return e=function(e={}){return"string"==typeof e?{text:e}:e}(e),i?n[r]({theData:e,dependencies:l,full:o},...s):function(e,r){if(null==r)return null;const n=t(a)(e),o=a;return n.forEach(((e,t)=>{if(e.includes(o.TG_PRX)){const a=e.replace(o.TG_PRX,"").replace(o.TG_SFX,"").trim();r.hasOwnProperty(a)&&null!=r[a]&&(n[t]=r[a])}})),n.join("")}(n[r],e)}function s(r,i=!1,c={}){let{hasError:u,placeholders:f,chop:d,helpers:p,handshake:h,snippets:m}=function(e){const{template:r,helpers:n={},handshake:o}=e,{TG_PRX:l,TG_SFX:s}=a,i=[],c={},u="string"==typeof r?r.replace(/<!--[\s\S]*?-->/g,"").replace(/\s{2,}/g," "):r;let f=null;const d=t(a)(u);return"string"==typeof d?f=d:d.forEach(((e,t)=>{const r=RegExp(l+"\\s*(.*?)\\s*(?::\\s*(.*?)\\s*)?(?::\\s*(.*?)\\s*)?"+s,"g");if(e.includes(l)){const a=r.exec(e);if(!a)return;let o={index:t,data:(n=a[1],n||null),action:a[2]?a[2].split(",").map((e=>e.trim())):null,name:a[3]?a[3].trim():null};i.push(o),c[i.length-1]=o,o.name&&(c[o.name]=o)}var n})),{hasError:f,placeholders:i,chop:d,helpers:n,handshake:o,snippets:c}}(r);if(u){function y(){return u}return i?[!1,y]:y}{const g=structuredClone(f);function b(t="render",r={},a={},...i){const u=structuredClone(d);let y=!1;if("string"!=typeof t)return`Error: Wrong command "${t}". Available commands: render, debug, snippets, set, curry.`;if(!["render","debug","snippets","set","curry"].includes(t)&&!t.startsWith("snippets"))return`Error: Wrong command "${t}". Available commands: render, debug, snippets, set, curry.`;if(t.startsWith("snippets")&&t.includes(":")){y=!0;let e=t.split(":").slice(1)[0].trim().split(",").map((e=>e.trim()));f=e.map((e=>m[e]))}else if("snippets"===t)y=!0;else{if("set"===t){if("object"!=typeof r||!r)return"Error: 'set' command requires an object with placeholders, helpers, handshake.";const e={...p,...r.helpers||{}},t=h?{...h,...r.handshake||{}}:r.handshake||{},n=[...d];r.placeholders&&Object.entries(r.placeholders).forEach((([e,t])=>{if(isNaN(e)){let r=f.find((t=>t.name===e));n[r.index]=t}else{let r=f[e].index;n[r]=t}}));const a=s({template:n.join(""),helpers:e,handshake:t},!1,c);return"function"==typeof a?a:()=>a}if("curry"===t){return s({template:b("render",r,a,...i),helpers:p,handshake:h},!1,c)}f=structuredClone(g)}if("string"==typeof r)switch(r){case"raw":return u.join("");case"demo":if(!h)return"Error: No handshake data.";r=h;break;case"handshake":return h?structuredClone(h):"Error: No handshake data.";case"helpers":return Object.keys(p).join(", ");case"placeholders":return f.map((e=>u[e.index])).join(", ");case"count":return f.length;default:return`Error: Wrong instruction "${r}". Available instructions: raw, demo, handshake, helpers, placeholders, count.`}const v=[],k={};let E=o(r),j={...c,...a};r=e({data:r});let x=e({data:r});return"null"===E?u.join(""):("array"!==E&&(r=[r]),"null"===E?u.join(""):(r.forEach((t=>{f.forEach((a=>{const{index:s,data:i,action:c}=a,f=!c&&i,d=structuredClone(k),h={dependencies:j,memory:d};let m=t;if(i&&i.includes("/")?m.hasOwnProperty(i)?m=m[i]:i.split("/").forEach((e=>{m=m.hasOwnProperty(e)?m[e]:[]})):"@all"===i||null===i||"@root"===i?m=t:i&&(m=m[i]),f){switch(o(m)){case"function":return void(u[s]=m());case"primitive":return void(u[s]=m);case"array":return void("primitive"===o(m[0])&&(u[s]=m[0]));case"object":return void(m.text&&(u[s]=m.text))}}else{let{dataDeepLevel:a,nestedData:i}=function(t,r){const n=[];let a=0;if(t instanceof Function)return{dataDeepLevel:0,nestedData:[[t()]]};if(null==t)return{dataDeepLevel:0,nestedData:[null]};if("string"==typeof t)return{dataDeepLevel:0,nestedData:[[t]]};const o=structuredClone(t);return r.includes("#")?(e({data:o,objectCallback:function({key:e,value:t,breadcrumbs:r}){return e===r?(n[0]=[t],t):(a=r.split("/").length-1,n[a]||(n[a]=[]),n[a].push(t),t)}}),{dataDeepLevel:a,nestedData:n}):(n.push([o]),{dataDeepLevel:0,nestedData:n})}(m,c),f=n(function(e,t=10){let r={},n=[...e],a=0,o=0,l=0;n.forEach((e=>{"#"===e&&l++})),l<t&&console.error(`Error: Not enough level markers (#) for data with depth level ${t}. Found ${l} level markers in actions: ${e.join(", ")}`);do{r[o]=[],o++}while(o<=t);return n.every((e=>"#"===e?(a++,!(a>t)):e.startsWith("^")&&"^^"!==e?(r[a].push({type:"save",name:e.replace("^",""),level:a}),!0):"^^"===e?(r[a].push({type:"overwrite",name:"none",level:a}),!0):e.startsWith("+")?(r[a].push({type:"extendedRender",name:e.replace("+",""),level:a}),!0):e.startsWith("[]")?(r[a].push({type:"mix",name:e.replace("[]",""),level:a}),!0):e.startsWith(">")?(r[a].push({type:"data",name:e.replace(">",""),level:a}),!0):(""===e||r[a].push({type:"render",name:e,level:a}),!0))),r}(c,a),a);for(let n of f){let{type:a,name:s,level:c}=n;(i[c]||[]).forEach(((n,u)=>{let f=o(n);switch(a){case"route":switch(f){case"array":n.forEach(((e,t)=>{if(null==e)return;const r=o(e),a=p[s]({data:e,...h,full:e});null!=a&&("object"===r?n[t].text=l(e,a,p,x,j):n[t]=l(e,a,p,x,j))}));break;case"object":n.text=l(n,s,p,x,j)}break;case"save":k[s]=structuredClone(n);break;case"overwrite":t=structuredClone(n);break;case"data":switch(f){case"array":n.forEach(((e,t)=>n[t]=e instanceof Function?p[s]({data:e(),...h,full:e}):p[s]({data:e,...h,full:e})));break;case"object":i[c]=[p[s]({data:n,...h,full:r})];break;case"function":i[c]=[p[s]({data:n(),...h,full:r})];break;case"primitive":i[c]=p[s]({data:n,...h,full:r})}break;case"render":const d="function"==typeof p[s];switch(f){case"array":d?n.forEach(((e,t)=>{if(null==e)return;const r=o(e),a=p[s]({data:e,...h,full:x});null==a&&(n[t]=null),"object"===r?e.text=a:n[t]=a})):n.forEach(((e,t)=>{if(null==e)return;const r=o(e),a=l(e,s,p,x,j);null==a?n[t]=null:"object"===r?e.text=a:n[t]=a}));break;case"function":i[c]=p[s]({data:n(),...h,full:r});break;case"primitive":i[c]=d?p[s]({data:n,...h,full:r}):l(n,s,p,x,j);break;case"object":d?i[c][u].text=p[s]({data:n,...h,full:r}):n.text=l(n,s,p,x,j)}break;case"extendedRender":"function"==typeof p[s]&&i[0].forEach(((e,t)=>i[0][t]=p[s]({data:e,...h,full:e})));break;case"mix":if(""===s)switch(f){case"object":Object.keys(n).find((e=>e.includes("/")))?Object.entries(n).forEach((([e,t])=>i[c][e]=t.text)):i[c]=n.text;for(let y=c-1;y>=0;y--)i[y]=e({data:i[y],objectCallback:m});function m({value:e,breadcrumbs:t}){return i[c][t]?i[c][t]:e}break;case"array":n.forEach(((e,t)=>{if(t>0){let r=o(e);if(null==e)return;n[0]+="object"===r?`${e.text}`:`${e}`,n.toSpliced(t,1)}else{let t=o(e);if(n[0]="",null===t)return;n[0]="object"===t?`${e.text}`:`${e}`}})),n.length=1}else{let g=p[s]({data:n,...h,full:r}),b=o(g);switch(n.forEach(((e,t)=>n.splice(t,1))),n.length=0,b){case"primitive":n[0]=g;break;case"array":n.push(...g)}}}}))}if(i instanceof Array&&1===i.length&&i[0]instanceof Array&&(i=i[0]),null==i[0])return;let d=o(i[0]),y=i[0];switch(d){case"primitive":if(null==y)return;u[s]=y;break;case"object":if(null==y.text)return;u[s]=y.text;break;case"array":const e=o(y[0]);u[s]="object"===e?y.map((e=>e.text)).join(""):y.join("")}}})),y?v.push(f.map((e=>u[e.index])).join("<~>")):v.push(u.join(""))})),"array"===E?v:i?i.reduce(((e,t)=>"function"!=typeof t?e:t(e,j)),v.join("")):v.join("")))}return i?[!0,b]:b}}const i={default:{}};return{build:s,get:function(e){if(!(e instanceof Array))return function(){return'Error: Argument "location" is a string. Should be an array. E.g. ["templateName", "storageName"].'};const[t,r="default"]=e;return i[r]?i[r][t]?i[r][t]:function(){return`Error: Template "${t}" does not exist in storage "${r}".`}:function(){return`Error: Storage "${r}" does not exist.`}},add:function(e,t,...r){const[n,a="default"]=e;if(null==t)return void console.warn(`Warning: Template ${a}/${n} is not added to storage. The template is null.`);let o=t,l=!0;if(i[a]||(i[a]={}),"function"!=typeof t){let e=s(t,!0,...r);l=e[0],o=e[1]}l?i[a][n]=o:console.error(`Error: Template "${n}" looks broken and is not added to storage.`)},list:function(e=["default"]){return e.map((e=>i[e]?Object.keys(i[e]):[])).flat()},clear:function(){Object.keys(i).forEach((e=>{"default"!=e?delete i[e]:i.default={}}))},remove:function(e){const[t,r="default"]=e;return i[r]?i[r][t]?void delete i[r][t]:`Error: Template "${t}" does not exist in storage "${r}".`:`Error: Storage "${r}" does not exist.`}}}));
package/morph.png ADDED
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@peter.naydenov/morph",
3
3
  "description": "Template engine",
4
- "version": "3.1.5",
4
+ "version": "3.2.0",
5
5
  "license": "MIT",
6
6
  "author": "Peter Naydenov",
7
7
  "main": "./src/main.js",
@@ -33,8 +33,9 @@
33
33
  "@rollup/plugin-terser": "^0.4.4",
34
34
  "c8": "^10.1.3",
35
35
  "chai": "6.2.1",
36
+ "esm": "^3.2.25",
36
37
  "mocha": "11.7.5",
37
- "rollup": "^4.53.2",
38
+ "rollup": "^4.53.3",
38
39
  "typescript": "^5.9.3"
39
40
  },
40
41
  "repository": {
package/simple.js ADDED
@@ -0,0 +1,17 @@
1
+ import morph from './src/main.js'
2
+
3
+ const template = morph.build({
4
+ template: 'Hello {{name}}!',
5
+ helpers: {},
6
+ handshake: { name: 'World' }
7
+ });
8
+
9
+ const extensions = {
10
+ placeholders: { 0: '{{ greeting }}' }
11
+ };
12
+
13
+ const newTemplate = template('set', extensions);
14
+
15
+ const result = newTemplate('render', { greeting: 'Hi' });
16
+
17
+ console.log(result);
@@ -0,0 +1,35 @@
1
+ # Specification Quality Checklist: Extend Templates
2
+
3
+ **Purpose**: Validate specification completeness and quality before proceeding to planning
4
+ **Created**: 2025-11-30
5
+ **Feature**: specs/001-extend-templates/spec.md
6
+
7
+ ## Content Quality
8
+
9
+ - [x] No implementation details (languages, frameworks, APIs)
10
+ - [x] Focused on user value and business needs
11
+ - [x] Written for non-technical stakeholders
12
+ - [x] All mandatory sections completed
13
+
14
+ ## Requirement Completeness
15
+
16
+ - [x] No [NEEDS CLARIFICATION] markers remain
17
+ - [x] Requirements are testable and unambiguous
18
+ - [x] Success criteria are measurable
19
+ - [x] Success criteria are technology-agnostic (no implementation details)
20
+ - [x] All acceptance scenarios are defined
21
+ - [x] Edge cases are identified
22
+ - [x] Scope is clearly bounded
23
+ - [x] Dependencies and assumptions identified
24
+
25
+ ## Feature Readiness
26
+
27
+ - [x] All functional requirements have clear acceptance criteria
28
+ - [x] User scenarios cover primary flows
29
+ - [x] Feature meets measurable outcomes defined in Success Criteria
30
+ - [x] No implementation details leak into specification
31
+
32
+ ## Notes
33
+
34
+ All items pass. Specification is ready for planning phase.</content>
35
+ <parameter name="filePath">specs/001-extend-templates/checklists/requirements.md
@@ -0,0 +1,39 @@
1
+ {
2
+ "method": "set",
3
+ "description": "Extend a template with new placeholders, helpers, and handshake data",
4
+ "parameters": {
5
+ "placeholders": {
6
+ "type": "object",
7
+ "description": "Object with keys as placeholder identifiers (string/number) and values as new placeholder definitions (string)",
8
+ "example": {
9
+ "0": "{{ newField: action }}",
10
+ "header": "{{ title: formatTitle }}"
11
+ }
12
+ },
13
+ "helpers": {
14
+ "type": "object",
15
+ "description": "Object with helper names and implementations (functions or strings)",
16
+ "example": {
17
+ "formatTitle": "function(data) { return data.toUpperCase(); }",
18
+ "action": "function(data) { return '<div>' + data + '</div>'; }"
19
+ }
20
+ },
21
+ "handshake": {
22
+ "type": "object",
23
+ "description": "Object with demo data keys and values",
24
+ "example": {
25
+ "title": "Example Title",
26
+ "newField": "example data"
27
+ }
28
+ }
29
+ },
30
+ "returns": {
31
+ "type": "function",
32
+ "description": "New template function with extensions applied"
33
+ },
34
+ "errors": {
35
+ "invalidPlaceholder": "If placeholder definition is malformed",
36
+ "invalidHelper": "If helper is not function or string"
37
+ }
38
+ }</content>
39
+ <parameter name="filePath">specs/001-extend-templates/contracts/set-method.json
@@ -0,0 +1,76 @@
1
+ # Data Model: Extend Templates
2
+
3
+ **Feature**: Extend Templates
4
+ **Date**: 2025-11-30
5
+
6
+ ## Entities
7
+
8
+ ### Template
9
+ Core entity representing a Morph template.
10
+
11
+ **Fields**:
12
+ - `template`: string - The template string with placeholders
13
+ - `helpers`: object - Key-value pairs of helper functions or sub-templates
14
+ - `handshake`: object - Key-value pairs of demo data
15
+
16
+ **Relationships**:
17
+ - Contains multiple Placeholders (derived from template string)
18
+ - Contains multiple Helpers
19
+ - Contains Handshake data
20
+
21
+ **Validation Rules**:
22
+ - `template` must be non-empty string
23
+ - `helpers` must be object or undefined
24
+ - `handshake` must be object or undefined
25
+
26
+ ### Placeholder
27
+ Represents a dynamic part in the template.
28
+
29
+ **Fields**:
30
+ - `key`: string|number - Position (number) or name (string) identifier
31
+ - `definition`: string - The placeholder syntax (e.g., "{{ name: action }}")
32
+
33
+ **Relationships**:
34
+ - Belongs to Template
35
+
36
+ **Validation Rules**:
37
+ - `key` must be string or number
38
+ - `definition` must be valid template syntax
39
+
40
+ ### Helper
41
+ Represents a helper function or sub-template.
42
+
43
+ **Fields**:
44
+ - `name`: string - Helper identifier
45
+ - `value`: function|string - The helper implementation
46
+
47
+ **Relationships**:
48
+ - Belongs to Template
49
+
50
+ **Validation Rules**:
51
+ - `name` must be non-empty string
52
+ - `value` must be function or string
53
+
54
+ ### Handshake
55
+ Represents demo data for testing.
56
+
57
+ **Fields**:
58
+ - `key`: string - Data identifier
59
+ - `value`: any - The demo data
60
+
61
+ **Relationships**:
62
+ - Belongs to Template
63
+
64
+ **Validation Rules**:
65
+ - `key` must be non-empty string
66
+ - `value` can be any type
67
+
68
+ ## State Transitions
69
+
70
+ N/A - Static data structures, no lifecycle states.
71
+
72
+ ## Data Flow
73
+
74
+ 1. Original Template → 'set' command → Extended Template
75
+ 2. Extensions merged: placeholders replace, helpers merge (new overwrite old), handshake merge</content>
76
+ <parameter name="filePath">specs/001-extend-templates/data-model.md