web-signature 0.2.5 → 0.2.8
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/bundle/d/index.d.ts +16 -1
- package/bundle/index.js +42 -2
- package/bundle/index.min.js +1 -1
- package/dist/Component.js +2 -0
- package/dist/Plugin.js +3 -0
- package/dist/Signature.js +35 -1
- package/dist/d/Component.d.ts +2 -0
- package/dist/d/Plugin.d.ts +4 -0
- package/dist/d/Signature.d.ts +4 -0
- package/dist/d/index.d.ts +1 -0
- package/dist/d/types/Component.d.ts +5 -0
- package/dist/d/types/MetaPlugin.d.ts +5 -0
- package/dist/index.js +1 -0
- package/dist/types/MetaPlugin.js +1 -0
- package/package.json +1 -1
- package/src/Component.ts +4 -0
- package/src/Plugin.ts +6 -0
- package/src/Signature.ts +50 -3
- package/src/index.ts +2 -1
- package/src/types/Component.ts +7 -1
- package/src/types/MetaPlugin.ts +6 -0
package/bundle/d/index.d.ts
CHANGED
|
@@ -66,6 +66,10 @@ interface Component$1 {
|
|
|
66
66
|
* Optional content that is specified in the component tag.
|
|
67
67
|
*/
|
|
68
68
|
content?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Optional groups that the component belongs to.
|
|
71
|
+
*/
|
|
72
|
+
groups: string[];
|
|
69
73
|
/**
|
|
70
74
|
* Defining component properties.
|
|
71
75
|
*/
|
|
@@ -74,6 +78,7 @@ interface Component$1 {
|
|
|
74
78
|
* The properties of the component.
|
|
75
79
|
*/
|
|
76
80
|
data: Record<string, string | number | boolean | null>;
|
|
81
|
+
$: Record<string, unknown>;
|
|
77
82
|
/**
|
|
78
83
|
* Returns the component as a string (template).
|
|
79
84
|
* @returns {html | Promise<html>} The rendered component as a string (template).
|
|
@@ -112,10 +117,12 @@ interface Component$1 {
|
|
|
112
117
|
declare abstract class Component implements Component$1 {
|
|
113
118
|
abstract readonly name: string;
|
|
114
119
|
content?: string;
|
|
120
|
+
groups: string[];
|
|
115
121
|
options: Options;
|
|
116
122
|
ref?: Ref;
|
|
117
123
|
readonly props: Record<string, Prop$1>;
|
|
118
124
|
readonly data: Record<string, string | number | boolean | null>;
|
|
125
|
+
$: Record<string, unknown>;
|
|
119
126
|
abstract render(): html$1 | Promise<html$1>;
|
|
120
127
|
onInit?(): void;
|
|
121
128
|
onRender?(): void;
|
|
@@ -180,6 +187,11 @@ declare class Library {
|
|
|
180
187
|
}>;
|
|
181
188
|
}
|
|
182
189
|
|
|
190
|
+
declare abstract class Plugin {
|
|
191
|
+
readonly abstract modules: Record<string, () => Record<string, unknown>>;
|
|
192
|
+
abstract define(modules: Record<string, Record<string, unknown>>): Record<string, unknown>;
|
|
193
|
+
}
|
|
194
|
+
|
|
183
195
|
type ResolvedLib = {
|
|
184
196
|
components: string[];
|
|
185
197
|
dependencies: Record<string, ResolvedLib>;
|
|
@@ -189,6 +201,8 @@ declare class Signature {
|
|
|
189
201
|
private refs;
|
|
190
202
|
private libs;
|
|
191
203
|
private bank;
|
|
204
|
+
private $;
|
|
205
|
+
private $g;
|
|
192
206
|
constructor();
|
|
193
207
|
/**
|
|
194
208
|
* Adds a component to the signature.
|
|
@@ -214,6 +228,7 @@ declare class Signature {
|
|
|
214
228
|
* @return {Record<string, ResolvedLib>} A object of formatted libraries with their components and dependencies.
|
|
215
229
|
*/
|
|
216
230
|
libraries(): Record<string, ResolvedLib>;
|
|
231
|
+
use(name: string, plugin: Plugin): void;
|
|
217
232
|
/**
|
|
218
233
|
* Contacts the Component.onContact method through its reference.
|
|
219
234
|
* @param {string} name The name of the reference.
|
|
@@ -263,4 +278,4 @@ declare function unsafeHTML(value: any): {
|
|
|
263
278
|
|
|
264
279
|
declare function export_default(): Signature;
|
|
265
280
|
|
|
266
|
-
export { Component, Library, Prop, Signature, export_default as default, html, unsafeHTML };
|
|
281
|
+
export { Component, Library, Plugin, Prop, Signature, export_default as default, html, unsafeHTML };
|
package/bundle/index.js
CHANGED
|
@@ -26,6 +26,8 @@ class Signature {
|
|
|
26
26
|
refs = {};
|
|
27
27
|
libs = {};
|
|
28
28
|
bank = new Map();
|
|
29
|
+
$ = {};
|
|
30
|
+
$g = {};
|
|
29
31
|
constructor() {
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
@@ -99,6 +101,20 @@ class Signature {
|
|
|
99
101
|
};
|
|
100
102
|
return resolve(this.libs);
|
|
101
103
|
}
|
|
104
|
+
use(name, plugin) {
|
|
105
|
+
if (this.$[name]) {
|
|
106
|
+
throw new Error(`Plugin with name ${name} already exists.`);
|
|
107
|
+
}
|
|
108
|
+
let modules = {};
|
|
109
|
+
Object.keys(plugin.modules).forEach((key) => {
|
|
110
|
+
modules[key] = plugin.modules[key]();
|
|
111
|
+
});
|
|
112
|
+
this.$[name] = {
|
|
113
|
+
plugin: plugin.define(modules),
|
|
114
|
+
modules
|
|
115
|
+
};
|
|
116
|
+
this.$g[name] = this.$[name].plugin;
|
|
117
|
+
}
|
|
102
118
|
/**
|
|
103
119
|
* Contacts the Component.onContact method through its reference.
|
|
104
120
|
* @param {string} name The name of the reference.
|
|
@@ -153,6 +169,15 @@ class Signature {
|
|
|
153
169
|
throw new Error(`Component '${component.name}' must render a single root element.`);
|
|
154
170
|
}
|
|
155
171
|
const newElement = template.content.firstElementChild;
|
|
172
|
+
// Preserve old ref attribute
|
|
173
|
+
let oldRef = ref.element.getAttribute("ref");
|
|
174
|
+
if (oldRef) {
|
|
175
|
+
newElement.setAttribute("ref", oldRef);
|
|
176
|
+
}
|
|
177
|
+
// Preserve si-group attribute
|
|
178
|
+
if (component.groups.length > 0) {
|
|
179
|
+
newElement.setAttribute("si-group", component.groups.join(" "));
|
|
180
|
+
}
|
|
156
181
|
this.render(template.content);
|
|
157
182
|
component.onRender?.(); // lifecycle hook
|
|
158
183
|
ref.element.replaceWith(newElement);
|
|
@@ -244,7 +269,10 @@ class Signature {
|
|
|
244
269
|
if (typeof value === "object" && value.type === "unsafeHTML") {
|
|
245
270
|
let obj = document.createElement("div");
|
|
246
271
|
obj.innerHTML = value.value;
|
|
247
|
-
|
|
272
|
+
while (obj.firstChild) {
|
|
273
|
+
node.parentNode?.insertBefore(obj.firstChild, node);
|
|
274
|
+
}
|
|
275
|
+
node.remove();
|
|
248
276
|
}
|
|
249
277
|
else {
|
|
250
278
|
node.replaceWith(document.createTextNode(String(value)));
|
|
@@ -283,6 +311,8 @@ class Signature {
|
|
|
283
311
|
// Find all elements with the component name in the frame
|
|
284
312
|
for (const el of Array.from(frame.querySelectorAll(com)).concat(Array.from(frame.querySelectorAll(`[si-component="${com}"]`)))) {
|
|
285
313
|
const renderer = new component();
|
|
314
|
+
renderer.$ = this.$g; // injecting plugins
|
|
315
|
+
Object.freeze(renderer.$); // prevent plugins from being modified by components
|
|
286
316
|
renderer.onInit?.(); // lifecycle hook
|
|
287
317
|
if (el instanceof HTMLElement) {
|
|
288
318
|
// Fill the renderer's content
|
|
@@ -408,6 +438,10 @@ class Signature {
|
|
|
408
438
|
this.render(body.content);
|
|
409
439
|
renderer.onRender?.(); // lifecycle hook
|
|
410
440
|
const mountEl = body.firstElementChild;
|
|
441
|
+
// Processing group
|
|
442
|
+
if (renderer?.groups.length > 0) {
|
|
443
|
+
mountEl.setAttribute("si-group", renderer.groups.join(" "));
|
|
444
|
+
}
|
|
411
445
|
// Processing ref
|
|
412
446
|
if (el.hasAttribute("ref") || renderer.options.generateRefIfNotSpecified) {
|
|
413
447
|
let refName = el.getAttribute("ref");
|
|
@@ -440,12 +474,14 @@ class Signature {
|
|
|
440
474
|
|
|
441
475
|
class Component {
|
|
442
476
|
content;
|
|
477
|
+
groups = [];
|
|
443
478
|
options = {
|
|
444
479
|
generateRefIfNotSpecified: false,
|
|
445
480
|
};
|
|
446
481
|
ref;
|
|
447
482
|
props = {};
|
|
448
483
|
data = {};
|
|
484
|
+
$ = {};
|
|
449
485
|
onInit() {
|
|
450
486
|
}
|
|
451
487
|
;
|
|
@@ -584,8 +620,12 @@ function unsafeHTML(value) {
|
|
|
584
620
|
return { type: "unsafeHTML", value: value };
|
|
585
621
|
}
|
|
586
622
|
|
|
623
|
+
// todo: make description
|
|
624
|
+
class Plugin {
|
|
625
|
+
}
|
|
626
|
+
|
|
587
627
|
function index () {
|
|
588
628
|
return new Signature();
|
|
589
629
|
}
|
|
590
630
|
|
|
591
|
-
export { Component, Library, Prop, Signature, index as default, html, unsafeHTML };
|
|
631
|
+
export { Component, Library, Plugin, Prop, Signature, index as default, html, unsafeHTML };
|
package/bundle/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class e{element;instance;constructor(e,t){this.instance=e,this.element=t}}const t={"element-not-found":"Element not found for selector: #selector","prop-is-required":"Property '#prop' in component '#component' is required but not provided.","unsupported-type-for-property":"Unsupported type for property '#prop' in component '#component': #type","invalid-value-for-property":"Invalid value for property '#prop' in component '#component': #value (value: #attr)","multiple-root-elements":"Component '#component' must render a single root element. \n\t#elements","ref-collision":"Ref collision detected for ref '#ref' in component '#component'.",unknown:"An unknown error occurred.","unknown-from":"An unknown error occurred in component '#from'.","stack-overflow":"Stack Overflow detected: possible recursive component rendering.","render-async-failed":"Error during asynchronous rendering of the component #component."};let n=0;class r{components={};refs={};libs={};bank=new Map;constructor(){}add(e,t){const n="string"==typeof t?t:e.name;this.components[n]&&console.warn(new Error(`Component with name ${n} already exists.`)),this.components[n]=e}register(e,...t){this.libs[e.name]&&console.warn(new Error(`Library with name ${e.name} already exists.`));const n=e.list().filter(e=>!(e.name in t));this.libs[e.name]={name:e.name,version:e.version,author:e.author,components:n.map(e=>e.name),dependencies:e.libs};for(const t of n)this.add(t.component,`${e.name}-${t.name}`)}lib(e){return this.libs[e]}libraries(){const e=e=>{let t=e.name;return e.version&&(t+=`@${e.version}`),e.author&&(t+=`#${e.author}`),t},t=(n,r=new Set)=>{const o={};for(const[s,i]of Object.entries(n)){const n=e(i);r.has(n)||(r.add(n),o[n]={components:i.components,dependencies:t(i.dependencies,r)})}return o};return t(this.libs)}contactWith(e,...t){const n=this.refs[e];if(!n)throw new Error(`Ref with name ${e} does not exist.`);const r=n.instance;return r.onContact?.(...t)}updateRef(e){const t=this.refs[e];if(!t)throw new Error(`Ref with name ${e} does not exist.`);const n=t.instance;let r={strings:Object.assign([],{raw:[]}),values:[]};try{r=n.render()}catch(e){if(e instanceof Error)throw{id:"unknown-from",from:n.name,err:e}}const o=document.createElement("template");(e=>{r instanceof Promise?r.then(t=>{o.content.appendChild(this.templateToElement(t,n.name)),e()}).catch(e=>{throw{id:"unknown-from",from:n.name,err:e}}):"object"==typeof r&&(o.content.appendChild(this.templateToElement(r,n.name)),e())})(()=>{if(1!==o.content.children.length)throw new Error(`Component '${n.name}' must render a single root element.`);const e=o.content.firstElementChild;this.render(o.content),n.onRender?.(),t.element.replaceWith(e),t.element=e,n.onMount?.(e)})}contact(e,n){new Promise((t,r)=>{try{const t=document.querySelector(e);if(!t)return void r({id:"element-not-found",selector:e});const o=document.createElement("div");o.innerHTML=t.innerHTML,this.render(o),t.replaceChildren(...Array.from(o.childNodes)),n&&n()}catch(e){e instanceof Error?e instanceof RangeError&&e.message.includes("stack")?r({id:"stack-overflow",err:e}):r({id:"unknown",err:e}):r(e)}}).catch(e=>{let n=t[e.id];throw Object.keys(e).filter(e=>!(e in["id","err"])).forEach(t=>{n=n.replace(new RegExp(`#${t}`,"gm"),String(e[t]))}),window.SIGNATURE?.DEV_MODE&&console.log(e),e.id in["unknown","unknown-from","render-async-failed"]?console.error(`[${e.id}] ${n}`,e.err):console.error(`[${e.id}] ${n}`),"Page rendering was interrupted by Signature due to the above error."})}templateToString(e){let t="";for(let n=0;n<e.strings.length;n++)t+=e.strings[n],n<e.values.length&&(t+=`\x3c!--si-mark-${n}--\x3e`);return t}fillTemplate(e,t){let n;return this.bank.has(e.strings.join("@@"))?n=this.bank.get(e.strings.join("@@"))?.cloneNode(!0):(n=document.createElement("template"),n.innerHTML=t,this.bank.set(e.strings.join("@@"),n.cloneNode(!0))),(()=>{let t,r=document.createTreeWalker(n.content,NodeFilter.SHOW_COMMENT),o=[];for(;t=r.nextNode();)/si-mark-\d+/gm.test(t.nodeValue??"")&&o.push(t);for(const t of o){const n=e.values[Number((t.nodeValue??"").match(/si-mark-(\d+)/m)[1])];if("object"==typeof n&&"unsafeHTML"===n.type){let e=document.createElement("div");e.innerHTML=n.value
|
|
1
|
+
class e{element;instance;constructor(e,t){this.instance=e,this.element=t}}const t={"element-not-found":"Element not found for selector: #selector","prop-is-required":"Property '#prop' in component '#component' is required but not provided.","unsupported-type-for-property":"Unsupported type for property '#prop' in component '#component': #type","invalid-value-for-property":"Invalid value for property '#prop' in component '#component': #value (value: #attr)","multiple-root-elements":"Component '#component' must render a single root element. \n\t#elements","ref-collision":"Ref collision detected for ref '#ref' in component '#component'.",unknown:"An unknown error occurred.","unknown-from":"An unknown error occurred in component '#from'.","stack-overflow":"Stack Overflow detected: possible recursive component rendering.","render-async-failed":"Error during asynchronous rendering of the component #component."};let n=0;class r{components={};refs={};libs={};bank=new Map;$={};$g={};constructor(){}add(e,t){const n="string"==typeof t?t:e.name;this.components[n]&&console.warn(new Error(`Component with name ${n} already exists.`)),this.components[n]=e}register(e,...t){this.libs[e.name]&&console.warn(new Error(`Library with name ${e.name} already exists.`));const n=e.list().filter(e=>!(e.name in t));this.libs[e.name]={name:e.name,version:e.version,author:e.author,components:n.map(e=>e.name),dependencies:e.libs};for(const t of n)this.add(t.component,`${e.name}-${t.name}`)}lib(e){return this.libs[e]}libraries(){const e=e=>{let t=e.name;return e.version&&(t+=`@${e.version}`),e.author&&(t+=`#${e.author}`),t},t=(n,r=new Set)=>{const o={};for(const[s,i]of Object.entries(n)){const n=e(i);r.has(n)||(r.add(n),o[n]={components:i.components,dependencies:t(i.dependencies,r)})}return o};return t(this.libs)}use(e,t){if(this.$[e])throw new Error(`Plugin with name ${e} already exists.`);let n={};Object.keys(t.modules).forEach(e=>{n[e]=t.modules[e]()}),this.$[e]={plugin:t.define(n),modules:n},this.$g[e]=this.$[e].plugin}contactWith(e,...t){const n=this.refs[e];if(!n)throw new Error(`Ref with name ${e} does not exist.`);const r=n.instance;return r.onContact?.(...t)}updateRef(e){const t=this.refs[e];if(!t)throw new Error(`Ref with name ${e} does not exist.`);const n=t.instance;let r={strings:Object.assign([],{raw:[]}),values:[]};try{r=n.render()}catch(e){if(e instanceof Error)throw{id:"unknown-from",from:n.name,err:e}}const o=document.createElement("template");(e=>{r instanceof Promise?r.then(t=>{o.content.appendChild(this.templateToElement(t,n.name)),e()}).catch(e=>{throw{id:"unknown-from",from:n.name,err:e}}):"object"==typeof r&&(o.content.appendChild(this.templateToElement(r,n.name)),e())})(()=>{if(1!==o.content.children.length)throw new Error(`Component '${n.name}' must render a single root element.`);const e=o.content.firstElementChild;let r=t.element.getAttribute("ref");r&&e.setAttribute("ref",r),n.groups.length>0&&e.setAttribute("si-group",n.groups.join(" ")),this.render(o.content),n.onRender?.(),t.element.replaceWith(e),t.element=e,n.onMount?.(e)})}contact(e,n){new Promise((t,r)=>{try{const t=document.querySelector(e);if(!t)return void r({id:"element-not-found",selector:e});const o=document.createElement("div");o.innerHTML=t.innerHTML,this.render(o),t.replaceChildren(...Array.from(o.childNodes)),n&&n()}catch(e){e instanceof Error?e instanceof RangeError&&e.message.includes("stack")?r({id:"stack-overflow",err:e}):r({id:"unknown",err:e}):r(e)}}).catch(e=>{let n=t[e.id];throw Object.keys(e).filter(e=>!(e in["id","err"])).forEach(t=>{n=n.replace(new RegExp(`#${t}`,"gm"),String(e[t]))}),window.SIGNATURE?.DEV_MODE&&console.log(e),e.id in["unknown","unknown-from","render-async-failed"]?console.error(`[${e.id}] ${n}`,e.err):console.error(`[${e.id}] ${n}`),"Page rendering was interrupted by Signature due to the above error."})}templateToString(e){let t="";for(let n=0;n<e.strings.length;n++)t+=e.strings[n],n<e.values.length&&(t+=`\x3c!--si-mark-${n}--\x3e`);return t}fillTemplate(e,t){let n;return this.bank.has(e.strings.join("@@"))?n=this.bank.get(e.strings.join("@@"))?.cloneNode(!0):(n=document.createElement("template"),n.innerHTML=t,this.bank.set(e.strings.join("@@"),n.cloneNode(!0))),(()=>{let t,r=document.createTreeWalker(n.content,NodeFilter.SHOW_COMMENT),o=[];for(;t=r.nextNode();)/si-mark-\d+/gm.test(t.nodeValue??"")&&o.push(t);for(const t of o){const n=e.values[Number((t.nodeValue??"").match(/si-mark-(\d+)/m)[1])];if("object"==typeof n&&"unsafeHTML"===n.type){let e=document.createElement("div");for(e.innerHTML=n.value;e.firstChild;)t.parentNode?.insertBefore(e.firstChild,t);t.remove()}else t.replaceWith(document.createTextNode(String(n)))}})(),(()=>{let t,r=document.createTreeWalker(n.content,NodeFilter.SHOW_ELEMENT);for(;t=r.nextNode();)for(const n of Array.from(t.attributes))if(/<!--si-mark-\d+-->/gm.test(n.value)){const r=n.value.match(/si-mark-(\d+)/m);if(r){const o=e.values[Number(r[1])];t.setAttribute(n.name,String(o))}}})(),n}templateToElement(e,t){const n=this.templateToString(e),r=this.fillTemplate(e,n);if(1!==r.content.children.length)throw{id:"multiple-root-elements",elements:r.innerHTML,component:t};return r.content.firstElementChild}render(t){for(const r of Object.keys(this.components)){const o=this.components[r];for(const s of Array.from(t.querySelectorAll(r)).concat(Array.from(t.querySelectorAll(`[si-component="${r}"]`)))){const t=new o;if(t.$=this.$g,Object.freeze(t.$),t.onInit?.(),s instanceof HTMLElement){t.content=s.innerHTML.trim();for(const e of Object.keys(t.props)){const n=s.getAttribute(e);if(null===n){if(t.props[e].required)throw{id:"prop-is-required",component:r,prop:e};t.data[e]=null}else if(""===n){if(t.props[e].required)throw{id:"prop-is-required",component:r,prop:e};t.props[e].isValid(n)&&(t.data[e]=null)}else{let o;switch(t.props[e].type){case"boolean":o=Boolean(n);break;case"number":o=Number(n);break;case"string":o=String(n);break;case"array":try{o=JSON.parse(n)}catch(t){throw{id:"invalid-value-for-property",component:r,prop:e,value:n,attr:n}}break;default:if(t.props[e].required)throw{id:"unsupported-type-for-property",component:r,prop:e,type:t.props[e].type}}if(void 0!==o){if(!t.props[e].isValid(o))throw{id:"invalid-value-for-property",component:r,prop:e,value:o,attr:n};if(t.props[e].validate&&!t.props[e].validate(o))throw{id:"invalid-value-for-property",component:r,prop:e,value:o,attr:n};t.data[e]=o,t.onPropParsed?.(t.props[e],o)}}}t.onPropsParsed?.()}const i=document.createElement("template");let a={strings:Object.assign([],{raw:[]}),values:[]};try{a=t.render()}catch(e){if(e instanceof Error)throw{id:"unknown-from",from:t.name,err:e}}(e=>{if(a instanceof Promise)try{a.then(t=>{i.appendChild(this.templateToElement(t,r)),e()}).catch(e=>{throw{id:"unknown-from",from:t.name,err:e}})}catch(e){throw{id:"render-async-failed",component:r,err:e}}else"object"==typeof a&&(i.appendChild(this.templateToElement(a,r)),e())})(()=>{this.render(i.content),t.onRender?.();const o=i.firstElementChild;if(t?.groups.length>0&&o.setAttribute("si-group",t.groups.join(" ")),s.hasAttribute("ref")||t.options.generateRefIfNotSpecified){let i=s.getAttribute("ref");if(null===i&&(i=""),n++,""===i&&(i=`r${n}${Math.random().toString(36).substring(2,15)}${n}`),this.refs[i])throw{id:"ref-collision",ref:i,component:r};this.refs[i]=new e(t,o),o.setAttribute("ref",i),t.ref={id:i,contact:(...e)=>this.contactWith(i,...e),update:()=>this.updateRef(i)}}s.replaceWith(i.firstElementChild),t.onMount?.(o)})}}}}class o{content;groups=[];options={generateRefIfNotSpecified:!1};ref;props={};data={};$={};onInit(){}onRender(){}onMount(e){}onContact(...e){}onPropsParsed(){}onPropParsed(e,t){}}class s{type;required=!0;validate;constructor(e,t=!0,n){this.type=e,this.required=t,this.validate=n||(()=>!0)}isValid(e){switch(this.type){case"boolean":return"boolean"==typeof e;case"number":return"number"==typeof e&&!isNaN(e);case"string":return"string"==typeof e;case"array":return Array.isArray(e);case"null":return null===e;default:return!1}}}class i{name;version;author;libs={};components={};constructor(e,t,n){this.name=e,this.author=t,this.version=n}add(e,t){const n="string"==typeof t?t:e.name;this.components[n]&&console.warn(new Error(`Component with name ${n} already exists.`)),this.components[n]=e}register(e,...t){this.libs[e.name]&&console.warn(new Error(`Library with name ${e.name} already exists in ${this.name}.`));const n=e.list().filter(e=>!(e.name in t));this.libs[e.name]={name:e.name,version:e.version,author:e.author,components:n.map(e=>e.name),dependencies:e.libs};for(const t of n)this.add(t.component,`${e.name}-${t.name}`)}get(e){return this.components[e]}lib(e){return this.libs[e]}list(){return Object.entries(this.components).map(([e,t])=>({component:t,name:e}))}}function a(e,...t){return{strings:e,values:t}}function c(e){return{type:"unsafeHTML",value:e}}class l{}function p(){return new r}export{o as Component,i as Library,l as Plugin,s as Prop,r as Signature,p as default,a as html,c as unsafeHTML};
|
package/dist/Component.js
CHANGED
package/dist/Plugin.js
ADDED
package/dist/Signature.js
CHANGED
|
@@ -6,6 +6,8 @@ export default class Signature {
|
|
|
6
6
|
refs = {};
|
|
7
7
|
libs = {};
|
|
8
8
|
bank = new Map();
|
|
9
|
+
$ = {};
|
|
10
|
+
$g = {};
|
|
9
11
|
constructor() {
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
@@ -79,6 +81,20 @@ export default class Signature {
|
|
|
79
81
|
};
|
|
80
82
|
return resolve(this.libs);
|
|
81
83
|
}
|
|
84
|
+
use(name, plugin) {
|
|
85
|
+
if (this.$[name]) {
|
|
86
|
+
throw new Error(`Plugin with name ${name} already exists.`);
|
|
87
|
+
}
|
|
88
|
+
let modules = {};
|
|
89
|
+
Object.keys(plugin.modules).forEach((key) => {
|
|
90
|
+
modules[key] = plugin.modules[key]();
|
|
91
|
+
});
|
|
92
|
+
this.$[name] = {
|
|
93
|
+
plugin: plugin.define(modules),
|
|
94
|
+
modules
|
|
95
|
+
};
|
|
96
|
+
this.$g[name] = this.$[name].plugin;
|
|
97
|
+
}
|
|
82
98
|
/**
|
|
83
99
|
* Contacts the Component.onContact method through its reference.
|
|
84
100
|
* @param {string} name The name of the reference.
|
|
@@ -133,6 +149,15 @@ export default class Signature {
|
|
|
133
149
|
throw new Error(`Component '${component.name}' must render a single root element.`);
|
|
134
150
|
}
|
|
135
151
|
const newElement = template.content.firstElementChild;
|
|
152
|
+
// Preserve old ref attribute
|
|
153
|
+
let oldRef = ref.element.getAttribute("ref");
|
|
154
|
+
if (oldRef) {
|
|
155
|
+
newElement.setAttribute("ref", oldRef);
|
|
156
|
+
}
|
|
157
|
+
// Preserve si-group attribute
|
|
158
|
+
if (component.groups.length > 0) {
|
|
159
|
+
newElement.setAttribute("si-group", component.groups.join(" "));
|
|
160
|
+
}
|
|
136
161
|
this.render(template.content);
|
|
137
162
|
component.onRender?.(); // lifecycle hook
|
|
138
163
|
ref.element.replaceWith(newElement);
|
|
@@ -224,7 +249,10 @@ export default class Signature {
|
|
|
224
249
|
if (typeof value === "object" && value.type === "unsafeHTML") {
|
|
225
250
|
let obj = document.createElement("div");
|
|
226
251
|
obj.innerHTML = value.value;
|
|
227
|
-
|
|
252
|
+
while (obj.firstChild) {
|
|
253
|
+
node.parentNode?.insertBefore(obj.firstChild, node);
|
|
254
|
+
}
|
|
255
|
+
node.remove();
|
|
228
256
|
}
|
|
229
257
|
else {
|
|
230
258
|
node.replaceWith(document.createTextNode(String(value)));
|
|
@@ -263,6 +291,8 @@ export default class Signature {
|
|
|
263
291
|
// Find all elements with the component name in the frame
|
|
264
292
|
for (const el of Array.from(frame.querySelectorAll(com)).concat(Array.from(frame.querySelectorAll(`[si-component="${com}"]`)))) {
|
|
265
293
|
const renderer = new component();
|
|
294
|
+
renderer.$ = this.$g; // injecting plugins
|
|
295
|
+
Object.freeze(renderer.$); // prevent plugins from being modified by components
|
|
266
296
|
renderer.onInit?.(); // lifecycle hook
|
|
267
297
|
if (el instanceof HTMLElement) {
|
|
268
298
|
// Fill the renderer's content
|
|
@@ -388,6 +418,10 @@ export default class Signature {
|
|
|
388
418
|
this.render(body.content);
|
|
389
419
|
renderer.onRender?.(); // lifecycle hook
|
|
390
420
|
const mountEl = body.firstElementChild;
|
|
421
|
+
// Processing group
|
|
422
|
+
if (renderer?.groups.length > 0) {
|
|
423
|
+
mountEl.setAttribute("si-group", renderer.groups.join(" "));
|
|
424
|
+
}
|
|
391
425
|
// Processing ref
|
|
392
426
|
if (el.hasAttribute("ref") || renderer.options.generateRefIfNotSpecified) {
|
|
393
427
|
let refName = el.getAttribute("ref");
|
package/dist/d/Component.d.ts
CHANGED
|
@@ -5,10 +5,12 @@ import type { Ref } from "./types/Component.js";
|
|
|
5
5
|
export default abstract class Component implements component {
|
|
6
6
|
abstract readonly name: string;
|
|
7
7
|
content?: string;
|
|
8
|
+
groups: string[];
|
|
8
9
|
options: Options;
|
|
9
10
|
ref?: Ref;
|
|
10
11
|
readonly props: Record<string, Prop>;
|
|
11
12
|
readonly data: Record<string, string | number | boolean | null>;
|
|
13
|
+
$: Record<string, unknown>;
|
|
12
14
|
abstract render(): html | Promise<html>;
|
|
13
15
|
onInit?(): void;
|
|
14
16
|
onRender?(): void;
|
package/dist/d/Signature.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ComponentConstructor } from "./Component.js";
|
|
2
2
|
import Library, { LibMeta } from "./Library.js";
|
|
3
|
+
import Plugin from "./Plugin.js";
|
|
3
4
|
type ResolvedLib = {
|
|
4
5
|
components: string[];
|
|
5
6
|
dependencies: Record<string, ResolvedLib>;
|
|
@@ -9,6 +10,8 @@ export default class Signature {
|
|
|
9
10
|
private refs;
|
|
10
11
|
private libs;
|
|
11
12
|
private bank;
|
|
13
|
+
private $;
|
|
14
|
+
private $g;
|
|
12
15
|
constructor();
|
|
13
16
|
/**
|
|
14
17
|
* Adds a component to the signature.
|
|
@@ -34,6 +37,7 @@ export default class Signature {
|
|
|
34
37
|
* @return {Record<string, ResolvedLib>} A object of formatted libraries with their components and dependencies.
|
|
35
38
|
*/
|
|
36
39
|
libraries(): Record<string, ResolvedLib>;
|
|
40
|
+
use(name: string, plugin: Plugin): void;
|
|
37
41
|
/**
|
|
38
42
|
* Contacts the Component.onContact method through its reference.
|
|
39
43
|
* @param {string} name The name of the reference.
|
package/dist/d/index.d.ts
CHANGED
|
@@ -37,6 +37,10 @@ interface Component {
|
|
|
37
37
|
* Optional content that is specified in the component tag.
|
|
38
38
|
*/
|
|
39
39
|
content?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Optional groups that the component belongs to.
|
|
42
|
+
*/
|
|
43
|
+
groups: string[];
|
|
40
44
|
/**
|
|
41
45
|
* Defining component properties.
|
|
42
46
|
*/
|
|
@@ -45,6 +49,7 @@ interface Component {
|
|
|
45
49
|
* The properties of the component.
|
|
46
50
|
*/
|
|
47
51
|
data: Record<string, string | number | boolean | null>;
|
|
52
|
+
$: Record<string, unknown>;
|
|
48
53
|
/**
|
|
49
54
|
* Returns the component as a string (template).
|
|
50
55
|
* @returns {html | Promise<html>} The rendered component as a string (template).
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
package/src/Component.ts
CHANGED
|
@@ -8,6 +8,8 @@ export default abstract class Component implements component {
|
|
|
8
8
|
abstract readonly name: string;
|
|
9
9
|
content?: string;
|
|
10
10
|
|
|
11
|
+
groups: string[] = [];
|
|
12
|
+
|
|
11
13
|
options: Options = {
|
|
12
14
|
generateRefIfNotSpecified: false,
|
|
13
15
|
}
|
|
@@ -17,6 +19,8 @@ export default abstract class Component implements component {
|
|
|
17
19
|
readonly props: Record<string, Prop> = {};
|
|
18
20
|
readonly data: Record<string, string | number | boolean | null> = {};
|
|
19
21
|
|
|
22
|
+
public $: Record<string, unknown> = {};
|
|
23
|
+
|
|
20
24
|
abstract render(): html | Promise<html>;
|
|
21
25
|
|
|
22
26
|
onInit?(): void {
|
package/src/Plugin.ts
ADDED
package/src/Signature.ts
CHANGED
|
@@ -3,7 +3,9 @@ import Ref from "./Ref.js";
|
|
|
3
3
|
import ErrorUnion from "./types/Errors.js";
|
|
4
4
|
import Errors from "./Errors.js";
|
|
5
5
|
import Library, {LibMeta} from "./Library.js";
|
|
6
|
-
import {html} from "./types/Component";
|
|
6
|
+
import {html} from "./types/Component.js";
|
|
7
|
+
import MetaPlugin from "./types/MetaPlugin.js";
|
|
8
|
+
import Plugin from "./Plugin.js";
|
|
7
9
|
|
|
8
10
|
let _counter = 0;
|
|
9
11
|
|
|
@@ -16,7 +18,10 @@ export default class Signature {
|
|
|
16
18
|
private components: Record<string, ComponentConstructor> = {};
|
|
17
19
|
private refs: Record<string, Ref> = {};
|
|
18
20
|
private libs: Record<string, LibMeta> = {};
|
|
19
|
-
private bank: Map<string, HTMLTemplateElement> = new Map<string, HTMLTemplateElement>()
|
|
21
|
+
private bank: Map<string, HTMLTemplateElement> = new Map<string, HTMLTemplateElement>();
|
|
22
|
+
|
|
23
|
+
private $: Record<string, MetaPlugin> = {};
|
|
24
|
+
private $g: Record<string, unknown> = {};
|
|
20
25
|
|
|
21
26
|
constructor() {
|
|
22
27
|
}
|
|
@@ -109,6 +114,25 @@ export default class Signature {
|
|
|
109
114
|
return resolve(this.libs);
|
|
110
115
|
}
|
|
111
116
|
|
|
117
|
+
public use(name: string, plugin: Plugin): void {
|
|
118
|
+
if (this.$[name]) {
|
|
119
|
+
throw new Error(`Plugin with name ${name} already exists.`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let modules: Record<string, Record<string, unknown>> = {};
|
|
123
|
+
|
|
124
|
+
Object.keys(plugin.modules).forEach((key) => {
|
|
125
|
+
modules[key] = plugin.modules[key]();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
this.$[name] = {
|
|
129
|
+
plugin: plugin.define(modules),
|
|
130
|
+
modules
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.$g[name] = this.$[name].plugin;
|
|
134
|
+
}
|
|
135
|
+
|
|
112
136
|
/**
|
|
113
137
|
* Contacts the Component.onContact method through its reference.
|
|
114
138
|
* @param {string} name The name of the reference.
|
|
@@ -172,6 +196,17 @@ export default class Signature {
|
|
|
172
196
|
|
|
173
197
|
const newElement = template.content.firstElementChild as Element;
|
|
174
198
|
|
|
199
|
+
// Preserve old ref attribute
|
|
200
|
+
let oldRef = ref.element.getAttribute("ref");
|
|
201
|
+
if (oldRef) {
|
|
202
|
+
newElement.setAttribute("ref", oldRef);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Preserve si-group attribute
|
|
206
|
+
if (component.groups.length > 0) {
|
|
207
|
+
newElement.setAttribute("si-group", component.groups.join(" "));
|
|
208
|
+
}
|
|
209
|
+
|
|
175
210
|
this.render(template.content);
|
|
176
211
|
|
|
177
212
|
component.onRender?.(); // lifecycle hook
|
|
@@ -289,7 +324,10 @@ export default class Signature {
|
|
|
289
324
|
let obj = document.createElement("div");
|
|
290
325
|
obj.innerHTML = value.value;
|
|
291
326
|
|
|
292
|
-
|
|
327
|
+
while (obj.firstChild) {
|
|
328
|
+
node.parentNode?.insertBefore(obj.firstChild, node);
|
|
329
|
+
}
|
|
330
|
+
node.remove();
|
|
293
331
|
} else {
|
|
294
332
|
node.replaceWith(document.createTextNode(String(value)));
|
|
295
333
|
}
|
|
@@ -338,6 +376,10 @@ export default class Signature {
|
|
|
338
376
|
// Find all elements with the component name in the frame
|
|
339
377
|
for (const el of Array.from(frame.querySelectorAll(com)).concat(Array.from(frame.querySelectorAll(`[si-component="${com}"]`)))) {
|
|
340
378
|
const renderer: Component = new component();
|
|
379
|
+
|
|
380
|
+
renderer.$ = this.$g; // injecting plugins
|
|
381
|
+
Object.freeze(renderer.$); // prevent plugins from being modified by components
|
|
382
|
+
|
|
341
383
|
renderer.onInit?.(); // lifecycle hook
|
|
342
384
|
|
|
343
385
|
if (el instanceof HTMLElement) {
|
|
@@ -473,6 +515,11 @@ export default class Signature {
|
|
|
473
515
|
|
|
474
516
|
const mountEl: Element = body.firstElementChild as Element;
|
|
475
517
|
|
|
518
|
+
// Processing group
|
|
519
|
+
if (renderer?.groups.length > 0) {
|
|
520
|
+
mountEl.setAttribute("si-group", renderer.groups.join(" "));
|
|
521
|
+
}
|
|
522
|
+
|
|
476
523
|
// Processing ref
|
|
477
524
|
if (el.hasAttribute("ref") || renderer.options.generateRefIfNotSpecified) {
|
|
478
525
|
let refName = el.getAttribute("ref");
|
package/src/index.ts
CHANGED
|
@@ -8,4 +8,5 @@ export {default as Signature} from './Signature.js';
|
|
|
8
8
|
export {default as Component} from './Component.js';
|
|
9
9
|
export {default as Prop} from './Prop.js';
|
|
10
10
|
export {default as Library} from './Library.js';
|
|
11
|
-
export {default as html, unsafeHTML} from './html.js';
|
|
11
|
+
export {default as html, unsafeHTML} from './html.js';
|
|
12
|
+
export {default as Plugin} from './Plugin.js';
|
package/src/types/Component.ts
CHANGED
|
@@ -47,6 +47,11 @@ interface Component {
|
|
|
47
47
|
*/
|
|
48
48
|
content?: string;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Optional groups that the component belongs to.
|
|
52
|
+
*/
|
|
53
|
+
groups: string[];
|
|
54
|
+
|
|
50
55
|
/**
|
|
51
56
|
* Defining component properties.
|
|
52
57
|
*/
|
|
@@ -56,7 +61,8 @@ interface Component {
|
|
|
56
61
|
* The properties of the component.
|
|
57
62
|
*/
|
|
58
63
|
data: Record<string, string | number | boolean | null>;
|
|
59
|
-
|
|
64
|
+
// todo: make description
|
|
65
|
+
$: Record<string, unknown>;
|
|
60
66
|
|
|
61
67
|
/**
|
|
62
68
|
* Returns the component as a string (template).
|