vaderjs 1.3.3-8924566yt812 → 1.3.3-9bn28b17d42e1

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,91 +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
-
51
75
 
52
- let childs = [];
53
76
 
54
- const spreadAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*\$\s*=\s*{{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)}})/gs;
55
- let spreadMatch;
56
- while ((spreadMatch = spreadAttributeRegex.exec(string)) !== null) {
57
- let [, element, spread] = spreadMatch;
58
- let isJSXComponent = element.match(/[A-Z]/) ? true : false;
59
- if (isJSXComponent) {
60
- continue
61
-
62
- }
63
- let old = spread;
64
-
65
- // turn spread into attributes
66
- spread = spread.replace(/\s*$\s*=\s*/, "");
67
- spread = spread.replace(/{{/, "");
68
- spread = spread.replace(/}}/, "");
69
- spread = spread.replace(/\$\s*=\s*/, "");
70
-
71
- // turn : into =
72
77
 
73
- // do not split inner Objects ex: {color: 'red', background: {color: 'blue'}} -> {color: 'red', background: {color: 'blue'}}
74
- let splitByCommas = spread.split(/,(?![^{]*})/g);
75
- splitByCommas = splitByCommas.map((e) => e.trim())
76
- splitByCommas = splitByCommas.map((e) => {
77
- switch (true) {
78
- case e.includes('function') || e.includes('=>'):
79
- e = e.replace(/:(.*)/gs, '={$1}')
80
- break;
81
- case e.includes('style'):
82
- e = e.replace(/:(.*)/gs, '="${this.parseStyle($1)}"')
83
- break;
84
- case e.includes('[') && e.includes(']'):
85
- e = e.replace(/:(.*)/gs, '={$1.join(" ")}')
86
- break;
87
- default:
88
- e = e.replace(/:(.*)/gs, '=$1')
89
- break;
90
- }
91
78
 
92
- return e.trim()
93
- })
94
79
 
95
- let newSpread = `\t` + splitByCommas.join(' ') + `\t`
80
+ let childs = [];
96
81
 
97
- string = string.replace(old, newSpread);
98
82
 
99
- }
83
+ // or : value boolean variable etc
84
+ const spreadAttributeRegex = /\s*([a-zA-Z0-9_-]+)\s*(\$\s*=\s*\{\s*\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}\s*\})/gs;
100
85
 
101
86
 
87
+ string = parseComponents(string);
102
88
 
103
89
  function extractAttributes(code) {
104
- // Match elements with opening tags
90
+ // grab $={...} and ={...}
105
91
  const elementRegex = /<([a-zA-Z0-9_-]+)([^>]*)>/gs;
106
92
 
107
93
  // Match attributes in an opening tag, including those with ={}
108
94
  // Match attributes in an opening tag, including those with ={...}
109
95
  const attributeRegex =
110
- /\s*([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
96
+ /\s*([a-zA-Z0-9_-]+)(\s*=\s*("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
111
97
 
112
-
113
98
 
114
-
115
- const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
99
+
100
+ const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)\s*(=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
116
101
 
117
102
  let attributesList = [];
118
103
 
119
104
  let spreadAttributes = [];
120
- let spreadMatch;
105
+
121
106
  /**
122
107
  * @search - handle spread for html elements
123
108
  * @keywords - spread, spread attributes, spread props, spread html attributes
@@ -130,19 +115,46 @@ function Compiler(func, file) {
130
115
  *
131
116
  */
132
117
  let functionAttributes = [];
118
+ let spreadFunctions = [];
133
119
  let functionMatch;
120
+
121
+ /**
122
+ * @search - handle attributes for html elements
123
+ * @keywords - attributes, props, html attributes
124
+ */
125
+ let match;
126
+ while ((match = elementRegex.exec(string)) !== null) {
127
+ let [, element, attributes] = match;
128
+
129
+
130
+ let attributesMatch;
131
+ let elementAttributes = {};
132
+
133
+ while ((attributesMatch = attributeRegex.exec(attributes)) !== null) {
134
+ let [, attributeName, attributeValue] = attributesMatch;
135
+
136
+ elementAttributes[attributeName] = attributeValue || null;
137
+ }
138
+
139
+ attributesList.push({ element, attributes: elementAttributes });
140
+ }
141
+
142
+
143
+
134
144
  while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
135
145
 
136
146
  let [, attributeName, attributeValue] = functionMatch;
137
147
  let attribute = {};
138
148
 
139
- if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")
149
+ if (attributeValue && attributeValue.includes("=>")
150
+ && !attributeValue.includes("this.bind")
151
+ || attributeValue && attributeValue.includes("function")
140
152
  && !spreadFunctions.includes(attributeValue)
141
153
  ) {
142
-
154
+
143
155
  let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('')
144
156
  let old = `${attributeName}${attributeValue}`
145
-
157
+
146
158
  let elementMatch = string.match(/<([a-zA-Z0-9_-]+)([^>]*)>/gs);
147
159
  let isJSXComponent = false;
148
160
  elementMatch.forEach((element) => {
@@ -155,11 +167,15 @@ function Compiler(func, file) {
155
167
  isJSXComponent = elementTag.match(/^[A-Z]/) ? true : false;
156
168
  }
157
169
  });
170
+ if (isJSXComponent) {
171
+ continue
172
+ }
158
173
  // add ; after newlines
159
174
 
160
175
 
161
176
  let newvalue = attributeValue.includes('=>') ? attributeValue.split("=>").slice(1).join("=>").trim() : attributeValue.split("function").slice(1).join("function").trim()
162
177
 
178
+ // add ; after newlines
163
179
 
164
180
 
165
181
  newvalue = newvalue.trim();
@@ -196,7 +212,7 @@ function Compiler(func, file) {
196
212
  newvalue = newvalue.replace(/}\s*$/, '');
197
213
 
198
214
 
199
-
215
+
200
216
 
201
217
  newvalue = newvalue.replaceAll(',,', ',')
202
218
  let paramnames = params ? params.split(',').map((e) => e.trim()) : null
@@ -206,8 +222,11 @@ function Compiler(func, file) {
206
222
 
207
223
  // add ; after newlines
208
224
  newvalue = newvalue.replaceAll(/\n/g, ";\n")
225
+ // remove () from newvalue
226
+ newvalue = newvalue.replace(/\(\s*=>/gs, '=>').replace(/\function\s*\([^\)]*\)\s*\{/gs, '{')
227
+ newvalue = newvalue + `\n /**@id=${ref}**/ \n`
209
228
 
210
- let bind = isJSXComponent ? `${attributeName}='function(${params}){${newvalue}}'` : `${attributeName}="\$\{this.bind(function(){${newvalue}}.bind(this), ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
229
+ let bind = !isJSXComponent && `${attributeName}="\$\{this.bind(function(){${newvalue}}.bind(this), ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
211
230
  if (e.length < 1) return ''
212
231
  if (e.length > 0) {
213
232
  index == 0 ? e : ',' + e
@@ -215,30 +234,10 @@ function Compiler(func, file) {
215
234
  return e
216
235
  }) : ''}" ${params ? params.split(',').map((e) => e.trim()).filter(Boolean).map((e) => `,${e}`).join('') : ''})}"`
217
236
 
218
- string = string.replace(old, bind);
237
+ string = string.replace(old, bind)
219
238
  }
220
239
  }
221
240
 
222
- /**
223
- * @search - handle attributes for html elements
224
- * @keywords - attributes, props, html attributes
225
- */
226
- let match;
227
- while ((match = elementRegex.exec(code)) !== null) {
228
- let [, element, attributes] = match;
229
-
230
- let attributesMatch;
231
- let elementAttributes = {};
232
-
233
- while ((attributesMatch = attributeRegex.exec(attributes)) !== null) {
234
- let [, attributeName, attributeValue] = attributesMatch;
235
-
236
- elementAttributes[attributeName] = attributeValue || null;
237
- }
238
-
239
- attributesList.push({ element, attributes: elementAttributes });
240
- }
241
-
242
241
  return attributesList;
243
242
  }
244
243
 
@@ -247,7 +246,7 @@ function Compiler(func, file) {
247
246
  let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
248
247
 
249
248
  return returns || [];
250
- }
249
+ }
251
250
  if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
252
251
  || string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
253
252
  ) {
@@ -280,17 +279,22 @@ function Compiler(func, file) {
280
279
  }
281
280
  let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
282
281
 
283
-
282
+ let attributes = extractAttributes(string);
284
283
  contents = contents.trim().replace(/\]$/, "")
285
284
  contents = contents.replace(/\)$/, "");
286
285
  usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
287
286
  updatedContents = contents;
288
- let attributes = extractAttributes(contents);
287
+
289
288
 
290
289
  let newAttributes = [];
291
290
  let oldAttributes = [];
292
291
  attributes.forEach((attribute) => {
293
292
  const { element, attributes } = attribute;
293
+ // make sure it isnt a jsx component
294
+ let isJSXComponent = element.match(/[A-Z]/) ? true : false;
295
+ if (isJSXComponent) {
296
+ return;
297
+ }
294
298
  if (Object.keys(attributes).length === 0) return;
295
299
 
296
300
 
@@ -300,7 +304,9 @@ function Compiler(func, file) {
300
304
  let value = attributes[key];
301
305
  let oldvalue = value;
302
306
  if (value && !value.new) {
307
+
303
308
  if (value && value.includes("={")) {
309
+
304
310
  value = value.replace("=", "");
305
311
  value == "undefined" ? (value = '"') : (value = value);
306
312
 
@@ -354,9 +360,15 @@ function Compiler(func, file) {
354
360
  switch (true) {
355
361
  case line.includes("useState") && !line.includes("import"):
356
362
  let varType = line.split("[")[0]
363
+ if (!line.split("=")[0].split(",")[1]) {
364
+ throw new Error('You forgot to value selector (useState) ' + ' at ' + `${file}:${string.split(line)[0].split('\n').length}`)
365
+ }
357
366
  let key = line.split("=")[0].split(",")[0].trim().split('[')[1];
358
367
 
359
- let setKey = line.split("=")[0].trim().split(",")[1].trim().replace("]", "");
368
+ if (!line.split("=")[0].split(",")[1]) {
369
+ throw new Error('You forgot to add a setter (useState) ' + ' at ' + `${file}:${string.split(line)[0].split('\n').length}`)
370
+ }
371
+ let setKey = line.split("=")[0].split(",")[1].trim().replace("]", "");
360
372
  key = key.replace("[", "").replace(",", "");
361
373
  let valuestate = line.split("=")[1].split("useState(")[1];
362
374
 
@@ -425,86 +437,80 @@ function Compiler(func, file) {
425
437
  let myChildrens = [];
426
438
 
427
439
  let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
428
- let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
429
- const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|\$=\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\}/gs;
430
-
431
-
432
-
433
-
434
-
435
- let props = component.match(dynamicAttributesRegex)
436
-
437
- let filteredProps = [];
438
- let isWithinComponent = false;
439
- let componentName = name
440
- let currentProps = []
440
+ let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
441
+ let props = component.includes('/>') ? component.split(`<`)[1].split('/>')[0] : component.split(`<`)[1].split(`>`)[0]
442
+ props = props.replaceAll(/\s+/g, " ").trim()
443
+ props = props.replace(name, '').trim()
444
+ component = component.replace(componentAttributes, '')
445
+ // or spread attributes {...{}, ...{}}
446
+ const dynamicAttributesRegex = /([a-zA-Z0-9_-]+)\s*=\s*("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{(.*.*?)\})*)*\})*)*\}|(?:\([^)]*\)|()\s*=>\s*(?:\{.*.*\})?|\{.*\})|\[[^\]]*\])|\${\{.*\}}/gs;
441
447
 
448
+ const attributeObject = {};
449
+ let propstring = ''
442
450
  let $_ternaryprops = []
451
+ let spreadRegex = /\$\{.*\}/gs;
452
+ let spreadMatch;
453
+ let hasSpread = false;
454
+ while ((spreadMatch = spreadRegex.exec(props)) !== null) {
455
+ let spread = spreadMatch[0].trim().replace(/\s+/g, " ");
456
+ if (!spread.includes('...')) {
457
+ continue
458
+ }
459
+ let old = spread;
460
+ spread = spread.trim().replace(/\s+/g, " ");
461
+ //${...( loaded ? { title: "Malik Whitten - Blog" } : { title: "Loading" } )} -> ...( loaded ? { title: "Malik Whitten - Blog" } : { title: "Loading" } )
443
462
 
444
- for (let prop of props) {
445
-
446
- if (prop === componentName) {
447
-
448
- isWithinComponent = true;
449
- filteredProps.push(prop);
450
- } else if (isWithinComponent && prop.includes('=')) {
451
-
452
- if (prop.startsWith('$=')) {
453
- let old = prop
454
- prop = prop.replace('$=', '$_ternary=')
455
-
456
- // remove trailing }
457
- prop = prop.replace(/}\s*$/, '')
458
- component = component.replace(old, prop)
459
- componentAttributes = componentAttributes.replace(old, prop)
460
-
461
- $_ternaryprops.push(prop)
463
+ let spreadContent = spread.match(/\$\{.*\}/gs) ? spread.match(/\$\{.*.*\}/gs)[0] : null
464
+ spreadContent = spread.split('${')[1]
462
465
 
463
- }
464
- else if (prop.includes('${')) {
466
+ spreadContent = spread.replace(/\${/, '')
467
+ spreadContent = spreadContent.replaceAll(/\s+/g, " ").trim()
468
+ spreadContent = spreadContent.replace(')}', ')').replace('}}', '}')
465
469
 
466
- prop = prop.replace('="', ':').replace('}"', '}')
467
- if (prop.includes('${')) {
468
- prop = prop.replace('="', ':')
469
- prop = prop.replace('${', '')
470
- prop = prop.replace('}', '')
470
+ propstring += spreadContent + ','
471
+ hasSpread = true
471
472
 
472
- }
473
- if (prop.includes('="${{')) {
474
- prop = prop.replace('${{', '{')
475
- prop = prop.replace('}}', '}')
476
- prop = prop.replace('="', ':')
477
- prop = prop.replace('}"', '}')
478
- }
473
+ }
479
474
 
480
- }
481
- else if (prop.startsWith('={')) {
482
- prop = prop.replace('={', ':`${')
483
- prop.replace('} ', '}`')
484
- }
475
+ let match;
485
476
 
486
- if (prop.includes('function')) {
487
- // parse 'function' to function
488
- prop = prop.replace("'", '')
477
+ // props right now is just a string with all of them on one line and a space between each
478
+ while ((match = dynamicAttributesRegex.exec(props)) !== null) {
479
+ let str = match[0].trim().replace(/\s+/g, " ");
480
+ if (!str.includes('=')) {
481
+ continue
482
+ }
489
483
 
490
- if (prop.endsWith("}'")) {
491
- prop = prop.replace("}'", '}')
484
+ if (hasSpread) {
492
485
 
493
- }
494
-
495
- prop = prop.replace('=function', ':function')
486
+ console.log('\x1b[33m', '🚨 Code confliction detected', '\x1b[0m')
487
+ let line = string.split(match[0])[0].split('\n').length
488
+ try {
489
+ throw new Error('You cannot use spread props and named props\n on the same component at ' + match[0] + ' at ' + `${file}:${line}`)
490
+ } catch (error) {
491
+ console.error(error)
492
+ process.exit(1)
496
493
  }
494
+ }
497
495
 
498
- filteredProps.push(prop);
499
-
496
+ str = str.replaceAll(/\s+/g, " ")
497
+ str = str.split('=')
498
+ let key = str[0].trim()
499
+ let value = str.slice(1).join('=').trim().match(/\{.*\}/gs) ? str.slice(1).join('=').trim().match(/\{.*\}/gs)[0] : str.slice(1).join('=').trim();
500
500
 
501
501
 
502
+ let isObject = value.startsWith('{{') && value.endsWith('}}')
503
+ if (isObject) {
504
+ value = value.split('{{')[1].split('}}')[0].trim()
505
+ value = `{${value}}`
506
+ propstring += `${key}:${value},`
507
+ } else {
508
+ // remove starting { and ending } using regex
509
+ value = value.replace(/^{/, '').replace(/}$/, '')
510
+ propstring += `${key}:${value},`
502
511
  }
512
+ propstring = propstring.replaceAll(/\s+/g, " ").trim()
503
513
 
504
-
505
- else {
506
- isWithinComponent = false;
507
- }
508
514
  }
509
515
  component = component.replaceAll(/\s+/g, " ");
510
516
 
@@ -513,10 +519,9 @@ function Compiler(func, file) {
513
519
  component = component.replace(prop, '')
514
520
  })
515
521
 
516
- let children = component.split(`<${name}`)[1].split(`</${name}>`)[0].trim().replace(/\s+/g, " ").trim().replace(/,$/, '').replace('>', '').replace(/\/$/, '').trim()
522
+ let children = new RegExp(`<${name}[^>]*>([^]*)<\/${name}>`, 'gs').exec(component)?.[1] || null;
517
523
 
518
524
 
519
- props = filteredProps.join(',').replace(/\s+/g, " ").trim().replace(/,$/, '')
520
525
 
521
526
  let savedname = name;
522
527
 
@@ -549,24 +554,15 @@ function Compiler(func, file) {
549
554
 
550
555
 
551
556
 
552
-
553
- props = props.replaceAll(`,${savedname}`, '').replaceAll(savedname, '')
554
- if (props.startsWith(',')) {
555
- props = props.replace(',', '')
556
- }
557
- props = props.replaceAll("='", ":'")
558
- .replaceAll('=`', ':`')
559
- .replaceAll('="', ':"')
560
- .replaceAll('={', ':')
557
+ propstring = propstring.replace(/,$/, '')
561
558
 
562
559
 
563
560
  /**
564
561
  * @memoize - memoize a component to be remembered on each render and replace the old jsx
565
562
  */
566
563
 
567
-
568
564
  let replace = "";
569
- replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join('')}\`]))}`;
565
+ replace = `\${this.memoize(this.createComponent(${savedname}, {${propstring}}, [\`${myChildrens.join('')}\`]))}`;
570
566
 
571
567
 
572
568
  body = body.replace(before, replace);
@@ -582,48 +578,49 @@ function Compiler(func, file) {
582
578
  let replaceMents = [];
583
579
 
584
580
 
585
- for (let match of imports) {
586
- let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
587
- switch (true) {
588
- case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
589
- let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
590
- componentFolder = componentFolder.split(process.cwd())[1]
591
- if (!fs.existsSync(process.cwd() + componentFolder)) {
592
- throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
593
- }
581
+ if (imports) {
582
+ for (let match of imports) {
583
+ let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
584
+ switch (true) {
585
+ case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
586
+ let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
587
+ componentFolder = componentFolder.split(process.cwd())[1]
588
+ if (!fs.existsSync(process.cwd() + componentFolder)) {
589
+ throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
590
+ }
594
591
 
595
- if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
596
- fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
597
- }
592
+ if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
593
+ fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
594
+ }
598
595
 
599
- let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
600
- let glp = globSync('**/**/**/**.{jsx,js}', {
601
- cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
602
- absolute: true,
603
- recursive: true
604
- })
605
- for (let file of glp) {
606
- let text = fs.readFileSync(file, "utf8");
607
- if (!file.endsWith('.js') && file.endsWith('.jsx')) {
608
- text = Compiler(text, file);
596
+ let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
597
+ let glp = globSync('**/**/**/**.{jsx,js}', {
598
+ cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
599
+ absolute: true,
600
+ recursive: true
601
+ })
602
+ for (let file of glp) {
603
+ let text = fs.readFileSync(file, "utf8");
604
+ if (!file.endsWith('.js') && file.endsWith('.jsx')) {
605
+ text = Compiler(text, file);
609
606
 
607
+ }
608
+ let dest = file.split('node_modules')[1]
609
+ dest = dest.split(baseFolder)[1]
610
+ writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
611
+ let importname = match.split('import')[1].split('from')[0].trim()
612
+ let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
613
+ let newImport = `/src/${baseFolder + dest}`
614
+ newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
615
+ replaceMents.push({ match: oldImportstring, replace: newImport })
616
+ console.log(`📦 imported Node Package ${baseFolder} `)
610
617
  }
611
- let dest = file.split('node_modules')[1]
612
- dest = dest.split(baseFolder)[1]
613
- // write to dist
614
- writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
615
- let importname = match.split('import')[1].split('from')[0].trim()
616
- let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
617
- let newImport = `/src/${baseFolder + dest}`
618
- newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
619
- replaceMents.push({ match: oldImportstring, replace: newImport })
620
- console.log(`📦 imported Node Package ${baseFolder} `)
621
- }
622
618
 
623
619
 
624
- break;
625
- default:
626
- break;
620
+ break;
621
+ default:
622
+ break;
623
+ }
627
624
  }
628
625
  }
629
626
 
@@ -644,7 +641,7 @@ function Compiler(func, file) {
644
641
  string = string.replaceAll(/\$\{\/\*.*\*\/\}/gs, "");
645
642
  string = string.replaceAll("<>", "`").replaceAll("</>", "`");
646
643
  string = string.replaceAll(".jsx", ".js");
647
- string = parseComponents(string);
644
+
648
645
 
649
646
  string = string
650
647
  .replaceAll("className", "class")
@@ -810,236 +807,165 @@ function Compiler(func, file) {
810
807
 
811
808
  globalThis.isBuilding = false
812
809
  globalThis.isWriting = null
813
- const glb = await glob("**/**/**/**.{jsx,js}", {
814
- ignore: ["node_modules/**/*", "dist/**/*"],
815
- cwd: process.cwd() + '/pages/',
816
- absolute: true,
817
- recursive: true
818
- });
810
+
811
+ let hasRendered = []
812
+
819
813
  async function Build() {
814
+ const glb = await glob("**/**/**/**.{jsx,js}", {
815
+ ignore: ["node_modules/**/*", "dist/**/*"],
816
+ cwd: process.cwd() + '/pages/',
817
+ absolute: true,
818
+ recursive: true
819
+ });
820
+
820
821
  globalThis.isBuilding = true
821
- console.log('Compiling......')
822
+ console.log(globalThis.isProduction ? 'Creating Optimized Production Build\n' : '')
823
+ let str = `Page \t\t\t\t Size\n`
824
+ globalThis.isProduction ? console.log('\x1b[32m%s\x1b[0m', str) : null
822
825
  let reader = async (file) => {
823
826
  let text = await fs.readFileSync(file, "utf8");
824
827
  return text;
825
828
  };
826
829
 
827
-
830
+
831
+
828
832
  function ssg(routes = []) {
829
833
  globalThis.isBuilding = true
830
- console.log(`Generating html files for ${routes.length} routes`)
834
+ let server = http.createServer((req, res) => {
835
+ let route = routes.find((e) => e.url === req.url)
836
+ if (route) {
837
+ let document = globalThis.routeDocuments.find((e) => e.url === req.url)
838
+ res.writeHead(200, { 'Content-Type': 'text/html' });
839
+ res.end(document.document);
840
+ } else {
841
+ const filePath = process.cwd() + '/dist/' + req.url
842
+
843
+ fs.readFile(filePath, (err, data) => {
844
+ if (err) {
845
+ res.writeHead(404, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
846
+ res.end('File not found');
847
+ } else {
848
+ res.writeHead(200, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
849
+ res.end(data);
850
+ }
851
+ });
852
+ }
853
+ });
854
+
855
+ let port = 12000
856
+ server.on('error', (err) => {
857
+ if (err.code === 'EADDRINUSE') {
858
+ setTimeout(() => {
859
+ server.close();
860
+ server.listen(++port);
861
+ }, 1000);
862
+ }
863
+ })
864
+
865
+ const browser = puppeteer.launch({
866
+ headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
867
+ warning: false,
868
+ })
869
+ server.listen(port);
870
+
831
871
  routes.forEach(async (route) => {
832
872
  if (route.url.includes(':')) {
833
- console.log('Route ' + route.url + ' is a dynamic route and will not be generated')
834
873
  return
835
874
  }
836
- let equalparamroute = routes.map((e) => {
837
- if (e.url.includes(':')) {
838
- let url = e.url.split('/:')[0]
839
- if (url && route.url === url) {
840
- return e
841
- } else {
842
- return null
843
-
844
- }
845
- }
846
- return null
847
- }).filter(Boolean)
848
- let document = `
849
- <!DOCTYPE html>
850
- <html lang="en">
851
- <head>
852
- <script>
853
- window.routes = JSON.parse('${JSON.stringify(routes)}')
854
- </script>
855
- <script id="isServer">
856
- window.isServer = true
857
- </script>
858
- <meta charset="UTF-8">
859
- <meta name="viewport" content="width=device-width,initial-scale=1.0">
860
- <script type="module" id="meta">
861
- window.history.pushState({}, '', '${route.url}')
862
- window.module = await import('/${route.fileName.replace('.jsx', '.js')}')
863
- let metadata = await module.$metadata
864
- if(metadata && metadata.title){
865
- document.head.innerHTML += '<title>' + metadata.title + '</title>'
866
- }
867
- if(metadata && metadata.description){
868
- document.head.innerHTML += '<meta name="description" content="' + metadata.description + '">'
869
- }
870
- if(metadata && metadata.keywords){
871
- document.head.innerHTML += '<meta name="keywords" content="' + metadata.keywords + '">'
872
- }
873
- if(metadata && metadata.author){
874
- document.head.innerHTML += '<meta name="author" content="' + metadata.author + '">'
875
- }
876
- if(metadata && metadata.image){
877
- let image = metadata.image.file
878
- let type = metadata.image.type
879
-
880
- document.head.innerHTML += '<meta property="og:image" content="' + image + '">'
881
- document.head.innerHTML += '<meta property="og:image:type" content="' + type + '">'
882
- }
883
- if(metadata && metadata.url){
884
- document.head.innerHTML += '<meta property="og:url" content="' + metadata.url + '">'
885
- }
886
-
887
- if(metadata && metadata.robot){
888
- document.head.innerHTML += '<meta name="robots" content="' + metadata.robot + '">'
889
- }
890
- if(metadata && metadata.manifest){
891
- document.head.innerHTML += '<link rel="manifest" href="' + metadata.manifest + '">'
892
- }
893
- if(metadata && metadata.tags){
894
- metadata.tags.forEach(tag => {
895
- document.head.innerHTML += tag
896
- })
897
- }
875
+ globalThis.listen = true;
898
876
 
899
- if(metadata && metadata.styles){
900
- metadata.styles.forEach(style => {
901
- style = style.replaceAll('./', '/')
902
- style = style.replaceAll('../', '/')
903
- style = style.replace("'", '')
904
- document.head.innerHTML += '<link rel="stylesheet" href="' + style + '">'
905
- })
906
- }
907
- if(metadata && metadata.icon){
908
- document.head.innerHTML += '<link rel="icon" href="' + metadata.icon + '">'
909
- }
910
- </script>
911
- <script type="module" id="router">
912
- import VaderRouter from '/router.js'
913
- const router = new VaderRouter('${route.url}', 3000)
914
- router.get('${route.url}', async (req, res) => {
915
- let module = await import('/${route.fileName.replace('.jsx', '.js')}')
916
- if(Object.keys(module).includes('$prerender') && !module.$prerender){
917
- document.head.setAttribute('prerender', 'false')
918
- }
919
- res.render(module, req, res, module.$metadata)
920
- })
921
- ${equalparamroute.length > 0 ? equalparamroute.map((e) => {
922
-
923
-
924
-
925
- return `router.get('${e.url}', async (req, res) => {
926
- let module = await import('/${e.fileName.replace('.jsx', '.js')}')
927
- res.render(module, req, res, module.$metadata)
928
- })\n`
929
- }) : ''}
930
- router.listen(3000)
931
-
932
- </script>
933
- </head>
934
- <body>
935
- <div id="root"></div>
936
- </body>
937
-
938
-
939
- </html>
940
- `;
877
+ try {
941
878
 
942
- // generate random but common ports
943
- let port = Math.floor(Math.random() * (65535 - 49152 + 1) + 49152)
879
+ route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
880
+ let page = (await browser).newPage()
881
+ page.then(async (page) => {
882
+ page.on('error', (err) => {
883
+ console.error('JS ERROR:', JSON.parse(err));
884
+ });
885
+ try {
886
+ page.on('pageerror', async err => {
887
+ let errorObj = JSON.parse(await page.evaluate(() => document.documentElement.getAttribute('error')) || '{}')
888
+ console.log('\x1b[31m%s\x1b[0m', 'Compiler Error:', errorObj)
889
+
890
+ console.log('\x1b[31m%s\x1b[0m', 'Error:', err)
891
+ });
892
+ } catch (error) {
893
+ page.close()
894
+ }
895
+ page.on('crash', () => {
896
+ console.error(`Render process crashed for ${route.url}`)
897
+ });
944
898
 
945
- const server = http.createServer((req, res) => {
899
+ await page.goto(`http://localhost:${port}${route.url}`, { waitUntil: 'networkidle2' });
946
900
 
947
- if (req.url === '/') {
948
- res.writeHead(200, { 'Content-Type': 'text/html' });
949
- res.end(document);
950
- } else {
951
- // Serve static files (adjust the file paths based on your project structure)
952
- const filePath = process.cwd() + '/dist/' + req.url
953
-
954
- fs.readFile(filePath, (err, data) => {
955
- if (err) {
956
- res.writeHead(404, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
957
- res.end('File not found');
958
- } else {
959
- res.writeHead(200, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
960
- res.end(data);
901
+ page.evaluate(() => {
902
+ document.querySelector('#meta').remove()
903
+ document.querySelector('#isServer').innerHTML = 'window.isServer = false'
904
+ if (document.head.getAttribute('prerender') === 'false') {
905
+ document.querySelector('#root').innerHTML = ''
906
+ console.log(`Disabled prerendering for ${window.location.pathname}`)
961
907
  }
962
- });
963
- }
964
- });
908
+ })
909
+ let html = await page.content();
965
910
 
966
- server.listen(port)
967
- server.on('error', (err) => {
968
- if (err.code === 'EADDRINUSE') {
969
- console.log(`Port ${port} is in use, trying another port...`);
970
- setTimeout(() => {
971
- server.close();
972
- server.listen(++port);
973
- }, 1000);
974
- }
975
- })
911
+ html = await prettier.format(html, { parser: "html" })
976
912
 
977
- globalThis.listen = true;
913
+ writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
978
914
 
979
- const browser = await puppeteer.launch({
980
- headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
981
- warning: false,
982
- })
983
-
984
- const browserPID = browser.process().pid
985
- try {
915
+ console.log(`\x1b[32m%s\x1b[0m`, `Prerendered ${route.url}...`)
986
916
 
987
- route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
988
- let page = await browser.newPage();
989
- await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle2' });
990
- await page.on('console', msg => console.log('PAGE LOG:', msg.text()));
991
- await page.on('error', err => console.log('PAGE LOG:', err));
992
- await page.on('pageerror', err => console.log('PAGE LOG:', err));
993
- await page.evaluate(() => {
994
- window.onerror = function (msg, url, lineNo, columnNo, error) {
995
- console.log(msg, url, lineNo, columnNo, error)
996
- }
917
+ hasRendered.push(route.url)
997
918
  })
998
- await page.waitForSelector('#root', { timeout: 10000 })
999
- await page.evaluate(() => {
1000
- document.getElementById('meta').remove()
1001
- document.querySelector('#isServer').innerHTML = 'window.isServer = false'
1002
- if (document.head.getAttribute('prerender') === 'false') {
1003
- document.querySelector('#root').innerHTML = ''
1004
- console.log(`Disabled prerendering for ${window.location.pathname}`)
1005
- }
1006
- })
1007
- const html = await page.content();
1008
919
 
1009
- await page.close();
1010
- await writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
1011
- await browser.close();
1012
- server.close()
920
+
921
+
1013
922
 
1014
923
  } catch (error) {
1015
- server.close()
1016
- await browser.close();
924
+ console.log('\x1b[31m%s\x1b[0m', 'Error:', error)
925
+
1017
926
  }
1018
927
  finally {
1019
- await browser.close();
1020
- server.close()
1021
- }
1022
- try {
1023
- process.kill(browserPID)
1024
- } catch (error) {
1025
928
  }
929
+ })
930
+
1026
931
 
1027
932
 
1028
- })
1029
933
 
1030
- let timeout = setTimeout(() => {
934
+
935
+ function kill() {
936
+ console.log(`\x1b[32m%s\x1b[0m`, `\nPrerendered ${routes.length} pages...\n`)
937
+ server.close()
938
+ browser.then((browser) => {
939
+ browser.close()
940
+ })
941
+ hasRendered = []
1031
942
  globalThis.isBuilding = false
1032
- clearTimeout(timeout)
1033
- }, 1000)
1034
- console.log(`Generated ${routes.length} html files for ${routes.length} routes`)
1035
- }
1036
943
 
944
+ }
945
+
946
+ if (hasRendered.length === routes.length) {
947
+ kill()
948
+ } else {
949
+ console.log(`\x1b[32m%s\x1b[0m`, `Prerendering ${routes.length} pages...\n`)
950
+ let interval = setInterval(() => {
951
+ if (hasRendered.length === routes.length) {
952
+ kill()
953
+ clearInterval(interval)
954
+ }
955
+ }, 1000);
956
+ }
957
+ }
1037
958
  globalThis.routes = []
959
+ globalThis.paramRoutes = []
1038
960
 
1039
961
  for await (let file of glb) {
1040
962
  // Normalize file paths
1041
963
  let origin = file.replace(/\\/g, '/');
1042
964
  let fileName = origin.split('/pages/')[1].split('.jsx')[0].replace('.jsx', '') + '.jsx';
965
+ if (file.endsWith('.js')) {
966
+ console.warn('\x1b[33m%s\x1b[0m', `Warning: js files in the pages directory are not supported. Skipping ${file}`)
967
+ continue
968
+ }
1043
969
  let isBasePath = fileName === 'index.jsx';
1044
970
  let isParamRoute = fileName.includes('[') && fileName.includes(']') ? true : false
1045
971
 
@@ -1063,17 +989,15 @@ async function Build() {
1063
989
 
1064
990
 
1065
991
  let data = await fs.readFileSync(origin, "utf8");
1066
- console.log(`Compiling ${fileName}...`)
1067
- data = Compiler(data, origin);
1068
-
1069
992
 
1070
- await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data).then(async () => {
1071
-
1072
-
1073
-
1074
- await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data)
1075
-
1076
- })
993
+ // gen sourcemap if not production
994
+ let size = fs.statSync(origin).size;
995
+ if (!globalThis.isProduction) {
996
+ let { sourceMap } = sourceMapGen({ origin: origin, fileName: fileName }, await Compiler(data, origin))
997
+ data = data + `\n//# sourceMappingURL=/src/maps/${fileName.replace('.jsx', '.js.map')}\n //#sourceURL=${origin}`
998
+ await writer(process.cwd() + "/dist/src/maps/" + fileName.replace('.jsx', '.js.map'), JSON.stringify(sourceMap, null, 2))
999
+ }
1000
+ await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), await Compiler(data, origin))
1077
1001
 
1078
1002
  // configure routing for each page
1079
1003
 
@@ -1135,13 +1059,148 @@ async function Build() {
1135
1059
  }
1136
1060
 
1137
1061
 
1138
- globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
1062
+ if (obj.url.includes(':') && !paramRoutes.includes(obj.url)) {
1063
+ globalThis.paramRoutes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
1064
+ } else {
1139
1065
 
1066
+ globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
1067
+ }
1140
1068
 
1069
+ // check if route has a index.html file
1141
1070
 
1071
+ if (!fs.existsSync(process.cwd() + '/dist/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html'))
1072
+ && !obj.url.includes(':') && !globalThis.isProduction
1073
+ ) {
1074
+ let document = `<!DOCTYPE html>
1075
+ <html lang="en">
1076
+ <head>
1077
+ <meta charset="UTF-8">
1078
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1079
+ <title>${obj.url}</title>
1080
+ <script>
1081
+ window.routes = JSON.parse('${JSON.stringify(globalThis.routes)}')
1082
+ </script>
1083
+ <script type="module">
1084
+ import VaderRouter from '/router.js'
1085
+ const router = new VaderRouter('${obj.url}')
1086
+ router.get('${obj.url}', async (req, res) => {
1087
+ let module = await import('${obj.url === '/' ? 'index.js' : obj.url}/index.js')
1088
+ if(Object.keys(module).includes('$prerender') && !module.$prerender){
1089
+ document.head.setAttribute('prerender', 'false')
1090
+ }
1091
+ res.render(module, req, res, module.$metadata)
1092
+ })
1093
+ router.listen(3000)
1094
+ </script>
1095
+ </head>
1096
+ <body>
1097
+ <div id="root"></div>
1098
+ </body>
1099
+ </html>`
1100
+ writer(process.cwd() + '/dist/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html'), document)
1101
+ }
1102
+
1103
+ let stats = {
1104
+ route: obj.url.padEnd(30),
1105
+ size: Math.round(size / 1000) + 'kb',
1106
+ letParentFolder: obj.url.split('/').slice(0, -1).join('/'),
1107
+ isChildRoute: obj.url.split('/').slice(0, -1).join('/').includes(':') ? true : false,
1108
+ parentRoute: obj.url.split('/').slice(0, -1).join('/').split(':')[0],
1109
+
1110
+ }
1111
+ stats.isChildRoute ? stats.route = `? ${obj.url}` : null
1112
+ let string = `${isBasePath ? '+' : '+'} ${stats.route.padEnd(30)} ${stats.size}`
1113
+
1114
+ globalThis.isProduction ? console.log(string) : null
1142
1115
  }
1143
1116
 
1144
- ssg(globalThis.routes)
1117
+ globalThis.routeDocuments = []
1118
+ globalThis.routes.map((route) => {
1119
+ let equalparamroute = globalThis.paramRoutes.map((e) => {
1120
+ console
1121
+ if (e.url.includes(':')) {
1122
+ let url = e.url.split('/:')[0]
1123
+ if (url && route.url === url) {
1124
+ return e
1125
+ } else {
1126
+ return null
1127
+
1128
+ }
1129
+ }
1130
+ return null
1131
+ }).filter(Boolean)
1132
+ let document = `
1133
+ <!DOCTYPE html>
1134
+ <html lang="en">
1135
+ <head>
1136
+ <script>
1137
+ window.routes = JSON.parse('${JSON.stringify(globalThis.routes)}')
1138
+ </script>
1139
+ <script type="module" id="meta">
1140
+ window.history.pushState({}, '', '${route.url}')
1141
+
1142
+ </script>
1143
+ <script id="isServer">
1144
+ window.isServer = true
1145
+ </script>
1146
+ <meta charset="UTF-8">
1147
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
1148
+
1149
+ <script type="module" id="router">
1150
+ import VaderRouter from '/router.js'
1151
+ const router = new VaderRouter('${route.url}')
1152
+ router.get('${route.url}', async (req, res) => {
1153
+ try{
1154
+ let module = await import('/${route.fileName.replace('.jsx', '.js')}')
1155
+ if(Object.keys(module).includes('$prerender') && !module.$prerender){
1156
+ document.head.setAttribute('prerender', 'false')
1157
+ }
1158
+ res.render(module, req, res, module.$metadata)
1159
+ }
1160
+ catch(error){
1161
+ let errorMessage = {
1162
+ message: error.message,
1163
+ name: error.name,
1164
+ stack: error.stack,
1165
+ path: window.location.pathname
1166
+ };
1167
+
1168
+
1169
+ document.documentElement.setAttribute('error', JSON.stringify(errorMessage));
1170
+ throw new Error(error)
1171
+ }
1172
+ })
1173
+ ${equalparamroute.length > 0 ? equalparamroute.map((e) => {
1174
+
1175
+
1176
+
1177
+ return `router.get('${e.url}', async (req, res) => {
1178
+ let module = await import('/${e.fileName.replace('.jsx', '.js')}')
1179
+ res.render(module, req, res, module.$metadata)
1180
+ })\n`
1181
+ }) : ''}
1182
+ router.listen(3000)
1183
+
1184
+ </script>
1185
+ </head>
1186
+ <body>
1187
+ <div id="root"></div>
1188
+ </body>
1189
+
1190
+
1191
+ </html>
1192
+ `;
1193
+ globalThis.routeDocuments.push({ url: route.url, document: document })
1194
+ })
1195
+
1196
+ if (globalThis.devMode && !globalThis.oneAndDone) {
1197
+ ssg(globalThis.routes)
1198
+ globalThis.oneAndDone = true
1199
+ console.log(`In Development Mode, Prerendering ${globalThis.routes.length} pages... Once`)
1200
+ }
1201
+ else if (globalThis.isProduction) {
1202
+ ssg(globalThis.routes)
1203
+ }
1145
1204
 
1146
1205
 
1147
1206
  const scannedSourceFiles = await glob("**/**.{jsx,js,json}", {
@@ -1169,23 +1228,25 @@ async function Build() {
1169
1228
  scannedSourceFiles.forEach(async (file) => {
1170
1229
  file = file.replace(/\\/g, '/');
1171
1230
  let name = file.split('/src/')[1]
1172
- //parse jsx
1173
1231
 
1174
1232
  let data = await reader(process.cwd() + "/src/" + name)
1175
1233
  if (name.includes('.jsx')) {
1176
- data = Compiler(data, process.cwd() + "/src/" + name);
1177
-
1178
- await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), data).then(async () => {
1179
- await writer(process.cwd() + "/dist/src/" + name.replace('.jsx', '.js'), data)
1180
-
1181
- })
1234
+ let origin = process.cwd() + "/src/" + name
1235
+ if (!globalThis.isProduction) {
1236
+ let { sourceMap } = sourceMapGen({ origin: origin, fileName: name }, await Compiler(data, origin))
1237
+ data = data + `\n//# sourceMappingURL=/src/maps/${name.replace('.jsx', '.js.map')}\n //#sourceURL=${origin}`
1238
+ await writer(process.cwd() + "/dist/src/maps/" + name.replace('.jsx', '.js.map'), JSON.stringify(sourceMap, null, 2))
1239
+ }
1240
+ await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), await Compiler(data, origin))
1182
1241
  return
1183
1242
  }
1184
- bundleSize += fs.statSync(process.cwd() + "/src/" + name).size;
1243
+ if (!name.includes('.map')) {
1244
+ bundleSize += fs.statSync(process.cwd() + "/src/" + name).size;
1245
+ }
1185
1246
  await writer(process.cwd() + "/dist/src/" + name, data);
1186
1247
  })
1187
1248
 
1188
- const scannedPublicFiles = await glob("**/**.{css,js,html,mjs,cjs}", {
1249
+ const scannedPublicFiles = await glob("**/**/**.{css,js,html,mjs,cjs,png,jpg,jpeg,gif,svg,mp4,webm,ogg}", {
1189
1250
  ignore: ["node_modules/**/*", "dist/**/*"],
1190
1251
  cwd: process.cwd() + '/public/',
1191
1252
  absolute: true,
@@ -1193,7 +1254,7 @@ async function Build() {
1193
1254
  scannedPublicFiles.forEach(async (file) => {
1194
1255
  file = file.replace(/\\/g, '/');
1195
1256
  file = file.split('/public/')[1]
1196
- let data = await reader(process.cwd() + "/public/" + file)
1257
+ let data = fs.readFileSync(process.cwd() + "/public/" + file);
1197
1258
  bundleSize += fs.statSync(process.cwd() + "/public/" + file).size;
1198
1259
  await writer(process.cwd() + "/dist/public/" + file, data);
1199
1260
  })
@@ -1223,26 +1284,78 @@ async function Build() {
1223
1284
  await writer(process.cwd() + "/dist/" + file, data);
1224
1285
  });
1225
1286
 
1287
+
1226
1288
  }
1227
1289
 
1228
1290
  globalThis.isBuilding = false
1229
- console.log(`📦 Build completed: Build Size -> ${Math.round(bundleSize / 1000)}kb`)
1230
1291
 
1292
+ globalThis.isProduction ? console.log(`Total Bundle Size: ${Math.round(bundleSize / 1000)}kb`) : null
1231
1293
  bundleSize = 0;
1294
+ if (!globalThis.isBuilding && !globalThis.isProduction) {
1295
+ let folders = fs.readdirSync(process.cwd() + '/dist/', { withFileTypes: true })
1296
+ let mapfolders = fs.readdirSync(process.cwd() + '/dist/src/maps/', { withFileTypes: true }) || []
1297
+ console.log(`Cleaning up dist folder...`)
1298
+ folders.forEach((folder) => {
1299
+ // exclude files
1300
+ if (folder.name.includes('src') || folder.name.includes('public') || folder.name.includes('pages')
1301
+ || !folder.isDirectory()
1302
+ ) {
1303
+ return
1304
+ }
1305
+
1306
+ let existsInPages = fs.existsSync(process.cwd() + '/pages/' + folder.name)
1232
1307
 
1308
+ if (existsInPages) {
1309
+ return
1310
+ }
1311
+ fs.rm(process.cwd() + '/dist/' + folder.name, { recursive: true }, (err) => {
1312
+ if (err) {
1313
+ throw err
1314
+ }
1315
+ })
1316
+ })
1317
+ mapfolders.forEach((folder) => {
1318
+
1319
+ let existsInPages = fs.existsSync(process.cwd() + '/pages/' + folder.name)
1320
+ let existsInSrc = fs.existsSync(process.cwd() + '/src/' + folder.name)
1321
+ if (existsInPages || existsInSrc) {
1322
+ return
1323
+ }
1324
+
1325
+ let name = folder.name.includes('.js.map') ? folder.name.split('.js.map').join('.js') : folder.name
1326
+ if (fs.existsSync(process.cwd() + '/dist/src/maps/' + name)) {
1327
+ fs.rm(process.cwd() + '/dist/src/maps/' + name, { recursive: true }, (err) => {
1328
+ if (err) {
1329
+ throw err
1330
+ }
1331
+ })
1332
+ }
1333
+
1334
+
1335
+
1336
+ })
1337
+ }
1233
1338
  return true
1234
1339
  }
1235
- const s = () => {
1340
+ const s = (port) => {
1236
1341
 
1237
1342
  const server = http.createServer((req, res) => {
1238
1343
 
1239
- const validExtensions = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'];
1344
+ const validExtensions = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg', '.map']
1240
1345
 
1241
1346
  if (!validExtensions.some(ext => req.url.endsWith(ext))) {
1242
- req.url = req.url !== '/' ? req.url.split('/')[1] : req.url;
1347
+ req.url = req.url + '/'
1348
+ let baseRoute = '/' + req.url.split('/')[1]
1349
+ let paramRoute = globalThis.paramRoutes.find((e) => {
1350
+ let base = e.url.split('/:')[0]
1351
+ return base === baseRoute
1352
+ })
1353
+ if (paramRoute) {
1354
+ req.url = baseRoute
1355
+ }
1243
1356
  req.url = path.join(process.cwd(), 'dist', req.url, 'index.html');
1244
1357
  } else {
1245
- req.url = path.join(process.cwd(), 'dist', req.url);
1358
+ req.url = path.join(process.cwd(), 'dist/', req.url);
1246
1359
  }
1247
1360
 
1248
1361
  const filePath = req.url
@@ -1259,6 +1372,7 @@ const s = () => {
1259
1372
  let ws = new WebSocket('ws://localhost:${process.env.PORT || 3000}')
1260
1373
  ws.onmessage = (e) => {
1261
1374
  if(e.data === 'reload'){
1375
+ console.log('Reloading...')
1262
1376
  window.location.reload()
1263
1377
  }
1264
1378
  }
@@ -1280,7 +1394,7 @@ const s = () => {
1280
1394
 
1281
1395
 
1282
1396
  function getContentType(filePath) {
1283
- let ext = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'].includes(path.extname(filePath)) ? path.extname(filePath) : '.html'
1397
+ 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'
1284
1398
  switch (ext) {
1285
1399
  case '.js':
1286
1400
  return 'text/javascript';
@@ -1292,6 +1406,8 @@ const s = () => {
1292
1406
  return 'text/javascript';
1293
1407
  case '.html':
1294
1408
  return 'text/html';
1409
+ case '.map':
1410
+ return 'application/json';
1295
1411
  case '.json':
1296
1412
  return 'application/json';
1297
1413
  case '.png':
@@ -1315,34 +1431,29 @@ const s = () => {
1315
1431
  }
1316
1432
  }
1317
1433
 
1318
- const PORT = process.env.PORT || 3000;
1319
- server.listen(PORT, () => {
1320
- console.log(`Server is running on port ${PORT}`);
1434
+ server.listen(port, () => {
1435
+ console.log(`Server is running on port ${port}`);
1436
+ globalThis.ws = ws
1321
1437
  });
1322
- let i =
1323
- setInterval(() => {
1324
- if (globalThis.isBuilding && globalThis.devMode) {
1325
1438
 
1326
- ws.clients.forEach((client) => {
1327
- client.send('reload')
1328
- })
1329
- } else {
1330
- clearInterval(i)
1331
- }
1332
- }, 120)
1333
1439
 
1334
1440
  }
1335
1441
 
1336
1442
 
1337
1443
  switch (true) {
1338
- case process.argv.includes('--watch') && !process.argv.includes('--build') && !process.argv.includes('--serve'):
1444
+ case process.argv.includes('dev') && !process.argv.includes('build') && !process.argv.includes('start'):
1339
1445
 
1340
1446
  globalThis.devMode = true
1447
+ globalThis.isProduction = false
1448
+
1449
+ let p = process.env.PORT || config.port || process.argv.includes('-p') ? process.argv[process.argv.indexOf('-p') + 1] : 3000
1450
+ globalThis.oneAndDone = false
1341
1451
  console.log(`
1342
- Vader.js v1.3.3
1452
+ Vader.js v${fs.readFileSync(process.cwd() + '/node_modules/vaderjs/package.json', 'utf8').split('"version": "')[1].split('"')[0]}
1343
1453
  - Watching for changes in ./pages
1344
1454
  - Watching for changes in ./src
1345
1455
  - Watching for changes in ./public
1456
+ - Serving on port ${p}
1346
1457
  `)
1347
1458
  !globalThis.isBuilding ? Build() : null
1348
1459
 
@@ -1354,51 +1465,63 @@ Vader.js v1.3.3
1354
1465
  if (event == 'change'
1355
1466
  && !globalThis.isBuilding
1356
1467
  ) {
1468
+ if (globalThis.ws && !globalThis.isWriting) {
1469
+ globalThis.ws.clients.forEach((client) => {
1470
+ console.log('Reloading...')
1471
+ client.send('reload')
1472
+ })
1473
+ }
1357
1474
 
1475
+ console.log('\nRebuilding...')
1476
+ globalThis.isBuilding = true
1358
1477
  Build()
1359
1478
  }
1360
1479
  }).on('error', (err) => console.log(err))
1361
1480
  })
1362
- let p = process.argv[process.argv.indexOf('--watch') + 1] || process.env.PORT || 3000
1363
-
1364
- process.env.PORT = p
1365
- s()
1481
+ s(p)
1366
1482
 
1367
1483
  globalThis.listen = true;
1368
1484
 
1369
1485
  break;
1370
- case process.argv.includes('--build') && !process.argv.includes('--watch') && !process.argv.includes('--serve'):
1486
+ case process.argv.includes('build') && !process.argv.includes('dev') && !process.argv.includes('start'):
1371
1487
  globalThis.devMode = false
1488
+ globalThis.isProduction = true
1489
+ globalThis.routeStates = []
1372
1490
  console.log(`
1373
1491
  Vader.js v1.3.3
1374
1492
  Building to ./dist
1375
1493
  `)
1494
+ if (fs.existsSync(process.cwd() + '/dist/src/maps')) {
1495
+ fs.rmSync(process.cwd() + '/dist/src/maps', { recursive: true })
1496
+ }
1376
1497
  Build()
1377
1498
 
1378
1499
  break;
1379
- case process.argv.includes('--serve') && !process.argv.includes('--watch') && !process.argv.includes('--build'):
1380
- let port = process.argv[process.argv.indexOf('--serve') + 1] || 3000
1381
- process.env.PORT = port
1500
+ case process.argv.includes('start') && !process.argv.includes('dev') && !process.argv.includes('build'):
1501
+ let port = process.env.PORT || config.port || process.argv.includes('-p') ? process.argv[process.argv.indexOf('-p') + 1] : 3000
1502
+ console.log(port)
1382
1503
  globalThis.devMode = false
1383
1504
  console.log(`
1384
1505
  Vader.js v1.3.3
1385
1506
  Serving ./dist on port ${port}
1386
1507
  url: http://localhost:${port}
1387
1508
  `)
1388
- s()
1509
+ s(port)
1389
1510
  break;
1390
1511
  default:
1391
- console.log(`
1512
+ // add color
1513
+ console.log(`
1392
1514
  Vader.js is a reactive framework for building interactive applications for the web built ontop of bun.js!
1393
1515
 
1394
1516
  Usage: vader <command>
1395
1517
 
1396
1518
  Commands:
1397
- --watch (port) Watch the pages folder for changes with hot reloading
1519
+
1520
+ vaderjs dev -p <number> Start the development server
1398
1521
 
1399
- --build Build the project to ./dist
1522
+ vaderjs build Build the project to ./dist
1400
1523
 
1401
- --serve (400) Serve the project on a port (default 3000 or process.env.PORT)
1524
+ vaderjs start -p <number> Production Mode (default 3000 or process.env.PORT)
1402
1525
 
1403
1526
  Learn more about vader: https://vader-js.pages.dev/
1404
1527