vaderjs 1.3.3-7924566dd811 → 1.3.3-7b27417d42e1

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,11 +1,40 @@
1
1
  #!/usr/bin/env node
2
- import fs from "fs";
2
+ import fs, { fstatSync } from "fs";
3
3
  import { glob, globSync, globStream, globStreamSync, Glob, } from 'glob'
4
4
  import puppeteer from 'puppeteer';
5
5
  import http from 'http'
6
+ import { SourceMapGenerator } from 'source-map'
7
+
6
8
  import { WebSocketServer } from 'ws'
9
+ import prettier from 'prettier'
7
10
  import { watch } from "fs";
8
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++;
30
+ }
31
+
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
+
9
38
  let config = await import('file://' + process.cwd() + '/vader.config.js').then((e) => e.default || e)
10
39
  let writer = async (file, data) => {
11
40
  globalThis.isWriting = file
@@ -22,8 +51,8 @@ let writer = async (file, data) => {
22
51
  globalThis.isWriting = null
23
52
  return { _written: true };
24
53
  };
25
-
26
- let bundleSize = 0;
54
+
55
+ let bundleSize = 0;
27
56
 
28
57
  if (!fs.existsSync(process.cwd() + '/dist')) {
29
58
  fs.mkdirSync(process.cwd() + '/dist')
@@ -33,102 +62,47 @@ if (!fs.existsSync(process.cwd() + '/dist')) {
33
62
 
34
63
 
35
64
  if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cwd() + '/dist/index.html')) {
36
- let htmlFile = fs.readFileSync(process.cwd() + "/node_modules/vaderjs/runtime/index.html", 'utf8')
37
- fs.writeFileSync(process.cwd() + "/dist/index.html", htmlFile)
65
+ fs.writeFileSync(process.cwd() + "/dist/index.html", '')
38
66
  }
39
67
 
40
68
 
41
69
 
42
70
  function Compiler(func, file) {
43
- let string = func;
71
+ let string = func;
44
72
  let returns = []
45
73
  let comments = string.match(/\{\s*\/\*.*\*\/\s*}/gs)?.map((comment) => comment.trim());
46
74
 
47
-
48
-
49
-
50
75
 
51
- // get all Obj({}) and parse to JSON.stringify
52
76
 
53
- let objects = string.match(/Obj\({.*}\)/gs);
54
77
 
55
- objects && objects.forEach((obj) => {
56
- let key = obj.split("Obj")[1].split("(")[1].split(")")[0].trim();
57
- let newobj = obj.replaceAll(`Obj(${key})`, `${key}`);
58
- // let newobj = obj.replaceAll(`Obj(${key})`, `JSON.parse('${key}')`)
59
- string = string.replaceAll(obj, `this.handleObject('${newobj}')`);
60
- });
61
78
 
62
79
 
63
80
  let childs = [];
64
81
 
65
- const spreadAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*\$\s*=\s*{{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)}})/gs;
66
- let spreadMatch;
67
- while ((spreadMatch = spreadAttributeRegex.exec(string)) !== null) {
68
- let [, element, spread] = spreadMatch;
69
- let isJSXComponent = element.match(/[A-Z]/) ? true : false;
70
- if (isJSXComponent) {
71
- continue
72
-
73
- }
74
- let old = spread;
75
-
76
- // turn spread into attributes
77
- spread = spread.replace(/\s*$\s*=\s*/, "");
78
- spread = spread.replace(/{{/, "");
79
- spread = spread.replace(/}}/, "");
80
- spread = spread.replace(/\$\s*=\s*/, "");
81
-
82
- // turn : into =
83
-
84
- // do not split inner Objects ex: {color: 'red', background: {color: 'blue'}} -> {color: 'red', background: {color: 'blue'}}
85
- let splitByCommas = spread.split(/,(?![^{]*})/g);
86
- splitByCommas = splitByCommas.map((e) => e.trim())
87
- splitByCommas = splitByCommas.map((e) => {
88
- switch (true) {
89
- case e.includes('function') || e.includes('=>'):
90
- e = e.replace(/:(.*)/gs, '={$1}')
91
- break;
92
- case e.includes('style'):
93
- e = e.replace(/:(.*)/gs, '="${this.parseStyle($1)}"')
94
- break;
95
- case e.includes('[') && e.includes(']'):
96
- e = e.replace(/:(.*)/gs, '={$1.join(" ")}')
97
- break;
98
- default:
99
- }
100
-
101
- return e.trim()
102
- })
103
-
104
- let newSpread = `\t` + splitByCommas.join(' ') + `\t`
105
82
 
106
- string = string.replace(old, newSpread);
83
+ // or : value boolean variable etc
84
+ const spreadAttributeRegex = /\s*([a-zA-Z0-9_-]+)\s*(\$\s*=\s*\{\s*\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}\s*\})/gs;
107
85
 
108
- }
109
86
 
110
87
 
111
88
 
112
89
  function extractAttributes(code) {
113
- // Match elements with opening tags
90
+ // grab $={...} and ={...}
114
91
  const elementRegex = /<([a-zA-Z0-9_-]+)([^>]*)>/gs;
115
92
 
116
93
  // Match attributes in an opening tag, including those with ={}
117
94
  // Match attributes in an opening tag, including those with ={...}
118
95
  const attributeRegex =
119
- /\s*([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
120
-
121
- // <div $={{color: 'red'}}></div>
96
+ /\s*([a-zA-Z0-9_-]+)(\s*=\s*("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
122
97
 
123
98
 
124
99
 
125
- // only return elements with attribute {()=>{}} or if it also has parameters ex: onclick={(event)=>{console.log(event)}} also get muti line functions or onClick=()=>{}
126
- const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
100
+ const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)\s*(=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
127
101
 
128
102
  let attributesList = [];
129
103
 
130
104
  let spreadAttributes = [];
131
- let spreadMatch;
105
+
132
106
  /**
133
107
  * @search - handle spread for html elements
134
108
  * @keywords - spread, spread attributes, spread props, spread html attributes
@@ -141,19 +115,22 @@ function Compiler(func, file) {
141
115
  *
142
116
  */
143
117
  let functionAttributes = [];
118
+ let spreadFunctions = [];
144
119
  let functionMatch;
145
120
  while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
146
121
 
147
122
  let [, attributeName, attributeValue] = functionMatch;
148
123
  let attribute = {};
149
124
 
150
- if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")
125
+ if (attributeValue && attributeValue.includes("=>")
126
+ && !attributeValue.includes("this.bind")
127
+ || attributeValue && attributeValue.includes("function")
151
128
  && !spreadFunctions.includes(attributeValue)
152
129
  ) {
153
-
130
+
154
131
  let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('')
155
132
  let old = `${attributeName}${attributeValue}`
156
-
133
+
157
134
  let elementMatch = string.match(/<([a-zA-Z0-9_-]+)([^>]*)>/gs);
158
135
  let isJSXComponent = false;
159
136
  elementMatch.forEach((element) => {
@@ -166,11 +143,15 @@ function Compiler(func, file) {
166
143
  isJSXComponent = elementTag.match(/^[A-Z]/) ? true : false;
167
144
  }
168
145
  });
146
+ if (isJSXComponent) {
147
+ continue
148
+ }
169
149
  // add ; after newlines
170
150
 
171
151
 
172
152
  let newvalue = attributeValue.includes('=>') ? attributeValue.split("=>").slice(1).join("=>").trim() : attributeValue.split("function").slice(1).join("function").trim()
173
153
 
154
+ // add ; after newlines
174
155
 
175
156
 
176
157
  newvalue = newvalue.trim();
@@ -207,7 +188,7 @@ function Compiler(func, file) {
207
188
  newvalue = newvalue.replace(/}\s*$/, '');
208
189
 
209
190
 
210
-
191
+
211
192
 
212
193
  newvalue = newvalue.replaceAll(',,', ',')
213
194
  let paramnames = params ? params.split(',').map((e) => e.trim()) : null
@@ -217,6 +198,8 @@ function Compiler(func, file) {
217
198
 
218
199
  // add ; after newlines
219
200
  newvalue = newvalue.replaceAll(/\n/g, ";\n")
201
+ // remove () from newvalue
202
+ newvalue = newvalue.replace(/\(\s*=>/gs, '=>').replace(/\function\s*\([^\)]*\)\s*\{/gs, '{')
220
203
 
221
204
  let bind = isJSXComponent ? `${attributeName}='function(${params}){${newvalue}}'` : `${attributeName}="\$\{this.bind(function(){${newvalue}}.bind(this), ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
222
205
  if (e.length < 1) return ''
@@ -235,9 +218,10 @@ function Compiler(func, file) {
235
218
  * @keywords - attributes, props, html attributes
236
219
  */
237
220
  let match;
238
- while ((match = elementRegex.exec(code)) !== null) {
221
+ while ((match = elementRegex.exec(string)) !== null) {
239
222
  let [, element, attributes] = match;
240
223
 
224
+
241
225
  let attributesMatch;
242
226
  let elementAttributes = {};
243
227
 
@@ -250,6 +234,60 @@ function Compiler(func, file) {
250
234
  attributesList.push({ element, attributes: elementAttributes });
251
235
  }
252
236
 
237
+ let spreadMatch;
238
+ while ((spreadMatch = spreadAttributeRegex.exec(code)) !== null) {
239
+ let [, element, spread] = spreadMatch;
240
+ let isJSXComponent = element.match(/^[A-Z]/) ? true : false;
241
+ if (isJSXComponent) {
242
+ continue;
243
+ }
244
+ let old = spread;
245
+ spread = spread.trim().replace(/\s+/g, " ");
246
+ // re,pve $={ and }
247
+ spread = spread.replace(/\s*\$\s*=\s*{\s*{/gs, '')
248
+
249
+ // replace trailing }
250
+ spread = spread.replace(/}}\s*$/, '').replace(/}\s*}$/, '')
251
+ let splitByCommas = spread.split(/,(?![^{}]*})/gs)
252
+ // remove empty strings
253
+ splitByCommas = splitByCommas.filter((e) => e.split(':')[0].trim().length > 0)
254
+ splitByCommas = splitByCommas.map((e, index) => {
255
+ let key = e.split(':')[0].trim()
256
+ switch (true) {
257
+ case e.includes('function') && !e.includes('this.bind') || e && e.includes('=>') && !e.includes('this.bind'):
258
+ let value = e.split(':')[1].trim()
259
+ let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('');
260
+ value = `this.bind(${value}, false, "${ref}", "")`
261
+ e = `${key}="\${${value}}"`
262
+ break;
263
+ case e.includes('style:'):
264
+ let v2 = e.split('style:')[1].trim().replace(/,$/, '')
265
+ v2 = v2.replace(/,$/, '')
266
+ e = `${key}="\${this.parseStyle(${v2})}"`
267
+ break;
268
+
269
+ default:
270
+ let v = e.split(':')
271
+ key = v[0].trim()
272
+ // remove key from v
273
+ v.shift()
274
+ v = v.join(' ')
275
+ e = `${key}="\${${v}}"`
276
+
277
+ break;
278
+ }
279
+
280
+
281
+ return e;
282
+ });
283
+
284
+
285
+ let newSpread = splitByCommas.join(' ').trim().replace(/,$/, '');
286
+
287
+ // remove trailing }
288
+ string = string.replace(old, newSpread);
289
+ }
290
+
253
291
  return attributesList;
254
292
  }
255
293
 
@@ -258,7 +296,7 @@ function Compiler(func, file) {
258
296
  let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
259
297
 
260
298
  return returns || [];
261
- }
299
+ }
262
300
  if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
263
301
  || string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
264
302
  ) {
@@ -291,17 +329,22 @@ function Compiler(func, file) {
291
329
  }
292
330
  let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
293
331
 
294
-
332
+ let attributes = extractAttributes(string);
295
333
  contents = contents.trim().replace(/\]$/, "")
296
334
  contents = contents.replace(/\)$/, "");
297
335
  usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
298
336
  updatedContents = contents;
299
- let attributes = extractAttributes(contents);
337
+
300
338
 
301
339
  let newAttributes = [];
302
340
  let oldAttributes = [];
303
341
  attributes.forEach((attribute) => {
304
342
  const { element, attributes } = attribute;
343
+ // make sure it isnt a jsx component
344
+ let isJSXComponent = element.match(/[A-Z]/) ? true : false;
345
+ if (isJSXComponent) {
346
+ return;
347
+ }
305
348
  if (Object.keys(attributes).length === 0) return;
306
349
 
307
350
 
@@ -311,7 +354,9 @@ function Compiler(func, file) {
311
354
  let value = attributes[key];
312
355
  let oldvalue = value;
313
356
  if (value && !value.new) {
357
+
314
358
  if (value && value.includes("={")) {
359
+
315
360
  value = value.replace("=", "");
316
361
  value == "undefined" ? (value = '"') : (value = value);
317
362
 
@@ -437,85 +482,44 @@ function Compiler(func, file) {
437
482
 
438
483
  let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
439
484
  let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
440
- const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|\$=\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\}/gs;
441
-
442
-
443
-
444
-
445
-
446
- let props = component.match(dynamicAttributesRegex)
447
-
448
- let filteredProps = [];
449
- let isWithinComponent = false;
450
- let componentName = name
451
- let currentProps = []
452
-
453
- let $_ternaryprops = []
454
-
455
- for (let prop of props) {
485
+ let props = component.includes('/>')? component.split(`<`)[1].split('/>')[0] : component.split(`<`)[1].split(`>`)[1].split(`</${name}`)[0].trim()
486
+ props = props.replaceAll(/\s+/g, " ").trim()
487
+ props = props.replace(name, '').trim()
488
+ component = component.replace(componentAttributes, '')
489
+ const dynamicAttributesRegex = /([a-zA-Z0-9_-]+)\s*=\s*("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{(.*.*?)\})*)*\})*)*\}|(?:\([^)]*\)|()\s*=>\s*(?:\{.*.*\})?|\{.*\})|\[[^\]]*\])/gs;
456
490
 
457
- if (prop === componentName) {
491
+ const attributeObject = {};
458
492
 
459
- isWithinComponent = true;
460
- filteredProps.push(prop);
461
- } else if (isWithinComponent && prop.includes('=')) {
462
-
463
- if (prop.startsWith('$=')) {
464
- let old = prop
465
- prop = prop.replace('$=', '$_ternary=')
466
-
467
- // remove trailing }
468
- prop = prop.replace(/}\s*$/, '')
469
- component = component.replace(old, prop)
470
- componentAttributes = componentAttributes.replace(old, prop)
471
-
472
- $_ternaryprops.push(prop)
473
-
474
- }
475
- else if (prop.includes('${')) {
476
-
477
- prop = prop.replace('="', ':').replace('}"', '}')
478
- if (prop.includes('${')) {
479
- prop = prop.replace('="', ':')
480
- prop = prop.replace('${', '')
481
- prop = prop.replace('}', '')
482
-
483
- }
484
- if (prop.includes('="${{')) {
485
- prop = prop.replace('${{', '{')
486
- prop = prop.replace('}}', '}')
487
- prop = prop.replace('="', ':')
488
- prop = prop.replace('}"', '}')
489
- }
490
-
491
- }
492
- else if (prop.startsWith('={')) {
493
- prop = prop.replace('={', ':`${')
494
- prop.replace('} ', '}`')
495
- }
496
-
497
- if (prop.includes('function')) {
498
- // parse 'function' to function
499
- prop = prop.replace("'", '')
500
-
501
- if (prop.endsWith("}'")) {
502
- prop = prop.replace("}'", '}')
503
-
504
- }
505
-
506
- prop = prop.replace('=function', ':function')
507
- }
508
-
509
- filteredProps.push(prop);
510
-
511
-
512
-
513
- }
493
+ let $_ternaryprops = []
514
494
 
495
+ let match;
496
+ let propstring = ''
497
+ // props right now is just a string with all of them on one line and a space between each
498
+ while ((match = dynamicAttributesRegex.exec(props)) !== null) {
499
+ let str = match[0].trim().replace(/\s+/g, " ");
500
+ if (!str.includes('=')) {
501
+ continue
502
+ }
503
+
504
+
505
+ str = str.replaceAll(/\s+/g, " ")
506
+ str = str.split('=')
507
+ let key = str[0].trim()
508
+ let value = str.slice(1).join('=').trim().match(/\{.*\}/gs) ? str.slice(1).join('=').trim().match(/\{.*\}/gs)[0] : str.slice(1).join('=').trim();
515
509
 
516
- else {
517
- isWithinComponent = false;
510
+
511
+
512
+ let isObject = value.startsWith('{{') && value.endsWith('}}')
513
+ if (isObject) {
514
+ value = value.split('{{')[1].split('}}')[0].trim()
515
+ value = `{${value}}`
516
+ propstring += `${key}:${value},`
517
+ } else {
518
+ // remove starting { and ending } using regex
519
+ value = value.replace(/^{/, '').replace(/}$/, '')
520
+ propstring += `${key}:${value},`
518
521
  }
522
+
519
523
  }
520
524
  component = component.replaceAll(/\s+/g, " ");
521
525
 
@@ -524,10 +528,9 @@ function Compiler(func, file) {
524
528
  component = component.replace(prop, '')
525
529
  })
526
530
 
527
- let children = component.split(`<${name}`)[1].split(`</${name}>`)[0].trim().replace(/\s+/g, " ").trim().replace(/,$/, '').replace('>', '').replace(/\/$/, '').trim()
531
+ let children = new RegExp(`<${name}[^>]*>([^]*)<\/${name}>`, 'gs').exec(component)?.[1] || null;
528
532
 
529
533
 
530
- props = filteredProps.join(',').replace(/\s+/g, " ").trim().replace(/,$/, '')
531
534
 
532
535
  let savedname = name;
533
536
 
@@ -560,24 +563,15 @@ function Compiler(func, file) {
560
563
 
561
564
 
562
565
 
563
-
564
- props = props.replaceAll(`,${savedname}`, '').replaceAll(savedname, '')
565
- if (props.startsWith(',')) {
566
- props = props.replace(',', '')
567
- }
568
- props = props.replaceAll("='", ":'")
569
- .replaceAll('=`', ':`')
570
- .replaceAll('="', ':"')
571
- .replaceAll('={', ':')
566
+ propstring = propstring.replace(/,$/, '')
572
567
 
573
568
 
574
569
  /**
575
570
  * @memoize - memoize a component to be remembered on each render and replace the old jsx
576
571
  */
577
572
 
578
-
579
573
  let replace = "";
580
- replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join('')}\`]))}`;
574
+ replace = `\${this.memoize(this.createComponent(${savedname}, {${propstring}}, [\`${myChildrens.join('')}\`]))}`;
581
575
 
582
576
 
583
577
  body = body.replace(before, replace);
@@ -593,48 +587,49 @@ function Compiler(func, file) {
593
587
  let replaceMents = [];
594
588
 
595
589
 
596
- for (let match of imports) {
597
- let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
598
- switch (true) {
599
- case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
600
- let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
601
- componentFolder = componentFolder.split(process.cwd())[1]
602
- if (!fs.existsSync(process.cwd() + componentFolder)) {
603
- throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
604
- }
605
-
606
- if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
607
- fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
608
- }
609
-
610
- let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
611
- let glp = globSync('**/**/**/**.{jsx,js}', {
612
- cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
613
- absolute: true,
614
- recursive: true
615
- })
616
- for (let file of glp) {
617
- let text = fs.readFileSync(file, "utf8");
618
- if (!file.endsWith('.js') && file.endsWith('.jsx')) {
619
- text = Compiler(text, file);
620
-
590
+ if(imports){
591
+ for (let match of imports) {
592
+ let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
593
+ switch (true) {
594
+ case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
595
+ let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
596
+ componentFolder = componentFolder.split(process.cwd())[1]
597
+ if (!fs.existsSync(process.cwd() + componentFolder)) {
598
+ throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
621
599
  }
622
- let dest = file.split('node_modules')[1]
623
- dest = dest.split(baseFolder)[1]
624
- // write to dist
625
- writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
626
- let importname = match.split('import')[1].split('from')[0].trim()
627
- let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
628
- let newImport = `/src/${baseFolder + dest}`
629
- newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
630
- replaceMents.push({ match: oldImportstring, replace: newImport })
631
- console.log(`📦 imported Node Package ${baseFolder} `)
632
- }
633
-
634
-
635
- break;
636
- default:
637
- break;
600
+
601
+ if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
602
+ fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
603
+ }
604
+
605
+ let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
606
+ let glp = globSync('**/**/**/**.{jsx,js}', {
607
+ cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
608
+ absolute: true,
609
+ recursive: true
610
+ })
611
+ for (let file of glp) {
612
+ let text = fs.readFileSync(file, "utf8");
613
+ if (!file.endsWith('.js') && file.endsWith('.jsx')) {
614
+ text = Compiler(text, file);
615
+
616
+ }
617
+ let dest = file.split('node_modules')[1]
618
+ dest = dest.split(baseFolder)[1]
619
+ writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
620
+ let importname = match.split('import')[1].split('from')[0].trim()
621
+ let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
622
+ let newImport = `/src/${baseFolder + dest}`
623
+ newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
624
+ replaceMents.push({ match: oldImportstring, replace: newImport })
625
+ console.log(`📦 imported Node Package ${baseFolder} `)
626
+ }
627
+
628
+
629
+ break;
630
+ default:
631
+ break;
632
+ }
638
633
  }
639
634
  }
640
635
 
@@ -827,224 +822,146 @@ const glb = await glob("**/**/**/**.{jsx,js}", {
827
822
  absolute: true,
828
823
  recursive: true
829
824
  });
825
+ let hasRendered = []
826
+
830
827
  async function Build() {
831
828
  globalThis.isBuilding = true
832
- console.log('Compiling......')
829
+ console.log(globalThis.isProduction ? 'Creating Optimized Production Build\n' : '')
830
+ let str = `Page \t\t\t\t Size\n`
831
+ globalThis.isProduction ? console.log('\x1b[32m%s\x1b[0m', str) : null
833
832
  let reader = async (file) => {
834
833
  let text = await fs.readFileSync(file, "utf8");
835
834
  return text;
836
835
  };
837
836
 
838
-
837
+
838
+
839
839
  function ssg(routes = []) {
840
840
  globalThis.isBuilding = true
841
- console.log(`Generating html files for ${routes.length} routes`)
842
- routes.forEach(async (route) => {
843
- if (route.url.includes(':')) {
844
- console.log('Route ' + route.url + ' is a dynamic route and will not be generated')
845
- return
846
- }
847
- let equalparamroute = routes.map((e) => {
848
- if (e.url.includes(':')) {
849
- let url = e.url.split('/:')[0]
850
- if (url && route.url === url) {
851
- return e
852
- } else {
853
- return null
841
+ let server = http.createServer((req, res) => {
842
+ let route = routes.find((e) => e.url === req.url)
843
+ if (route) {
844
+ let document = globalThis.routeDocuments.find((e) => e.url === req.url)
845
+ res.writeHead(200, { 'Content-Type': 'text/html' });
846
+ res.end(document.document);
847
+ } else {
848
+ const filePath = process.cwd() + '/dist/' + req.url
854
849
 
850
+ fs.readFile(filePath, (err, data) => {
851
+ if (err) {
852
+ res.writeHead(404, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
853
+ res.end('File not found');
854
+ } else {
855
+ res.writeHead(200, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
856
+ res.end(data);
855
857
  }
856
- }
857
- return null
858
- }).filter(Boolean)
859
- let document = `
860
- <!DOCTYPE html>
861
- <html lang="en">
862
- <head>
863
- <script>
864
- window.routes = JSON.parse('${JSON.stringify(routes)}')
865
- </script>
866
- <script id="isServer">
867
- window.isServer = true
868
- </script>
869
- <meta charset="UTF-8">
870
- <meta name="viewport" content="width=device-width,initial-scale=1.0">
871
- <script type="module" id="meta">
872
- window.history.pushState({}, '', '${route.url}')
873
- window.module = await import('/${route.fileName.replace('.jsx', '.js')}')
874
- let metadata = await module.$metadata
875
- if(metadata && metadata.title){
876
- document.head.innerHTML += '<title>' + metadata.title + '</title>'
877
- }
878
- if(metadata && metadata.description){
879
- document.head.innerHTML += '<meta name="description" content="' + metadata.description + '">'
880
- }
881
- if(metadata && metadata.keywords){
882
- document.head.innerHTML += '<meta name="keywords" content="' + metadata.keywords + '">'
883
- }
884
- if(metadata && metadata.author){
885
- document.head.innerHTML += '<meta name="author" content="' + metadata.author + '">'
886
- }
887
- if(metadata && metadata.image){
888
- let image = metadata.image.file
889
- let type = metadata.image.type
890
-
891
- document.head.innerHTML += '<meta property="og:image" content="' + image + '">'
892
- document.head.innerHTML += '<meta property="og:image:type" content="' + type + '">'
893
- }
894
- if(metadata && metadata.url){
895
- document.head.innerHTML += '<meta property="og:url" content="' + metadata.url + '">'
896
- }
897
-
898
- if(metadata && metadata.robot){
899
- document.head.innerHTML += '<meta name="robots" content="' + metadata.robot + '">'
900
- }
901
- if(metadata && metadata.manifest){
902
- document.head.innerHTML += '<link rel="manifest" href="' + metadata.manifest + '">'
903
- }
904
- if(metadata && metadata.tags){
905
- metadata.tags.forEach(tag => {
906
- document.head.innerHTML += tag
907
- })
908
- }
909
-
910
- if(metadata && metadata.styles){
911
- metadata.styles.forEach(style => {
912
- style = style.replaceAll('./', '/')
913
- style = style.replaceAll('../', '/')
914
- style = style.replace("'", '')
915
- document.head.innerHTML += '<link rel="stylesheet" href="' + style + '">'
916
- })
917
- }
918
- if(metadata && metadata.icon){
919
- document.head.innerHTML += '<link rel="icon" href="' + metadata.icon + '">'
920
- }
921
- </script>
922
- <script type="module" id="router">
923
- import VaderRouter from '/router.js'
924
- const router = new VaderRouter('${route.url}', 3000)
925
- router.get('${route.url}', async (req, res) => {
926
- let module = await import('/${route.fileName.replace('.jsx', '.js')}')
927
- if(Object.keys(module).includes('$prerender') && !module.$prerender){
928
- document.head.setAttribute('prerender', 'false')
929
- }
930
- res.render(module, req, res, module.$metadata)
931
- })
932
- ${equalparamroute.length > 0 ? equalparamroute.map((e) => {
933
-
934
-
935
-
936
- return `router.get('${e.url}', async (req, res) => {
937
- let module = await import('/${e.fileName.replace('.jsx', '.js')}')
938
- res.render(module, req, res, module.$metadata)
939
- })\n`
940
- }) : ''}
941
- router.listen(3000)
942
-
943
- </script>
944
- </head>
945
- <body>
946
- <div id="root"></div>
947
- </body>
948
-
949
-
950
- </html>
951
- `;
952
-
953
- // generate random but common ports
954
- let port = Math.floor(Math.random() * (65535 - 49152 + 1) + 49152)
955
-
956
- const server = http.createServer((req, res) => {
858
+ });
859
+ }
860
+ });
957
861
 
958
- if (req.url === '/') {
959
- res.writeHead(200, { 'Content-Type': 'text/html' });
960
- res.end(document);
961
- } else {
962
- // Serve static files (adjust the file paths based on your project structure)
963
- const filePath = process.cwd() + '/dist/' + req.url
964
-
965
- fs.readFile(filePath, (err, data) => {
966
- if (err) {
967
- res.writeHead(404, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
968
- res.end('File not found');
969
- } else {
970
- res.writeHead(200, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
971
- res.end(data);
972
- }
973
- });
974
- }
975
- });
862
+ let port = 12000
863
+ server.on('error', (err) => {
864
+ if (err.code === 'EADDRINUSE') {
865
+ setTimeout(() => {
866
+ server.close();
867
+ server.listen(++port);
868
+ }, 1000);
869
+ }
870
+ })
976
871
 
977
- server.listen(port)
978
- server.on('error', (err) => {
979
- if (err.code === 'EADDRINUSE') {
980
- console.log(`Port ${port} is in use, trying another port...`);
981
- setTimeout(() => {
982
- server.close();
983
- server.listen(++port);
984
- }, 1000);
985
- }
986
- })
872
+ const browser = puppeteer.launch({
873
+ headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
874
+ warning: false,
875
+ })
876
+ server.listen(port);
987
877
 
878
+ routes.forEach(async (route) => {
879
+ if (route.url.includes(':')) {
880
+ return
881
+ }
988
882
  globalThis.listen = true;
989
883
 
990
- const browser = await puppeteer.launch({
991
- headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
992
- warning: false,
993
- })
994
-
995
- const browserPID = browser.process().pid
996
884
  try {
997
885
 
998
886
  route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
999
- let page = await browser.newPage();
1000
- await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle2' });
1001
- await page.on('console', msg => console.log('PAGE LOG:', msg.text()));
1002
- await page.on('error', err => console.log('PAGE LOG:', err));
1003
- await page.on('pageerror', err => console.log('PAGE LOG:', err));
1004
- await page.evaluate(() => {
1005
- window.onerror = function (msg, url, lineNo, columnNo, error) {
1006
- console.log(msg, url, lineNo, columnNo, error)
887
+ let page = (await browser).newPage()
888
+ page.then(async (page) => {
889
+ page.on('error', (err) => {
890
+ console.error('JS ERROR:', JSON.parse(err));
891
+ });
892
+ try {
893
+ page.on('pageerror', async err => {
894
+ let errorObj = JSON.parse(await page.evaluate(() => document.documentElement.getAttribute('error')) || '{}')
895
+ console.log('\x1b[31m%s\x1b[0m', 'Compiler Error:', errorObj)
896
+
897
+ console.log('\x1b[31m%s\x1b[0m', 'Error:', err)
898
+ });
899
+ } catch (error) {
900
+ page.close()
1007
901
  }
902
+ page.on('crash', () => {
903
+ console.error(`Render process crashed for ${route.url}`)
904
+ });
905
+
906
+ await page.goto(`http://localhost:${port}${route.url}`, { waitUntil: 'networkidle2' });
907
+
908
+ page.evaluate(() => {
909
+ document.querySelector('#meta').remove()
910
+ document.querySelector('#isServer').innerHTML = 'window.isServer = false'
911
+ if (document.head.getAttribute('prerender') === 'false') {
912
+ document.querySelector('#root').innerHTML = ''
913
+ console.log(`Disabled prerendering for ${window.location.pathname}`)
914
+ }
915
+ })
916
+ let html = await page.content();
917
+
918
+ html = await prettier.format(html, { parser: "html" })
919
+
920
+ writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
921
+
922
+ console.log(`\x1b[32m%s\x1b[0m`, `Prerendered ${route.url}...`)
923
+
924
+ hasRendered.push(route.url)
1008
925
  })
1009
- await page.waitForSelector('#root', { timeout: 10000 })
1010
- await page.evaluate(() => {
1011
- document.getElementById('meta').remove()
1012
- document.querySelector('#isServer').innerHTML = 'window.isServer = false'
1013
- if (document.head.getAttribute('prerender') === 'false') {
1014
- document.querySelector('#root').innerHTML = ''
1015
- console.log(`Disabled prerendering for ${window.location.pathname}`)
1016
- }
1017
- })
1018
- const html = await page.content();
1019
926
 
1020
- await page.close();
1021
- await writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
1022
- await browser.close();
1023
- server.close()
927
+
928
+
1024
929
 
1025
930
  } catch (error) {
1026
- server.close()
1027
- await browser.close();
931
+ console.log('\x1b[31m%s\x1b[0m', 'Error:', error)
932
+
1028
933
  }
1029
934
  finally {
1030
- await browser.close();
1031
- server.close()
1032
- }
1033
- try {
1034
- process.kill(browserPID)
1035
- } catch (error) {
1036
935
  }
936
+ })
1037
937
 
1038
938
 
1039
- })
1040
939
 
1041
- let timeout = setTimeout(() => {
1042
- globalThis.isBuilding = false
1043
- clearTimeout(timeout)
1044
- }, 1000)
1045
- console.log(`Generated ${routes.length} html files for ${routes.length} routes`)
1046
- }
1047
940
 
941
+
942
+ function kill() {
943
+ console.log(`\x1b[32m%s\x1b[0m`, `\nPrerendered ${routes.length} pages...\n`)
944
+ server.close()
945
+ browser.then((browser) => {
946
+ browser.close()
947
+ })
948
+ hasRendered = []
949
+ globalThis.isBuilding = false
950
+
951
+ }
952
+
953
+ if (hasRendered.length === routes.length) {
954
+ kill()
955
+ } else {
956
+ console.log(`\x1b[32m%s\x1b[0m`, `Prerendering ${routes.length} pages...\n`)
957
+ let interval = setInterval(() => {
958
+ if (hasRendered.length === routes.length) {
959
+ kill()
960
+ clearInterval(interval)
961
+ }
962
+ }, 1000);
963
+ }
964
+ }
1048
965
  globalThis.routes = []
1049
966
 
1050
967
  for await (let file of glb) {
@@ -1074,16 +991,15 @@ async function Build() {
1074
991
 
1075
992
 
1076
993
  let data = await fs.readFileSync(origin, "utf8");
1077
- data = Compiler(data, origin);
1078
-
1079
-
1080
- await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data).then(async () => {
1081
-
1082
-
1083
994
 
1084
- await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data)
1085
-
1086
- })
995
+ // gen sourcemap if not production
996
+ let size = fs.statSync(origin).size;
997
+ if (!globalThis.isProduction) {
998
+ let { sourceMap } = sourceMapGen({ origin: origin, fileName: fileName }, await Compiler(data, origin))
999
+ data = data + `\n//# sourceMappingURL=/src/maps/${fileName.replace('.jsx', '.js.map')}\n //#sourceURL=${origin}`
1000
+ await writer(process.cwd() + "/dist/src/maps/" + fileName.replace('.jsx', '.js.map'), JSON.stringify(sourceMap, null, 2))
1001
+ }
1002
+ await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), await Compiler(data, origin))
1087
1003
 
1088
1004
  // configure routing for each page
1089
1005
 
@@ -1145,13 +1061,111 @@ async function Build() {
1145
1061
  }
1146
1062
 
1147
1063
 
1148
- globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
1064
+ if (!obj.url.includes(':')) {
1065
+ globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
1066
+ }
1067
+
1149
1068
 
1069
+ let stats = {
1070
+ route: obj.url.padEnd(30),
1071
+ size: Math.round(size / 1000) + 'kb',
1072
+ letParentFolder: obj.url.split('/').slice(0, -1).join('/'),
1073
+ isChildRoute: obj.url.split('/').slice(0, -1).join('/').includes(':') ? true : false,
1074
+ parentRoute: obj.url.split('/').slice(0, -1).join('/').split(':')[0],
1150
1075
 
1076
+ }
1077
+ stats.isChildRoute ? stats.route = `? ${obj.url}` : null
1078
+ let string = `${isBasePath ? '+' : '+'} ${stats.route.padEnd(30)} ${stats.size}`
1151
1079
 
1080
+ globalThis.isProduction ? console.log(string) : null
1152
1081
  }
1153
1082
 
1154
- ssg(globalThis.routes)
1083
+ globalThis.routeDocuments = []
1084
+ globalThis.routes.map((route) => {
1085
+ let equalparamroute = globalThis.routes.map((e) => {
1086
+ if (e.url.includes(':')) {
1087
+ let url = e.url.split('/:')[0]
1088
+ if (url && route.url === url) {
1089
+ return e
1090
+ } else {
1091
+ return null
1092
+
1093
+ }
1094
+ }
1095
+ return null
1096
+ }).filter(Boolean)
1097
+ let document = `
1098
+ <!DOCTYPE html>
1099
+ <html lang="en">
1100
+ <head>
1101
+ <script>
1102
+ window.routes = JSON.parse('${JSON.stringify(globalThis.routes)}')
1103
+ </script>
1104
+ <script type="module" id="meta">
1105
+ window.history.pushState({}, '', '${route.url}')
1106
+
1107
+ </script>
1108
+ <script id="isServer">
1109
+ window.isServer = true
1110
+ </script>
1111
+ <meta charset="UTF-8">
1112
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
1113
+
1114
+ <script type="module" id="router">
1115
+ import VaderRouter from '/router.js'
1116
+ const router = new VaderRouter('${route.url}')
1117
+ router.get('${route.url}', async (req, res) => {
1118
+ try{
1119
+ let module = await import('/${route.fileName.replace('.jsx', '.js')}')
1120
+ if(Object.keys(module).includes('$prerender') && !module.$prerender){
1121
+ document.head.setAttribute('prerender', 'false')
1122
+ }
1123
+ res.render(module, req, res, module.$metadata)
1124
+ }
1125
+ catch(error){
1126
+ let errorMessage = {
1127
+ message: error.message,
1128
+ name: error.name,
1129
+ stack: error.stack,
1130
+ path: window.location.pathname
1131
+ };
1132
+
1133
+
1134
+ document.documentElement.setAttribute('error', JSON.stringify(errorMessage));
1135
+ throw new Error(error)
1136
+ }
1137
+ })
1138
+ ${equalparamroute.length > 0 ? equalparamroute.map((e) => {
1139
+
1140
+
1141
+
1142
+ return `router.get('${e.url}', async (req, res) => {
1143
+ let module = await import('/${e.fileName.replace('.jsx', '.js')}')
1144
+ res.render(module, req, res, module.$metadata)
1145
+ })\n`
1146
+ }) : ''}
1147
+ router.listen(3000)
1148
+
1149
+ </script>
1150
+ </head>
1151
+ <body>
1152
+ <div id="root"></div>
1153
+ </body>
1154
+
1155
+
1156
+ </html>
1157
+ `;
1158
+ globalThis.routeDocuments.push({ url: route.url, document: document })
1159
+ })
1160
+
1161
+ if(globalThis.devMode && !globalThis.oneAndDone){
1162
+ ssg(globalThis.routes)
1163
+ globalThis.oneAndDone = true
1164
+ console.log(`In Development Mode, Prerendering ${globalThis.routes.length} pages... Once`)
1165
+ }
1166
+ else if(globalThis.isProduction){
1167
+ ssg(globalThis.routes)
1168
+ }
1155
1169
 
1156
1170
 
1157
1171
  const scannedSourceFiles = await glob("**/**.{jsx,js,json}", {
@@ -1179,23 +1193,25 @@ async function Build() {
1179
1193
  scannedSourceFiles.forEach(async (file) => {
1180
1194
  file = file.replace(/\\/g, '/');
1181
1195
  let name = file.split('/src/')[1]
1182
- //parse jsx
1183
1196
 
1184
1197
  let data = await reader(process.cwd() + "/src/" + name)
1185
1198
  if (name.includes('.jsx')) {
1186
- data = Compiler(data, process.cwd() + "/src/" + name);
1187
-
1188
- await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), data).then(async () => {
1189
- await writer(process.cwd() + "/dist/src/" + name.replace('.jsx', '.js'), data)
1190
-
1191
- })
1199
+ let origin = process.cwd() + "/src/" + name
1200
+ if (!globalThis.isProduction) {
1201
+ let { sourceMap } = sourceMapGen({ origin: origin, fileName: name }, await Compiler(data, origin))
1202
+ data = data + `\n//# sourceMappingURL=/src/maps/${name.replace('.jsx', '.js.map')}\n //#sourceURL=${origin}`
1203
+ await writer(process.cwd() + "/dist/src/maps/" + name.replace('.jsx', '.js.map'), JSON.stringify(sourceMap, null, 2))
1204
+ }
1205
+ await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), await Compiler(data, origin))
1192
1206
  return
1193
1207
  }
1194
- bundleSize += fs.statSync(process.cwd() + "/src/" + name).size;
1208
+ if (!name.includes('.map')) {
1209
+ bundleSize += fs.statSync(process.cwd() + "/src/" + name).size;
1210
+ }
1195
1211
  await writer(process.cwd() + "/dist/src/" + name, data);
1196
1212
  })
1197
1213
 
1198
- const scannedPublicFiles = await glob("**/**.{css,js,html,mjs,cjs}", {
1214
+ const scannedPublicFiles = await glob("**/**/**.{css,js,html,mjs,cjs,png,jpg,jpeg,gif,svg,mp4,webm,ogg}", {
1199
1215
  ignore: ["node_modules/**/*", "dist/**/*"],
1200
1216
  cwd: process.cwd() + '/public/',
1201
1217
  absolute: true,
@@ -1203,7 +1219,7 @@ async function Build() {
1203
1219
  scannedPublicFiles.forEach(async (file) => {
1204
1220
  file = file.replace(/\\/g, '/');
1205
1221
  file = file.split('/public/')[1]
1206
- let data = await reader(process.cwd() + "/public/" + file)
1222
+ let data = fs.readFileSync(process.cwd() + "/public/" + file);
1207
1223
  bundleSize += fs.statSync(process.cwd() + "/public/" + file).size;
1208
1224
  await writer(process.cwd() + "/dist/public/" + file, data);
1209
1225
  })
@@ -1232,21 +1248,22 @@ async function Build() {
1232
1248
  let data = await reader(process.cwd() + "/node_modules/vaderjs/runtime/" + file)
1233
1249
  await writer(process.cwd() + "/dist/" + file, data);
1234
1250
  });
1251
+
1235
1252
 
1236
1253
  }
1237
1254
 
1238
1255
  globalThis.isBuilding = false
1239
- console.log(`📦 Build completed: Build Size -> ${Math.round(bundleSize / 1000)}kb`)
1240
1256
 
1257
+ globalThis.isProduction ? console.log(`Total Bundle Size: ${Math.round(bundleSize / 1000)}kb`) : null
1241
1258
  bundleSize = 0;
1242
1259
 
1243
1260
  return true
1244
1261
  }
1245
- const s = () => {
1262
+ const s = (port) => {
1246
1263
 
1247
1264
  const server = http.createServer((req, res) => {
1248
1265
 
1249
- const validExtensions = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'];
1266
+ const validExtensions = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg', '.map']
1250
1267
 
1251
1268
  if (!validExtensions.some(ext => req.url.endsWith(ext))) {
1252
1269
  req.url = req.url !== '/' ? req.url.split('/')[1] : req.url;
@@ -1269,6 +1286,7 @@ const s = () => {
1269
1286
  let ws = new WebSocket('ws://localhost:${process.env.PORT || 3000}')
1270
1287
  ws.onmessage = (e) => {
1271
1288
  if(e.data === 'reload'){
1289
+ console.log('Reloading...')
1272
1290
  window.location.reload()
1273
1291
  }
1274
1292
  }
@@ -1290,7 +1308,7 @@ const s = () => {
1290
1308
 
1291
1309
 
1292
1310
  function getContentType(filePath) {
1293
- let ext = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'].includes(path.extname(filePath)) ? path.extname(filePath) : '.html'
1311
+ 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'
1294
1312
  switch (ext) {
1295
1313
  case '.js':
1296
1314
  return 'text/javascript';
@@ -1302,6 +1320,8 @@ const s = () => {
1302
1320
  return 'text/javascript';
1303
1321
  case '.html':
1304
1322
  return 'text/html';
1323
+ case '.map':
1324
+ return 'application/json';
1305
1325
  case '.json':
1306
1326
  return 'application/json';
1307
1327
  case '.png':
@@ -1324,35 +1344,30 @@ const s = () => {
1324
1344
  return 'application/octet-stream';
1325
1345
  }
1326
1346
  }
1327
-
1328
- const PORT = process.env.PORT || 3000;
1329
- server.listen(PORT, () => {
1330
- console.log(`Server is running on port ${PORT}`);
1347
+
1348
+ server.listen(port, () => {
1349
+ console.log(`Server is running on port ${port}`);
1350
+ globalThis.ws = ws
1331
1351
  });
1332
- let i =
1333
- setInterval(() => {
1334
- if (globalThis.isBuilding && globalThis.devMode) {
1335
1352
 
1336
- ws.clients.forEach((client) => {
1337
- client.send('reload')
1338
- })
1339
- } else {
1340
- clearInterval(i)
1341
- }
1342
- }, 120)
1343
1353
 
1344
1354
  }
1345
1355
 
1346
1356
 
1347
1357
  switch (true) {
1348
- case process.argv.includes('--watch') && !process.argv.includes('--build') && !process.argv.includes('--serve'):
1358
+ case process.argv.includes('dev') && !process.argv.includes('build') && !process.argv.includes('start'):
1349
1359
 
1350
1360
  globalThis.devMode = true
1361
+ globalThis.isProduction = false
1362
+
1363
+ let p = process.env.PORT || config.port || process.argv.includes('-p') ? process.argv[process.argv.indexOf('-p') + 1] : 3000
1364
+ globalThis.oneAndDone = false
1351
1365
  console.log(`
1352
- Vader.js v1.3.3
1366
+ Vader.js v${fs.readFileSync(process.cwd() + '/node_modules/vaderjs/package.json', 'utf8').split('"version": "')[1].split('"')[0]}
1353
1367
  - Watching for changes in ./pages
1354
1368
  - Watching for changes in ./src
1355
1369
  - Watching for changes in ./public
1370
+ - Serving on port ${p}
1356
1371
  `)
1357
1372
  !globalThis.isBuilding ? Build() : null
1358
1373
 
@@ -1364,51 +1379,63 @@ Vader.js v1.3.3
1364
1379
  if (event == 'change'
1365
1380
  && !globalThis.isBuilding
1366
1381
  ) {
1382
+ if (globalThis.ws && !globalThis.isWriting) {
1383
+ globalThis.ws.clients.forEach((client) => {
1384
+ console.log('Reloading...')
1385
+ client.send('reload')
1386
+ })
1387
+ }
1367
1388
 
1389
+ console.log('\nRebuilding...')
1390
+ globalThis.isBuilding = true
1368
1391
  Build()
1369
1392
  }
1370
1393
  }).on('error', (err) => console.log(err))
1371
- })
1372
- let p = process.argv[process.argv.indexOf('--watch') + 1] || process.env.PORT || 3000
1373
-
1374
- process.env.PORT = p
1375
- s()
1394
+ })
1395
+ s(p)
1376
1396
 
1377
1397
  globalThis.listen = true;
1378
1398
 
1379
1399
  break;
1380
- case process.argv.includes('--build') && !process.argv.includes('--watch') && !process.argv.includes('--serve'):
1400
+ case process.argv.includes('build') && !process.argv.includes('dev') && !process.argv.includes('start'):
1381
1401
  globalThis.devMode = false
1402
+ globalThis.isProduction = true
1403
+ globalThis.routeStates = []
1382
1404
  console.log(`
1383
1405
  Vader.js v1.3.3
1384
1406
  Building to ./dist
1385
1407
  `)
1408
+ if (fs.existsSync(process.cwd() + '/dist/src/maps')) {
1409
+ fs.rmSync(process.cwd() + '/dist/src/maps', { recursive: true })
1410
+ }
1386
1411
  Build()
1387
1412
 
1388
1413
  break;
1389
- case process.argv.includes('--serve') && !process.argv.includes('--watch') && !process.argv.includes('--build'):
1390
- let port = process.argv[process.argv.indexOf('--serve') + 1] || 3000
1391
- process.env.PORT = port
1414
+ case process.argv.includes('start') && !process.argv.includes('dev') && !process.argv.includes('build'):
1415
+ let port = process.env.PORT || config.port || process.argv.includes('-p') ? process.argv[process.argv.indexOf('-p') + 1] : 3000
1416
+ console.log(port)
1392
1417
  globalThis.devMode = false
1393
1418
  console.log(`
1394
1419
  Vader.js v1.3.3
1395
1420
  Serving ./dist on port ${port}
1396
1421
  url: http://localhost:${port}
1397
1422
  `)
1398
- s()
1423
+ s(port)
1399
1424
  break;
1400
1425
  default:
1401
- console.log(`
1426
+ // add color
1427
+ console.log(`
1402
1428
  Vader.js is a reactive framework for building interactive applications for the web built ontop of bun.js!
1403
1429
 
1404
1430
  Usage: vader <command>
1405
1431
 
1406
1432
  Commands:
1407
- --watch (port) Watch the pages folder for changes with hot reloading
1433
+
1434
+ vaderjs dev -p <number> Start the development server
1408
1435
 
1409
- --build Build the project to ./dist
1436
+ vaderjs build Build the project to ./dist
1410
1437
 
1411
- --serve (400) Serve the project on a port (default 3000 or process.env.PORT)
1438
+ vaderjs start -p <number> Production Mode (default 3000 or process.env.PORT)
1412
1439
 
1413
1440
  Learn more about vader: https://vader-js.pages.dev/
1414
1441