vaderjs 1.4.4 → 1.4.6

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.
Files changed (3) hide show
  1. package/index.ts +100 -61
  2. package/main.js +97 -24
  3. package/package.json +1 -1
package/index.ts CHANGED
@@ -1,18 +1,43 @@
1
+ //@ts-nocheck
1
2
  let isClassComponent = function(element) {
2
3
  return element.toString().startsWith("class");
3
4
  };
4
5
 
6
+ const memoizes = new Map();
7
+ //@ts-ignore
8
+
5
9
  declare global {
6
10
  interface Window {
7
11
  onbeforeunload: any;
8
12
  localStorage: any;
9
13
  sessionStorage: any;
14
+ state: any;
10
15
  }
11
16
  const genKey: any;
12
17
  let isServer: boolean;
18
+ /**
19
+ * @description - The params object is used to store the query parameters of the current URL
20
+ * @example
21
+ * // URL: https://example.com?name=John
22
+ * console.log(params.name) // John
23
+ * @example
24
+ * // URL: https://example.com/:name/:age
25
+ * // GO: https://example.com/John/20
26
+ * console.log(params.name) // John
27
+ * console.log(params.age) // 20
28
+ */
29
+ let params: { [key: string]: string };
13
30
  }
14
31
  //@ts-ignore
15
32
  globalThis.isServer = typeof window === "undefined";
33
+ //@ts-ignore
34
+ globalThis.params = {
35
+ [Symbol.iterator]: function* () {
36
+ for (const key in this) {
37
+ yield [key, this[key]];
38
+ }
39
+ },
40
+ };
16
41
 
17
42
 
18
43
  /**
@@ -41,6 +66,17 @@ export const useEffect = (callback:any, dependencies: any[]) => {
41
66
  }
42
67
  }
43
68
 
69
+ function memoizeComponent(Component: any) {
70
+ let key = Component.toString();
71
+ if (memoizes.has(key)) {
72
+ return memoizes.get(key);
73
+ }
74
+ let instance = new Component();
75
+ memoizes.set(key, instance);
76
+ return instance;
77
+
78
+ }
79
+
44
80
  /**
45
81
  * @description - Create a new element
46
82
  * @param element
@@ -63,20 +99,16 @@ export const e = (element: any, props: any, ...children: any[]) => {
63
99
  * @param initialState
64
100
  * @returns {state, (newState: any, Element: string) => void, key}
65
101
  */
66
- export const useState = (key, initialState) => {
67
- const instance = new Component;
68
- return [instance.state[key],
69
- /**
70
- * @description - Set the state of the component
71
- * @param newState
72
- * @param Element
73
- */
74
- (newState: any, Element: string) => {
75
- instance.setState({ [key]: newState }, Element);
76
- }, key];
102
+ export const useState = <T>(initialState: T) => {
103
+ const setState = (newState: T, Element: string) => {
104
+ initialState = newState;
105
+ }
106
+ return [initialState, setState];
107
+ }
108
+ //@ts-ignore
109
+ if(!isServer){
110
+ window.state = {};
77
111
  }
78
- //@ts-ignore
79
- let state = {};
80
112
  export class Component {
81
113
  props: any;
82
114
  state: any;
@@ -88,21 +120,24 @@ export class Component {
88
120
  constructor() {
89
121
  this.key = Math.random().toString(36).substring(7);
90
122
  this.props = {};
91
- //@ts-ignore
92
- state[this.key] = {};
93
- this.state = state[this.key];
123
+ //@ts-ignore
124
+ if(!isServer){
125
+ window.state[this.key] = {};
126
+ this.state = window.state[this.key];
127
+ }else{
128
+ this.state = {};
129
+ }
94
130
  this.effect = [];
95
131
  this.Mounted = false;
96
132
  this.element = null;
97
133
  }
98
134
  setState(newState: any, Element: string) {
99
- state[this.key] = { ...this.state, ...newState };
100
- console.log(state[this.key]);
135
+ globalThis.window.state[this.key] = { ...this.state, ...newState };
101
136
  this.forceUpdate(Element);
102
137
  }
103
138
 
104
139
  useEffect(callback: any, dependencies: any[]) {
105
- if (dependencies.length === 0 && this.Mounted && this.effect.length === 0) {
140
+ if (dependencies.length === 0 && this.Mounted && this.effect.length === 0) {
106
141
  callback();
107
142
  this.effect.push(callback);
108
143
  }else{
@@ -114,34 +149,26 @@ export class Component {
114
149
  }
115
150
  }
116
151
  }
117
- useState(key: string, initialState: any) {
118
- if (!this.state[key]) {
119
- console.log(`State ${key} does not exist`)
120
- this.state[key] = initialState;
121
- }
152
+ useState <T> (key: string, defaultValue: T) {
153
+ let value = sessionStorage.getItem("state_" + key) || defaultValue;
154
+ typeof value === "string" ? value = JSON.parse(value) : value;
122
155
 
123
- window.onbeforeunload = () => {
124
- sessionStorage.setItem("state", JSON.stringify({}));
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)
125
164
  }
126
- /**
127
- *
128
- * @param {*} newState
129
- * @param {*} Element - The element affected by the state change
130
- */
131
- const setState = (newState, Element) => {
132
- this.prevState = { ...this.state };
133
- this.state[key] = newState;
134
- state[this.key] = { ...this.state };
135
- this.forceUpdate(Element);
136
- };
137
- const returnKey = key;
138
- return [this.state[key], setState, returnKey];
165
+ return [value, setValue];
139
166
  }
167
+
168
+
140
169
 
141
170
  useFetch(url, options) {
142
- const { key } = options;
143
-
144
- console.log(key);
171
+ const { key } = options;
145
172
  if (!key) {
146
173
  throw new Error(`You must supply a key for the affected element in the options object`);
147
174
  }
@@ -151,23 +178,23 @@ export class Component {
151
178
  const dataKey = "_data" + url;
152
179
 
153
180
  // Initialize loading to false if loadingKey doesn't exist in state
154
- const loading = state[this.key][loadingKey] !== undefined ? state[this.key][loadingKey] : true;
155
- const error = state[this.key][errorKey] || null;
156
- const data = state[this.key][dataKey] || null;
181
+ let [loading, setLoading] = this.useState(loadingKey, false);
182
+ let [error, setError] = this.useState(errorKey, null);
183
+ let [data, setData] = this.useState(dataKey, null);
157
184
 
158
185
  if (loading && !error && !data) {
159
- state[this.key][loadingKey] = true;
186
+ this.state[this.key][loadingKey] = true;
160
187
 
161
188
  fetch(url, options)
162
189
  .then((res) => res.json())
163
190
  .then((data) => {
164
- state[this.key][dataKey] = data;
165
- state[this.key][loadingKey] = false;
191
+ //@ts-ignore
192
+ setLoading(false, options.key);
193
+ setData(data, options.key);
166
194
  this.forceUpdate(key);
167
195
  })
168
196
  .catch((err) => {
169
- state[this.key][errorKey] = err;
170
- state[this.key][loadingKey] = false;
197
+ setError(err, options.key);
171
198
  this.forceUpdate(key);
172
199
  });
173
200
  }
@@ -179,7 +206,7 @@ export class Component {
179
206
  forceUpdate(key) {
180
207
  //@ts-ignore
181
208
  let el = Array.from(document.querySelectorAll("*")).filter((el2: any) =>{ return el2.key === key})[0];
182
- let newl = this.parseToElement(this.render());
209
+ let newl = this.parseToElement(this.render());
183
210
  if(newl.key !== key){
184
211
  //@ts-ignore
185
212
  newl = Array.from(newl.children).filter((el2) => el2.key === key)[0];
@@ -239,8 +266,7 @@ export class Component {
239
266
  continue;
240
267
  }
241
268
  //@ts-ignore
242
- if (key.startsWith("on")) {
243
- console.log(key.substring(2).toLowerCase());
269
+ if (key.startsWith("on")) {
244
270
  el.addEventListener(key.substring(2).toLowerCase(), attributes[key]);
245
271
  continue;
246
272
  }
@@ -281,8 +307,21 @@ export class Component {
281
307
  render() {
282
308
  return "";
283
309
  }
310
+
311
+
284
312
  }
285
313
 
314
+ function memoizeClassComponent(Component: any) {
315
+ let key = Component.toString();
316
+ if (memoizes.has(key)) {
317
+ return memoizes.get(key);
318
+ }
319
+ let instance = new Component();
320
+ memoizes.set(key, instance);
321
+ return instance;
322
+
323
+ }
324
+
286
325
  export function render(element: any, container) {
287
326
  if (isClassComponent(element)) {
288
327
  const instance = new element();
@@ -292,14 +331,14 @@ export function render(element: any, container) {
292
331
  container.innerHTML = "";
293
332
  container.replaceWith(el);
294
333
  } else {
295
- const newInstance = new Component;
296
- element = element.bind(newInstance);
297
- newInstance.render = element;
298
- newInstance.Mounted = true;
299
- let el = newInstance.toElement();
300
- newInstance.element = el;
301
- container.innerHTML = "";
302
- container.replaceWith(el);
334
+ // memoizeComponent(element);
335
+ let memoizedInstance = memoizeClassComponent(Component);
336
+ memoizedInstance.Mounted = true;
337
+ memoizedInstance.render = element.bind(memoizedInstance);
338
+ let el = memoizedInstance.toElement();
339
+ container.innerHTML = "";
340
+ container.replaceWith(el);
341
+
303
342
  }
304
343
  }
305
344
 
package/main.js CHANGED
@@ -12,19 +12,19 @@ if (!fs.existsSync(process.cwd() + '/app')) {
12
12
  if (!fs.existsSync(process.cwd() + '/public')) {
13
13
  fs.mkdirSync(process.cwd() + '/public')
14
14
  }
15
- const mode = args.includes('dev') ? 'development' : args.includes('prod') ? 'production' : null
15
+ const mode = args.includes('dev') ? 'development' : args.includes('prod') || args.includes('build') ? 'production' : null
16
16
  if(!mode){
17
17
  console.log(`
18
18
  Usage:
19
19
  bun vaderjs dev - Start development server output in dist/
20
- bun vaderjs prod - Build for production output in dist/
20
+ bun vaderjs prod - Build for production output in dist/
21
21
  `)
22
22
  process.exit(1)
23
23
  }
24
24
 
25
25
  let start = Date.now()
26
26
  console.log(`Starting build at ${new Date().toLocaleTimeString()}`)
27
- let { port, host, } = require(process.cwd() + '/config').default
27
+ let { port, host, host_provider } = require(process.cwd() + '/config').default
28
28
 
29
29
  if (!fs.existsSync(process.cwd() + '/jsconfig.json')) {
30
30
  let json = {
@@ -96,21 +96,36 @@ const handleReplacements = (code, file) => {
96
96
  }
97
97
 
98
98
 
99
- async function generateApp() {
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
+ }
100
106
  return new Promise(async (resolve, reject) => {
101
107
  let routes = new Bun.FileSystemRouter({
102
108
  dir: process.cwd() + '/app',
103
109
  style: 'nextjs'
104
110
  })
105
- globalThis.routes = routes.routes
111
+ routes.reload()
112
+ globalThis.routes = routes.routes
106
113
  Object.keys(routes.routes).forEach(async (route) => {
107
114
  let r = routes.routes[route]
108
115
  let code = await Bun.file(r).text()
109
116
  code = handleReplacements(code)
110
117
  r = r.replace(process.cwd().replace(/\\/g, '/') + '/app', '')
111
- r = r.replace('.jsx', '.js')
112
- fs.mkdirSync(path.dirname(process.cwd() + '/dist/' + r), { recursive: true })
113
- fs.writeFileSync(process.cwd() + '/dist/' + r, code)
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), `
121
+ let route = window.location.pathname.split('/').filter(v => v !== '')
122
+ let params = {
123
+ ${Object.keys(routes.match(route).params || {}).length > 0 ? Object.keys(routes.match(route).params || {}) .map(p => {
124
+ return `${p}: route[${Object.keys(routes.match(route).params).indexOf(p) + 1}]`
125
+ }).join(',') : ""}
126
+ }
127
+ \n${code}
128
+ `)
114
129
  fs.mkdirSync(process.cwd() + '/dev', { recursive: true })
115
130
  fs.writeFileSync(path.join(process.cwd() + '/dev/bundler.js'), `
116
131
  import { Component, e, useState, useEffect, useFetch, useAsyncState } from 'vaderjs'
@@ -134,14 +149,16 @@ async function generateApp() {
134
149
  getElementById: (id) => {},
135
150
  }
136
151
  await Bun.build({
137
- entrypoints: [process.cwd() + '/dist/' + process.env.ENTRYPOINT],
138
- minify:true,
139
- root: process.cwd() + '/dist',
140
- outdir: process.cwd() + '/dist',
141
- })
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
+ })
142
159
  let isClass = function(element) {
143
160
  return element.toString().startsWith('class');
144
- };
161
+ };
145
162
  const generatePage = async (data = {path: process.env.INPUT, route: process.env.OUT}) => {
146
163
  const { path, route } = data
147
164
  if(path.includes('root.js')) return
@@ -168,10 +185,10 @@ async function generateApp() {
168
185
  let headHtml = ''
169
186
  if(head){
170
187
  headHtml = document(head())
171
- }
188
+ }
172
189
  Bun.write(process.cwd() + '/dist/' + route + '/index.html', \`<!DOCTYPE html><head>\${headHtml}</head>\${h}
173
190
  <script type="module">
174
- import c from '\${route === '/' ? './index.js' : route + '/index.js'}'
191
+ import c from '\${process.env.filePath}'
175
192
  import {render} from '/src/vader/index.js'
176
193
  render(c, document.body.firstChild)
177
194
  </script>
@@ -183,15 +200,19 @@ async function generateApp() {
183
200
  fs.mkdirSync(process.cwd() + '/dist/src/vader', { recursive: true })
184
201
  fs.writeFileSync(process.cwd() + '/dist/src/vader/index.js', await new Bun.Transpiler({
185
202
  loader: 'ts',
186
- }).transformSync(await Bun.file(require.resolve('vaderjs')).text()))
203
+ }).transformSync(await Bun.file(require.resolve('vaderjs')).text()))
204
+
187
205
  const spawn = Bun.spawn({
188
206
  cmd: ['bun', 'run', './dev/bundler.js'],
189
207
  cwd: process.cwd(),
190
208
  stdout: 'inherit',
191
209
  env: {
192
- ENTRYPOINT: r,
210
+ ENTRYPOINT: path.join(process.cwd() + '/dist/' + path.dirname(r) + '/' + path.basename(r)),
193
211
  ROOT: process.cwd() + '/app/',
194
- OUT: route,
212
+ OUT: path.dirname(r),
213
+ file: process.cwd() + '/dist/' + path.dirname(r) + '/index.js',
214
+ DEV: mode === 'development',
215
+ filePath: r,
195
216
  INPUT: `../app/${r.replace('.js', '.jsx')}`,
196
217
  },
197
218
  onExit({exitCode: code}) {
@@ -205,12 +226,50 @@ async function generateApp() {
205
226
  })
206
227
 
207
228
  })
229
+
230
+ switch(host_provider){
231
+ case 'vercel':
232
+
233
+ let vercelData = {
234
+ rewrites: []
235
+ }
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
243
+ }
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('/')
254
+ }
255
+
256
+ if(!exists){
257
+ vercelData.rewrites.push({
258
+ source: `/${base}/${paramString}`,
259
+ destination: `${path.dirname(routes.routes[route]).replace(process.cwd().replace(/\\/g, '/') + '/app', '')}/index.html`
260
+ })
261
+ }
262
+ }
263
+ fs.writeFileSync(process.cwd() + '/vercel.json', JSON.stringify(vercelData, null, 4))
264
+
265
+ break;
266
+ }
208
267
 
209
268
  })
210
269
 
211
270
  }
212
271
 
213
- generateApp()
272
+ await generateApp()
214
273
  function handleFiles(){
215
274
  return new Promise(async (resolve, reject) => {
216
275
  try {
@@ -226,7 +285,7 @@ function handleFiles(){
226
285
  }
227
286
  })
228
287
  }
229
- handleFiles()
288
+ await handleFiles()
230
289
  let isBuilding = false;
231
290
  let timeout = null
232
291
  if(mode === 'development'){
@@ -245,6 +304,8 @@ if(mode === 'development'){
245
304
  watcher.on('change', async (event, filename) => {
246
305
  try {
247
306
  await generateApp()
307
+
308
+ handleFiles()
248
309
  clients.forEach(c => {
249
310
  c.send('reload')
250
311
  })
@@ -289,14 +350,26 @@ if(mode === 'development'){
289
350
  }
290
351
  })
291
352
  }
292
- return new Response(fs.readFileSync(process.cwd() + '/dist/' + url.pathname + '/index.html') + `
353
+ let router = new Bun.FileSystemRouter({
354
+ dir: process.cwd() + '/app',
355
+ style: 'nextjs'
356
+ })
357
+ router.reload()
358
+ let route = router.match(url.pathname)
359
+ if(!route){
360
+ return new Response('Not found', { status: 404 })
361
+ }
362
+ let p = route.pathname;
363
+ let base = path.dirname(route.filePath)
364
+ base = base.replace(path.join(process.cwd() + '/app'), '')
365
+ base = base.replace(/\\/g, '/').replace('/app', '/dist')
366
+ return new Response(await Bun.file(path.join(base, 'index.html')).text() + `
293
367
  <script>
294
368
  let ws = new WebSocket('ws://localhost:${server.port}')
295
369
  ws.onmessage = (e) => {
296
370
  if(e.data === 'reload'){
297
371
  window.location.reload()
298
- }
299
- console.log(e.data)
372
+ }
300
373
  }
301
374
  </script>
302
375
  `, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "A simple and powerful JavaScript library for building modern web applications.",
5
5
  "main":"./index.js",
6
6
  "bin": {