vaderjs 1.3.2 → 1.3.3-5b5a772ebe58

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