vaderjs 1.2.7 → 1.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/vader.js CHANGED
@@ -39,12 +39,16 @@ function markdown(content) {
39
39
  }
40
40
  if (bold) {
41
41
  bold.forEach((b) => {
42
- line = line.replace(b, `<strong>${b.replace(/\*\*/g, "")}</strong>`);
42
+ line = line.replace(b, `<strong
43
+ className="$vader_markdown_bold"
44
+ >${b.replace(/\*\*/g, "")}</strong>`);
43
45
  });
44
46
  }
45
47
  if (italic) {
46
48
  italic.forEach((i) => {
47
- line = line.replace(i, `<em>${i.replace(/\*/g, "")}</em>`);
49
+ line = line.replace(i, `<em
50
+ className="$vader_markdown_italic"
51
+ >${i.replace(/\*/g, "")}</em>`);
48
52
  });
49
53
  }
50
54
 
@@ -55,22 +59,30 @@ function markdown(content) {
55
59
  let text = l.match(/\[(.*?)\]/g)[0].replace(/\[|\]/g, "");
56
60
  // @ts-ignore
57
61
  let href = l.match(/\((.*?)\)/g)[0].replace(/\(|\)/g, "");
58
- line = line.replace(l, `<a href="${href}">${text}</a>`);
62
+ line = line.replace(l, `<a
63
+ className="$vader_markdown_link"
64
+ href="${href}">${text}</a>`);
59
65
  });
60
66
  }
61
67
  if (ul) {
62
- line = line.replace(ul[0], `<li style="list-style-type: disc;">`);
68
+ line = line.replace(ul[0], `<li
69
+ className="$vader_markdown_ul"
70
+ style="list-style-type: disc;">`);
63
71
  line += `</li>`;
64
72
  }
65
73
  if (ol) {
66
- line = line.replace(ol[0], `<li style="list-style-type: decimal;">`);
74
+ line = line.replace(ol[0], `<li
75
+ className="$vader_markdown_ol"
76
+ style="list-style-type: decimal;">`);
67
77
  line += `</li>`;
68
78
  }
69
79
  if (hr) {
70
80
  line = line.replace(hr[0], `<hr/>`);
71
81
  }
72
82
  if (blockquote) {
73
- line = line.replace(blockquote[0], `<blockquote>`);
83
+ line = line.replace(blockquote[0], `<blockquote
84
+ className="$vader_markdown_blockquote"
85
+ >`);
74
86
  line += `</blockquote>`;
75
87
  }
76
88
  if (image) {
@@ -80,7 +92,9 @@ function markdown(content) {
80
92
  // @ts-ignore
81
93
  let src = i.match(/\((.*?)\)/g)[0].replace(/\(|\)/g, "");
82
94
  i.replace(i, `<img src="${src}" alt="${alt}"/>`);
83
- line = line.replace(i, `<img src="${src}" alt="${alt}"/>`).replace('!','')
95
+ line = line.replace(i, `<img
96
+ className="$vader_markdown_image"
97
+ src="${src}" alt="${alt}"/>`).replace('!','')
84
98
  });
85
99
  }
86
100
  if (li) {
@@ -88,7 +102,7 @@ function markdown(content) {
88
102
  line += `</li>`;
89
103
  }
90
104
  if (codeBlock) {
91
- line = line.replace(codeBlock[0], `<pre><code>`);
105
+ line = line.replace(codeBlock[0], `<pre className="$vader_markdown_code_block" ><code>`);
92
106
  }
93
107
  if (codeBlockEnd) {
94
108
  line = line.replace(codeBlockEnd[0], `</code></pre>`);
@@ -97,8 +111,8 @@ function markdown(content) {
97
111
  if (code) {
98
112
  code.forEach((c) => {
99
113
  line = line.replace(c, `<code
114
+ className="$vader_markdown_code"
100
115
  style="background-color: #f5f5f5; padding: 5px; border-radius: 5px;
101
-
102
116
  "
103
117
  >${c.replace(/\`/g, "")}</code>`);
104
118
  });
@@ -194,6 +208,19 @@ export class Component {
194
208
  });
195
209
  this.snapshots = [];
196
210
 
211
+ /**
212
+ * @property {Boolean} cfr
213
+ * @description Allows you to compile html code on the fly - client fly rendering
214
+ *
215
+ */
216
+ this.cfr = false
217
+ /**
218
+ * @property {Boolean} worker
219
+ * @description Allows you to use a web worker to compile html code on the fly - client fly rendering
220
+
221
+ */
222
+ // @ts-ignore
223
+ this.worker = new Worker(new URL('./worker.js', import.meta.url));
197
224
  }
198
225
 
199
226
  /**
@@ -207,6 +234,7 @@ export class Component {
207
234
  }
208
235
  init() {
209
236
  this.registerComponent();
237
+
210
238
  }
211
239
 
212
240
  registerComponent() {
@@ -775,92 +803,11 @@ export class Component {
775
803
  return /^[a-zA-Z0-9-_]+$/.test(className);
776
804
  }
777
805
 
778
- /**
779
- * The `html` method generates and processes HTML content for a component, performing various validations and tasks.
780
- *
781
- * @param {String} strings - The HTML content to be processed.
782
- * @param {...any} args - Dynamic values to be inserted into the template.
783
- * @returns {string} - The processed HTML content as a string.
784
- *
785
- * @throws {SyntaxError} - Throws a `SyntaxError` if image-related attributes are missing or invalid.
786
- * @throws {Error} - Throws an `Error` if there are issues with class names or relative paths.
787
- *
788
- * @example
789
- * // Example usage within a component:
790
- * const myComponent = new Component();
791
- * const htmlContent = myComponent.html`
792
- * <div>
793
- * <img src="/images/example.jpg" alt="Example Image" />
794
- * </div>
795
- * `;
796
- * document.body.innerHTML = htmlContent;
797
- *
798
- * @remarks
799
- * The `html` method is a core function used in component rendering. It allows you to define and generate HTML content within your component while enforcing best practices and accessibility standards. The method performs several essential tasks:
800
- *
801
- * 1. **Image Validation**: It checks images for the presence of 'alt' attributes and their validity.
802
- * - Throws a `SyntaxError` if an image is missing the 'alt' attribute.
803
- * - Throws a `SyntaxError` if the 'alt' attribute is empty.
804
- * - Checks for an 'aria-hidden' attribute for image elements.
805
- *
806
- * 2. **Class Attribute Handling**: It enforces class attribute usage and allows optional configuration via comments.
807
- * - Throws an `Error` if 'class' attributes are used without permission.
808
- * - Supports 'className' attributes for class definitions.
809
- * - Allows or disallows class-related comments based on your configuration.
810
- *
811
- * 3. **Relative Path Handling**: It processes relative paths in 'href' and 'src' attributes, ensuring proper routing.
812
- * - Converts relative 'href' attributes to anchor links with appropriate routing.
813
- * - Converts relative 'src' attributes to absolute paths with 'public' directories.
814
- *
815
- * 4. **Custom Component Attributes**: It supports adding a 'data-component' attribute to the root element.
816
- * - Ensures that the 'data-component' attribute is present for component identification.
817
- *
818
- * 5. **Lifecycle Method Invocation**: It invokes the `componentDidMount` method if called from a 'render' context.
819
- * - Executes `componentDidMount` to handle component initialization once the DOM is ready.
820
- *
821
- * @see {@link Component}
822
- * @see {@link Component#componentDidMount}
823
- */
824
-
825
- html(strings, ...args) {
826
- // @ts-ignore
827
- if (
828
- // @ts-ignore
829
- new Error().stack &&
830
- // @ts-ignore
831
- new Error().stack.split("\n").length > 0 &&
832
- // @ts-ignore
833
- new Error().stack.split("\n")[2] &&
834
- // @ts-ignore
835
- new Error().stack.split("\n")[2].includes("render") &&
836
- !this.componentMounted
837
- ) {
838
- this.componentMounted = true;
839
- this.componentDidMount();
840
- console.log("component mounted");
841
- }
842
-
843
- let result = "";
844
- for (let i = 0; i < strings.length; i++) {
845
- result += strings[i];
846
- if (i < args.length) {
847
- result += args[i];
848
- }
849
- }
850
-
851
- result = result.replace(/\\n/g, '\n').trim()
852
- // replace `
853
- result = result.replace(/`/g, '\`').trim()
854
-
855
- result = new Function("useRef", `return \`${result}\``)(useRef)
856
806
 
857
- if (!result.trim().startsWith("<body>")) {
858
- console.warn(
859
- "You should wrap your html in a body tag, vader may not grab all html!"
860
- );
861
- }
862
-
807
+ parseHTML(result) {
808
+
863
809
  const dom = new DOMParser().parseFromString(result, "text/html");
810
+ console.log(dom)
864
811
  const elements = dom.documentElement.querySelectorAll("*");
865
812
 
866
813
  elements.forEach((element) => {
@@ -899,6 +846,7 @@ export class Component {
899
846
  element.setAttribute("hidden", "true");
900
847
  // if window.lcoation.pathname includes a html file remove it and only use the path
901
848
  let url = window.location.origin + window.location.pathname.replace(/\/[^\/]*$/, '') + '/public/' + element.getAttribute("src");
849
+ // @ts-ignore
902
850
  let image = new Image();
903
851
  image.src = url;
904
852
  image.onerror = () => {
@@ -941,27 +889,6 @@ export class Component {
941
889
  );
942
890
  }
943
891
  } else if (element.hasAttribute("className")) {
944
- const isLocalhost = window.location.href.includes("localhost");
945
- const is127001 = window.location.href.includes("127.0.0.1");
946
- const ignoreClassComments = document.documentElement.outerHTML
947
- .trim()
948
- .includes("<!-- #vader-class-ignore -->");
949
- const allowClassComments = document.documentElement.outerHTML
950
- .trim()
951
- .includes("<!-- #vader-allow_class -->");
952
-
953
- if (
954
- // @ts-ignore
955
- (!this.validateClassName(element.getAttribute("className")) &&
956
- isLocalhost) ||
957
- (is127001 && !ignoreClassComments && !allowClassComments)
958
- ) {
959
- throw new Error(
960
- `Invalid className ${element.getAttribute(
961
- "className"
962
- )}, please use camelCase instead - example: myClass`
963
- );
964
- }
965
892
  // @ts-ignore
966
893
  element.setAttribute("class", element.getAttribute("className"));
967
894
  element.removeAttribute("className");
@@ -998,6 +925,7 @@ export class Component {
998
925
  }
999
926
  break;
1000
927
  }
928
+
1001
929
  });
1002
930
 
1003
931
  result = dom.body.innerHTML;
@@ -1005,12 +933,128 @@ export class Component {
1005
933
  this.Componentcontent = result;
1006
934
 
1007
935
  if (!result.includes("<div data-component")) {
1008
- result = `<div
1009
-
1010
- data-component="${this.name}">${result}</div>`;
936
+ result = `<div data-component="${this.name}">${result}</div>`;
1011
937
  }
938
+ return markdown(result.replace(/\\n/g, '\n').trim())
1012
939
 
1013
- return result;
940
+ }
941
+
942
+ /**
943
+ * The `html` method generates and processes HTML content for a component, performing various validations and tasks.
944
+ *
945
+ * @param {String} strings - The HTML content to be processed.
946
+ * @param {...any} args - Dynamic values to be inserted into the template.
947
+ * @returns {string} - The processed HTML content as a string.
948
+ *
949
+ * @throws {SyntaxError} - Throws a `SyntaxError` if image-related attributes are missing or invalid.
950
+ * @throws {Error} - Throws an `Error` if there are issues with class names or relative paths.
951
+ *
952
+ * @example
953
+ * // Example usage within a component:
954
+ * const myComponent = new Component();
955
+ * const htmlContent = myComponent.html`
956
+ * <div>
957
+ * <img src="/images/example.jpg" alt="Example Image" />
958
+ * </div>
959
+ * `;
960
+ * document.body.innerHTML = htmlContent;
961
+ *
962
+ * @remarks
963
+ * The `html` method is a core function used in component rendering. It allows you to define and generate HTML content within your component while enforcing best practices and accessibility standards. The method performs several essential tasks:
964
+ *
965
+ * 1. **Image Validation**: It checks images for the presence of 'alt' attributes and their validity.
966
+ * - Throws a `SyntaxError` if an image is missing the 'alt' attribute.
967
+ * - Throws a `SyntaxError` if the 'alt' attribute is empty.
968
+ * - Checks for an 'aria-hidden' attribute for image elements.
969
+ *
970
+ * 2. **Class Attribute Handling**: It enforces class attribute usage and allows optional configuration via comments.
971
+ * - Throws an `Error` if 'class' attributes are used without permission.
972
+ * - Supports 'className' attributes for class definitions.
973
+ * - Allows or disallows class-related comments based on your configuration.
974
+ *
975
+ * 3. **Relative Path Handling**: It processes relative paths in 'href' and 'src' attributes, ensuring proper routing.
976
+ * - Converts relative 'href' attributes to anchor links with appropriate routing.
977
+ * - Converts relative 'src' attributes to absolute paths with 'public' directories.
978
+ *
979
+ * 4. **Custom Component Attributes**: It supports adding a 'data-component' attribute to the root element.
980
+ * - Ensures that the 'data-component' attribute is present for component identification.
981
+ *
982
+ * 5. **Lifecycle Method Invocation**: It invokes the `componentDidMount` method if called from a 'render' context.
983
+ * - Executes `componentDidMount` to handle component initialization once the DOM is ready.
984
+ *
985
+ * @see {@link Component}
986
+ * @see {@link Component#componentDidMount}
987
+ */
988
+
989
+
990
+
991
+ html(strings, ...args) {
992
+ // @ts-ignore
993
+ if (
994
+ // @ts-ignore
995
+ new Error().stack &&
996
+ // @ts-ignore
997
+ new Error().stack.split("\n").length > 0 &&
998
+ // @ts-ignore
999
+ new Error().stack.split("\n")[2] &&
1000
+ // @ts-ignore
1001
+ new Error().stack.split("\n")[2].includes("render") &&
1002
+ !this.componentMounted
1003
+ ) {
1004
+ this.componentMounted = true;
1005
+ this.componentDidMount();
1006
+ }
1007
+
1008
+
1009
+ if(this.cfr){
1010
+
1011
+ this.worker.postMessage({strings, args, location: window.location.href, name: this.name})
1012
+ let promise = new Promise((resolve, reject)=>{
1013
+ this.worker.onmessage = (e)=>{
1014
+ if(e.data.error){
1015
+ throw new Error(e.data.error)
1016
+ }
1017
+
1018
+ let d = ""
1019
+ if(new Function("useRef", `return \`${e.data}\``)(useRef).includes('#')){
1020
+ d = markdown(new Function("useRef", `return \`${e.data}\``)(useRef))
1021
+ }else{
1022
+ d = new Function("useRef", `return \`${e.data}\``)(useRef)
1023
+ }
1024
+
1025
+ resolve(d)
1026
+
1027
+
1028
+
1029
+ }
1030
+ this.worker.onerror = (e)=>{
1031
+ reject(e)
1032
+ }
1033
+ })
1034
+ // @ts-ignore
1035
+ return promise;
1036
+ }else{
1037
+ let result = "";
1038
+ for (let i = 0; i < strings.length; i++) {
1039
+ result += strings[i];
1040
+ if (i < args.length) {
1041
+ result += args[i];
1042
+ }
1043
+ }
1044
+ result = new Function("useRef", `return \`${result}\``)(useRef)
1045
+
1046
+ if (!result.trim().startsWith("<body>")) {
1047
+ console.warn(
1048
+ "You should wrap your html in a body tag, vader may not grab all html!"
1049
+ );
1050
+ }
1051
+
1052
+
1053
+
1054
+ return this.parseHTML(result);
1055
+ }
1056
+
1057
+
1014
1058
  }
1015
1059
  // write types to ensure it returns a string
1016
1060
  /**
@@ -1117,38 +1161,80 @@ export const include = async (path) => {
1117
1161
  return res.text();
1118
1162
  })
1119
1163
  .then(async (data) => {
1120
- // Handle includes
1121
- let includes = data.match(/<include src="(.*)"\/>/g);
1122
- if (includes) {
1123
-
1124
- const includePromises = includes.map((e) => {
1164
+ data = new Function(`return \`${data}\`;`)();
1165
+
1166
+ let dom = new DOMParser().parseFromString(data, "text/html");
1167
+ let elements = dom.documentElement.querySelectorAll("*");
1168
+ let conccurentIncludes = [];
1169
+ if(cache[path]){
1170
+ return new Function(`return \`${cache[path]}\`;`)();
1171
+ }
1172
+ if (elements.length > 0) {
1173
+ for (var i = 0; i < elements.length; i++) {
1125
1174
 
1126
- // @ts-ignore
1127
- let includePath = e.match(/<include src="(.*)"\/>/)[1];
1175
+ if (elements[i].nodeName === "INCLUDE") {
1176
+ if(!elements[i].getAttribute("src") || elements[i].getAttribute("src") === ""){
1177
+ throw new Error("Include tag must have src attribute")
1178
+ }
1179
+
1180
+ let componentName = elements[i].getAttribute("src")?.split("/").pop()?.split(".")[0]
1181
+ // @ts-ignore
1182
+ let filedata = await include(elements[i].getAttribute("src"));
1183
+ filedata = new Function(`return \`${filedata}\`;`)();
1184
+ let newdom = new DOMParser().parseFromString(filedata, "text/html");
1128
1185
 
1129
- if (
1130
- includePath.startsWith("/") &&
1131
- !document.documentElement.outerHTML
1132
- .trim()
1133
- .includes("<!-- #vader-disable_relative-paths -->")
1134
- ) {
1135
- includePath = "/src" + includePath;
1136
- }
1137
- return include(includePath).then((includeData) => {
1138
- data = data.replace(e, includeData);
1139
- });
1140
- });
1186
+ newdom.querySelectorAll("include").forEach((el)=>{
1187
+ el.remove()
1188
+ })
1189
+ // @ts-ignore
1190
+
1191
+ let els = dom.querySelectorAll(componentName)
1192
+
1193
+ els.forEach((el)=>{
1194
+
1195
+ console.log(el)
1196
+ if(el.attributes.length > 0){
1197
+ for(var i = 0; i < el.attributes.length; i++){
1198
+ newdom.body.outerHTML = newdom.body.outerHTML.replace(`{{${el.attributes[i].name}}}`, el.attributes[i].value)
1199
+ }
1200
+
1201
+ }
1202
+ if(el.children.length > 0 && newdom.body.querySelector('slot')){
1203
+ for(var i = 0; i < el.children.length; i++){
1204
+ let slots = newdom.body.querySelectorAll("slot")
1205
+ slots.forEach((slot)=>{
1206
+ let id = slot.getAttribute("id")
1207
+ if(id === el.nodeName.toLowerCase()){
1208
+ slot.outerHTML = `<div>${el.innerHTML}</div>`
1209
+ }
1210
+ })
1211
+
1212
+
1213
+ }
1214
+
1215
+ }
1141
1216
 
1142
- // Wait for all includes to be fetched and replaced
1143
- return Promise.all(includePromises).then(() => {
1144
- cache[path] = data;
1217
+ dom.body.querySelectorAll('include').forEach((el)=>{
1218
+ el.remove()
1219
+ })
1220
+
1221
+ dom.body.outerHTML = dom.body.outerHTML.replace(el.outerHTML, newdom.body.innerHTML)
1222
+
1223
+
1224
+ })
1225
+
1145
1226
 
1146
- return data
1147
- });
1148
- } else {
1149
- cache[path] = data;
1150
- return data;
1227
+
1228
+
1229
+ }
1230
+ }
1231
+
1232
+
1151
1233
  }
1234
+
1235
+ data = dom.body.outerHTML
1236
+
1237
+ return data;
1152
1238
  });
1153
1239
  };
1154
1240
 
package/worker.js ADDED
@@ -0,0 +1,106 @@
1
+ onmessage = (e)=>{
2
+ let time_started = Date.now()
3
+ let strings = e.data.strings
4
+ let args = e.data.args
5
+ let l = e.data.location.split('/#/')[0]
6
+ let result = "";
7
+ for (let i = 0; i < strings.length; i++) {
8
+ result += strings[i];
9
+ if (i < args.length) {
10
+ result += args[i];
11
+ }
12
+ }
13
+
14
+ let comments = result.match(/--([^>]*)--/gs)
15
+ if(comments){
16
+ while(comments.length){
17
+ let comment = comments.pop()
18
+ console.log(comment)
19
+ // @ts-ignore
20
+ result = result.replace(comment,'')
21
+ }
22
+ }
23
+
24
+ let code = result.match(/<code([^>]*)>/g)
25
+ if(code){
26
+ while(code.length){
27
+ let c = code.pop()
28
+ // @ts-ignore
29
+ c = c.replace('<code','').replace('>','')
30
+ result = result.replace(`<code${c}>`,`<code${c} style="background:#000;color:#fff;padding:5px;border-radius:5px;font-size:12px;font-weight:bold">`)
31
+ }
32
+ }
33
+
34
+
35
+ if(!result.includes('<body>')){
36
+ throw new Error(`Vader Error: You must enclose your html in a body tag for all components. \n\n${result}`)
37
+ }
38
+ /**
39
+ * @type {string[]}
40
+ * @description - grabbing all className attributes and replace them with class
41
+ */
42
+ // @ts-ignore
43
+ result = result.replace(/classname/g,'class')
44
+ /**
45
+ * @type {string[]}
46
+ * @description - grabbing all image tags and replace the src attribute with the absolute path
47
+ */
48
+ // @ts-ignore
49
+ let images = result.match(/<img([^>]*)>/g)
50
+ for(let i = 0; i < images.length; i++){
51
+ let image = images[i]
52
+ let src = image.match(/src="([^"]*)"/)
53
+ let alt = image.match(/alt="([^"]*)"/)
54
+ if(src){
55
+ if(!src[1].includes('http') || !result.includes('<!-- #vader-disable_relative-paths -->')){
56
+ result = result.replace(src[0],`src="${l}/public/${src[1]}"`)
57
+ }else{
58
+ throw new Error(`Vader Error: You cannot use relative paths in the src attribute of ${src[0]}. Use absolute paths instead. \n\n${src[0]}`)
59
+ }
60
+ }
61
+ if(!alt && !result.includes('<!-- #vader-disable_accessibility -->')){
62
+ throw new Error(`Vader Error: You must include an alt attribute in the image tag \n\n${image} of class ${e.data.name}. `)
63
+ }
64
+
65
+ // @ts-ignore
66
+ if(!caches.match(`${l}/public/${src[1]}`)){
67
+ caches.open('vader').then((cache)=>{
68
+ // @ts-ignore
69
+ cache.add(`${l}/public/${src[1]}`)
70
+ // @ts-ignore
71
+ console.log('cached', `${l}/public/${src[1]}`)
72
+ }).catch((err)=>{
73
+ console.log(err)
74
+ })
75
+ }else{
76
+ // @ts-ignore
77
+ console.log('already cached', caches.match(`${l}/public/${src[1]}`))
78
+ }
79
+ }
80
+
81
+ let href = result.match(/href="([^"]*)"/g)
82
+ if(href){
83
+ while(href.length){
84
+ let h = href.pop()
85
+ // @ts-ignore
86
+ h = h.replace('href="','').replace('"','')
87
+ if(!h.includes('http') || !result.includes('<!-- #vader-disable_relative-paths -->')){
88
+ result = result.replace(`href="${h}"`,`href="#${h}"`)
89
+ }else{
90
+ throw new Error(`Vader Error: You cannot use relative paths in ${e.data.file}. Use absolute paths instead. \n\n${h}`)
91
+ }
92
+ }
93
+ }
94
+
95
+ let time_ended = Date.now()
96
+ let time_taken = time_ended - time_started
97
+ let hasran = false
98
+ if(l.includes('localhost') || l.includes('127.0.0.1') && !hasran){
99
+ hasran = true
100
+ result+= `\$\{console.log('%c${e.data.name} component rendered in ${time_taken}ms','color:#fff;background:#000;padding:5px;border-radius:5px;font-size:12px;font-weight:bold'),""\}`
101
+ }
102
+
103
+
104
+ postMessage(`<div data-component=${e.data.name}>${result}</div>`)
105
+
106
+ }