vaderjs 1.4.6 → 1.4.8

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/README.MD CHANGED
@@ -45,7 +45,7 @@ Keyword folders - all files are passed from these folders to the build folder
45
45
  # Define your config
46
46
 
47
47
  ```ts
48
- import defineConfig from "../config";
48
+ import defineConfig from "vaderjs/config";
49
49
 
50
50
  export default defineConfig({
51
51
  port: 3000,
@@ -0,0 +1,97 @@
1
+ import {
2
+ Component,
3
+ e,
4
+ useState,
5
+ useEffect,
6
+ useFetch,
7
+ useAsyncState,
8
+ Fragment,
9
+ } from "vaderjs";
10
+ import { document } from "vaderjs/document";
11
+ import fs from "fs";
12
+ import ansiColors from "ansi-colors";
13
+ let path2 = require("path");
14
+ globalThis.Fragment = Fragment;
15
+ globalThis.window = {
16
+ location: {
17
+ hash: "",
18
+ host: "",
19
+ },
20
+ };
21
+ globalThis.Component = Component;
22
+ globalThis.e = e;
23
+ globalThis.useFetch = useFetch;
24
+ globalThis.useEffect = useEffect;
25
+ globalThis.useAsyncState = useAsyncState;
26
+ globalThis.useState = useState;
27
+ globalThis.genKey = () => {
28
+ return crypto.randomUUID();
29
+ };
30
+ globalThis.document = {
31
+ createElement: (tag) => { },
32
+ getElementById: (id) => { },
33
+ querySelector: (query) => { },
34
+ };
35
+ await Bun.build({
36
+ entrypoints: [process.env.ENTRYPOINT],
37
+ minify: true,
38
+ root: process.cwd() + "/dist/",
39
+ outdir: process.cwd() + "/dist/",
40
+ format: "esm",
41
+ ...(process.env.DEV ? { sourcemap: "inline" } : {}),
42
+ });
43
+ let isClass = function (element) {
44
+ return element.toString().startsWith("class");
45
+ };
46
+ const generatePage = async (
47
+ data = { path: process.env.INPUT, route: process.env.OUT }
48
+ ) => {
49
+ const { path, route } = data;
50
+ if (path.includes("root.js")) return;
51
+ let html = await import(path).then((m) => m.default);
52
+ let { head } = await import(path).then((m) => m);
53
+ let isFunction = false;
54
+ globalThis.isServer = true;
55
+ if (isClass(html)) {
56
+ html = new html();
57
+ html.Mounted = true;
58
+ html = html.render();
59
+ } else {
60
+ isFunction = true;
61
+ let instance = new Component();
62
+ html = html.bind(instance);
63
+ instance.render = html;
64
+ html = instance.render();
65
+ }
66
+
67
+ let h = document(html);
68
+ if (!fs.existsSync(process.cwd() + "/dist" + path2.dirname(route))) {
69
+ fs.mkdirSync(process.cwd() + "/dist" + path2.dirname(route), {
70
+ recursive: true,
71
+ });
72
+ }
73
+ let headHtml = "";
74
+ if (head) {
75
+ headHtml = document(head());
76
+ }
77
+ Bun.write(
78
+ process.cwd() + "/dist/" + route + "/index.html",
79
+ `<!DOCTYPE html><head>${headHtml}</head>${h}
80
+ <script type="module">
81
+ import c from '${process.env.filePath}'
82
+ import {render} from '/src/vader/index.js'
83
+ render(c, document.body.firstChild)
84
+ </script>
85
+ `
86
+ );
87
+
88
+ console.log(
89
+ ansiColors.blue(
90
+ `${process.env.filePath.replace(".js", ".jsx")} - ${parseInt(
91
+ process.env.size
92
+ ).toFixed(2)}kb`
93
+ )
94
+ );
95
+ process.exit(0);
96
+ };
97
+ generatePage({ path: process.env.INPUT, route: process.env.OUT });
package/document/index.ts CHANGED
@@ -21,7 +21,7 @@ export const document = (element: any) => {
21
21
  continue;
22
22
  }
23
23
  //@ts-ignore
24
- if (key.includes("on")) {
24
+ if (key.startsWith("on")){
25
25
  continue;
26
26
  }
27
27
  el += ` ${key}="${attributes[key]}"`;
package/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  //@ts-nocheck
2
2
  let isClassComponent = function(element) {
3
3
  return element.toString().startsWith("class");
4
- };
4
+ };
5
5
 
6
6
  const memoizes = new Map();
7
7
  //@ts-ignore
@@ -14,9 +14,12 @@ declare global {
14
14
  state: any;
15
15
  }
16
16
  const genKey: any;
17
+ /**
18
+ * @description Allows you to check if current session is server or client
19
+ */
17
20
  let isServer: boolean;
18
21
  /**
19
- * @description - The params object is used to store the query parameters of the current URL
22
+ * @description - The params object is used to store the parameters of the current URL
20
23
  * @example
21
24
  * // URL: https://example.com?name=John
22
25
  * console.log(params.name) // John
@@ -27,6 +30,7 @@ declare global {
27
30
  * console.log(params.age) // 20
28
31
  */
29
32
  let params: { [key: string]: string };
33
+ let localStorage : []
30
34
  }
31
35
  //@ts-ignore
32
36
  globalThis.isServer = typeof window === "undefined";
@@ -39,6 +43,7 @@ globalThis.params = {
39
43
  },
40
44
  };
41
45
 
46
+
42
47
 
43
48
  /**
44
49
  * @description useFetch allows you to make POST - GET - PUT - DELETE requests then returns the data, loading state and error
@@ -51,12 +56,11 @@ export const useFetch = (url: string, options: any) => {
51
56
  };
52
57
 
53
58
  /**
54
- * @description A custom hook that allows you to use state in a functional component
55
- * @param key
59
+ * @description - Handle asyncronous promises and return the data or error;
56
60
  * @param promise
57
61
  * @returns
58
62
  */
59
- export const useAsyncState = (key: string, promise: Promise<any>) => {
63
+ export const useAsyncState = (promise: Promise<any>) => {
60
64
  return [null, () => {}];
61
65
  }
62
66
  export const useEffect = (callback:any, dependencies: any[]) => {
@@ -64,19 +68,18 @@ export const useEffect = (callback:any, dependencies: any[]) => {
64
68
  if (dependencies.length === 0) {
65
69
  callback();
66
70
  }
67
- }
71
+ }
68
72
 
69
- function memoizeComponent(Component: any) {
70
- let key = Component.toString();
71
- if (memoizes.has(key)) {
72
- return memoizes.get(key);
73
+ export const Fragment = (props: any, children: any) => {
74
+ return {
75
+ type: "div",
76
+ props: props || {},
77
+ children: children || [],
73
78
  }
74
- let instance = new Component();
75
- memoizes.set(key, instance);
76
- return instance;
77
-
78
79
  }
79
80
 
81
+ globalThis.Fragment = Fragment;
82
+
80
83
  /**
81
84
  * @description - Create a new element
82
85
  * @param element
@@ -85,30 +88,52 @@ function memoizeComponent(Component: any) {
85
88
  * @returns
86
89
  */
87
90
  export const e = (element: any, props: any, ...children: any[]) => {
88
- if(typeof element === "function"){
89
- let instance = new Component();
90
- instance.render = element;
91
- return instance.render()
92
- }
93
- return { type: element, props: props || {}, children: children || [] };
91
+ let instance;
92
+ switch (true){
93
+ case isClassComponent(element):
94
+ instance = new element();
95
+ instance.props = props;
96
+ instance.children = children;
97
+ return instance.render();
98
+ case typeof element === "function":
99
+ instance = new Component();
100
+ instance.render = element;
101
+ return instance.render();
102
+ default:
103
+ return { type: element, props: props || {}, children: children || [] };
104
+ }
94
105
  };
95
106
 
96
107
  /**
97
- * @description - Create a new element
108
+ * @description - Manage state and forceupdate specific affected elements
98
109
  * @param key
99
110
  * @param initialState
100
111
  * @returns {state, (newState: any, Element: string) => void, key}
101
112
  */
102
113
  export const useState = <T>(initialState: T) => {
103
- const setState = (newState: T, Element: string) => {
114
+ const setState = (newState: T) => {
104
115
  initialState = newState;
105
116
  }
106
117
  return [initialState, setState];
107
- }
108
- //@ts-ignore
109
- if(!isServer){
110
- window.state = {};
111
- }
118
+ }
119
+
120
+ /**
121
+ * @description - Create a new component
122
+ * @param element
123
+ * @param props
124
+ * @param children
125
+ * @returns
126
+ * @example
127
+ * const App = (props) => {
128
+ * return (
129
+ * <div>
130
+ * <h1>Hello, {props.name}</h1>
131
+ * </div>
132
+ * )
133
+ * }
134
+ *
135
+ * render(<App name="John" />, document.getElementById("root"));
136
+ */
112
137
  export class Component {
113
138
  props: any;
114
139
  state: any;
@@ -120,21 +145,12 @@ export class Component {
120
145
  constructor() {
121
146
  this.key = Math.random().toString(36).substring(7);
122
147
  this.props = {};
123
- //@ts-ignore
124
- if(!isServer){
125
- window.state[this.key] = {};
126
- this.state = window.state[this.key];
127
- }else{
128
- this.state = {};
129
- }
148
+ this.state = {};
130
149
  this.effect = [];
131
150
  this.Mounted = false;
132
- this.element = null;
133
- }
134
- setState(newState: any, Element: string) {
135
- globalThis.window.state[this.key] = { ...this.state, ...newState };
136
- this.forceUpdate(Element);
151
+ this.element = null;
137
152
  }
153
+
138
154
 
139
155
  useEffect(callback: any, dependencies: any[]) {
140
156
  if (dependencies.length === 0 && this.Mounted && this.effect.length === 0) {
@@ -149,35 +165,41 @@ export class Component {
149
165
  }
150
166
  }
151
167
  }
152
- useState <T> (key: string, defaultValue: T) {
153
- let value = sessionStorage.getItem("state_" + key) || defaultValue;
154
- typeof value === "string" ? value = JSON.parse(value) : value;
155
-
156
- window.onbeforeunload = function() {
157
- sessionStorage.removeItem("state_" + key);
158
- }
159
- const setValue = (newValue: T ,element: string) => {
160
- value = newValue;
161
- sessionStorage.setItem("state_" + key, JSON.stringify(value));
162
- this.state[key] = value;
163
- this.forceUpdate(element)
168
+ useState<T>(key: string, defaultValue: T) {
169
+ let value = sessionStorage.getItem("state_" + key) ? JSON.parse(sessionStorage.getItem("state_" + key)).value : defaultValue;
170
+
171
+ // Parse value if it's a stringified object or number
172
+ if (typeof value === 'string') {
173
+ try {
174
+ value = JSON.parse(value);
175
+ } catch (error) {
176
+ // Not a valid JSON, keep it as is
177
+ }
178
+ }
179
+
180
+ // Add listener for unload event to save state
181
+ if (!window['listener' + key]) {
182
+ window['listener' + key] = true;
183
+ window.addEventListener('beforeunload', () => {
184
+ sessionStorage.removeItem('state_' + key)
185
+ });
164
186
  }
165
- return [value, setValue];
187
+
188
+ const setValue = (newValue: T) => {
189
+ value = newValue;
190
+ sessionStorage.setItem("state_" + key, JSON.stringify({ type: typeof newValue, value: newValue }));
191
+ this.forceUpdate(this.key)
192
+ };
193
+
194
+ return [value as T, setValue];
166
195
  }
167
196
 
168
197
 
169
198
 
170
- useFetch(url, options) {
171
- const { key } = options;
172
- if (!key) {
173
- throw new Error(`You must supply a key for the affected element in the options object`);
174
- }
175
-
199
+ useFetch(url: string, options: any) {
176
200
  const loadingKey = "loading_" + url;
177
201
  const errorKey = "error" + url;
178
- const dataKey = "_data" + url;
179
-
180
- // Initialize loading to false if loadingKey doesn't exist in state
202
+ const dataKey = "_data" + url;
181
203
  let [loading, setLoading] = this.useState(loadingKey, false);
182
204
  let [error, setError] = this.useState(errorKey, null);
183
205
  let [data, setData] = this.useState(dataKey, null);
@@ -189,13 +211,13 @@ export class Component {
189
211
  .then((res) => res.json())
190
212
  .then((data) => {
191
213
  //@ts-ignore
192
- setLoading(false, options.key);
193
- setData(data, options.key);
194
- this.forceUpdate(key);
214
+ setLoading(false);
215
+ setData(data);
216
+ this.forceUpdate(this.key);
195
217
  })
196
218
  .catch((err) => {
197
- setError(err, options.key);
198
- this.forceUpdate(key);
219
+ setError(err);
220
+ this.forceUpdate(this.key);
199
221
  });
200
222
  }
201
223
 
@@ -206,38 +228,39 @@ export class Component {
206
228
  forceUpdate(key) {
207
229
  //@ts-ignore
208
230
  let el = Array.from(document.querySelectorAll("*")).filter((el2: any) =>{ return el2.key === key})[0];
209
- let newl = this.parseToElement(this.render());
231
+ let newl = this.toElement();
210
232
  if(newl.key !== key){
211
233
  //@ts-ignore
212
234
  newl = Array.from(newl.children).filter((el2) => el2.key === key)[0];
213
- }
214
- if (this.Reconciler.shouldUpdate(el, newl)) {
215
- el.replaceWith(newl);
216
- }
235
+ }
236
+ this.Reconciler.update(el, newl);
217
237
  }
218
238
  Reconciler = {
219
- shouldUpdate(oldElement, newElement) {
220
- if(oldElement.outerHTML === newElement.outerHTML){
221
- return false;
222
- }
223
- let attributes = oldElement.attributes;
224
- let newAttributes = newElement.attributes;
225
- for (let i = 0; i < attributes.length; i++) {
226
- let attribute = attributes[i];
227
- if (attribute.name === "key") {
228
- continue;
229
- }
230
- if (attribute.name === "class") {
231
- if (attribute.value !== newElement.className) {
232
- return true;
233
- }
234
- continue;
235
- }
236
- if (attribute.value !== newAttributes[attribute.name]) {
237
- return true;
239
+ update: (oldElement, newElement) => {
240
+ if(!oldElement || !newElement) return;
241
+ if (this.Reconciler.shouldUpdate(oldElement, newElement) && oldElement.tagName == newElement.tagName){
242
+ oldElement.replaceWith(newElement)
243
+ } else {
244
+ let children = oldElement.childNodes;
245
+ for (let i = 0; i < children.length; i++) {
246
+ this.Reconciler.update(children[i], newElement.childNodes[i]);
238
247
  }
239
248
  }
240
- return true;
249
+ },
250
+ shouldUpdate(oldElement, newElement) {
251
+ if (oldElement.nodeType !== newElement.nodeType) {
252
+ return true;
253
+ }
254
+ if (oldElement.nodeType === 3 && newElement.nodeType === 3) {
255
+ return oldElement.textContent !== newElement.textContent;
256
+ }
257
+ if (oldElement.nodeName !== newElement.nodeName) {
258
+ return true;
259
+ }
260
+ if (oldElement.childNodes.length !== newElement.childNodes.length) {
261
+ return true;
262
+ }
263
+ return false;
241
264
  }
242
265
  };
243
266
 
@@ -301,7 +324,12 @@ export class Component {
301
324
  }
302
325
  toElement() {
303
326
  let children = this.render();
327
+ //@ts-ignore
328
+ if(children.props['key']){
329
+ this.key = children.props['key'];
330
+ }
304
331
  let el = this.parseToElement(children);
332
+ el.key = this.key;
305
333
  return el;
306
334
  }
307
335
  render() {
@@ -321,8 +349,12 @@ function memoizeClassComponent(Component: any) {
321
349
  return instance;
322
350
 
323
351
  }
324
-
325
- export function render(element: any, container) {
352
+ /**
353
+ * @description - Render jsx Componenet to the DOM
354
+ * @param element
355
+ * @param container
356
+ */
357
+ export function render(element: any, container: HTMLElement) {
326
358
  if (isClassComponent(element)) {
327
359
  const instance = new element();
328
360
  instance.Mounted = true;
@@ -330,15 +362,13 @@ export function render(element: any, container) {
330
362
  instance.element = el;
331
363
  container.innerHTML = "";
332
364
  container.replaceWith(el);
333
- } else {
334
- // memoizeComponent(element);
365
+ } else {
335
366
  let memoizedInstance = memoizeClassComponent(Component);
336
367
  memoizedInstance.Mounted = true;
337
368
  memoizedInstance.render = element.bind(memoizedInstance);
338
- let el = memoizedInstance.toElement();
369
+ let el = memoizedInstance.toElement();
339
370
  container.innerHTML = "";
340
371
  container.replaceWith(el);
341
372
 
342
373
  }
343
- }
344
-
374
+ }
package/main.js CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ import ansiColors from 'ansi-colors'
3
4
  import { Glob } from 'bun'
4
5
  const args = Bun.argv.slice(2)
5
6
  import fs from 'fs'
6
- import path from 'path'
7
-
7
+ import path from 'path'
8
8
  if (!fs.existsSync(process.cwd() + '/app')) {
9
9
  console.error(`App directory not found in ${process.cwd()}/app`)
10
10
  process.exit(1)
11
11
  }
12
12
  if (!fs.existsSync(process.cwd() + '/public')) {
13
13
  fs.mkdirSync(process.cwd() + '/public')
14
- }
15
- const mode = args.includes('dev') ? 'development' : args.includes('prod') || args.includes('build') ? 'production' : null
16
- if(!mode){
14
+ }
15
+ const mode = args.includes('dev') ? 'development' : args.includes('prod') || args.includes('build') ? 'production' : null
16
+ if (!mode) {
17
17
  console.log(`
18
18
  Usage:
19
19
  bun vaderjs dev - Start development server output in dist/
@@ -22,15 +22,26 @@ if(!mode){
22
22
  process.exit(1)
23
23
  }
24
24
 
25
+ console.log(
26
+ `VaderJS - v${require(process.cwd() + '/node_modules/vaderjs/package.json').version} 🚀
27
+ Mode: ${mode}
28
+ SSR: ${require(process.cwd() + '/config').default.ssr ? 'Enabled' : 'Disabled'}
29
+ PORT: ${require(process.cwd() + '/config').default.port || 8080}
30
+ `
31
+ )
32
+
25
33
  let start = Date.now()
26
- console.log(`Starting build at ${new Date().toLocaleTimeString()}`)
34
+ console.log(`Starting build at ${new Date().toLocaleTimeString()}`)
27
35
  let { port, host, host_provider } = require(process.cwd() + '/config').default
28
-
36
+ if (host_provider === 'apache' && mode === 'development') {
37
+ console.warn('Note: SSR will not work with Apache')
38
+ }
29
39
  if (!fs.existsSync(process.cwd() + '/jsconfig.json')) {
30
40
  let json = {
31
41
  "compilerOptions": {
32
42
  "jsx": "react",
33
43
  "jsxFactory": "e",
44
+ "jsxFragmentFactory": "Fragment",
34
45
  }
35
46
  }
36
47
  await Bun.write(process.cwd() + '/jsconfig.json', JSON.stringify(json, null, 4))
@@ -43,27 +54,27 @@ const handleReplacements = (code, file) => {
43
54
  let newLines = []
44
55
  for (let line of lines) {
45
56
  let hasImport = line.includes('import')
46
- if(hasImport && line.includes('.jsx')){
57
+ if (hasImport && line.includes('.jsx')) {
47
58
  line = line.replace('.jsx', '.js')
48
59
  }
49
- if(hasImport && line.includes('.css')){
50
- try {
60
+ if (hasImport && line.includes('.css')) {
61
+ try {
51
62
  let url = path.join('/' + line.split("'")[1])
52
63
  let css = fs.readFileSync(process.cwd() + url, 'utf-8')
53
64
  line = '';
54
- if(!bindes.includes(`<link rel="stylesheet" href="${url}">`)) {
65
+ if (!bindes.includes(`<link rel="stylesheet" href="${url}">`)) {
55
66
  bindes.push(`<link rel="stylesheet" href="${url}">`)
56
- }
67
+ }
57
68
  fs.mkdirSync(process.cwd() + '/dist' + path.dirname(url), { recursive: true })
58
69
  fs.writeFileSync(process.cwd() + '/dist' + url, css)
59
- } catch (error) {
70
+ } catch (error) {
60
71
  console.error(error)
61
- }
62
- }
72
+ }
73
+ }
63
74
  if (line.toLowerCase().includes('genkey()')) {
64
75
  line = line.toLowerCase().replace('genkey()', `this.key = "${crypto.randomUUID()}"`)
65
76
  }
66
- if(!hasImport && line.includes('useFetch')){
77
+ if (!hasImport && line.includes('useFetch')) {
67
78
  line = line.replace('useFetch', 'this.useFetch')
68
79
  }
69
80
  if (!hasImport && line.includes('useState')) {
@@ -72,7 +83,7 @@ const handleReplacements = (code, file) => {
72
83
  b4 = line.replace('useState(', `this.useState('${key}',`)
73
84
  line = b4
74
85
  }
75
- if(!hasImport && line.includes('useAsyncState')){
86
+ if (!hasImport && line.includes('useAsyncState')) {
76
87
  let key = line.split(',')[0].split('[')[1].replace(' ', '')
77
88
  let b4 = line
78
89
  b4 = line.replace('useAsyncState(', `this.useAsyncState('${key}',`)
@@ -88,239 +99,180 @@ const handleReplacements = (code, file) => {
88
99
  let key = line.split(' ')[1].split('=')[0]
89
100
  b4 = line.replace('useRef(', `this.useRef('${key}',`)
90
101
  line = b4
91
- }
102
+ }
92
103
  newLines.push(line)
93
104
  }
94
105
  let c = newLines.join('\n')
95
106
  return c
96
- }
97
-
107
+ }
98
108
 
99
- async function generateApp() {
100
- // remove files from dist
101
- if(mode === 'development'){
102
- fs.rmdirSync(process.cwd() + '/dist', { recursive: true })
103
- }else{
104
- fs.mkdirSync(process.cwd() + '/dist', { recursive: true })
105
- }
106
- return new Promise(async (resolve, reject) => {
107
- let routes = new Bun.FileSystemRouter({
108
- dir: process.cwd() + '/app',
109
- style: 'nextjs'
110
- })
111
- routes.reload()
112
- globalThis.routes = routes.routes
113
- Object.keys(routes.routes).forEach(async (route) => {
114
- let r = routes.routes[route]
115
- let code = await Bun.file(r).text()
116
- code = handleReplacements(code)
117
- r = r.replace(process.cwd().replace(/\\/g, '/') + '/app', '')
118
- r = r.replace('.jsx', '.js')
119
- fs.mkdirSync(path.dirname(process.cwd() + '/dist/' + r), { recursive: true })
120
- fs.writeFileSync(process.cwd() + '/dist/' + path.dirname(r) + '/' + path.basename(r), `
109
+
110
+ async function generateApp() {
111
+ // remove files from dist
112
+ if (mode === 'development') {
113
+ fs.rmdirSync(process.cwd() + '/dist', { recursive: true })
114
+ } else {
115
+ fs.mkdirSync(process.cwd() + '/dist', { recursive: true })
116
+ }
117
+ return new Promise(async (resolve, reject) => {
118
+ let routes = new Bun.FileSystemRouter({
119
+ dir: process.cwd() + '/app',
120
+ style: 'nextjs'
121
+ })
122
+ routes.reload()
123
+ globalThis.routes = routes.routes
124
+ console.log(ansiColors.green(`Processing ${Object.keys(routes.routes).length} routes`))
125
+
126
+ Object.keys(routes.routes).forEach(async (route) => {
127
+
128
+ let r = routes.routes[route]
129
+ let code = await Bun.file(r).text()
130
+ code = handleReplacements(code)
131
+ let size = code.length / 1024
132
+ r = r.replace(process.cwd().replace(/\\/g, '/') + '/app', '')
133
+ r = r.replace('.jsx', '.js')
134
+ fs.mkdirSync(path.dirname(process.cwd() + '/dist/' + r), { recursive: true })
135
+ fs.writeFileSync(process.cwd() + '/dist/' + path.dirname(r) + '/' + path.basename(r), `
121
136
  let route = window.location.pathname.split('/').filter(v => v !== '')
122
137
  let params = {
123
- ${Object.keys(routes.match(route).params || {}).length > 0 ? Object.keys(routes.match(route).params || {}) .map(p => {
138
+ ${Object.keys(routes.match(route).params || {}).length > 0 ? Object.keys(routes.match(route).params || {}).map(p => {
124
139
  return `${p}: route[${Object.keys(routes.match(route).params).indexOf(p) + 1}]`
125
140
  }).join(',') : ""}
126
141
  }
127
142
  \n${code}
128
143
  `)
129
- fs.mkdirSync(process.cwd() + '/dev', { recursive: true })
130
- fs.writeFileSync(path.join(process.cwd() + '/dev/bundler.js'), `
131
- import { Component, e, useState, useEffect, useFetch, useAsyncState } from 'vaderjs'
132
- import { document } from 'vaderjs/document'
133
- import fs from 'fs'
134
- let path2 = require('path')
135
- globalThis.window = {
136
- location: {
137
- hash: '',
138
- host: '',
139
- }
140
- }
141
- globalThis.Component = Component
142
- globalThis.e = e
143
- globalThis.useState = useState
144
- globalThis.genKey = () => {
145
- return crypto.randomUUID()
146
- };
147
- globalThis.document = {
148
- createElement: (tag) => {},
149
- getElementById: (id) => {},
150
- }
151
- await Bun.build({
152
- entrypoints: [process.env.ENTRYPOINT],
153
- minify:true,
154
- root: process.cwd() + '/dist/',
155
- outdir: process.cwd() + '/dist/',
156
- format: 'esm',
157
- ...(process.env.DEV ? { sourcemap: 'inline' } : {})
158
- })
159
- let isClass = function(element) {
160
- return element.toString().startsWith('class');
161
- };
162
- const generatePage = async (data = {path: process.env.INPUT, route: process.env.OUT}) => {
163
- const { path, route } = data
164
- if(path.includes('root.js')) return
165
- let html = await import(path).then(m => m.default)
166
- let { head } = await import(path).then(m => m)
167
- let isFunction = false
168
- globalThis.isServer = true
169
- if(isClass(html)){
170
- html = new html()
171
- html.Mounted = true
172
- html = html.render()
173
- }else{
174
- isFunction = true
175
- let instance = new Component()
176
- html = html.bind(instance)
177
- instance.render = html
178
- html = instance.render()
179
- }
180
-
181
- let h = document(html)
182
- if(!fs.existsSync(process.cwd() + '/dist' + path2.dirname(route))){
183
- fs.mkdirSync(process.cwd() + '/dist' + path2.dirname(route), { recursive: true })
184
- }
185
- let headHtml = ''
186
- if(head){
187
- headHtml = document(head())
188
- }
189
- Bun.write(process.cwd() + '/dist/' + route + '/index.html', \`<!DOCTYPE html><head>\${headHtml}</head>\${h}
190
- <script type="module">
191
- import c from '\${process.env.filePath}'
192
- import {render} from '/src/vader/index.js'
193
- render(c, document.body.firstChild)
194
- </script>
195
- \`)
196
- }
197
- generatePage({path: process.env.INPUT, route: process.env.OUT})
198
- `)
199
-
200
- fs.mkdirSync(process.cwd() + '/dist/src/vader', { recursive: true })
201
- fs.writeFileSync(process.cwd() + '/dist/src/vader/index.js', await new Bun.Transpiler({
202
- loader: 'ts',
203
- }).transformSync(await Bun.file(require.resolve('vaderjs')).text()))
204
-
205
- const spawn = Bun.spawn({
206
- cmd: ['bun', 'run', './dev/bundler.js'],
207
- cwd: process.cwd(),
208
- stdout: 'inherit',
209
- env: {
210
- ENTRYPOINT: path.join(process.cwd() + '/dist/' + path.dirname(r) + '/' + path.basename(r)),
211
- ROOT: process.cwd() + '/app/',
212
- OUT: path.dirname(r),
213
- file: process.cwd() + '/dist/' + path.dirname(r) + '/index.js',
214
- DEV: mode === 'development',
215
- filePath: r,
216
- INPUT: `../app/${r.replace('.js', '.jsx')}`,
217
- },
218
- onExit({exitCode: code}) {
219
- if (code === 0) {
220
- console.log('Build complete')
221
- resolve()
222
- } else {
223
- reject()
224
- }
144
+ fs.mkdirSync(process.cwd() + '/dev', { recursive: true })
145
+ if (!fs.existsSync(process.cwd() + '/dev/bundler.js')) {
146
+ fs.copyFileSync(require.resolve('vaderjs/bundler/index.js'), process.cwd() + '/dev/bundler.js')
225
147
  }
226
- })
227
-
228
- })
229
148
 
230
- switch(host_provider){
231
- case 'vercel':
232
-
233
- let vercelData = {
234
- rewrites: []
149
+ if (!fs.existsSync(process.cwd() + '/dev/readme.md')) {
150
+ fs.writeFileSync(process.cwd() + '/dev/readme.md', `# Please do not edit the bundler.js file in the dev directory. This file is automatically generated by the bundler. \n\n`)
235
151
  }
236
-
237
- for(let route in routes.routes){
238
- let {filePath, kind, name, params, pathname,query} = routes.match(route)
239
- let exists = vercelData.rewrites.find(v => v.source === `/${route}`)
240
- let paramString = ''
241
- if(pathname === '/'){
242
- continue
152
+ fs.mkdirSync(process.cwd() + '/dist/src/vader', { recursive: true })
153
+ fs.writeFileSync(process.cwd() + '/dist/src/vader/index.js', await new Bun.Transpiler({
154
+ loader: 'ts',
155
+ }).transformSync(await Bun.file(require.resolve('vaderjs')).text()))
156
+ Bun.spawn({
157
+ cmd: ['bun', 'run', './dev/bundler.js'],
158
+ cwd: process.cwd(),
159
+ stdout: 'inherit',
160
+ env: {
161
+ ENTRYPOINT: path.join(process.cwd() + '/dist/' + path.dirname(r) + '/' + path.basename(r)),
162
+ ROOT: process.cwd() + '/app/',
163
+ OUT: path.dirname(r),
164
+ file: process.cwd() + '/dist/' + path.dirname(r) + '/index.js',
165
+ DEV: mode === 'development',
166
+ size,
167
+ filePath: r,
168
+ INPUT: `../app/${r.replace('.js', '.jsx')}`,
169
+ },
170
+ onExit({ exitCode: code }) {
171
+ if (code === 0) {
172
+ resolve()
173
+ } else {
174
+ reject()
175
+ }
243
176
  }
244
- if(params){
245
- paramString = Object.keys(params).map(p => `:${p}`).join('/')
246
- }
247
- // replace double slashes
248
- paramString = paramString.replace(/\/\//g, '/')
249
- let base = path.dirname(route)
250
- if(base.includes('[')){
251
- base = base.split('[')[0].split('/').filter(v => v !== '').join('/')
252
- }else{
253
- base = base.split('/').filter(v => v !== '').join('/')
177
+ })
178
+
179
+ })
180
+
181
+ switch (host_provider) {
182
+ case 'vercel':
183
+
184
+ let vercelData = {
185
+ rewrites: []
254
186
  }
255
-
256
- if(!exists){
187
+
188
+ for (let route in routes.routes) {
189
+ let { filePath, kind, name, params, pathname, query } = routes.match(route)
190
+ let paramString = ''
191
+ if (params) {
192
+ paramString = Object.keys(params).map(p => `:${p}`).join('/')
193
+ }
194
+ // replace double slashes
195
+ paramString = paramString.replace(/\/\//g, '/')
196
+ let base = route.split('/').filter(v => v !== '').join('/')
197
+
198
+ if (base.includes('[')) {
199
+ base = base.split('[')[0].split('/').filter(v => v !== '').join('/')
200
+ }
201
+ // remove double slashes
202
+ base = base.replace(/\/\//g, '/')
203
+ if (base === '/' || base === '') {
204
+ continue;
205
+ }
206
+
257
207
  vercelData.rewrites.push({
258
208
  source: `/${base}/${paramString}`,
259
209
  destination: `${path.dirname(routes.routes[route]).replace(process.cwd().replace(/\\/g, '/') + '/app', '')}/index.html`
260
210
  })
261
211
  }
262
- }
263
- fs.writeFileSync(process.cwd() + '/vercel.json', JSON.stringify(vercelData, null, 4))
212
+
213
+ fs.writeFileSync(process.cwd() + '/vercel.json', JSON.stringify(vercelData, null, 4))
214
+ break;
215
+ }
264
216
 
265
- break;
266
- }
267
-
268
- })
217
+ })
269
218
 
270
219
  }
271
220
 
272
221
  await generateApp()
273
- function handleFiles(){
274
- return new Promise(async (resolve, reject) => {
275
- try {
276
- let glob = new Glob('public/**/*')
277
- for await (var i of glob.scan()){
278
- let file = i
279
- fs.mkdirSync(path.join(process.cwd() + '/dist', path.dirname(file)), { recursive: true })
280
- fs.copyFileSync(file, path.join(process.cwd() + '/dist', file))
281
- }
282
- resolve()
283
- } catch (error) {
284
- reject(error)
285
- }
286
- })
287
- }
288
- await handleFiles()
289
- let isBuilding = false;
290
- let timeout = null
291
- if(mode === 'development'){
292
- const watcher = fs.watch(path.join(process.cwd() + '/app'), { recursive: true })
293
- const publicWatcher = fs.watch(path.join(process.cwd() + '/public'), { recursive: true })
294
- publicWatcher.on('change', async (event, filename) => {
295
- try {
296
- await handleFiles()
297
- clients.forEach(c => {
298
- c.send('reload')
299
- })
300
- } catch (error) {
301
- console.error(error)
302
- }
303
- })
304
- watcher.on('change', async (event, filename) => {
222
+ function handleFiles() {
223
+ return new Promise(async (resolve, reject) => {
305
224
  try {
306
- await generateApp()
307
-
308
- handleFiles()
309
- clients.forEach(c => {
310
- c.send('reload')
311
- })
225
+ let glob = new Glob('public/**/*')
226
+ for await (var i of glob.scan()) {
227
+ let file = i
228
+ fs.mkdirSync(path.join(process.cwd() + '/dist', path.dirname(file)), { recursive: true })
229
+ if (fs.existsSync(path.join(process.cwd() + '/dist', file))) {
230
+ fs.rmSync(path.join(process.cwd() + '/dist', file))
231
+ }
232
+ fs.copyFileSync(file, path.join(process.cwd() + '/dist', file))
233
+ }
234
+ resolve()
312
235
  } catch (error) {
313
- console.error(error)
236
+ reject(error)
314
237
  }
315
-
316
- })
317
- watcher.on('error', (err) => {
318
- console.error(err)
319
238
  })
320
239
  }
240
+ await handleFiles()
321
241
  globalThis.clients = []
322
242
 
323
- if(mode === 'development'){
243
+ if (mode === 'development') {
244
+ const watcher = fs.watch(path.join(process.cwd() + '/app'), { recursive: true })
245
+ const publicWatcher = fs.watch(path.join(process.cwd() + '/public'), { recursive: true })
246
+ let isBuilding = false; // Flag to track build status
247
+
248
+ // Initialize a variable to hold the timeout ID
249
+ let debounceTimeout;
250
+
251
+ // Function to handle file changes with debounce
252
+ const handleFileChangeDebounced = async () => {
253
+ clearTimeout(debounceTimeout);
254
+ debounceTimeout = setTimeout(async () => {
255
+ if (!isBuilding) { // Check if not already building
256
+ isBuilding = true; // Set build flag to true
257
+ try {
258
+ await generateApp();
259
+ await handleFiles();
260
+ clients.forEach(c => {
261
+ c.send('reload');
262
+ });
263
+ } catch (error) {
264
+ console.error(error);
265
+ } finally {
266
+ isBuilding = false; // Reset build flag
267
+ }
268
+ }
269
+ }, 500);
270
+ };
271
+
272
+ // Event listeners with debounced handling
273
+ publicWatcher.on('change', handleFileChangeDebounced);
274
+ watcher.on('change', handleFileChangeDebounced);
275
+
324
276
  let server = Bun.serve({
325
277
  port: port || 8080,
326
278
  websocket: {
@@ -333,34 +285,34 @@ if(mode === 'development'){
333
285
  c.send(message)
334
286
  })
335
287
  },
336
-
288
+
337
289
  },
338
290
  async fetch(req, res) {
339
- if(res.upgrade(req)){
291
+ if (res.upgrade(req)) {
340
292
  return new Response('Upgraded', { status: 101 })
341
293
  }
342
-
343
- let url = new URL(req.url)
344
- if(url.pathname.includes('.')){
345
- let file = await Bun.file(path.join(process.cwd() + '/dist' + url.pathname))
346
- if(!await file.exists()) return new Response('Not found', { status: 404 })
294
+
295
+ let url = new URL(req.url)
296
+ if (url.pathname.includes('.')) {
297
+ let file = await Bun.file(path.join(process.cwd() + '/dist' + url.pathname))
298
+ if (!await file.exists()) return new Response('Not found', { status: 404 })
347
299
  return new Response(await file.text(), {
348
300
  headers: {
349
- 'Content-Type': file.type
301
+ 'Content-Type': file.type
350
302
  }
351
303
  })
352
- }
304
+ }
353
305
  let router = new Bun.FileSystemRouter({
354
306
  dir: process.cwd() + '/app',
355
307
  style: 'nextjs'
356
308
  })
357
- router.reload()
309
+ router.reload()
358
310
  let route = router.match(url.pathname)
359
- if(!route){
311
+ if (!route) {
360
312
  return new Response('Not found', { status: 404 })
361
- }
362
- let p = route.pathname;
363
- let base = path.dirname(route.filePath)
313
+ }
314
+ let p = route.pathname;
315
+ let base = path.dirname(route.filePath)
364
316
  base = base.replace(path.join(process.cwd() + '/app'), '')
365
317
  base = base.replace(/\\/g, '/').replace('/app', '/dist')
366
318
  return new Response(await Bun.file(path.join(base, 'index.html')).text() + `
@@ -376,12 +328,10 @@ if(mode === 'development'){
376
328
  headers: {
377
329
  'Content-Type': 'text/html'
378
330
  }
379
- })
331
+ })
380
332
  }
381
333
  })
382
-
383
- console.log(`Server running on http://localhost:${server.port}`)
384
- }else{
334
+ } else {
385
335
  console.log(`Build complete in ${Date.now() - start}ms at ${new Date().toLocaleTimeString()}`)
386
336
  process.exit(0)
387
337
  }
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "description": "A simple and powerful JavaScript library for building modern web applications.",
5
5
  "main":"./index.js",
6
6
  "bin": {
7
7
  "vaderjs": "./main.js"
8
+ },
9
+ "dependencies": {
10
+ "ansi-colors":"latest"
8
11
  }
9
12
  }