vaderjs 1.3.2 → 1.3.3-12219a8b18b-hotfix

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
@@ -1,1335 +1,1446 @@
1
- let dom = [];
2
- let states = {};
3
- let worker = new Worker(new URL("./worker.js", import.meta.url));
4
- /**
5
- * @function markdown
6
- * @param {String} content
7
- * @description Allows you to convert markdown to html
8
- */
9
- function markdown(content) {
10
- let headers = content.match(/(#+)(.*)/g);
11
- if (headers) {
12
- headers.forEach((header) => {
13
- if (
14
- header.includes("/") ||
15
- header.includes("<") ||
16
- header.includes(">")
17
- ) {
18
- return;
19
- }
20
- let level = header.split("#").length;
21
- content = content.replace(
22
- header,
23
- `<h${level} class="markdown_heading">${header.replace(
24
- /#/g,
25
- ""
26
- )}</h${level}>`
27
- );
1
+ #!/usr/bin/env node
2
+ import fs, { fstatSync } from "fs";
3
+ import { glob, globSync, globStream, globStreamSync, Glob, } from 'glob'
4
+ import puppeteer from 'puppeteer';
5
+ import http from 'http'
6
+ import { SourceMapGenerator } from 'source-map'
7
+
8
+ import { WebSocketServer } from 'ws'
9
+ import prettier from 'prettier'
10
+ import { watch } from "fs";
11
+ import path from 'path'
12
+
13
+ globalThis.compiledFiles = []
14
+
15
+ const sourceMapGen = (data, code) => {
16
+ let { origin, fileName } = data
17
+ const sourceMap = new SourceMapGenerator({ file: '/src/' + fileName.replace('.jsx', '.js') });
18
+
19
+ const lines = fs.readFileSync(origin, "utf8").split("\n");
20
+ let line = 1;
21
+ let column = 0;
22
+ for (const l of lines) {
23
+ sourceMap.addMapping({
24
+ source: origin,
25
+ sourceRoot: '/src',
26
+ original: { line: line, column: 0 },
27
+ generated: { line: line, column: 0 },
28
28
  });
29
+ line++;
29
30
  }
30
31
 
31
- content = content.replace(/\*\*(.*?)\*\*/g, (match, text) => {
32
- return `<b class="markdown_bold">${text}</b>`;
33
- });
34
- content = content.replace(/\*(.*?)\*/g, (match, text) => {
35
- return `<i class="markdown_italic">${text}</i>`;
36
- });
37
- content = content.replace(/`(.*?)`/g, (match, text) => {
38
- return `<code>${text}</code>`;
39
- });
40
- content = content.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
41
- return `<a class="markdown_link" href="${url}">${text}</a>`;
42
- });
43
- content = content.replace(/!\[([^\]]+)\]\(([^)]+)\)/g, (match, alt, src) => {
44
- return `<img class="markdown_image" src="${src}" alt="${alt}" />`;
45
- });
46
- content = content
47
- .split("\n")
48
- .map((line, index, arr) => {
49
- if (line.match(/^\s*-\s+(.*?)$/gm)) {
50
- if (index === 0 || !arr[index - 1].match(/^\s*-\s+(.*?)$/gm)) {
51
- return `<ul class="markdown_unordered" style="list-style-type:disc;list-style:inside"><li>${line.replace(
52
- /^\s*-\s+(.*?)$/gm,
53
- "$1"
54
- )}</li>`;
55
- } else if (
56
- index === arr.length - 1 ||
57
- !arr[index + 1].match(/^\s*-\s+(.*?)$/gm)
58
- ) {
59
- return `<li>${line.replace(/^\s*-\s+(.*?)$/gm, "$1")}</li></ul>`;
60
- } else {
61
- return `<li>${line.replace(/^\s*-\s+(.*?)$/gm, "$1")}</li>`;
62
- }
63
- } else {
64
- return line;
65
- }
66
- })
67
- .join("\n");
68
-
69
- content = content
70
- .split("\n")
71
- .map((line, index, arr) => {
72
- if (line.match(/^\s*\d+\.\s+(.*?)$/gm)) {
73
- if (index === 0 || !arr[index - 1].match(/^\s*\d+\.\s+(.*?)$/gm)) {
74
- return `<ol class="markdown_ordered" style="list-style-type:decimal;"><li>${line.replace(
75
- /^\s*\d+\.\s+(.*?)$/gm,
76
- "$1"
77
- )}</li>`;
78
- } else if (
79
- index === arr.length - 1 ||
80
- !arr[index + 1].match(/^\s*\d+\.\s+(.*?)$/gm)
81
- ) {
82
- return `<li>${line.replace(/^\s*\d+\.\s+(.*?)$/gm, "$1")}</li></ol>`;
83
- } else {
84
- return `<li>${line.replace(/^\s*\d+\.\s+(.*?)$/gm, "$1")}</li>`;
85
- }
86
- } else {
87
- return line;
88
- }
89
- })
90
- .join("\n");
32
+ sourceMap.setSourceContent(origin, fs.readFileSync(origin, "utf8"));
91
33
 
92
- return content;
34
+ code = code + `\n//# sourceMappingURL=./src/maps/${fileName.replace('.jsx', '.js')}.map \n //#sourceURL=/src/maps/${fileName.replace('.jsx', '.js')}.map`
35
+ return { code, sourceMap };
93
36
  }
94
37
 
95
- /**
96
- * @function useRef
97
- * @description Allows you to get reference to DOM element
98
- * @param {String} ref
99
- * @returns {void | Object} {current, update}
100
- */
101
-
102
- export const useRef = (ref) => {
103
- const element = document.querySelector(`[ref="${ref}"]`);
104
- const getElement = () => element;
105
-
106
- const update = (data) => {
107
- const newDom = new DOMParser().parseFromString(data, "text/html");
108
- const newElement = newDom.body.firstChild;
109
-
110
- if (element) {
111
- // @ts-ignore
112
- const isDifferent = !newElement.isEqualNode(element);
113
- if (isDifferent) {
114
- // @ts-ignore
115
- element.parentNode.replaceChild(newElement, element);
116
- }
117
- }
118
- };
38
+ let config = await import('file://' + process.cwd() + '/vader.config.js').then((e) => e.default || e)
39
+ let writer = async (file, data) => {
40
+ globalThis.isWriting = file
41
+ switch (true) {
42
+ case !fs.existsSync(file):
43
+ fs.mkdirSync(file.split('/').slice(0, -1).join('/'), { recursive: true })
44
+ break;
45
+ }
46
+ if (globalThis.isWriting !== file) {
47
+ return
48
+ }
49
+ await fs.writeFileSync(file, data);
119
50
 
120
- return {
121
- current: getElement(),
122
- update
123
- };
51
+ globalThis.isWriting = null
52
+ return { _written: true };
124
53
  };
125
54
 
126
- let components = [];
127
- /**
128
- * @class Component
129
- * @description Allows you to create a component
130
- * @returns {void}
131
- * @example
132
- * import { Vader } from "../../dist/vader/index.js";
133
- * export class Home extends Vader.Component {
134
- * constructor() {
135
- * super();
136
- * }
137
- * async render() {
138
- * return this.html(`
139
- * <div className="hero p-5">
140
- * <h1>Home</h1>
141
- * </div>
142
- * `);
143
- * }
144
- * }
145
- */
146
- export class Component {
147
- constructor() {
148
- this.states = {};
149
- //@ts-ignore
150
- this.name = this.constructor.name;
151
- this.executedEffects = {};
152
- this.storedProps = {};
153
- this.componentMounted = false;
154
- this.hasMounted = false;
155
- this.$_signal_subscribers = [];
55
+ let bundleSize = 0;
56
+
57
+ if (!fs.existsSync(process.cwd() + '/dist')) {
58
+ fs.mkdirSync(process.cwd() + '/dist')
59
+ }
60
+
61
+
62
+
63
+
64
+ if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cwd() + '/dist/index.html')) {
65
+ fs.writeFileSync(process.cwd() + "/dist/index.html", '')
66
+ }
67
+
68
+
69
+
70
+ function Compiler(func, file) {
71
+ let string = func;
72
+ let returns = []
73
+ let comments = string.match(/\{\s*\/\*.*\*\/\s*}/gs)?.map((comment) => comment.trim());
74
+
75
+
76
+
77
+
78
+
79
+
80
+ let childs = [];
81
+
82
+
83
+ // or : value boolean variable etc
84
+ const spreadAttributeRegex = /\s*([a-zA-Z0-9_-]+)\s*(\$\s*=\s*\{\s*\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}\s*\})/gs;
85
+
86
+
87
+
88
+
89
+ function extractAttributes(code) {
90
+ // grab $={...} and ={...}
91
+ const elementRegex = /<([a-zA-Z0-9_-]+)([^>]*)>/gs;
92
+
93
+ // Match attributes in an opening tag, including those with ={}
94
+ // Match attributes in an opening tag, including those with ={...}
95
+ const attributeRegex =
96
+ /\s*([a-zA-Z0-9_-]+)(\s*=\s*("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
97
+
98
+
99
+
100
+ const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)\s*(=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
101
+
102
+ let attributesList = [];
103
+
104
+ let spreadAttributes = [];
105
+
156
106
  /**
157
- * @property {Array} $_signal_subscribers_ran
158
- * @description Allows you to keep track of signal subscribers
159
- * @private
107
+ * @search - handle spread for html elements
108
+ * @keywords - spread, spread attributes, spread props, spread html attributes
160
109
  */
161
- this.$_signal_subscribers_ran = [];
162
- this.effects = {};
163
- this.$_useStore_subscribers = [];
164
- this.init();
165
- this.Componentcontent = null;
166
- this.$_signal_dispatch_event = new CustomEvent("SignalDispatch", {
167
- detail: {
168
- hasUpdated: false,
169
- state: null
170
- }
171
- });
110
+
111
+
172
112
  /**
173
- * @property {Object} $_signal_dispatch_cleanup_event
174
- * @description Allows you to dispatch a signal cleanup event
175
- * @private
113
+ * @search - handle function parsing for html elements
114
+ * @keywords - function, function attributes, function props, function html attributes
115
+ *
176
116
  */
177
- this.$_signal_dispatch_cleanup_event = new CustomEvent(
178
- "Signal_Cleanup_Dispatch",
179
- {
180
- detail: {
181
- state: null,
182
- lastState: null
117
+ let functionAttributes = [];
118
+ let spreadFunctions = [];
119
+ let functionMatch;
120
+ while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
121
+
122
+ let [, attributeName, attributeValue] = functionMatch;
123
+ let attribute = {};
124
+
125
+ if (attributeValue && attributeValue.includes("=>")
126
+ && !attributeValue.includes("this.bind")
127
+ || attributeValue && attributeValue.includes("function")
128
+ && !spreadFunctions.includes(attributeValue)
129
+ ) {
130
+
131
+ let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('')
132
+ let old = `${attributeName}${attributeValue}`
133
+
134
+ let elementMatch = string.match(/<([a-zA-Z0-9_-]+)([^>]*)>/gs);
135
+ let isJSXComponent = false;
136
+ elementMatch.forEach((element) => {
137
+ element = element.trim().replace(/\s+/g, " ");
138
+ if (element.includes(attributeName)) {
139
+ let elementTag = element
140
+ .split("<")[1]
141
+ .split(">")[0]
142
+ .split(" ")[0];
143
+ isJSXComponent = elementTag.match(/^[A-Z]/) ? true : false;
144
+ }
145
+ });
146
+ if (isJSXComponent) {
147
+ continue
183
148
  }
149
+ // add ; after newlines
150
+
151
+
152
+ let newvalue = attributeValue.includes('=>') ? attributeValue.split("=>").slice(1).join("=>").trim() : attributeValue.split("function").slice(1).join("function").trim()
153
+
154
+ // add ; after newlines
155
+
156
+
157
+ newvalue = newvalue.trim();
158
+
159
+ //remove starting {
160
+ newvalue = newvalue.replace("{", "")
161
+
162
+
163
+
164
+ let params = attributeValue
165
+ .split("=>")[0]
166
+ .split("(")[1]
167
+ .split(")")[0]
168
+ .trim();
169
+
170
+ // remove comments
171
+ params = params.match(/\/\*.*\*\//gs)
172
+ ? params.replace(params.match(/\/\*.*\*\//gs)[0], "")
173
+ : params;
174
+ // split first {}
175
+ newvalue = newvalue.trim();
176
+
177
+
178
+
179
+ newvalue = newvalue.replace(/}\s*$/, '');
180
+
181
+
182
+
183
+ newvalue = newvalue.trim();
184
+
185
+ // remmove trailing }
186
+
187
+ newvalue = newvalue.trim();
188
+ newvalue = newvalue.replace(/}\s*$/, '');
189
+
190
+
191
+
192
+
193
+ newvalue = newvalue.replaceAll(',,', ',')
194
+ let paramnames = params ? params.split(',').map((e) => e.trim()) : null
195
+ paramnames = paramnames ? paramnames.filter((e) => e.length > 0) : null
196
+ // remove comments
197
+ paramnames = paramnames ? paramnames.map((e) => e.match(/\/\*.*\*\//gs) ? e.replace(e.match(/\/\*.*\*\//gs)[0], "") : e) : null
198
+
199
+ // add ; after newlines
200
+ newvalue = newvalue.replaceAll(/\n/g, ";\n")
201
+ // remove () from newvalue
202
+ newvalue = newvalue.replace(/\(\s*=>/gs, '=>').replace(/\function\s*\([^\)]*\)\s*\{/gs, '{')
203
+
204
+ let bind = isJSXComponent ? `${attributeName}='function(${params}){${newvalue}}'` : `${attributeName}="\$\{this.bind(function(){${newvalue}}.bind(this), ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
205
+ if (e.length < 1) return ''
206
+ if (e.length > 0) {
207
+ index == 0 ? e : ',' + e
208
+ }
209
+ return e
210
+ }) : ''}" ${params ? params.split(',').map((e) => e.trim()).filter(Boolean).map((e) => `,${e}`).join('') : ''})}"`
211
+
212
+ string = string.replace(old, bind);
184
213
  }
185
- );
186
- /**
187
- * @property {Array} snapshots
188
- * @private
189
- */
190
- this.snapshots = [];
191
- /**
192
- * @property {Object} dom
193
- * @description Allows you to get reference to DOM element
194
- * @returns {void | HTMLElement}
195
- *
196
- */
197
- this.dom = [];
214
+ }
198
215
 
199
216
  /**
200
- * @property {Boolean} cfr
201
- * @description Allows you to compile html code on the fly - client fly rendering
202
- *
203
- */
204
- this.cfr = false;
205
- /**
206
- * @property {Boolean} worker
207
- * @description Allows you to use a web worker to compile html code on the fly - client fly rendering
208
-
217
+ * @search - handle attributes for html elements
218
+ * @keywords - attributes, props, html attributes
209
219
  */
220
+ let match;
221
+ while ((match = elementRegex.exec(string)) !== null) {
222
+ let [, element, attributes] = match;
223
+
224
+
225
+ let attributesMatch;
226
+ let elementAttributes = {};
227
+
228
+ while ((attributesMatch = attributeRegex.exec(attributes)) !== null) {
229
+ let [, attributeName, attributeValue] = attributesMatch;
230
+
231
+ elementAttributes[attributeName] = attributeValue || null;
232
+ }
233
+
234
+ attributesList.push({ element, attributes: elementAttributes });
235
+ }
236
+
237
+ let spreadMatch;
238
+ while ((spreadMatch = spreadAttributeRegex.exec(code)) !== null) {
239
+ let [, element, spread] = spreadMatch;
240
+ let isJSXComponent = element.match(/^[A-Z]/) ? true : false;
241
+ if (isJSXComponent) {
242
+ continue;
243
+ }
244
+ let old = spread;
245
+ spread = spread.trim().replace(/\s+/g, " ");
246
+ // re,pve $={ and }
247
+ spread = spread.replace(/\s*\$\s*=\s*{\s*{/gs, '')
248
+
249
+ // replace trailing }
250
+ spread = spread.replace(/}}\s*$/, '').replace(/}\s*}$/, '')
251
+ let splitByCommas = spread.split(/,(?![^{}]*})/gs)
252
+ // remove empty strings
253
+ splitByCommas = splitByCommas.filter((e) => e.split(':')[0].trim().length > 0)
254
+ splitByCommas = splitByCommas.map((e, index) => {
255
+ let key = e.split(':')[0].trim()
256
+ switch (true) {
257
+ case e.includes('function') && !e.includes('this.bind') || e && e.includes('=>') && !e.includes('this.bind'):
258
+ let value = e.split(':')[1].trim()
259
+ let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('');
260
+ value = `this.bind(${value}, false, "${ref}", "")`
261
+ e = `${key}="\${${value}}"`
262
+ break;
263
+ case e.includes('style:'):
264
+ let v2 = e.split('style:')[1].trim().replace(/,$/, '')
265
+ v2 = v2.replace(/,$/, '')
266
+ e = `${key}="\${this.parseStyle(${v2})}"`
267
+ break;
268
+
269
+ default:
270
+ let v = e.split(':')
271
+ key = v[0].trim()
272
+ // remove key from v
273
+ v.shift()
274
+ v = v.join(' ')
275
+ e = `${key}="\${${v}}"`
276
+
277
+ break;
278
+ }
279
+
280
+
281
+ return e;
282
+ });
283
+
284
+
285
+ let newSpread = splitByCommas.join(' ').trim().replace(/,$/, '');
286
+
287
+ // remove trailing }
288
+ string = string.replace(old, newSpread);
289
+ }
290
+
291
+ return attributesList;
292
+ }
293
+
294
+ function extractOuterReturn(code) {
295
+ // match return [...]
296
+ let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
297
+
298
+ return returns || [];
299
+ }
300
+ if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
301
+ || string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
302
+ ) {
303
+
304
+ try {
305
+ throw new SyntaxError("You forgot to enclose jsx in a fragment return <> jsx html </> or return (<> jsx html </>) at line " + string.split(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)[0].split('\n').length + ' in file ' + file)
306
+ } catch (error) {
307
+ console.error(error)
308
+ process.exit(1)
309
+ }
210
310
  }
211
311
 
312
+ let outerReturn = extractOuterReturn(string);
313
+ let contents = "";
314
+ let updatedContents = "";
212
315
  /**
213
- * @method adapter
214
- * @description Allows you to create an adapter - this is used to create custom logic
215
- *
216
- *
316
+ * @search - handle return [...]
317
+ * @keywords - return, return jsx, return html, return [...]
217
318
  */
218
- adapter(options) {
219
- // allow you to override the compoent logic
220
-
221
-
319
+ outerReturn.forEach((returnStatement) => {
320
+
321
+ let lines = returnStatement.split("\n");
322
+
323
+ for (let i = 0; i < lines.length; i++) {
324
+ let line = lines[i];
325
+ if (line.match(/return\s*\<>|return\s*\(/gs)) {
326
+ continue;
327
+ }
328
+ contents += line + "\n";
329
+ }
330
+ let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
331
+
332
+ let attributes = extractAttributes(string);
333
+ contents = contents.trim().replace(/\]$/, "")
334
+ contents = contents.replace(/\)$/, "");
335
+ usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
336
+ updatedContents = contents;
337
+
338
+
339
+ let newAttributes = [];
340
+ let oldAttributes = [];
341
+ attributes.forEach((attribute) => {
342
+ const { element, attributes } = attribute;
343
+ // make sure it isnt a jsx component
344
+ let isJSXComponent = element.match(/[A-Z]/) ? true : false;
345
+ if (isJSXComponent) {
346
+ return;
347
+ }
348
+ if (Object.keys(attributes).length === 0) return;
349
+
350
+
351
+ newAttributes.push(attribute);
352
+ for (let key in attributes) {
353
+
354
+ let value = attributes[key];
355
+ let oldvalue = value;
356
+ if (value && !value.new) {
357
+
358
+ if (value && value.includes("={")) {
359
+
360
+ value = value.replace("=", "");
361
+ value == "undefined" ? (value = '"') : (value = value);
362
+
363
+ key == 'style'
364
+ && value.includes("{{")
365
+ ? value = `{this.parseStyle({${value.split('{{')[1].split('}}')[0]}})}` : null
366
+
367
+
368
+
369
+ value = `="\$${value}",`;
370
+ string = string.replace(oldvalue, value);
371
+
372
+ } else if (value && value.includes("={`")) {
373
+ value = value.replace("=", "");
374
+
375
+ value = `"\$${value}",`;
376
+ string = string.replace(oldvalue, value);
377
+
378
+ }
379
+ } else if (value && value.new) {
380
+ string = string.replace(oldvalue, value.new);
381
+ }
382
+ }
383
+ });
384
+ });
385
+
386
+
387
+
388
+ if (comments) {
389
+ comments.forEach((comment) => {
390
+ let before = comment.trim();
391
+
392
+ comment = comment.replaceAll(/\s+/g, " ");
393
+ comment = comment.trim();
394
+ string = string.replace(before, comment);
395
+ let to_remove = comment.split("{")[1].split("}")[0].trim();
396
+ let beforeComment = comment;
397
+ comment = comment.replaceAll(`{ ${to_remove} }`, "");
398
+
399
+ string = string.replace(beforeComment, comment);
400
+ });
222
401
  }
223
- init() {
224
- this.registerComponent();
402
+ let lines = string.split("\n");
403
+ lines.forEach((line) => {
404
+
405
+ if (
406
+ line.includes("let") ||
407
+ line.includes("const") ||
408
+ line.includes("var")
409
+ ) {
410
+ switch (true) {
411
+ case line.includes("useState") && !line.includes("import"):
412
+ let varType = line.split("[")[0]
413
+ let key = line.split("=")[0].split(",")[0].trim().split('[')[1];
414
+
415
+ let setKey = line.split("=")[0].trim().split(",")[1].trim().replace("]", "");
416
+ key = key.replace("[", "").replace(",", "");
417
+ let valuestate = line.split("=")[1].split("useState(")[1];
418
+
419
+ let regex = /useState\((.*)\)/gs;
420
+ valuestate = valuestate.match(regex) ? valuestate.match(regex)[0].split("useState(")[1].split(")")[0].trim() : valuestate
421
+
422
+
423
+ let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}`;
424
+ string = string.replace(line, newState);
425
+ break;
426
+ case line.includes("useRef") && !line.includes("import"):
427
+ line = line.trim();
428
+ let typeref = line.split(" ")[0]
429
+
430
+ let keyref = line.split(typeref)[1].split("=")[0].trim().replace("[", "").replace(",", "");
431
+
432
+
433
+ let valueref = line.split("=")[1].split("useRef(")[1];
434
+
435
+ let newStateref = `${typeref} ${keyref} = this.useRef('${keyref}', ${valueref}`;
436
+ string = string.replace(line, newStateref);
437
+ break;
438
+ case line.includes("useReducer") && !line.includes("import"):
439
+ line = line.trim();
440
+ line = line.replaceAll(/\s+/g, " ");
441
+
442
+ let varTypereducer = line.split(" ")[0];
443
+ let keyreducer = line
444
+ .split("=")[0]
445
+ .split(" ")[1]
446
+ .trim()
447
+ .replace("[", "")
448
+ .replace(",", "");
449
+ let setKeyreducer = line.split("=")[0].trim().split(",")[1].trim().replace("]", "");
450
+
451
+ let reducer = line.split("=")[1].split("useReducer(")[1];
452
+
453
+ let newStatereducer = `${varTypereducer} [${keyreducer}, ${setKeyreducer}] = this.useReducer('${keyreducer}', ${line.includes('=>') ? reducer + '=>{' : reducer}`;
454
+
455
+ string = string.replace(line, newStatereducer);
456
+ break;
457
+ }
458
+
459
+ }
460
+ });
461
+
462
+
463
+ string = string.replaceAll(/\$\{\/\*.*\*\/\}/gs, "");
464
+
465
+ string = string.replaceAll('../src', './src')
466
+
467
+ function parseComponents(body, isChild) {
468
+ let componentRegex = /<([A-Z][A-Za-z0-9_-]+)\s*([^>]*)>\s*([\s\S]*?)\s*<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs;
469
+
470
+ let componentMatch = body.match(componentRegex);
471
+ let topComponent = "";
472
+ componentMatch?.forEach(async (component) => {
473
+
474
+ let [, element, attributes] = component;
475
+ let before = component;
476
+ component = component.trim().replace(/\s+/g, " ");
477
+
478
+ !isChild ? (topComponent = component) : null;
479
+
480
+
481
+ let myChildrens = [];
482
+
483
+ let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
484
+ let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
485
+ // catchall = "" ={} =[]
486
+
487
+
488
+ let props = component.split("<")[1].split(">")[0]
489
+
490
+
491
+ const dynamicAttributesRegex = /([a-zA-Z0-9_-]+)\s*=\s*("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{.*.*\})*)*\})*)*\}|(?:\([^)]*\)|()\s*=>\s*(?:\{.*\})?|\{[^}]*\})|\[[^\]]*\])/gs;
492
+
493
+ const attributeObject = {};
494
+
495
+
496
+ let filteredProps = [];
497
+ let isWithinComponent = false;
498
+ let componentName = name
499
+ let currentProps = []
500
+
501
+ let $_ternaryprops = []
502
+
503
+ let match;
504
+ let propstring = ''
505
+ // props right now is just a string with all of them on one line and a space between each
506
+ while ((match = dynamicAttributesRegex.exec(props)) !== null) {
507
+ let str = match[0].trim().replace(/\s+/g, " ");
508
+ if (!str.includes('=')) {
509
+ continue
510
+ }
511
+ str = str.split('=')
512
+ let key = str[0].trim()
513
+ let value = str[1].trim()
514
+ if (value.startsWith('"') && !value.endsWith('"') || value.startsWith("'") && !value.endsWith("'")
515
+ || value.startsWith('`') && !value.endsWith('`')) {
516
+ // wrap in respective quotes
517
+ value = value + value[0]
518
+ }
519
+ let isObject = value.startsWith('{{') && value.endsWith('}}')
520
+ if (isObject) {
521
+ value = value.split('{{')[1].split('}}')[0].trim()
522
+ value = `{${value}}`
523
+ } else {
524
+ // remove starting { and ending } using regex
525
+ value = value.replace(/^{/, '').replace(/}$/, '')
526
+ }
527
+ propstring += `${key}:${value},`
528
+ }
529
+ component = component.replaceAll(/\s+/g, " ");
530
+
531
+ component = component.replace(componentAttributes, '')
532
+ $_ternaryprops.forEach((prop) => {
533
+ component = component.replace(prop, '')
534
+ })
535
+
536
+ let children = new RegExp(`<${name}[^>]*>([^]*)<\/${name}>`, 'gs').exec(component)?.[1] || null;
537
+
538
+
539
+
540
+ let savedname = name;
541
+
542
+
543
+
544
+ name = name + Math.random().toString(36).substring(2);
545
+ if (children && children.match(componentRegex)) {
546
+ children = parseComponents(children, true);
547
+ childs.push({ parent: name, children: children });
548
+ } else {
549
+
550
+ children ? childs.push({ parent: name, children: children }) : null;
551
+ }
552
+
553
+ childs.forEach((child) => {
554
+ if (child.parent == name) {
555
+ let html = child.children.match(
556
+ /<([A-Z][A-Za-z0-9_-]+)([^>]*)>(.*?)<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs
557
+ );
558
+ if (html) {
559
+ html = html.map((h) => h.trim().replace(/\s+/g, " ")).join(" ");
560
+ child.children = child.children.replaceAll(html, `${html}`);
561
+ // remove duplicate quotes
562
+ }
563
+
564
+ myChildrens.push(child.children);
565
+ childs = childs.filter((e) => e.parent !== name);
566
+ }
567
+ });
568
+
569
+
570
+
571
+ propstring = propstring.replace(/,$/, '')
572
+
573
+
574
+ /**
575
+ * @memoize - memoize a component to be remembered on each render and replace the old jsx
576
+ */
577
+
578
+ let replace = "";
579
+ replace = `\${this.memoize(this.createComponent(${savedname}, {${propstring}}, [\`${myChildrens.join('')}\`]))}`;
580
+
581
+
582
+ body = body.replace(before, replace);
583
+ });
584
+
585
+ return body;
225
586
  }
226
587
 
227
- registerComponent() {
228
- components.push(this);
588
+ string = string.replaceAll('vaderjs/client', '/vader.js')
589
+
590
+ const importRegex = /import\s*([^\s,]+|\{[^}]+\})\s*from\s*(['"])(.*?)\2/g;
591
+ const imports = string.match(importRegex);
592
+ let replaceMents = [];
593
+
594
+
595
+ if(imports){
596
+ for (let match of imports) {
597
+ let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
598
+ switch (true) {
599
+ case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
600
+ let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
601
+ componentFolder = componentFolder.split(process.cwd())[1]
602
+ if (!fs.existsSync(process.cwd() + componentFolder)) {
603
+ throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
604
+ }
605
+
606
+ if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
607
+ fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
608
+ }
609
+
610
+ let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
611
+ let glp = globSync('**/**/**/**.{jsx,js}', {
612
+ cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
613
+ absolute: true,
614
+ recursive: true
615
+ })
616
+ for (let file of glp) {
617
+ let text = fs.readFileSync(file, "utf8");
618
+ if (!file.endsWith('.js') && file.endsWith('.jsx')) {
619
+ text = Compiler(text, file);
620
+
621
+ }
622
+ let dest = file.split('node_modules')[1]
623
+ dest = dest.split(baseFolder)[1]
624
+ writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
625
+ let importname = match.split('import')[1].split('from')[0].trim()
626
+ let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
627
+ let newImport = `/src/${baseFolder + dest}`
628
+ newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
629
+ replaceMents.push({ match: oldImportstring, replace: newImport })
630
+ console.log(`📦 imported Node Package ${baseFolder} `)
631
+ }
632
+
633
+
634
+ break;
635
+ default:
636
+ break;
637
+ }
638
+ }
229
639
  }
230
640
 
231
- /**
232
- * @method setState
233
- * @description Allows you to set state
234
- * @param {String} key
235
- * @param {*} value
236
- * @returns {void}
237
- * @example
238
- * this.setState('count', 1)
239
- * */
240
- setState(key, value) {
241
- this.states[key] = value;
242
- this.updateComponent();
641
+ for (let replace of replaceMents) {
642
+ string = string.replaceAll(replace.match, replace.replace)
243
643
  }
244
- /**
245
- * @method componentUnmount
246
- * @description Allows you to run code after component has unmounted
247
- * @type {VoidFunction}
248
- * @returns {void}
249
- */
250
- unmount() {
251
- this.componentMounted = false;
252
- this.componentWillUnmount();
253
- // @ts-ignore
254
- document.querySelector(`[data-component="${this.name}"]`).remove();
255
- if (!document.querySelector(`[data-component="${this.name}"]`)) {
256
- components = components.filter(
257
- (component) => component.name !== this.name
258
- );
644
+
645
+ string = string.replaceAll(/\$\{[^{]*\.{3}/gs, (match) => {
646
+ if (match.includes('...')) {
647
+ // Your logic for replacement goes here
648
+ // For example, you can replace '...' with some other string
649
+ return match.replace('...', '');
259
650
  }
260
- }
261
651
 
262
- /**
263
- * @method componentUpdate
264
- * @description Allows you to run code after component has updated
265
- * @param {Object} prev_state
266
- * @param {Object} prev_props
267
- * @param {Object} snapshot
268
- * @returns {void}
269
- * @example
270
- * componentUpdate(prev_state, prev_props, snapshot) {
271
- * console.log(prev_state, prev_props, snapshot)
272
- * }
273
- * */
274
- componentUpdate(prev_state, prev_props, snapshot) {}
275
- /**
276
- * @method componentDidMount
277
- * @description Allows you to run code after component has mounted
278
- */
279
- componentDidMount() {}
652
+ return match;
653
+ });
280
654
 
281
- /**
282
- * @method componentWillUnmount
283
- * @description Allows you to run code before component unmounts
284
- * @type {VoidFunction}
285
- * @returns {void}
286
- */
287
- componentWillUnmount() {}
655
+ string = string.replaceAll(/\$\{\/\*.*\*\/\}/gs, "");
656
+ string = string.replaceAll("<>", "`").replaceAll("</>", "`");
657
+ string = string.replaceAll(".jsx", ".js");
658
+ string = parseComponents(string);
659
+
660
+ string = string
661
+ .replaceAll("className", "class")
662
+ .replaceAll("classname", "class");
663
+
664
+ string = string.replaceAll('../src', './src')
665
+ string += `\n\n //wascompiled`;
666
+
667
+
668
+ string = string.replaceAll("undefined", "");
669
+ const parse = (css) => {
670
+ let styles = {};
671
+ let currentSelector = '';
672
+
673
+ css.split('\n').forEach(line => {
674
+ line = line.trim();
675
+
676
+ if (line.endsWith('{')) {
677
+ // Start of a block, extract the selector
678
+ currentSelector = line.slice(0, -1).trim();
679
+ styles[currentSelector] = {};
680
+ } else if (line.endsWith('}')) {
681
+ // End of a block
682
+ currentSelector = '';
683
+ } else if (line.includes(':') && currentSelector) {
684
+ // Inside a block and contains key-value pair
685
+ let [key, value] = line.split(':').map(part => part.trim());
686
+ styles[currentSelector][key] = value;
687
+ }
688
+ });
288
689
 
289
- /**
290
- * @method signal
291
- * @description Allows you to create a signal
292
- * @param {String} key
293
- * @param {any} initialState
294
- * @returns {Object} {subscribe, cleanup, dispatch, call, set, get}
295
- * @example
296
- * let signal = this.signal('count', 0);
297
- * signal.subscribe((value) => {
298
- * console.log(value)
299
- * }, false) // false means it will run every time
300
- * signal.subscribe((value) => {
301
- * console.log(value)
302
- * }, true) // true means it will run once
303
- * signal.call() // this will call all subscribers
304
- * signal.set(1) // this will set the value of the signal
305
- * signal.get() // this will get the value of the signal
306
- * signal.cleanup() // this will remove all subscribers
307
- */
308
- signal = (key, initialState) => {
309
- let hasCaller = false;
310
- let [state, setState] = this.useState(key, initialState, () => {
311
- if (this.$_signal_subscribers.length > 0) {
312
- for (var i = 0; i < this.$_signal_subscribers.length; i++) {
313
- if (!hasCaller) {
314
- if (
315
- this.$_signal_subscribers[i].runonce &&
316
- // @ts-ignore
317
- this.$_signal_subscribers_ran.includes(
318
- this.$_signal_subscribers[i]
319
- )
320
- ) {
690
+ return styles;
691
+ };
692
+ string.split('\n').forEach(line => {
693
+ if (line.includes('import')) {
694
+ // Regular expression for matching import() statements
695
+ let asyncimportMatch = line.match(/import\s*\((.*)\)/gs);
696
+ // handle import { Component } from 'vaderjs/runtime/vader.js'
697
+ let regularimportMatch = line.match(/import\s*([A-Za-z0-9_-]+)\s*from\s*([A-Za-z0-9_-]+)|import\s*([A-Za-z0-9_-]+)\s*from\s*(".*")|import\s*([A-Za-z0-9_-]+)\s*from\s*('.*')|import\s*([A-Za-z0-9_-]+)\s*from\s*(\{.*\})/gs);
698
+
699
+ if (asyncimportMatch) {
700
+ asyncimportMatch.forEach(async (match) => {
701
+ let beforeimport = match
702
+ let path = match.split('(')[1].split(')')[0].trim()
703
+ let newImport = ''
704
+ let name = match.split('import')[1].split('from')[0].trim()
705
+ switch (true) {
706
+ case path && path.includes('json'):
707
+ path = path.replace(';', '')
708
+ newImport = `let ${name} = await fetch('${path}').then(res => res.json())`
709
+
710
+ break;
711
+ case path && path.includes('module.css'):
712
+ let css = await fs.readFileSync(process.cwd() + path, 'utf8')
713
+ css = css.replaceAll('.', '')
714
+
715
+ if (!name) {
716
+ throw new Error('Could not find name for css module ' + path + ' at' + beforeimport + ' file' + file)
717
+ }
718
+ newImport = `let ${name} = ${JSON.stringify(parse(css.replaceAll('.', '').replace(/\s+/g, " ")))}`
719
+
720
+ break;
721
+ default:
722
+ let deep = path.split('/').length - 1
723
+ for (let i = 0; i < deep; i++) {
724
+ path = path.split('../').join('')
725
+ path = path.split('./').join('')
726
+ }
727
+ path = path.replace(/'/g, '').trim().replace(/"/g, '').trim()
728
+ // remove double / from path
729
+ path = path.split('//').join('/')
730
+ if (!path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src')
731
+ && !path.startsWith('/public')
732
+ ) {
733
+ path = '/src/' + path
734
+ }
735
+
736
+ path = path.replaceAll('.jsx', '.js');
737
+ newImport = `await import(${path})`
738
+ }
739
+ if (newImport) {
740
+ string = string.replace(beforeimport, newImport)
741
+ }
742
+ })
743
+ }
744
+
745
+ if (regularimportMatch) {
746
+ for (let match of regularimportMatch) {
747
+ let beforeimport = match
748
+ let path = match.split('from')[1] ? match.split('from')[1].trim() : match.split('import')[1].trim()
749
+
750
+ let newImport = ''
751
+ let name = match.split('import')[1].split('from')[0].trim()
752
+
753
+
754
+ switch (true) {
755
+ case path && path.includes('json'):
756
+ path = path.replace(';', '')
757
+ newImport = `let ${name} = await fetch('${path}').then(res => res.json())`
758
+
759
+ break;
760
+ case path && path.includes('module.css'):
761
+
762
+ path = path.replace(';', '')
763
+ path = path.replace(/'/g, '').trim().replace(/"/g, '').trim()
764
+ path = path.replaceAll('.jsx', '.js');
765
+ path = path.replaceAll('../', '');
766
+
767
+ let css = fs.readFileSync(process.cwd() + '/' + path, 'utf8')
768
+
769
+ css = css.replaceAll('.', '')
770
+ newImport = `let ${name} = ${JSON.stringify(parse(css))}`
771
+ string = string.replace(beforeimport, newImport)
772
+ break;
773
+ case path && path.includes('.css'):
774
+ string = string.replace(beforeimport, '')
775
+ newImport = ``
776
+ break;
777
+ case path && !path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src') && !path.startsWith('public') &&
778
+ path.match(/^[A-Za-z0-9_-]+$/gs) && !path.includes('http') && !path.includes('https'):
779
+
780
+ break;
781
+ default:
782
+ let beforePath = path
783
+ let deep = path.split('/').length - 1
784
+ for (let i = 0; i < deep; i++) {
785
+ path = path.split('../').join('')
786
+ path = path.split('./').join('')
787
+ }
788
+ path = path.replace(/'/g, '').trim().replace(/"/g, '').trim()
789
+ // remove double / from path
790
+ path = path.split('//').join('/')
791
+ if (!path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src') && !path.startsWith('public')) {
792
+ path.includes('src') ? path.split('src')[1] : null
793
+ path = '/src/' + path
794
+ } else if (path.startsWith('src') || path.startsWith('public')) {
795
+ path = '/' + path
796
+ }
797
+ path = path.replaceAll('.jsx', '.js');
798
+
799
+
800
+ string = string.replace(beforePath, "'" + path + "'")
321
801
  break;
322
- } else {
323
- this.$_signal_subscribers[i].function(state);
324
- this.$_signal_subscribers_ran.push(this.$_signal_subscribers[i]);
325
- return;
326
- }
802
+
327
803
  }
804
+
805
+
806
+ if (newImport) {
807
+ string = string.replace(beforeimport, newImport)
808
+ }
809
+
328
810
  }
329
- } else {
330
- this.$_signal_dispatch_event.detail.hasUpdated = true;
331
- this.$_signal_dispatch_event.detail.state = state;
332
- window.dispatchEvent(this.$_signal_dispatch_event);
333
- }
334
- });
335
- /**
336
- * @function $_signal_subscribe
337
- * @description Allows you to subscribe to a signal
338
- * @param {*} fn
339
- * @param {*} runonce
340
- * @returns {void}
341
- *
342
- */
343
- this.$_signal_subscribe = (fn, runonce) => {
344
- this.$_signal_subscribers.push({
345
- function: fn,
346
- runonce: runonce
347
- });
348
- return fn;
349
- };
350
- this.$_signal_cleanup = (fn) => {
351
- this.lastState = state;
352
- this.$_signal_subscribers = this.$_signal_subscribers.filter(
353
- (subscriber) => subscriber.function !== fn
354
- );
355
- // @ts-ignore
356
- this.$_signal_dispatch_cleanup_event.detail.state = this.states;
357
- // @ts-ignore
358
- this.$_signal_dispatch_cleanup_event.detail.lastState = this.lastState;
359
- window.dispatchEvent(this.$_signal_dispatch_event);
360
- };
361
- this.$_signal_dispatch = () => {
362
- for (var i = 0; i < this.$_signal_subscribers.length; i++) {
363
- if (
364
- this.$_signal_subscribers[i].runonce &&
365
- // @ts-ignore
366
- this.$_signal_subscribers_ran.includes(this.$_signal_subscribers[i])
367
- ) {
368
- break;
369
- } else {
370
- this.$_signal_subscribers[i].function(state);
371
- this.$_signal_subscribers_ran.push(this.$_signal_subscribers[i]);
372
- }
811
+
812
+
373
813
  }
374
- };
375
- this.$_signal_get = () => {
376
- return state;
377
- };
378
- this.$_signal_call = () => {
379
- hasCaller = true;
380
- // @ts-ignore
381
- this.$_signal_dispatch();
382
- };
383
- /**
384
- * @function $_signal_set
385
- * @description Allows you to set the value of a signal
386
- * @param {*} detail
387
- */
388
- this.$_signal_set = (detail) => {
389
-
390
- setState(detail);
391
- };
814
+ }
392
815
 
393
- return {
394
- /**
395
- * @function subscribe
396
- * @description Allows you to subscribe to a signal
397
- * @param {*} fn
398
- * @param {*} runonce
399
- */
400
- subscribe: this.$_signal_subscribe,
401
- /**
402
- * @function cleanup
403
- * @description Allows you to cleanup a signal
404
- * @param {*} fn
405
- * @returns {null}
406
- */
407
- cleanup: this.$_signal_cleanup,
408
- /**
409
- * @function dispatch
410
- * @description Allows you to dispatch a signal
411
- * @returns {null}
412
- */
413
- dispatch: this.$_signal_dispatch,
414
- /**
415
- * @function call
416
- * @description Allows you to call a signal
417
- * @returns {null}
418
- */
419
- call: this.$_signal_call,
420
- /**
421
- * @function set
422
- * @description Allows you to set the value of a signal
423
- * @param {*} detail
424
- * @returns {null}
425
- */
426
- set: this.$_signal_set,
427
- /**
428
- * @function get
429
- * @readonly
430
- * @description Allows you to get the value of a signal
431
- * @returns {any}
432
- */
433
- get: this.$_signal_get
434
- };
816
+
817
+ })
818
+
819
+ return string
820
+ }
821
+
822
+ globalThis.isBuilding = false
823
+ globalThis.isWriting = null
824
+ const glb = await glob("**/**/**/**.{jsx,js}", {
825
+ ignore: ["node_modules/**/*", "dist/**/*"],
826
+ cwd: process.cwd() + '/pages/',
827
+ absolute: true,
828
+ recursive: true
829
+ });
830
+ let hasRendered = []
831
+
832
+ async function Build() {
833
+ globalThis.isBuilding = true
834
+ console.log(globalThis.isProduction ? 'Creating Optimized Production Build\n' : '')
835
+ let str = `Page \t\t\t\t Size\n`
836
+ globalThis.isProduction ? console.log('\x1b[32m%s\x1b[0m', str) : null
837
+ let reader = async (file) => {
838
+ let text = await fs.readFileSync(file, "utf8");
839
+ return text;
435
840
  };
436
- /**
437
- * @method useAuth
438
- * @description Allows you to create an auth object
439
- * @param {Object} options
440
- * @param {Array} options.rulesets
441
- * @param {Object} options.user
442
- * @returns {Object} {can, hasRole, canWithRole, assignRule, revokeRule, canAnyOf, canAllOf, canGroup}
443
- * @example
444
- * let auth = this.useAuth({
445
- * rulesets: [
446
- * {
447
- * action: 'create',
448
- * condition: (user) => {
449
- * return user.role === 'admin'
450
- * }
451
- * }
452
- * ],
453
- * user: {
454
- * role: 'admin'
455
- * }
456
- * })
457
- * auth.can('create') // true
458
- */
459
- useAuth(options) {
460
- /**@type {Array}**/
461
- let rules = options.rulesets;
462
- if (!rules) {
463
- throw new Error("No rulesets provided");
464
- }
465
- /**@type {Object}**/
466
- let user = options.user;
467
- let auth = {
468
- can: (action) => {
469
- let can = false;
470
- rules.forEach((rule) => {
471
- if (rule.action === action) {
472
- if (rule.condition(user)) {
473
- can = true;
474
- }
841
+
842
+
843
+
844
+ function ssg(routes = []) {
845
+ globalThis.isBuilding = true
846
+ let server = http.createServer((req, res) => {
847
+ let route = routes.find((e) => e.url === req.url)
848
+ if (route) {
849
+ let document = globalThis.routeDocuments.find((e) => e.url === req.url)
850
+ res.writeHead(200, { 'Content-Type': 'text/html' });
851
+ res.end(document.document);
852
+ } else {
853
+ const filePath = process.cwd() + '/dist/' + req.url
854
+
855
+ fs.readFile(filePath, (err, data) => {
856
+ if (err) {
857
+ res.writeHead(404, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
858
+ res.end('File not found');
859
+ } else {
860
+ res.writeHead(200, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
861
+ res.end(data);
475
862
  }
476
863
  });
477
- return can;
478
- },
479
- /**
480
- * @function hasRole
481
- * @description Allows you to check if user has a role
482
- * @param {String} role
483
- * @returns {Boolean}
484
- */
485
- hasRole: (role) => {
486
- return user.role && user.role.includes(role);
487
- },
488
- /**
489
- * @function canWithRole
490
- * @param {String} action
491
- * @param {String} role
492
- * @returns
493
- */
494
- canWithRole: (action, role) => {
495
- return auth.can(action) && auth.hasRole(role);
496
- },
497
- assignRule: (rule) => {
498
- if (
499
- !rules.some((existingRule) => existingRule.action === rule.action)
500
- ) {
501
- rules.push(rule);
502
- }
503
- },
504
- revokeRule: (action) => {
505
- rules = rules.filter((rule) => rule.action !== action);
506
- },
507
- canAnyOf: (actions) => {
508
- return actions.some((action) => auth.can(action));
509
- },
510
- canAllOf: (actions) => {
511
- return actions.every((action) => auth.can(action));
512
- },
513
- canGroup: (actions, logicalOperator = "any") => {
514
- return logicalOperator === "any"
515
- ? auth.canAnyOf(actions)
516
- : auth.canAllOf(actions);
517
864
  }
518
- };
519
- return auth;
520
- }
521
- /**
522
- * @method useReducer
523
- * @description Allows you to create a reducer
524
- * @param {*} key
525
- * @param {*} reducer
526
- * @param {*} initialState
527
- * @url - useReducer works similarly to - https://react.dev/reference/react/useReducer
528
- * @returns {Array} [state, dispatch]
529
- * @example
530
- * let [count, setCount] = this.useReducer('count', (state, action) => {
531
- * switch (action.type) {
532
- * case 'increment':
533
- * return state + 1
534
- * case 'decrement':
535
- * return state - 1
536
- * default:
537
- * throw new Error()
538
- * }
539
- * }, 0)
540
- * setCount({type: 'increment'})
541
- */
542
- useReducer(key, reducer, initialState) {
543
- if (!this.states[key]) {
544
- this.states[key] = initialState;
545
- }
546
- return [
547
- this.states[key],
548
- /**
549
- * @function dispatch
550
- * @description Allows you to dispatch a reducer
551
- * @param {*} action
552
- * @returns {void}
553
- */
554
- (action) => {
555
- this.states[key] = reducer(this.states[key], action);
556
- this.updateComponent();
865
+ });
866
+
867
+ let port = 12000
868
+ server.on('error', (err) => {
869
+ if (err.code === 'EADDRINUSE') {
870
+ setTimeout(() => {
871
+ server.close();
872
+ server.listen(++port);
873
+ }, 1000);
557
874
  }
558
- ];
559
- }
875
+ })
560
876
 
561
- runEffects() {
562
- Object.keys(this.effects).forEach((component) => {
563
- this.effects[component].forEach((effect) => {
564
- if (!this.executedEffects[effect]) {
565
- effect();
566
- this.executedEffects[effect] = true;
567
- }
568
- });
569
- });
570
- }
877
+ const browser = puppeteer.launch({
878
+ headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
879
+ warning: false,
880
+ })
881
+ server.listen(port);
571
882
 
572
- /**
573
- * @method useSyncStore
574
- * @description Allows you to create a store
575
- * @param {String} storeName
576
- * @param {*} initialState
577
- * @returns {Object} {getField, setField, subscribe, clear}
578
- * @example
579
- * let store = this.useSyncStore('store', {count: 0})
580
- * store.setField('count', 1)
581
- * store.getField('count') // 1
582
- *
583
- */
584
- useSyncStore(storeName, initialState) {
585
- let [storedState, setStoredState] = this.useState(
586
- storeName,
587
- initialState ||
588
- // @ts-ignore
589
- localStorage.getItem(`$_vader_${storeName}`, (s) => {
590
- localStorage.setItem(`$_vader_${storeName}`, JSON.stringify(s));
591
- this.$_useStore_subscribers.forEach((subscriber) => {
592
- subscriber(s);
883
+ routes.forEach(async (route) => {
884
+ if (route.url.includes(':')) {
885
+ return
886
+ }
887
+ globalThis.listen = true;
888
+
889
+ try {
890
+
891
+ route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
892
+ let page = (await browser).newPage()
893
+ page.then(async (page) => {
894
+ page.on('error', (err) => {
895
+ console.error('JS ERROR:', JSON.parse(err));
896
+ });
897
+ try {
898
+ page.on('pageerror', async err => {
899
+ let errorObj = JSON.parse(await page.evaluate(() => document.documentElement.getAttribute('error')) || '{}')
900
+ console.log('\x1b[31m%s\x1b[0m', 'Compiler Error:', errorObj)
901
+
902
+ console.log('\x1b[31m%s\x1b[0m', 'Error:', err)
903
+ });
904
+ } catch (error) {
905
+ page.close()
906
+ }
907
+ page.on('crash', () => {
908
+ console.error(`Render process crashed for ${route.url}`)
593
909
  });
594
- }) ||
595
- {}
596
- );
910
+
911
+ await page.goto(`http://localhost:${port}${route.url}`, { waitUntil: 'networkidle2' });
912
+
913
+ page.evaluate(() => {
914
+ document.querySelector('#meta').remove()
915
+ document.querySelector('#isServer').innerHTML = 'window.isServer = false'
916
+ if (document.head.getAttribute('prerender') === 'false') {
917
+ document.querySelector('#root').innerHTML = ''
918
+ console.log(`Disabled prerendering for ${window.location.pathname}`)
919
+ }
920
+ })
921
+ let html = await page.content();
597
922
 
598
- const getField = (fieldName) => {
599
- return storedState[fieldName];
600
- };
601
- const setField = (fieldName, value) => {
602
- const newState = { ...storedState, [fieldName]: value };
603
- setStoredState(newState);
604
- };
605
- const subscribe = (subscriber) => {
606
- return this.$_useStore_subscribers.push(subscriber);
607
- };
923
+ html = await prettier.format(html, { parser: "html" })
608
924
 
609
- const clear = (fieldName) => {
610
- let newState = storedState;
611
- delete newState[fieldName];
612
- setStoredState(newState);
613
- };
614
- return {
615
- getField,
616
- setField,
617
- subscribe,
618
- clear
619
- };
620
- }
621
- /**
622
- * @method useState
623
- * @description Allows you to create a state
624
- * @param {String} key
625
- * @param {*} initialValue
626
- * @param {*} callback
627
- * @url - useState works similarly to - https://react.dev/reference/react/useState
628
- * @returns {Array} [state, setState]
629
- * @example
630
- * let [count, setCount] = this.useState('count', 0, () => {
631
- * console.log('count has been updated')
632
- * })
633
- *
634
- * setCount(count + 1)
635
- */
636
- useState(key, initialValue, callback = null) {
637
- if (!this.states[key]) {
638
- this.states[key] = initialValue;
639
- }
925
+ writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
640
926
 
641
- return [
642
- this.states[key],
643
- /**
644
- * @function setState
645
- * @description Allows you to set state
646
- * @param {*} value
647
- * @returns {void}
648
- */
649
- (value) => {
650
- this.states[key] = value;
651
- this.updateComponent();
652
- // @ts-ignore
653
- typeof callback === "function" ? callback() : null;
654
- }
655
- ];
656
- }
657
- /**
658
- * @method useRef
659
- * @memberof Component
660
- * @param {string} ref
661
- * @description Allows you to get reference to DOM elements from the dom array
662
- * @returns {Object} {current, update}
663
- * @example
664
- * let ref = this.useRef('ref')
665
- * ref.current // returns the DOM element
666
-
667
- */
927
+ console.log(`\x1b[32m%s\x1b[0m`, `Prerendered ${route.url}...`)
668
928
 
669
- useRef(ref) {
670
- // get ref from array
671
- const element = this.dom[ref];
929
+ hasRendered.push(route.url)
930
+ })
672
931
 
673
- const getElement = () => element;
674
932
 
675
- const update = (data) => {
676
- const newDom = new DOMParser().parseFromString(data, "text/html");
677
- const newElement = newDom.body.firstChild;
678
933
 
679
- if (element) {
680
- // @ts-ignore
681
- const isDifferent = !newElement.isEqualNode(element);
682
- if (isDifferent) {
683
- // @ts-ignore
684
- element.parentNode.replaceChild(newElement, element);
685
- }
934
+
935
+ } catch (error) {
936
+ console.log('\x1b[31m%s\x1b[0m', 'Error:', error)
937
+
686
938
  }
687
- };
939
+ finally {
940
+ }
941
+ })
688
942
 
689
- return {
690
- /**@type {HTMLElement} */
691
- // @ts-ignore
692
- current: getElement,
693
- /**@type {Function} */
694
- update
695
- };
696
- }
697
943
 
698
- /**
699
- *
700
- * @function useEffect
701
- * @param {*} effectFn
702
- * @param {*} dependencies
703
- * @description Allows you to run side effects
704
- * @deprecated - this is no longer suggested please use vader signals instead
705
- * @returns {Object} {cleanup}
706
- */
707
- useEffect(effectFn, dependencies) {
708
- if (!this.effects[this.name]) {
709
- this.effects[this.name] = [];
944
+
945
+
946
+
947
+ function kill() {
948
+ console.log(`\x1b[32m%s\x1b[0m`, `\nPrerendered ${routes.length} pages...\n`)
949
+ server.close()
950
+ browser.then((browser) => {
951
+ browser.close()
952
+ })
953
+ hasRendered = []
954
+ globalThis.isBuilding = false
955
+ globalThis.isProduction ? console.log(`Total Bundle Size: ${Math.round(bundleSize / 1000)}kb`) : null
956
+ bundleSize = 0
710
957
  }
711
- this.effects[this.name].push(effectFn);
712
958
 
713
- if (dependencies.length > 0) {
714
- dependencies.forEach((d) => {
715
- if (d.set) {
716
- throw new Error(
717
- "signal found, do not use effect and signals at the same time - signals are more efficient"
718
- );
959
+ if (hasRendered.length === routes.length) {
960
+ kill()
961
+ } else {
962
+ console.log(`\x1b[32m%s\x1b[0m`, `Prerendering ${routes.length} pages...\n`)
963
+ let interval = setInterval(() => {
964
+ if (hasRendered.length === routes.length) {
965
+ kill()
966
+ clearInterval(interval)
719
967
  }
720
- });
721
- } else if (!this.hasMounted) {
722
- effectFn();
723
- this.hasMounted = true;
968
+ }, 1000);
724
969
  }
725
-
726
- return {
727
- cleanup: () => {
728
- this.effects[this.name] = this.effects[this.name].filter(
729
- (effect) => effect !== effectFn
730
- );
731
- }
732
- };
733
970
  }
734
- /**
735
- * @method $Function
736
- * @description Allows you to create a function in global scope
737
- * @returns {Function}
738
- * @example
739
- * let func = this.$Function(function add(e, a){
740
- * return e + a
741
- * })
742
- * @param {*} fn
743
- */
744
- $Function(fn) {
745
- // @ts-ignore
746
- if (!typeof fn === "function") {
747
- throw new Error("fn must be a function");
971
+ globalThis.routes = []
972
+
973
+ for await (let file of glb) {
974
+ // Normalize file paths
975
+ let origin = file.replace(/\\/g, '/');
976
+ let fileName = origin.split('/pages/')[1].split('.jsx')[0].replace('.jsx', '') + '.jsx';
977
+ let isBasePath = fileName === 'index.jsx';
978
+ let isParamRoute = fileName.includes('[') && fileName.includes(']') ? true : false
979
+
980
+ // Extract all dynamic parameters from the file path [param1]/[param2]/[param3
981
+ let aburl = origin.split('/pages')[1].split('.jsx')[0].replace('.jsx', '').split('[').join(':').split(']').join('');
982
+
983
+ if (aburl.includes('...')) {
984
+ // this is a catch all route
985
+ // it should be /pages/[...]/index.jsx or /pages/[...].jsx
986
+ aburl = aburl.split('...').join('*').split(':*').join('*')
987
+ aburl = aburl.replaceAll('./index', '')
988
+
748
989
  }
749
- let name = fn.name;
750
- if (!name) {
751
- name = "anonymous" + Math.floor(Math.random() * 100000000000000000);
990
+ // Create an object with URL and pathname properties
991
+ let obj = {
992
+ url: isBasePath ? '/' : aburl.replaceAll('/index', ''),
993
+ pathname: `/pages/${origin.split('pages/')[1].split('.jsx')[0].replace('.jsx', '')}.jsx`,
994
+ fullpath: origin,
995
+ };
996
+
997
+
998
+
999
+ let data = await fs.readFileSync(origin, "utf8");
1000
+
1001
+ // gen sourcemap if not production
1002
+ let size = fs.statSync(origin).size;
1003
+ if (!globalThis.isProduction) {
1004
+ let { sourceMap } = sourceMapGen({ origin: origin, fileName: fileName }, await Compiler(data, origin))
1005
+ data = data + `\n//# sourceMappingURL=/src/maps/${fileName.replace('.jsx', '.js.map')}\n //#sourceURL=${origin}`
1006
+ await writer(process.cwd() + "/dist/src/maps/" + fileName.replace('.jsx', '.js.map'), JSON.stringify(sourceMap, null, 2))
752
1007
  }
753
- window[name] = fn;
754
- // @ts-ignore
755
- return `window.${name}()`;
756
- }
1008
+ await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), await Compiler(data, origin))
757
1009
 
758
- // Add other methods like render, useEffect, useReducer, useAuth, etc.
1010
+ // configure routing for each page
759
1011
 
760
- updateComponent() {
761
- const fragment = document.createDocumentFragment();
762
- Object.keys(components).forEach(async (component) => {
763
- const { name } = components[component];
1012
+ obj.compiledPath = process.cwd() + "/dist/pages/" + fileName.replace('.jsx', '.js')
1013
+ let providerRedirects = { cloudflare: '_redirects', vercel: 'vercel.json', netlify: '_redirects' }
1014
+ switch (true) {
1015
+ case config && config.host && !config.host['_redirect']:
1016
+ let host = config.host.provider
764
1017
 
765
- let componentContainer = document.querySelector(
766
- `[data-component="${name}"]`
767
- );
768
- let time = new Date().getTime();
769
- /**
770
- * @property {Object} snapshot
771
- * @description Allows you to keep track of component snapshots
772
- * @private
773
- * @returns {Object} {name, time, prev_state, prev_props, content}
774
- */
775
- let snapshot = {
776
- name: name,
777
- time: time,
778
- prev_state: this.states,
779
- prev_props: this.storedProps,
780
- // @ts-ignore
781
- content: componentContainer.innerHTML
782
- };
783
-
784
- if (!componentContainer) return;
785
- const newHtml = await new Function(
786
- "useState",
787
- "useEffect",
788
- "useAuth",
789
- "useReducer",
790
- "useSyncStore",
791
- "signal",
792
- "rf",
793
- "props",
794
- "render",
795
- "return `" + (await this.render()) + "`;"
796
- )(
797
- this.useState,
798
- this.useEffect,
799
- this.useAuth,
800
- this.useReducer,
801
- this.useSyncStore,
802
- this.signal,
803
- this.render
804
- );
805
-
806
- if (newHtml !== componentContainer.innerHTML) {
807
- if (this.snapshots.length > 0) {
808
- let lastSnapshot = this.snapshots[this.snapshots.length - 1];
809
- if (lastSnapshot !== snapshot) {
810
- this.snapshots.push(snapshot);
811
- }
812
- } else {
813
- this.snapshots.push(snapshot);
814
- }
815
- this.componentUpdate(
816
- snapshot.prev_state,
817
- snapshot.prev_props,
818
- snapshot.content
819
- );
820
- // batch updates
821
- fragment.appendChild(
822
- document.createRange().createContextualFragment(newHtml)
823
- );
824
- componentContainer.innerHTML = "";
825
- componentContainer.appendChild(fragment);
826
- this.runEffects();
827
- }
828
- });
829
- }
830
- /**
831
- * @method validateClassName
832
- * @param {String} className
833
- * @private
834
- * @returns {Boolean}
835
- */
836
- validateClassName(className) {
837
- // validate classNames ensure they are camelCase but also allow for - and _
838
- return /^[a-zA-Z0-9-_]+$/.test(className);
839
- }
1018
+ let provider = providerRedirects[host]
1019
+ if (provider) {
840
1020
 
841
- parseHTML(result) {
842
- const dom = new DOMParser().parseFromString(result, "text/html");
843
- const elements = dom.documentElement.querySelectorAll("*");
844
-
845
- elements.forEach((element) => {
846
- switch (element.nodeName) {
847
- case "IMG":
848
- if (
849
- !element.hasAttribute("alt") &&
850
- !document.documentElement.outerHTML
851
- .trim()
852
- .includes("<!-- #vader-disable_accessibility -->")
853
- ) {
854
- throw new SyntaxError(
855
- `Image: ${element.outerHTML} missing alt attribute`
856
- );
857
- } else if (
858
- element.hasAttribute("alt") &&
859
- // @ts-ignore
860
- element.getAttribute("alt").length < 1 &&
861
- !document.documentElement.outerHTML
862
- .trim()
863
- .includes("<!-- #vader-disable_accessibility -->")
864
- ) {
865
- throw new SyntaxError(
866
- `Image: ${element.outerHTML} alt attribute cannot be empty`
867
- );
868
- } else if (
869
- (element.hasAttribute("src") &&
870
- !element.getAttribute("src")?.includes("http")) ||
871
- (!element.getAttribute("src")?.includes("https") &&
872
- !document.documentElement.outerHTML
873
- .trim()
874
- .includes("<!-- #vader-disable_accessibility -->"))
875
- ) {
876
- let prevurl = element.getAttribute("src");
877
- element.setAttribute("aria-hidden", "true");
878
- element.setAttribute("hidden", "true");
879
- // if window.lcoation.pathname includes a html file remove it and only use the path
880
- let url =
881
- window.location.origin +
882
- window.location.pathname.replace(/\/[^\/]*$/, "") +
883
- "/public/" +
884
- element.getAttribute("src");
885
- let image = new Image();
886
- image.src = url;
887
- image.onerror = () => {
888
- // @ts-ignore
889
- element.setAttribute("src", prevurl);
890
- throw new Error(`Image: ${element.outerHTML} not found`);
891
- };
892
- element.setAttribute("src", url);
893
-
894
- image.onload = () => {
895
- document.querySelectorAll(`img[src="${url}"]`).forEach((img) => {
896
- img.setAttribute("src", url);
897
- img.removeAttribute("aria-hidden");
898
- img.removeAttribute("hidden");
899
- });
900
- };
1021
+ let redirectFile = null
1022
+ switch (true) {
1023
+ case provider === '_redirects':
1024
+ redirectFile = fs.existsSync(process.cwd() + '/dist/' + provider) ? fs.readFileSync(process.cwd() + '/dist/' + provider, 'utf8') : ''
1025
+ break;
1026
+ case provider === 'vercel.json':
1027
+ redirectFile = fs.existsSync(process.cwd() + '/' + provider) ? fs.readFileSync(process.cwd() + '/' + provider, 'utf8') : ''
1028
+ break;
1029
+ default:
1030
+ break;
901
1031
  }
902
- break;
1032
+ let type = provider === '_redirects' ? 'text/plain' : 'application/json'
903
1033
 
904
- default:
905
- if (element.hasAttribute("ref")) {
906
- // @ts-ignore
907
- dom[element.getAttribute("ref")] = element;
908
- }
909
- if (element.nodeName === "MARKDOWN") {
910
- element.innerHTML = markdown(
911
- element.innerHTML.replace(/\\n/g, "\n").trim()
912
- );
913
- }
1034
+ let root = obj.url.includes(':') ? obj.url.split('/:')[0] : obj.url
1035
+ switch (true) {
1036
+ case root === '/':
1037
+ break;
1038
+ case type === 'text/plain' && !redirectFile.includes(obj.url) && obj.url.includes(':'):
1039
+ let page = obj.pathname.split('/pages/')[1].replace('.jsx', '.js')
1040
+ redirectFile += `\n/${page} /${page} 200\n${obj.url} ${root} 200\n`
1041
+ !redirectFile.includes('/404') ? redirectFile += `\n/404 /404 404` : null
1042
+ fs.writeFileSync(process.cwd() + '/dist/' + provider, redirectFile)
1043
+ console.log(`Added ${obj.url} ${obj.url} 200 to ${provider}`)
1044
+ break;
1045
+ case type === 'application/json' && !redirectFile?.includes(`${obj.url}`):
1046
+ let json = redirectFile ? JSON.parse(redirectFile) : {}
1047
+ let isVercel = provider === 'vercel.json' ? true : false
1048
+ if (isVercel) {
1049
+ json['rewrites'] = json['rewrites'] || []
1050
+ json['rewrites'].push({ "source": obj.url, "destination": `${root}/index.html` })
1051
+ fs.writeFileSync(process.cwd() + '/' + provider, JSON.stringify(json, null, 2))
1052
+ console.log(`Added ${obj.url} ${root}/index.html to ${provider}`)
1053
+ }
914
1054
 
915
- if (element.hasAttribute("class")) {
916
- const allowClassComments = document.documentElement.outerHTML.includes(
917
- "<!-- #vader-allow_class -->"
918
- );
919
- if (!allowClassComments) {
920
- console.warn(
921
- "you can disable class errors using, <!-- #vader-allow_class -->"
922
- );
923
- throw new Error(
924
- "class attribute is not allowed, please use className instead"
925
- );
926
- }
927
- } else if (element.hasAttribute("className")) {
928
- // @ts-ignore
929
- element.setAttribute("class", element.getAttribute("className"));
930
- element.removeAttribute("className");
931
1055
  }
1056
+ }
1057
+ break;
1058
+ case config && config.host && config.host['_redirect']:
1059
+ let file = config.host['_redirect']
1060
+ file = file.split('./').join('')
1061
+ let redirectFile = fs.existsSync(process.cwd() + '/' + file) ? fs.readFileSync(process.cwd() + '/' + file, 'utf8') : ''
1062
+ fs.writeFileSync(process.cwd() + '/dist/' + file, redirectFile)
1063
+ console.log(`Using ${file} for redirects`)
1064
+ default:
1065
+ break;
932
1066
 
933
- if (
934
- element.hasAttribute("href") &&
935
- // @ts-ignore
936
- element.getAttribute("href").startsWith("/") &&
937
- !document.documentElement.outerHTML
938
- .trim()
939
- .includes("<!-- #vader-disable_relative-paths -->")
940
- ) {
941
- element.setAttribute(
942
- "href",
943
- // @ts-ignore
944
- `#/${element.getAttribute("href").replace("/", "")}`
945
- );
946
- }
1067
+ }
947
1068
 
948
- if (
949
- element.hasAttribute("src") &&
950
- // @ts-ignore
951
- !element.getAttribute("src").includes("http") &&
952
- // @ts-ignore
953
- !element.getAttribute("src").includes("https") &&
954
- !document.documentElement.outerHTML.includes(
955
- `<!-- #vader-disable_relative-paths -->`
956
- )
957
- ) {
958
- element.setAttribute(
959
- "src",
960
- // @ts-ignore
961
- `./public/${element.getAttribute("src")}`
962
- );
963
- }
964
- break;
965
- }
966
- });
967
1069
 
968
- result = dom.body.innerHTML;
1070
+ if (!obj.url.includes(':')) {
1071
+ globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
1072
+ }
1073
+
969
1074
 
970
- this.Componentcontent = result;
1075
+ let stats = {
1076
+ route: obj.url.padEnd(30),
1077
+ size: Math.round(size / 1000) + 'kb',
1078
+ letParentFolder: obj.url.split('/').slice(0, -1).join('/'),
1079
+ isChildRoute: obj.url.split('/').slice(0, -1).join('/').includes(':') ? true : false,
1080
+ parentRoute: obj.url.split('/').slice(0, -1).join('/').split(':')[0],
971
1081
 
972
- if (!result.includes("<div data-component")) {
973
- result = `<div data-component="${this.name}">${result}</div>`;
974
1082
  }
975
- return markdown(result.replace(/\\n/g, "\n").trim());
1083
+ stats.isChildRoute ? stats.route = `? ${obj.url}` : null
1084
+ let string = `${isBasePath ? '+' : '+'} ${stats.route.padEnd(30)} ${stats.size}`
1085
+
1086
+ globalThis.isProduction ? console.log(string) : null
976
1087
  }
977
1088
 
978
- /**
979
- * The `html` method generates and processes HTML content for a component, performing various validations and tasks.
980
- *
981
- * @param {String} strings - The HTML content to be processed.
982
- * @param {...any} args - Dynamic values to be inserted into the template.
983
- * @returns {string} - The processed HTML content as a string.
984
- *
985
- * @throws {SyntaxError} - Throws a `SyntaxError` if image-related attributes are missing or invalid.
986
- * @throws {Error} - Throws an `Error` if there are issues with class names or relative paths.
987
- *
988
- * @example
989
- * // Example usage within a component:
990
- * const myComponent = new Component();
991
- * const htmlContent = myComponent.html`
992
- * <div>
993
- * <img src="/images/example.jpg" alt="Example Image" />
994
- * </div>
995
- * `;
996
- * document.body.innerHTML = htmlContent;
997
- *
998
- * @remarks
999
- * 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:
1000
- *
1001
- * 1. **Image Validation**: It checks images for the presence of 'alt' attributes and their validity.
1002
- * - Throws a `SyntaxError` if an image is missing the 'alt' attribute.
1003
- * - Throws a `SyntaxError` if the 'alt' attribute is empty.
1004
- * - Checks for an 'aria-hidden' attribute for image elements.
1005
- *
1006
- * 2. **Class Attribute Handling**: It enforces class attribute usage and allows optional configuration via comments.
1007
- * - Throws an `Error` if 'class' attributes are used without permission.
1008
- * - Supports 'className' attributes for class definitions.
1009
- * - Allows or disallows class-related comments based on your configuration.
1010
- *
1011
- * 3. **Relative Path Handling**: It processes relative paths in 'href' and 'src' attributes, ensuring proper routing.
1012
- * - Converts relative 'href' attributes to anchor links with appropriate routing.
1013
- * - Converts relative 'src' attributes to absolute paths with 'public' directories.
1014
- *
1015
- * 4. **Custom Component Attributes**: It supports adding a 'data-component' attribute to the root element.
1016
- * - Ensures that the 'data-component' attribute is present for component identification.
1017
- *
1018
- * 5. **Lifecycle Method Invocation**: It invokes the `componentDidMount` method if called from a 'render' context.
1019
- * - Executes `componentDidMount` to handle component initialization once the DOM is ready.
1020
- *
1021
- * @see {@link Component}
1022
- * @see {@link Component#componentDidMount}
1023
- */
1089
+ globalThis.routeDocuments = []
1090
+ globalThis.routes.map((route) => {
1091
+ let equalparamroute = globalThis.routes.map((e) => {
1092
+ if (e.url.includes(':')) {
1093
+ let url = e.url.split('/:')[0]
1094
+ if (url && route.url === url) {
1095
+ return e
1096
+ } else {
1097
+ return null
1024
1098
 
1025
- html(strings, ...args) {
1026
- // @ts-ignore
1027
- let tiemr = setInterval(() => {
1028
- if (document.querySelector(`[data-component="${this.name}"]`)) {
1029
- clearInterval(tiemr);
1030
- this.componentMounted = true;
1031
-
1032
- document
1033
- .querySelector(`[data-component="${this.name}"]`)
1034
- ?.querySelectorAll("*")
1035
- .forEach((element) => {
1036
- if (element.hasAttribute("ref")) {
1037
- // @ts-ignore
1038
- this.dom[element.getAttribute("ref")] = element;
1039
- }
1040
- });
1041
- this.componentDidMount();
1099
+ }
1042
1100
  }
1043
- }, 100);
1044
- let script = document.createElement("script");
1045
- script.setAttribute("type", "text/javascript");
1046
- script.setAttribute(`data-component-script`, this.name);
1047
-
1048
- let dom = this.dom;
1049
-
1050
- if (this.cfr) {
1051
- worker.postMessage({
1052
- strings,
1053
- args,
1054
- location:
1055
- window.location.origin +
1056
- window.location.pathname.replace(/\/[^\/]*$/, "") +
1057
- "/public/",
1058
- name: this.name
1059
- });
1060
- let promise = new Promise((resolve, reject) => {
1061
- worker.onmessage = (e) => {
1062
- if (e.data.error) {
1063
- throw new Error(e.data.error);
1101
+ return null
1102
+ }).filter(Boolean)
1103
+ let document = `
1104
+ <!DOCTYPE html>
1105
+ <html lang="en">
1106
+ <head>
1107
+ <script>
1108
+ window.routes = JSON.parse('${JSON.stringify(globalThis.routes)}')
1109
+ </script>
1110
+ <script type="module" id="meta">
1111
+ window.history.pushState({}, '', '${route.url}')
1112
+
1113
+ </script>
1114
+ <script id="isServer">
1115
+ window.isServer = true
1116
+ </script>
1117
+ <meta charset="UTF-8">
1118
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
1119
+
1120
+ <script type="module" id="router">
1121
+ import VaderRouter from '/router.js'
1122
+ const router = new VaderRouter('${route.url}')
1123
+ router.get('${route.url}', async (req, res) => {
1124
+ try{
1125
+ let module = await import('/${route.fileName.replace('.jsx', '.js')}')
1126
+ if(Object.keys(module).includes('$prerender') && !module.$prerender){
1127
+ document.head.setAttribute('prerender', 'false')
1064
1128
  }
1065
- const dom = this.dom; // Assuming this.dom is an object
1066
- let js = e.data.js;
1067
- let template = e.data.template;
1068
- // Bind the component's context
1069
-
1070
- const useState = this.useState.bind(this); // Bind the component's context
1071
- const useEffect = this.useEffect.bind(this); // Bind the component's context
1072
- const useReducer = this.useReducer.bind(this); // Bind the component's context
1073
- const useAuth = this.useAuth.bind(this); // Bind the component's context
1074
- const useSyncStore = this.useSyncStore.bind(this); // Bind the component's context
1075
- const signal = this.signal.bind(this); // Bind the component's context
1076
- const rf = this.$Function.bind(this); // Bind the component's context
1077
- let states = this.states;
1078
- const useRef = this.useRef.bind(this); // Bind the component's context
1079
- new Function(
1080
- "useState",
1081
- "useEffect",
1082
- "useAuth",
1083
- "useReducer",
1084
- "useSyncStore",
1085
- "signal",
1086
- "rf",
1087
- "dom",
1088
- "render",
1089
- "states",
1090
- "useRef",
1091
- js
1092
- )(
1093
- useState,
1094
- useEffect,
1095
- useAuth,
1096
- useReducer,
1097
- useSyncStore,
1098
- signal,
1099
- rf,
1100
- this.dom,
1101
- this.render,
1102
- this.states,
1103
- useRef
1104
- );
1129
+ res.render(module, req, res, module.$metadata)
1130
+ }
1131
+ catch(error){
1132
+ let errorMessage = {
1133
+ message: error.message,
1134
+ name: error.name,
1135
+ stack: error.stack,
1136
+ path: window.location.pathname
1137
+ };
1138
+
1139
+
1140
+ document.documentElement.setAttribute('error', JSON.stringify(errorMessage));
1141
+ throw new Error(error)
1142
+ }
1143
+ })
1144
+ ${equalparamroute.length > 0 ? equalparamroute.map((e) => {
1145
+
1146
+
1147
+
1148
+ return `router.get('${e.url}', async (req, res) => {
1149
+ let module = await import('/${e.fileName.replace('.jsx', '.js')}')
1150
+ res.render(module, req, res, module.$metadata)
1151
+ })\n`
1152
+ }) : ''}
1153
+ router.listen(3000)
1154
+
1155
+ </script>
1156
+ </head>
1157
+ <body>
1158
+ <div id="root"></div>
1159
+ </body>
1160
+
1161
+
1162
+ </html>
1163
+ `;
1164
+ globalThis.routeDocuments.push({ url: route.url, document: document })
1165
+ })
1166
+
1167
+ if(globalThis.devMode && !globalThis.oneAndDone){
1168
+ ssg(globalThis.routes)
1169
+ globalThis.oneAndDone = true
1170
+ console.log('Prerendering...')
1171
+ }
1105
1172
 
1106
- resolve(
1107
- new Function("useRef", "states", "return" + "`" + template + "`")(
1108
- useRef,
1109
- states
1110
- )
1111
- );
1112
- };
1113
- worker.onerror = (e) => {
1114
- reject(e);
1115
- };
1116
- });
1117
- // @ts-ignore
1118
- return promise;
1119
- } else {
1120
- let result = "";
1121
- for (let i = 0; i < strings.length; i++) {
1122
- result += strings[i];
1123
- if (i < args.length) {
1124
- result += args[i];
1125
- }
1173
+
1174
+ const scannedSourceFiles = await glob("**/**.{jsx,js,json}", {
1175
+ ignore: ["node_modules/**/*", "dist/**/*"],
1176
+ cwd: process.cwd() + '/src/',
1177
+ absolute: true,
1178
+ });
1179
+ const scannedVaderFiles = await glob("**/**.{html,js,json}", {
1180
+ cwd: process.cwd() + '/node_modules/vaderjs/runtime',
1181
+ absolute: true,
1182
+ });
1183
+
1184
+ scannedVaderFiles.forEach(async (file) => {
1185
+ file = file.replace(/\\/g, '/');
1186
+
1187
+
1188
+ let name = file.split('/node_modules/vaderjs/runtime/')[1]
1189
+ if (file.includes('index.html') && fs.existsSync(process.cwd() + "/dist/" + name)) {
1190
+ return
1191
+ }
1192
+ let data = await reader(file)
1193
+ bundleSize += fs.statSync(file).size;
1194
+ await writer(process.cwd() + "/dist/" + name, data);
1195
+ })
1196
+ scannedSourceFiles.forEach(async (file) => {
1197
+ file = file.replace(/\\/g, '/');
1198
+ let name = file.split('/src/')[1]
1199
+
1200
+ let data = await reader(process.cwd() + "/src/" + name)
1201
+ if (name.includes('.jsx')) {
1202
+ let origin = process.cwd() + "/src/" + name
1203
+ if (!globalThis.isProduction) {
1204
+ let { sourceMap } = sourceMapGen({ origin: origin, fileName: name }, await Compiler(data, origin))
1205
+ data = data + `\n//# sourceMappingURL=/src/maps/${name.replace('.jsx', '.js.map')}\n //#sourceURL=${origin}`
1206
+ await writer(process.cwd() + "/dist/src/maps/" + name.replace('.jsx', '.js.map'), JSON.stringify(sourceMap, null, 2))
1126
1207
  }
1127
- result = new Function("useRef", `return \`${result}\``)(useRef);
1208
+ await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), await Compiler(data, origin))
1209
+ return
1210
+ }
1211
+ if (!name.includes('.map')) {
1212
+ bundleSize += fs.statSync(process.cwd() + "/src/" + name).size;
1213
+ }
1214
+ await writer(process.cwd() + "/dist/src/" + name, data);
1215
+ })
1216
+
1217
+ const scannedPublicFiles = await glob("**/**/**.{css,js,html,mjs,cjs,png,jpg,jpeg,gif,svg,mp4,webm,ogg}", {
1218
+ ignore: ["node_modules/**/*", "dist/**/*"],
1219
+ cwd: process.cwd() + '/public/',
1220
+ absolute: true,
1221
+ });
1222
+ scannedPublicFiles.forEach(async (file) => {
1223
+ file = file.replace(/\\/g, '/');
1224
+ file = file.split('/public/')[1]
1225
+ let data = fs.readFileSync(process.cwd() + "/public/" + file);
1226
+ bundleSize += fs.statSync(process.cwd() + "/public/" + file).size;
1227
+ await writer(process.cwd() + "/dist/public/" + file, data);
1228
+ })
1229
+ const scannedFiles = await glob("**/**.{css,js,html}", {
1230
+ ignore: ["node_modules/**/*", "dist/**/*"],
1231
+ cwd: process.cwd() + "/runtime/",
1232
+ absolute: true,
1233
+ })
1234
+
1235
+
1236
+ if (!fs.existsSync(process.cwd() + "/dist/index.html")) {
1237
+
1238
+ scannedFiles.forEach(async (file) => {
1239
+ file = file.split(process.cwd() + '/runtime/')[1]
1128
1240
 
1129
- if (!result.trim().startsWith("<body>")) {
1130
- console.warn(
1131
- "You should wrap your html in a body tag, vader may not grab all html!"
1132
- );
1241
+ let objCase = {
1242
+ ...file == "app.js" ? { exit: true } : null,
1243
+ ...file.includes("index.html") && fs.existsSync(process.cwd() + "/dist/" + file) ? { exit: true } : null,
1244
+
1245
+ }
1246
+ if (objCase.exit) {
1247
+ console.log('exiting')
1248
+ return true
1133
1249
  }
1250
+ bundleSize += fs.statSync(process.cwd() + "/node_modules/vaderjs/runtime/" + file).size;
1251
+ let data = await reader(process.cwd() + "/node_modules/vaderjs/runtime/" + file)
1252
+ await writer(process.cwd() + "/dist/" + file, data);
1253
+ });
1134
1254
 
1135
- return this.parseHTML(result);
1136
- }
1137
1255
  }
1138
- // write types to ensure it returns a string
1139
- /**
1140
- * @method render
1141
- * @description Allows you to render html
1142
- * @returns {Promise <any>}
1143
- * @example
1144
- * async render() {
1145
- * return this.html(`
1146
- * <div className="hero p-5">
1147
- * <h1>Home</h1>
1148
- * </div>
1149
- * `);
1150
- */
1151
- async render(props) {}
1256
+
1257
+ globalThis.isBuilding = false
1258
+
1259
+ bundleSize = 0;
1260
+
1261
+ return true
1152
1262
  }
1263
+ const s = (port) => {
1153
1264
 
1154
- /**
1155
- * @object Vader
1156
- * @property {class} Component
1157
- * @property {function} useRef
1158
- * @description Allows you to create a component
1159
- * @example
1160
- * import { Vader } from "../../dist/vader/vader.js";
1161
- * export class Home extends Vader.Component {
1162
- * constructor() {
1163
- * super('Home');
1164
- * }
1165
- * async render() {
1166
- * return this.html(`
1167
- * <div className="hero p-5">
1168
- * <h1>Home</h1>
1169
- * </div>
1170
- * `);
1171
- * }
1172
- */
1173
- const Vader = {
1174
- /**
1175
- * @class Component
1176
- * @description Allows you to create a component
1177
- * @returns {void}
1178
- * @memberof {Vader}
1179
- * @example
1180
- * import { Vader } from "../../dist/vader/index.js";
1181
- * export class Home extends Vader.Component {
1182
- * constructor() {
1183
- * super();
1184
- * }
1185
- * async render() {
1186
- * return this.html(`
1187
- * <div className="hero p-5">
1188
- * <h1>Home</h1>
1189
- * </div>
1190
- * `);
1191
- * }
1192
- * }
1193
- */
1194
- Component: Component,
1195
- useRef: useRef
1196
- };
1197
- /**
1198
- * @function component
1199
- * @description Allows you to create a component
1200
- * @returns {Component}
1201
- */
1202
- export const component = () => {
1203
- return new Component()
1204
- };
1265
+ const server = http.createServer((req, res) => {
1205
1266
 
1206
- /**
1207
- * @function rf
1208
- * @param {*} name
1209
- * @param {*} fn
1210
- * @returns {void}
1211
- * @deprecated - rf has been replaced with Vader.Component.$Function
1212
- * @description Allows you to register function in global scope
1213
- */
1214
- export const rf = (name, fn) => {
1215
- window[name] = fn;
1216
- };
1217
- let cache = {};
1218
- async function handletemplate(data) {
1219
- let dom = new DOMParser().parseFromString(data, "text/html");
1220
- let elements = dom.documentElement.querySelectorAll("*");
1221
-
1222
- if (elements.length > 0) {
1223
- for (var i = 0; i < elements.length; i++) {
1224
- if (elements[i].nodeName === "INCLUDE") {
1225
- if (
1226
- !elements[i].getAttribute("src") ||
1227
- elements[i].getAttribute("src") === ""
1228
- ) {
1229
- throw new Error("Include tag must have src attribute");
1230
- }
1267
+ const validExtensions = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg', '.map']
1231
1268
 
1232
- let componentName = elements[i]
1233
- .getAttribute("src")
1234
- ?.split("/")
1235
- .pop()
1236
- ?.split(".")[0];
1237
- // @ts-ignore
1238
- let filedata = await include(elements[i].getAttribute("src"));
1239
- // replace ` with \`\` to allow for template literals
1240
- filedata = filedata.replace(/`/g, "\\`");
1241
- cache[elements[i].getAttribute("src")] = filedata;
1242
- filedata = new Function(`return \`${filedata}\`;`)();
1243
- let newdom = new DOMParser().parseFromString(filedata, "text/html");
1269
+ if (!validExtensions.some(ext => req.url.endsWith(ext))) {
1270
+ req.url = req.url !== '/' ? req.url.split('/')[1] : req.url;
1271
+ req.url = path.join(process.cwd(), 'dist', req.url, 'index.html');
1272
+ } else {
1273
+ req.url = path.join(process.cwd(), 'dist', req.url);
1274
+ }
1244
1275
 
1245
-
1246
- newdom.querySelectorAll("include").forEach((el) => {
1247
- el.remove();
1248
- });
1249
- // @ts-ignore
1250
-
1251
- let els = dom.querySelectorAll(componentName);
1252
-
1253
- els.forEach((el) => {
1254
- if (el.attributes.length > 0) {
1255
- for (var i = 0; i < el.attributes.length; i++) {
1256
- // @ts-ignore
1257
- newdom.body.outerHTML = newdom.body.outerHTML.replaceAll(
1258
- `{{${el.attributes[i].name}}}`,
1259
- el.attributes[i].value
1260
- );
1261
- }
1262
- }
1263
- if (el.children.length > 0 && newdom.body.querySelector("slot")) {
1264
- for (var i = 0; i < el.children.length; i++) {
1265
- let slots = newdom.body.querySelectorAll("slot");
1266
- slots.forEach((slot) => {
1267
-
1268
- let id = slot.getAttribute("id");
1269
- console.log(id)
1270
- if(el.hasAttribute('key') && el.getAttribute('key') === id || !el.hasAttribute('key') && el.nodeName === id){
1271
-
1272
- slot.outerHTML = `<div>${el.innerHTML}</div>`
1273
- }
1274
- });
1275
- }
1276
- }
1276
+ const filePath = req.url
1277
1277
 
1278
- dom.body.querySelectorAll("include").forEach((el) => {
1279
- el.remove();
1280
- });
1281
- // replace ` with \`\` to allow for template literals
1282
- dom.body.outerHTML = dom.body.outerHTML.replace(/`/g, "\\`");
1283
- dom.body.outerHTML = dom.body.outerHTML.replace(
1284
- el.outerHTML,
1285
- new Function(`return \`${newdom.body.outerHTML}\`;`)()
1286
- );
1287
- });
1278
+ fs.readFile(filePath, (err, data) => {
1279
+ if (err) {
1280
+ res.writeHead(404, { 'Content-Type': 'text/html' });
1281
+ res.end(fs.existsSync(process.cwd() + '/dist/404') ? fs.readFileSync(process.cwd() + '/dist/404/index.html') : '404');
1282
+ } else {
1283
+ const contentType = getContentType(filePath);
1284
+ switch (true) {
1285
+ case contentType === 'text/html' && globalThis.devMode:
1286
+ data = data.toString() + `<script type="module">
1287
+ let ws = new WebSocket('ws://localhost:${process.env.PORT || 3000}')
1288
+ ws.onmessage = (e) => {
1289
+ if(e.data === 'reload'){
1290
+ console.log('Reloading...')
1291
+ window.location.reload()
1292
+ }
1293
+ }
1294
+ </script>
1295
+ `
1296
+ }
1297
+ res.writeHead(200, { 'Content-Type': contentType });
1298
+ res.end(data);
1288
1299
  }
1300
+ });
1301
+ });
1302
+
1303
+
1304
+ const ws = new WebSocketServer({ server });
1305
+ ws.on('connection', (socket) => {
1306
+ console.log('WebSocket Hydration Client connected');
1307
+ socket.on('close', () => console.log('WebSocket Hydration Client disconnected'));
1308
+ });
1309
+
1310
+
1311
+ function getContentType(filePath) {
1312
+ let ext = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg', '.map'].includes(path.extname(filePath)) ? path.extname(filePath) : '.html'
1313
+ switch (ext) {
1314
+ case '.js':
1315
+ return 'text/javascript';
1316
+ case '.css':
1317
+ return 'text/css';
1318
+ case '.mjs':
1319
+ return 'text/javascript';
1320
+ case '.cjs':
1321
+ return 'text/javascript';
1322
+ case '.html':
1323
+ return 'text/html';
1324
+ case '.map':
1325
+ return 'application/json';
1326
+ case '.json':
1327
+ return 'application/json';
1328
+ case '.png':
1329
+ return 'image/png';
1330
+ case '.jpg':
1331
+ return 'image/jpg';
1332
+ case '.jpeg':
1333
+ return 'image/jpeg';
1334
+ case '.gif':
1335
+ return 'image/gif';
1336
+ case '.svg':
1337
+ return 'image/svg+xml';
1338
+ case '.mp4':
1339
+ return 'video/mp4';
1340
+ case '.webm':
1341
+ return 'video/webm';
1342
+ case '.ogg':
1343
+ return 'video/ogg';
1344
+ default:
1345
+ return 'application/octet-stream';
1289
1346
  }
1290
1347
  }
1348
+
1349
+ server.listen(port, () => {
1350
+ console.log(`Server is running on port ${port}`);
1351
+ globalThis.ws = ws
1352
+ });
1291
1353
 
1292
- // replace ` with \`\` to allow for template literals
1293
- dom.body.outerHTML = dom.body.outerHTML.replace(/`/g, "\\`");
1294
- data = new Function(`return \`${dom.body.outerHTML}\`;`)();
1295
1354
 
1296
- return data;
1297
1355
  }
1298
- /**
1299
- * @function include
1300
- * @description Allows you to include html file
1301
- * @returns {Promise} - modified string with html content
1302
- * @param {string} path
1303
- */
1304
-
1305
- export const include = async (path) => {
1306
- if (
1307
- path.startsWith("/") &&
1308
- !path.includes("/src/") &&
1309
- !document.documentElement.outerHTML
1310
- .trim()
1311
- .includes("<!-- #vader-disable_relative-paths -->")
1312
- ) {
1313
- path = "/src/" + path;
1314
- }
1315
- if (cache[path]) {
1316
- return await handletemplate(new Function(`return \`${cache[path]}\`;`)());
1317
- } else {
1318
- return fetch(`./${path}`)
1319
- .then((res) => {
1320
- if (res.status === 404) {
1321
- throw new Error(`No file found at ${path}`);
1322
- }
1323
- return res.text();
1324
- })
1325
- .then(async (data) => {
1326
- cache[path] = data;
1327
1356
 
1328
- data = await handletemplate(new Function(`return \`${data}\`;`)());
1329
1357
 
1330
- return data;
1331
- });
1332
- }
1333
- };
1358
+ switch (true) {
1359
+ case process.argv.includes('dev') && !process.argv.includes('build') && !process.argv.includes('start'):
1360
+
1361
+ globalThis.devMode = true
1362
+ globalThis.isProduction = false
1363
+
1364
+ let p = process.env.PORT || config.port || process.argv.includes('-p') ? process.argv[process.argv.indexOf('-p') + 1] : 3000
1365
+ globalThis.oneAndDone = false
1366
+ console.log(`
1367
+ Vader.js v${fs.readFileSync(process.cwd() + '/node_modules/vaderjs/package.json', 'utf8').split('"version": "')[1].split('"')[0]}
1368
+ - Watching for changes in ./pages
1369
+ - Watching for changes in ./src
1370
+ - Watching for changes in ./public
1371
+ - Serving on port ${p}
1372
+ `)
1373
+ !globalThis.isBuilding ? Build() : null
1374
+
1375
+
1376
+ Array.from(Array(3).keys()).forEach((i) => {
1377
+ let p = `${process.cwd()}${i == 0 ? '/pages/' : i == 1 ? '/src/' : '/public/'}`
1378
+ watch(p
1379
+ , { recursive: true }, (event, filename) => {
1380
+ if (event == 'change'
1381
+ && !globalThis.isBuilding
1382
+ ) {
1383
+ if (globalThis.ws && !globalThis.isWriting) {
1384
+ globalThis.ws.clients.forEach((client) => {
1385
+ console.log('Reloading...')
1386
+ client.send('reload')
1387
+ })
1388
+ }
1389
+
1390
+ console.log('\nRebuilding...')
1391
+ globalThis.isBuilding = true
1392
+ Build()
1393
+ }
1394
+ }).on('error', (err) => console.log(err))
1395
+ })
1396
+ s(p)
1397
+
1398
+ globalThis.listen = true;
1399
+
1400
+ break;
1401
+ case process.argv.includes('build') && !process.argv.includes('dev') && !process.argv.includes('start'):
1402
+ globalThis.devMode = false
1403
+ globalThis.isProduction = true
1404
+ globalThis.routeStates = []
1405
+ console.log(`
1406
+ Vader.js v1.3.3
1407
+ Building to ./dist
1408
+ `)
1409
+ if (fs.existsSync(process.cwd() + '/dist/src/maps')) {
1410
+ fs.rmSync(process.cwd() + '/dist/src/maps', { recursive: true })
1411
+ }
1412
+ Build()
1413
+
1414
+ break;
1415
+ case process.argv.includes('start') && !process.argv.includes('dev') && !process.argv.includes('build'):
1416
+ let port = process.env.PORT || config.port || process.argv.includes('-p') ? process.argv[process.argv.indexOf('-p') + 1] : 3000
1417
+ console.log(port)
1418
+ globalThis.devMode = false
1419
+ console.log(`
1420
+ Vader.js v1.3.3
1421
+ Serving ./dist on port ${port}
1422
+ url: http://localhost:${port}
1423
+ `)
1424
+ s(port)
1425
+ break;
1426
+ default:
1427
+ // add color
1428
+ console.log(`
1429
+ Vader.js is a reactive framework for building interactive applications for the web built ontop of bun.js!
1430
+
1431
+ Usage: vader <command>
1432
+
1433
+ Commands:
1434
+
1435
+ vaderjs dev -p <number> Start the development server
1436
+
1437
+ vaderjs build Build the project to ./dist
1438
+
1439
+ vaderjs start -p <number> Production Mode (default 3000 or process.env.PORT)
1440
+
1441
+ Learn more about vader: https://vader-js.pages.dev/
1442
+
1443
+ `)
1444
+ break;
1334
1445
 
1335
- export default Vader;
1446
+ }