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