vaderjs 1.4.1-lv56aadeg5 → 1.4.2-kml56
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/.editorconfig +11 -0
- package/.vscode/c_cpp_properties.json +21 -0
- package/.vscode/settings.json +12 -0
- package/README.md +35 -198
- package/binaries/Kalix/index.js +665 -0
- package/binaries/compiler/main.js +461 -0
- package/binaries/vader.js +74 -0
- package/binaries/watcher/hmr.js +36 -0
- package/client/index.d.ts +226 -0
- package/client/runtime/index.js +417 -0
- package/client/runtime/router.js +235 -0
- package/config/index.ts +68 -0
- package/index.ts +344 -0
- package/package.json +14 -25
- package/plugins/cloudflare/functions/index.js +98 -0
- package/plugins/cloudflare/toCopy/@server/Kalix/index.js +625 -0
- package/plugins/cloudflare/toCopy/@server/cloudflare_ssr/index.js +85 -0
- package/plugins/cloudflare/toCopy/node_modules/vaderjs/server/index.js +99 -0
- package/plugins/cloudflare/toCopy/src/client.js +432 -0
- package/plugins/cloudflare/toCopy/src/router.js +235 -0
- package/plugins/ssg/index.js +124 -0
- package/plugins/vercel/functions/index.ts +8 -0
- package/router/index.ts +181 -0
- package/server/index.js +129 -0
- package/vader.js +60 -113
- package/@integrations/ssg.js +0 -163
- package/LICENSE +0 -21
- package/binaries/IPC/index.js +0 -277
- package/binaries/main.js +0 -1328
- package/binaries/readme.md +0 -4
- package/binaries/watcher.js +0 -74
- package/binaries/win32/check.ps1 +0 -7
- package/client/index.js +0 -441
- package/config/index.js +0 -36
- package/logo.png +0 -0
- package/runtime/router.js +0 -1
- package/runtime/vader.js +0 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview - A simple router for vaderjs - Kuai
|
|
3
|
+
* @version - 1.0.0
|
|
4
|
+
*/
|
|
5
|
+
export class Kuai{
|
|
6
|
+
constructor(config = { container: '#app'}){
|
|
7
|
+
this.routes = [];
|
|
8
|
+
this.middleware = [];
|
|
9
|
+
this.container = config.container ? config.container : document.getElementById('app');
|
|
10
|
+
this.renderPlugins = [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
res = {
|
|
14
|
+
/**
|
|
15
|
+
* @description render text to the container
|
|
16
|
+
* @param {string} data
|
|
17
|
+
*/
|
|
18
|
+
text: (data) => {
|
|
19
|
+
this.container.innerHTML = data;
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* @method html
|
|
23
|
+
* @description render html to the container
|
|
24
|
+
* @param {any} data
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
html: (data) => {
|
|
28
|
+
switch(typeof data){
|
|
29
|
+
case 'function':
|
|
30
|
+
if(this.renderPlugins.length > 0){
|
|
31
|
+
this.renderPlugins.forEach((plugin) => {
|
|
32
|
+
if(plugin.for === 'html'){
|
|
33
|
+
console.log('Plugin', plugin)
|
|
34
|
+
plugin.plugin(data, this.container)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.container.innerHTML = data();
|
|
40
|
+
break;
|
|
41
|
+
case 'string':
|
|
42
|
+
this.container.innerHTML = data;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* @method json
|
|
49
|
+
* @description render json to the container
|
|
50
|
+
* @param {Object} data
|
|
51
|
+
*/
|
|
52
|
+
json: (data) => {
|
|
53
|
+
this.container.innerHTML = `<Oject data=${JSON.stringify(data)}></Object>`
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
req = {
|
|
57
|
+
/**
|
|
58
|
+
* @method navigate
|
|
59
|
+
* @description - navigate to a new route
|
|
60
|
+
* @param {string} path
|
|
61
|
+
*/
|
|
62
|
+
navigate: (path) => {
|
|
63
|
+
window.history.pushState({}, '', path);
|
|
64
|
+
let currentPath = this.match(window.location.pathname.replace('/index.html', ''))
|
|
65
|
+
if(currentPath){
|
|
66
|
+
this.currentRoute = currentPath.path
|
|
67
|
+
currentPath.callback(this.res, currentPath.params, this.extractQueryParams(window.location.search));
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* @method back
|
|
72
|
+
* @description - go back to the previous route
|
|
73
|
+
*/
|
|
74
|
+
back: () => {
|
|
75
|
+
window.history.back();
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @method forward
|
|
80
|
+
* @description - go forward to the next route
|
|
81
|
+
* @returns {void}
|
|
82
|
+
* **/
|
|
83
|
+
forward: () => {
|
|
84
|
+
window.history.forward();
|
|
85
|
+
},
|
|
86
|
+
url: window.location
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
extractQueryParams(path){
|
|
92
|
+
let params = new URLSearchParams(path);
|
|
93
|
+
let query = {};
|
|
94
|
+
for(let param of params){
|
|
95
|
+
query[param[0]] = param[1];
|
|
96
|
+
}
|
|
97
|
+
return query;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
extractParams(routePath, currentPath){
|
|
103
|
+
const routeParts = routePath.split('/').filter((part) => part !== '');
|
|
104
|
+
const hashParts = currentPath.split('/').filter((part) => part !== '');
|
|
105
|
+
const params = {};
|
|
106
|
+
routeParts.forEach((part, index) => {
|
|
107
|
+
if (part.startsWith(':')) {
|
|
108
|
+
const paramName = part.slice(1);
|
|
109
|
+
params[paramName] = hashParts[index];
|
|
110
|
+
}else if(part.startsWith('*')){
|
|
111
|
+
let array = hashParts.slice(index)
|
|
112
|
+
array.forEach((i, index)=>{
|
|
113
|
+
params[index] = i
|
|
114
|
+
})
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
return params;
|
|
118
|
+
}
|
|
119
|
+
use(path, middleware){
|
|
120
|
+
this.middleware.push({path, middleware});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* @method usePlugin
|
|
124
|
+
* @description - add a plugin to handle how the route should be rendered
|
|
125
|
+
* @param {Function} plugin
|
|
126
|
+
* @param {('html')} method
|
|
127
|
+
*/
|
|
128
|
+
usePlugin(plugin, method){
|
|
129
|
+
this.renderPlugins.push({plugin, for: method})
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* @method match
|
|
133
|
+
* @description - match a route to the current path and return the route object
|
|
134
|
+
* @param {string} route
|
|
135
|
+
* @returns {Object} - {path: string, callback: Function, params: Object}
|
|
136
|
+
*/
|
|
137
|
+
match(hash){
|
|
138
|
+
hash = hash.endsWith('/') ? hash.slice(0, -1) : hash;
|
|
139
|
+
hash.includes('index.html') ? hash = hash.replace('index.html', '') : null;
|
|
140
|
+
if(hash.includes('?')){
|
|
141
|
+
hash = hash.split('?')[0]
|
|
142
|
+
}
|
|
143
|
+
let route = this.routes.find((route) => {
|
|
144
|
+
if (route.path === hash) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if(hash === '' && route.path === '/'){
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if (route.path.includes('*') || route.path.includes(':')) {
|
|
154
|
+
const routeParts = route.path.split('/').filter((part) => part !== '');
|
|
155
|
+
const hashParts = hash.split('/').filter((part) => part !== '');
|
|
156
|
+
if(this.basePath){
|
|
157
|
+
hashParts.shift();
|
|
158
|
+
}
|
|
159
|
+
if (routeParts.length !== hashParts.length && !route.path.endsWith('*')) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (let index = 0; index < routeParts.length; index++) {
|
|
164
|
+
const routePart = routeParts[index];
|
|
165
|
+
const hashPart = hashParts[index];
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if (routePart.startsWith(':') || routePart.startsWith('*')) {
|
|
169
|
+
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (routePart !== hashPart) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
});
|
|
183
|
+
if(route){
|
|
184
|
+
let params = this.extractParams(route.path, hash)
|
|
185
|
+
return { ...route, params}
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @description - create a new route
|
|
193
|
+
* @param {string} path
|
|
194
|
+
* @param {Function} callback
|
|
195
|
+
*/
|
|
196
|
+
get(path, callback){
|
|
197
|
+
this.routes.push({path, callback});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @method listen
|
|
202
|
+
* @description - listen for route changes
|
|
203
|
+
*/
|
|
204
|
+
listen(){
|
|
205
|
+
let currentPath = this.match(window.location.pathname.replace('/index.html', ''))
|
|
206
|
+
if(currentPath){
|
|
207
|
+
this.middleware.forEach((middleware) => {
|
|
208
|
+
if(middleware.path === currentPath.path){
|
|
209
|
+
middleware.middleware();
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
this.currentRoute = currentPath.path
|
|
213
|
+
let obj = {
|
|
214
|
+
...this.res,
|
|
215
|
+
res: this.res,
|
|
216
|
+
req:{
|
|
217
|
+
...this.req,
|
|
218
|
+
params: (param) => currentPath.params[param]
|
|
219
|
+
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
currentPath.callback(obj);
|
|
223
|
+
}
|
|
224
|
+
window.onpopstate = () => {
|
|
225
|
+
let currentPath = this.match(window.location.pathname.replace('/index.html', ''))
|
|
226
|
+
if(currentPath){
|
|
227
|
+
this.currentRoute = currentPath.path
|
|
228
|
+
currentPath.callback(this.res, currentPath.params, this.extractQueryParams(window.location.search));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export default Kuai;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import * as Bun from 'bun'
|
|
2
|
+
import { Element, Document, DOMParser} from 'vaderjs/binaries/Kalix/index.js'
|
|
3
|
+
import { renderToString } from '../../server'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
let routes = new Bun.FileSystemRouter({
|
|
6
|
+
dir: process.cwd() + '/pages',
|
|
7
|
+
style:"nextjs"
|
|
8
|
+
}).routes
|
|
9
|
+
globalThis.isNotFirstRun = false
|
|
10
|
+
async function generate(){
|
|
11
|
+
let config = await import(process.cwd() + '/vader.config.js').then((config) => { return config.default })
|
|
12
|
+
let provider = config?.host?.provider
|
|
13
|
+
|
|
14
|
+
let providerRoutes = []
|
|
15
|
+
for(var i in routes){
|
|
16
|
+
let path = i
|
|
17
|
+
let file = routes[i]
|
|
18
|
+
let comp = require(file).default
|
|
19
|
+
let document = new Document()
|
|
20
|
+
let div = document.createElement('div')
|
|
21
|
+
div.setAttribute('id', 'root')
|
|
22
|
+
let dom = await renderToString(comp)
|
|
23
|
+
div.setContent(dom)
|
|
24
|
+
dom = div.toString("outerHTML")
|
|
25
|
+
let folder = path.split('/pages')[0]
|
|
26
|
+
let newPath = process.cwd() + '/build' + folder + '/index.html'
|
|
27
|
+
let name = comp.name
|
|
28
|
+
file = file.replace(/\\/g, '/').replace('\/\/', '/').replace(process.cwd().replace(/\\/g, '/'), '').split('/pages')[1].replace('.tsx', '.js').replace('.jsx', '.js')
|
|
29
|
+
let isParamRoute = path.includes('[')
|
|
30
|
+
let baseFolder = ''
|
|
31
|
+
if(isParamRoute){
|
|
32
|
+
baseFolder = path.split('[')[0]
|
|
33
|
+
let providerPath;
|
|
34
|
+
switch(true){
|
|
35
|
+
case provider === 'vercel':
|
|
36
|
+
providerPath = `${baseFolder}:${path.split('[')[1].split(']')[0]}`
|
|
37
|
+
providerRoutes.push({source: providerPath, dest: `${path}/index.html`})
|
|
38
|
+
break;
|
|
39
|
+
case provider === 'nginx':
|
|
40
|
+
providerPath = `RewriteRule ^${baseFolder.replace('/', '')}.*$ ${path}/index.html [L]`
|
|
41
|
+
providerRoutes.push(providerPath)
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}else{
|
|
45
|
+
let providerPath;
|
|
46
|
+
switch(true){
|
|
47
|
+
case provider === 'vercel':
|
|
48
|
+
providerPath = `${path}`
|
|
49
|
+
providerRoutes.push({source: providerPath, dest: `${path}/index.html`})
|
|
50
|
+
break;
|
|
51
|
+
case provider === 'nginx':
|
|
52
|
+
if(i == '/'){
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
providerPath = `RewriteRule ^${path.replace('/', '')}/$ ${path}/index.html [L]`
|
|
56
|
+
providerRoutes.push(providerPath)
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
dom = dom + `
|
|
62
|
+
<script type="module">
|
|
63
|
+
import { render } from '/src/client.js'
|
|
64
|
+
import ${name} from '/pages/${file.replace(process.cwd(), '')}'
|
|
65
|
+
import Kuai from '/src/router.js'
|
|
66
|
+
let kuai = new Kuai()
|
|
67
|
+
kuai.get('${path}', (c) => {
|
|
68
|
+
render(${name}, document.getElementById('root'), c.req, c.res)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
kuai.listen()
|
|
72
|
+
|
|
73
|
+
`
|
|
74
|
+
dom = dom + `</script>`
|
|
75
|
+
Bun.write(newPath, dom)
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
switch(true){
|
|
79
|
+
case provider === 'vercel':
|
|
80
|
+
let vercelJSON = process.cwd() + '/vercel.json'
|
|
81
|
+
let vercel = {
|
|
82
|
+
"rewrites": providerRoutes
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fs.writeFileSync(vercelJSON, JSON.stringify(vercel))
|
|
86
|
+
break;
|
|
87
|
+
case provider === 'nginx':
|
|
88
|
+
let full = `
|
|
89
|
+
Header add x-powered-by "vaderjs"
|
|
90
|
+
<IfModule mod_rewrite.c>
|
|
91
|
+
RewriteEngine On
|
|
92
|
+
RewriteBase /
|
|
93
|
+
|
|
94
|
+
# If the request is not for a file or directory
|
|
95
|
+
RewriteCond %{REQUEST_FILENAME} !-f
|
|
96
|
+
RewriteCond %{REQUEST_FILENAME} !-d
|
|
97
|
+
|
|
98
|
+
# Rewrite specific paths to index.html
|
|
99
|
+
${providerRoutes.join('\n')}
|
|
100
|
+
</IfModule>
|
|
101
|
+
`
|
|
102
|
+
fs.writeFileSync(process.cwd() + '/.htaccess', full)
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
console.log(`\x1b[32mSuccess\x1b[0m - Static files generated`)
|
|
106
|
+
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @name Vaderjs SSG Plugin
|
|
113
|
+
* @description This plugin is used to generate static files from the deep object - using kalix.js
|
|
114
|
+
* @version 0.0.1
|
|
115
|
+
*/
|
|
116
|
+
export default {
|
|
117
|
+
name: 'Vaderjs SSG Plugin',
|
|
118
|
+
version: '0.0.1',
|
|
119
|
+
init: async (component) => {
|
|
120
|
+
|
|
121
|
+
console.log(`\x1b[36mwait \x1b[0m - generating static files`)
|
|
122
|
+
return generate(component)
|
|
123
|
+
}
|
|
124
|
+
}
|
package/router/index.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
|
|
2
|
+
//@ts-nocheck
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
const router = new Bun.FileSystemRouter({
|
|
5
|
+
dir: process.cwd() + '/pages',
|
|
6
|
+
style:'nextjs'
|
|
7
|
+
})
|
|
8
|
+
let wsClients = []
|
|
9
|
+
function handleContentTypes(url: string){
|
|
10
|
+
let types: any = {
|
|
11
|
+
'html': 'text/html',
|
|
12
|
+
'css': 'text/css',
|
|
13
|
+
'js': 'text/javascript',
|
|
14
|
+
'json': 'application/json',
|
|
15
|
+
'png': 'image/png',
|
|
16
|
+
'jpg': 'image/jpg',
|
|
17
|
+
'jpeg': 'image/jpeg',
|
|
18
|
+
'svg': 'image/svg+xml',
|
|
19
|
+
'gif': 'image/gif',
|
|
20
|
+
'ico': 'image/x-icon',
|
|
21
|
+
'tiff': 'image/tiff',
|
|
22
|
+
'woff': 'font/woff',
|
|
23
|
+
'woff2': 'font/woff2',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let typeArray = Array.from(Object.keys(types))
|
|
27
|
+
|
|
28
|
+
let ext = url.split('.').pop()
|
|
29
|
+
if(typeArray.includes(ext)){
|
|
30
|
+
return types[ext]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function spawn_ssr_server(config: any ){
|
|
37
|
+
if(!fs.existsSync(process.cwd() + '/routes')){
|
|
38
|
+
throw new Error('SSR requires a routes directory to be present in your project')
|
|
39
|
+
}
|
|
40
|
+
let routesRouter = new Bun.FileSystemRouter({
|
|
41
|
+
dir: process.cwd() + '/routes',
|
|
42
|
+
style: 'nextjs'
|
|
43
|
+
})
|
|
44
|
+
async function serve (req,res){
|
|
45
|
+
routesRouter.reload()
|
|
46
|
+
if(res.upgrade(req)){
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
let url = new URL(req.url)
|
|
50
|
+
if(url.pathname.includes('.')){
|
|
51
|
+
url.pathname = url.pathname.replace('/\/', '/').replace('/build/', '')
|
|
52
|
+
if(url.pathname.includes('/build')){
|
|
53
|
+
url.pathname = url.pathname.replace('/build', '')
|
|
54
|
+
}
|
|
55
|
+
let file = process.cwd() + '/build' + url.pathname
|
|
56
|
+
let fileType = handleContentTypes(file)
|
|
57
|
+
if(fs.existsSync(file)){
|
|
58
|
+
let content = await Bun.file(file).text()
|
|
59
|
+
return new Response(content, {status: 200, headers: {'Content-Type': fileType, 'x-powered-by': 'Vader'}})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let routeMatch = routesRouter.match(url.pathname)
|
|
63
|
+
if(routeMatch){
|
|
64
|
+
let route = require(routeMatch.filePath).default
|
|
65
|
+
|
|
66
|
+
if(route?.name !== req?.method){
|
|
67
|
+
return new Response('Method Not Allowed', {status: 405})
|
|
68
|
+
}else{
|
|
69
|
+
try {
|
|
70
|
+
let Request = {
|
|
71
|
+
req,
|
|
72
|
+
res
|
|
73
|
+
}
|
|
74
|
+
let response = await route(Request)
|
|
75
|
+
|
|
76
|
+
if(response instanceof Response){
|
|
77
|
+
// set x-powered-by header
|
|
78
|
+
response.headers.set('x-powered-by', 'Vader')
|
|
79
|
+
return response
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`Route ${routeMatch.filePath.split('/routes')[1]} did not return a response in file ${routeMatch.filePath}`)
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.log(error)
|
|
84
|
+
return new Response('Internal Server Error', {status: 500})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
let server = Bun.serve({
|
|
92
|
+
port: config.env.PORT || 3000,
|
|
93
|
+
hostname: config.host.hostname || 'localhost',
|
|
94
|
+
reusePort: true,
|
|
95
|
+
lowMemoryMode: true,
|
|
96
|
+
development: false,
|
|
97
|
+
websocket: {
|
|
98
|
+
message(event){
|
|
99
|
+
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
open(ws){
|
|
103
|
+
wsClients.push(ws)
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
async fetch(req,res){
|
|
107
|
+
return await serve(req,res)
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
error(error) {
|
|
111
|
+
console.log(error)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
return {serve, server}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function spawnServer(config: any){
|
|
119
|
+
Bun.serve({
|
|
120
|
+
port: config.env.PORT || 3000,
|
|
121
|
+
hostname: config.host.hostname || 'localhost',
|
|
122
|
+
reusePort: true,
|
|
123
|
+
websocket: {
|
|
124
|
+
message(event){
|
|
125
|
+
|
|
126
|
+
},
|
|
127
|
+
open(ws){
|
|
128
|
+
wsClients.push(ws)
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
async fetch(req,res){
|
|
132
|
+
router.reload()
|
|
133
|
+
if(res.upgrade(req)){
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
let url = new URL(req.url)
|
|
137
|
+
if(url.pathname.includes('.')){
|
|
138
|
+
url.pathname = url.pathname.replace('/\/', '/')
|
|
139
|
+
url.pathname = url.pathname.replace('/build/', '')
|
|
140
|
+
if(url.pathname.includes('/build')){
|
|
141
|
+
url.pathname = url.pathname.replace('/build', '')
|
|
142
|
+
}
|
|
143
|
+
let file = process.cwd() + '/build' + url.pathname
|
|
144
|
+
let fileType = handleContentTypes(file)
|
|
145
|
+
if(fs.existsSync(file)){
|
|
146
|
+
let content = await Bun.file(file).text()
|
|
147
|
+
return new Response(content, {status: 200, headers: {'Content-Type': fileType, 'x-powered-by': 'Vader'}})
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
let route = router.match(url.pathname)
|
|
151
|
+
if(route){
|
|
152
|
+
let path = route.filePath.split('/pages')[1].replace('.jsx', '.js').replace('.tsx', '.js').replace('.ts', '.js')
|
|
153
|
+
let html = fs.readFileSync(process.cwd() + `/build/${path.replace('.js', '.html')}`).toString()
|
|
154
|
+
return new Response(html, {status: 200, headers: {'Content-Type': 'text/html', 'x-powered-by': 'Vader'}})
|
|
155
|
+
}
|
|
156
|
+
return new Response('Not Found', {status: 404})
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @name Vader Router
|
|
164
|
+
* @description This plugin is responsible for routing and serving the application
|
|
165
|
+
*/
|
|
166
|
+
export default {
|
|
167
|
+
name: 'Vader Router',
|
|
168
|
+
version: '1.0.0',
|
|
169
|
+
once: true,
|
|
170
|
+
init: ( ) => {
|
|
171
|
+
if(!globalThis.isListening){
|
|
172
|
+
let config = require(process.cwd() + '/vader.config.js').default
|
|
173
|
+
if(process.env.mode === 'production'){
|
|
174
|
+
console.log(`Listening at - http://${config.host.hostname}:${config.env.PORT}`)
|
|
175
|
+
spawnServer(config)
|
|
176
|
+
}
|
|
177
|
+
config?.env?.SSR ? spawn_ssr_server(config ) : spawnServer(config)
|
|
178
|
+
globalThis.isListening = true
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
package/server/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Document, Element} from "vaderjs/binaries/Kalix"
|
|
2
|
+
class Component {
|
|
3
|
+
constructor(props){
|
|
4
|
+
this.props = props
|
|
5
|
+
this._key = Math.random().toString(36).substring(7)
|
|
6
|
+
this.state = {}
|
|
7
|
+
this.htmlNode = null
|
|
8
|
+
this.firstChild = null
|
|
9
|
+
this.Element = Element
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
setState(newState){
|
|
14
|
+
this.state = newState
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
useState(name, initialValue){
|
|
19
|
+
if(!this.state[name]){
|
|
20
|
+
this.state[name] = initialValue
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let getState = () => this.state[name]
|
|
24
|
+
let setState = (newValue) => {
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
return [getState, setState]
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
useReducer(name, reducer, initialState){
|
|
32
|
+
let [state, setState] = this.useState(name, initialState)
|
|
33
|
+
let dispatch = (action) => {
|
|
34
|
+
let newState = reducer(state(), action)
|
|
35
|
+
setState(newState)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return [state, dispatch]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
useEffect(effect, deps){
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
render(){
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
export async function renderToString(element, args = []) {
|
|
51
|
+
let data = typeof element === 'function' ? await element(args) : element
|
|
52
|
+
let doc = new Document()
|
|
53
|
+
let el = doc.createElement(data)
|
|
54
|
+
|
|
55
|
+
return el.toString()
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @description This function is used to generate the server side rendered page
|
|
61
|
+
* @param {Element} element
|
|
62
|
+
* @param {Object} options
|
|
63
|
+
* @param {string} options.entry - The entry file for the component (refers to /build/pages/~)
|
|
64
|
+
* @param {Function} options.props - The server side props function
|
|
65
|
+
* @param {Array} args - The arguments to pass to the props function - ie Request or any other data
|
|
66
|
+
* @param {*} args
|
|
67
|
+
* @returns
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
export async function generatePage(element, options = {entry:"", props: any}, args = []) {
|
|
71
|
+
let config = await import(process.cwd() + '/vader.config.js').then((config) => { return config.default })
|
|
72
|
+
//@ts-ignore
|
|
73
|
+
globalThis.isServer = true
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
let serverSideProps = await options.props({req: args[0], res: args[1]}, args.slice(2))
|
|
76
|
+
let name = element.name
|
|
77
|
+
|
|
78
|
+
let comp = new Component(serverSideProps?.props)
|
|
79
|
+
element = element.bind(comp)
|
|
80
|
+
comp.render = (props)=> {
|
|
81
|
+
return element(props)
|
|
82
|
+
}
|
|
83
|
+
let data = new Document().createElement(comp.render({props: serverSideProps.props, ...args}))
|
|
84
|
+
let document = new Document()
|
|
85
|
+
document.documentElement.setContent(data.querySelector('html') ? data.querySelector('html').innerHTML : '')
|
|
86
|
+
document.documentElement.setAttribute('lang', data.querySelector('html') ? data.querySelector('html').getAttribute('lang') : 'en')
|
|
87
|
+
document.head.setContent(data.querySelector('head') ? data.querySelector('head').innerHTML : '')
|
|
88
|
+
data.removeChild(data.querySelector('head'))
|
|
89
|
+
let div = new Document().createElement('div')
|
|
90
|
+
div.setAttribute('id', 'app')
|
|
91
|
+
div.setContent(data.innerHTML)
|
|
92
|
+
document.body.appendChild(div)
|
|
93
|
+
let script = new Document().createElement('script')
|
|
94
|
+
script.setAttribute('type', 'module')
|
|
95
|
+
|
|
96
|
+
script.setContent(script.innerHTML + '\n' + `
|
|
97
|
+
|
|
98
|
+
import ${name} from "${options?.entry}"
|
|
99
|
+
import {render} from '/src/client.js'
|
|
100
|
+
import Kuai from '/src/router.js'
|
|
101
|
+
let kuai = new Kuai({container: document.getElementById('app')})
|
|
102
|
+
kuai.get('/', (c) => {
|
|
103
|
+
c.html(render(${name}, document.getElementById('app'), {...c, props: ${JSON.stringify(serverSideProps.props)}}))
|
|
104
|
+
})
|
|
105
|
+
kuai.use('/', () => console.log('Middleware'))
|
|
106
|
+
kuai.listen()
|
|
107
|
+
${
|
|
108
|
+
config?.mode === 'development' ? `
|
|
109
|
+
let ws = new WebSocket('ws://localhost:${env.PORT || 3000}')
|
|
110
|
+
ws.onopen = () => {
|
|
111
|
+
ws.send('Hello')
|
|
112
|
+
}
|
|
113
|
+
ws.onmessage = (e) => {
|
|
114
|
+
if(e.data === 'reload'){
|
|
115
|
+
window.location.reload()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
ws.onclose = () => {
|
|
119
|
+
console.log('Connection closed')
|
|
120
|
+
window.location.reload()
|
|
121
|
+
}
|
|
122
|
+
` :''
|
|
123
|
+
}
|
|
124
|
+
`)
|
|
125
|
+
document.body.appendChild(script)
|
|
126
|
+
return `<!DOCTYPE html><html lang="${document.documentElement.getAttribute('lang')}">${document.head.toString()}${document.body.innerHTML}</html>`
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export default renderToString
|