vaderjs 1.4.2-bml56 → 1.4.2-jpbvml56
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 +14 -4
- package/binaries/Kalix/index.js +21 -12
- package/binaries/compiler/main.js +71 -30
- package/binaries/watcher/hmr.js +3 -2
- package/client/runtime/index.js +1 -417
- package/client/runtime/router.js +1 -235
- package/config/index.ts +20 -1
- package/package.json +1 -2
- package/plugins/cloudflare/functions/index.js +4 -0
- package/plugins/cloudflare/toCopy/@server/Kalix/index.js +62 -14
- package/plugins/cloudflare/toCopy/src/client.js +1 -432
- package/plugins/cloudflare/toCopy/src/router.js +1 -235
- package/plugins/ssg/index.js +89 -19
- package/plugins/tailwind/index.ts +102 -0
- package/router/index.ts +37 -10
- package/server/index.js +15 -1
|
@@ -1,235 +1 @@
|
|
|
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;
|
|
1
|
+
export class Kuai{constructor(t={container:"#app"}){this.routes=[],this.middleware=[],this.container=t.container?t.container:document.getElementById("app"),this.renderPlugins=[]}res={text:t=>{this.container.innerHTML=t},html:t=>{switch(typeof t){case"function":if(this.renderPlugins.length>0)return void this.renderPlugins.forEach((e=>{"html"===e.for&&(console.log("Plugin",e),e.plugin(t,this.container))}));this.container.innerHTML=t();break;case"string":this.container.innerHTML=t}},json:t=>{this.container.innerHTML=`<Oject data=${JSON.stringify(t)}></Object>`}};req={navigate:t=>{window.history.pushState({},"",t);let e=this.match(window.location.pathname.replace("/index.html",""));e&&(this.currentRoute=e.path,e.callback(this.res,e.params,this.extractQueryParams(window.location.search)))},back:()=>{window.history.back()},forward:()=>{window.history.forward()},url:window.location};extractQueryParams(t){let e=new URLSearchParams(t),i={};for(let t of e)i[t[0]]=t[1];return i}extractParams(t,e){const i=t.split("/").filter((t=>""!==t)),r=e.split("/").filter((t=>""!==t)),a={};return i.forEach(((t,e)=>{if(t.startsWith(":")){const i=t.slice(1);a[i]=r[e]}else if(t.startsWith("*")){r.slice(e).forEach(((t,e)=>{a[e]=t}))}})),a}use(t,e){this.middleware.push({path:t,middleware:e})}usePlugin(t,e){this.renderPlugins.push({plugin:t,for:e})}match(t){(t=t.endsWith("/")?t.slice(0,-1):t).includes("index.html")&&(t=t.replace("index.html","")),t.includes("?")&&(t=t.split("?")[0]);let e=this.routes.find((e=>{if(e.path===t)return!0;if(""===t&&"/"===e.path)return!0;if(e.path.includes("*")||e.path.includes(":")){const i=e.path.split("/").filter((t=>""!==t)),r=t.split("/").filter((t=>""!==t));if(this.basePath&&r.shift(),i.length!==r.length&&!e.path.endsWith("*"))return!1;for(let t=0;t<i.length;t++){const e=i[t],a=r[t];if(!e.startsWith(":")&&!e.startsWith("*")&&e!==a)return!1}return!0}}));if(e){let i=this.extractParams(e.path,t);return{...e,params:i}}return null}get(t,e){this.routes.push({path:t,callback:e})}listen(){let t=this.match(window.location.pathname.replace("/index.html",""));if(t){this.middleware.forEach((e=>{e.path===t.path&&e.middleware()})),this.currentRoute=t.path;let e={...this.res,res:this.res,req:{...this.req,params:e=>t.params[e]}};t.callback(e)}window.onpopstate=()=>{let t=this.match(window.location.pathname.replace("/index.html",""));t&&(this.currentRoute=t.path,t.callback(this.res,t.params,this.extractQueryParams(window.location.search)))}}}export default Kuai;
|
package/plugins/ssg/index.js
CHANGED
|
@@ -11,38 +11,77 @@ async function generate(){
|
|
|
11
11
|
let config = await import(process.cwd() + '/vader.config.js').then((config) => { return config.default })
|
|
12
12
|
let provider = config?.host?.provider
|
|
13
13
|
|
|
14
|
-
let providerRoutes = []
|
|
14
|
+
let providerRoutes = []
|
|
15
|
+
Object.keys(routes).map((route) => {
|
|
16
|
+
if(route.includes('[')){
|
|
17
|
+
|
|
18
|
+
let root = Object.keys(routes).find((r) => {
|
|
19
|
+
return r === '/'
|
|
20
|
+
} )
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
let param = route.split('/[')[1].split(']')[0]
|
|
24
|
+
let p = {}
|
|
25
|
+
let existingParams = routes[root]?.params || []
|
|
26
|
+
existingParams.push({
|
|
27
|
+
isCatchAll: route.includes('[[catchall]]') ? true : false,
|
|
28
|
+
name: param,
|
|
29
|
+
file: routes[route],
|
|
30
|
+
baseFolder: route.split('[')[0]
|
|
31
|
+
})
|
|
32
|
+
p.file = routes[root]
|
|
33
|
+
p.params = existingParams
|
|
34
|
+
routes[root] = p
|
|
35
|
+
delete routes[route]
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
})
|
|
15
39
|
for(var i in routes){
|
|
16
|
-
let path = i
|
|
40
|
+
let path = i
|
|
17
41
|
let file = routes[i]
|
|
42
|
+
if(typeof file === 'object'){
|
|
43
|
+
file = file.file
|
|
44
|
+
}
|
|
18
45
|
let comp = require(file).default
|
|
19
46
|
if(!comp){
|
|
20
47
|
continue;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
let dom = await renderToString(comp)
|
|
26
|
-
div.setContent(dom)
|
|
27
|
-
dom = div.toString("outerHTML")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let dom = await renderToString(comp)
|
|
51
|
+
let isHtml = dom.includes('<html')
|
|
28
52
|
let folder = path.split('/pages')[0]
|
|
29
53
|
let newPath = process.cwd() + '/build' + folder + '/index.html'
|
|
30
54
|
let name = comp.name || 'App'
|
|
31
55
|
file = file.replace(/\\/g, '/').replace('\/\/', '/').replace(process.cwd().replace(/\\/g, '/'), '').split('/pages')[1].replace('.tsx', '.js').replace('.jsx', '.js')
|
|
32
|
-
let isParamRoute =
|
|
56
|
+
let isParamRoute = routes[i].params ? true : false
|
|
33
57
|
let baseFolder = ''
|
|
34
58
|
if(isParamRoute){
|
|
35
|
-
|
|
59
|
+
let route = routes[i].params[0]
|
|
60
|
+
baseFolder = i.split('[')[0]
|
|
36
61
|
let providerPath;
|
|
37
62
|
switch(true){
|
|
38
63
|
case provider === 'vercel':
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
if(route.isCatchAll){
|
|
65
|
+
providerPath = `${baseFolder}/*`
|
|
66
|
+
providerRoutes.push({source: providerPath, destination: `${path}/index.html`})
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
providerPath = `${baseFolder}:${route.name}`
|
|
70
|
+
providerRoutes.push({source: providerPath, dest: `/`})
|
|
41
71
|
break;
|
|
42
72
|
case provider === 'nginx':
|
|
43
73
|
providerPath = `RewriteRule ^${baseFolder.replace('/', '')}.*$ ${path}/index.html [L]`
|
|
44
74
|
providerRoutes.push(providerPath)
|
|
45
75
|
break;
|
|
76
|
+
case provider === 'cloudflare':
|
|
77
|
+
if(route.isCatchAll){
|
|
78
|
+
providerPath = `${baseFolder}/*`
|
|
79
|
+
providerRoutes.push({source: providerPath, destination: `${path}/index.html`})
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
providerPath = `${baseFolder}/*`
|
|
83
|
+
providerRoutes.push({source: providerPath, destination: `${path}/index.html`})
|
|
84
|
+
break;
|
|
46
85
|
}
|
|
47
86
|
}else{
|
|
48
87
|
let providerPath;
|
|
@@ -58,17 +97,48 @@ async function generate(){
|
|
|
58
97
|
providerPath = `RewriteRule ^${path.replace('/', '')}/$ ${path}/index.html [L]`
|
|
59
98
|
providerRoutes.push(providerPath)
|
|
60
99
|
break;
|
|
100
|
+
|
|
61
101
|
|
|
62
102
|
}
|
|
63
|
-
}
|
|
64
|
-
dom = dom + `
|
|
65
|
-
<script type="module">
|
|
103
|
+
}
|
|
104
|
+
dom = preRender ? dom : '<div id="root"></div>' + `
|
|
105
|
+
<script type="module">
|
|
106
|
+
|
|
66
107
|
import { render } from '/src/client.js'
|
|
67
|
-
|
|
108
|
+
let ${name} = await import('/pages/${file.replace(process.cwd(), '')}')
|
|
109
|
+
if(${name}.default){
|
|
110
|
+
${name} = ${name}.default
|
|
111
|
+
}else{
|
|
112
|
+
let keys = Object.keys(${name})
|
|
113
|
+
${name} = ${name}[keys[0]]
|
|
114
|
+
}
|
|
68
115
|
import Kuai from '/src/router.js'
|
|
69
116
|
let kuai = new Kuai()
|
|
117
|
+
${
|
|
118
|
+
routes[i].params ?
|
|
119
|
+
routes[i].params.map((param) => {
|
|
120
|
+
let name = param.name
|
|
121
|
+
let file = param.file.replace(/\\/g, '/').replace('\/\/', '/').replace(process.cwd().replace(/\\/g, '/'), '').split('/pages')[1].replace('.tsx', '.js').replace('.jsx', '.js')
|
|
122
|
+
return `
|
|
123
|
+
// handle if default or named
|
|
124
|
+
let ${name} = await import('/pages/${file}')
|
|
125
|
+
if(${name}.default){
|
|
126
|
+
${name} = ${name}.default
|
|
127
|
+
}else{
|
|
128
|
+
let keys = Object.keys(${name})
|
|
129
|
+
${name} = ${name}[keys[0]]
|
|
130
|
+
}
|
|
131
|
+
kuai.get('${param.baseFolder}:${name}', (c) => {
|
|
132
|
+
render(${name}, ${
|
|
133
|
+
isHtml ? `document.documentElement`: `document.body.firstChild`
|
|
134
|
+
}, c.req, c.res)
|
|
135
|
+
})`
|
|
136
|
+
}).join('\n') : ''
|
|
137
|
+
}
|
|
70
138
|
kuai.get('${path}', (c) => {
|
|
71
|
-
render(${name},
|
|
139
|
+
render(${name}, ${
|
|
140
|
+
isHtml ? `document.documentElement`: `document.body.firstChild`
|
|
141
|
+
}, c.req, c.res)
|
|
72
142
|
})
|
|
73
143
|
|
|
74
144
|
kuai.listen()
|
|
@@ -103,7 +173,7 @@ Header add x-powered-by "vaderjs"
|
|
|
103
173
|
</IfModule>
|
|
104
174
|
`
|
|
105
175
|
fs.writeFileSync(process.cwd() + '/.htaccess', full)
|
|
106
|
-
break;
|
|
176
|
+
break;
|
|
107
177
|
}
|
|
108
178
|
console.log(`\x1b[32mSuccess\x1b[0m - Static files generated`)
|
|
109
179
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
|
|
3
|
+
function checkIFtailwindInstalled() {
|
|
4
|
+
try {
|
|
5
|
+
require.resolve('tailwindcss');
|
|
6
|
+
return true;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
name: "tailwind",
|
|
15
|
+
description: "A plugin to install tailwindcss",
|
|
16
|
+
once: true,
|
|
17
|
+
init: async () => {
|
|
18
|
+
let config = require(process.cwd() + "/vader.config.js").default;
|
|
19
|
+
const fs = require("fs");
|
|
20
|
+
const path = require("path");
|
|
21
|
+
const { exec} = require("child_process");
|
|
22
|
+
const tailwindConfig = `
|
|
23
|
+
/** @type {import('tailwindcss').Config} */
|
|
24
|
+
module.exports = {
|
|
25
|
+
content: ['./src/**/*.{jsx,tsx,js,ts}', './pages/**/*.{jsx,tsx,js,ts}', './components/**/*.{jsx,tsx,js,ts}'],
|
|
26
|
+
theme: {
|
|
27
|
+
${
|
|
28
|
+
config?.tailwind?.theme ? JSON.stringify(config.tailwind.theme, null, 2) : ``
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
plugins: [
|
|
32
|
+
${
|
|
33
|
+
config?.tailwind?.plugins ? config?.tailwind?.plugins.map((plugin) => {
|
|
34
|
+
return `require('${plugin}')`
|
|
35
|
+
}).join(",") : ``
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
fs.writeFileSync(
|
|
41
|
+
path.join(process.cwd(), "tailwind.config.js"),
|
|
42
|
+
tailwindConfig
|
|
43
|
+
);
|
|
44
|
+
if (!checkIFtailwindInstalled()) {
|
|
45
|
+
console.log(`\x1b[36mwait \x1b[0m - installing tailwindcss & ${config?.tailwind?.plugins ? config?.tailwind?.plugins.length + " plugins" : ""} (First time only)`);
|
|
46
|
+
let npmPath = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
47
|
+
Bun.spawnSync({
|
|
48
|
+
cmd: [npmPath, "install", "tailwindcss", "postcss", "autoprefixer" , ...config?.tailwind?.plugins || []],
|
|
49
|
+
cwd: process.cwd(),
|
|
50
|
+
stderr: "inherit",
|
|
51
|
+
})
|
|
52
|
+
console.log(`\x1b[32msuccess \x1b[0m - tailwindcss installed`)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const postcssConfig = `
|
|
56
|
+
module.exports = {
|
|
57
|
+
plugins: {
|
|
58
|
+
tailwindcss: {},
|
|
59
|
+
autoprefixer: {},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
fs.mkdirSync(path.join(process.cwd(), "src/public/styles"), {
|
|
64
|
+
recursive: true,
|
|
65
|
+
});
|
|
66
|
+
fs.writeFileSync(
|
|
67
|
+
path.join(process.cwd(), "src/public/styles/tailwind.css"),
|
|
68
|
+
`@import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities';`
|
|
69
|
+
);
|
|
70
|
+
fs.writeFileSync(
|
|
71
|
+
path.join(process.cwd(), "postcss.config.js"),
|
|
72
|
+
postcssConfig
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if(!fs.existsSync(path.join(process.cwd(), "src/public/styles/tailwind.css"))){
|
|
77
|
+
fs.mkdirSync(path.join(process.cwd(), "src/public/styles"), {
|
|
78
|
+
recursive: true,
|
|
79
|
+
});
|
|
80
|
+
fs.writeFileSync(
|
|
81
|
+
path.join(process.cwd(), "src/public/styles/tailwind.css"),
|
|
82
|
+
`@import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities';`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let cmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
87
|
+
let ws = new WebSocket(`ws://${config.host.hostname || 'localhost'}:${config.env.PORT || 3000}`)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
Bun.spawnSync({
|
|
91
|
+
cmd: [ cmd, "tailwindcss", process.cwd() + '/tailwind.config.js' , "-i", "src/public/styles/tailwind.css", "-o", config?.tailwind?.output || "build/public/styles/tailwind.css", "--minify"],
|
|
92
|
+
cwd: process.cwd(),
|
|
93
|
+
stdin: "inherit",
|
|
94
|
+
stderr: "inherit",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
let content = await Bun.file(process.cwd() + '/build/public/styles/tailwind.css').text()
|
|
98
|
+
fs.writeFileSync(process.cwd() + '/public/styles/tailwind.css', content)
|
|
99
|
+
|
|
100
|
+
ws.send(JSON.stringify({type: "css:reload"}) )
|
|
101
|
+
},
|
|
102
|
+
}
|
package/router/index.ts
CHANGED
|
@@ -56,7 +56,11 @@ function spawn_ssr_server(config: any ){
|
|
|
56
56
|
let fileType = handleContentTypes(file)
|
|
57
57
|
if(fs.existsSync(file)){
|
|
58
58
|
let content = await Bun.file(file).text()
|
|
59
|
-
|
|
59
|
+
let data = Buffer.from(content, 'utf-8');
|
|
60
|
+
const compressed = Bun.gzipSync(data);
|
|
61
|
+
return new Response(compressed, {status: 200, headers: {'Content-Type': fileType, 'x-powered-by': 'Vader',
|
|
62
|
+
'Content-Encoding':'gzip',
|
|
63
|
+
...config?.routes.find(route => route.pathname === "*" || route.pathname === url.pathname)?.headers}})
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
let routeMatch = routesRouter.match(url.pathname)
|
|
@@ -76,6 +80,7 @@ function spawn_ssr_server(config: any ){
|
|
|
76
80
|
if(response instanceof Response){
|
|
77
81
|
// set x-powered-by header
|
|
78
82
|
response.headers.set('x-powered-by', 'Vader')
|
|
83
|
+
response.headers.set('Content-Type', 'text/html')
|
|
79
84
|
return response
|
|
80
85
|
}
|
|
81
86
|
throw new Error(`Route ${routeMatch.filePath.split('/routes')[1]} did not return a response in file ${routeMatch.filePath}`)
|
|
@@ -89,11 +94,12 @@ function spawn_ssr_server(config: any ){
|
|
|
89
94
|
}
|
|
90
95
|
}
|
|
91
96
|
let server = Bun.serve({
|
|
92
|
-
port: config
|
|
93
|
-
hostname: config
|
|
97
|
+
port: config?.env?.PORT || 3000,
|
|
98
|
+
hostname: config?.host?.hostname || 'localhost',
|
|
94
99
|
reusePort: true,
|
|
95
100
|
lowMemoryMode: true,
|
|
96
101
|
development: false,
|
|
102
|
+
...(config?.Router?.tls && {tls: {cert: config.Router.tls.cert, key: config.Router.tls.key}}),
|
|
97
103
|
websocket: {
|
|
98
104
|
message(event){
|
|
99
105
|
|
|
@@ -137,21 +143,42 @@ function spawnServer(config: any){
|
|
|
137
143
|
if(url.pathname.includes('.')){
|
|
138
144
|
url.pathname = url.pathname.replace('/\/', '/')
|
|
139
145
|
url.pathname = url.pathname.replace('/build/', '')
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
let file = process.cwd() + '/build' + url.pathname
|
|
146
|
+
|
|
147
|
+
let file = process.cwd() + '/build' + url.pathname
|
|
144
148
|
let fileType = handleContentTypes(file)
|
|
145
149
|
if(fs.existsSync(file)){
|
|
146
150
|
let content = await Bun.file(file).text()
|
|
147
|
-
|
|
151
|
+
let data = Buffer.from(content, 'utf-8');
|
|
152
|
+
const compressed = Bun.gzipSync(data);
|
|
153
|
+
|
|
154
|
+
return new Response(compressed, {status: 200, headers: {'Content-Type': fileType, 'x-powered-by': 'Vader', 'x-powered-by': 'Vader',
|
|
155
|
+
'Content-Encoding':'gzip',
|
|
156
|
+
'Accept-Encoding': 'gzip, deflate, br','Connection': 'keep-alive', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache', 'Expires': '0', ...config?.Router?.headers}})
|
|
148
157
|
}
|
|
149
158
|
}
|
|
150
159
|
let route = router.match(url.pathname)
|
|
151
160
|
if(route){
|
|
161
|
+
let isParamRoute = route.filePath.includes('[') && route.filePath.includes(']')
|
|
152
162
|
let path = route.filePath.split('/pages')[1].replace('.jsx', '.js').replace('.tsx', '.js').replace('.ts', '.js')
|
|
163
|
+
path = isParamRoute ? 'index.html' : path
|
|
153
164
|
let html = fs.readFileSync(process.cwd() + `/build/${path.replace('.js', '.html')}`).toString()
|
|
154
|
-
|
|
165
|
+
html = html + `
|
|
166
|
+
<script>
|
|
167
|
+
let ws = new WebSocket('ws://${config.host.hostname || 'localhost'}:${config.env.PORT || 3000}')
|
|
168
|
+
ws.onmessage = function(event){
|
|
169
|
+
console.log(event.data)
|
|
170
|
+
}
|
|
171
|
+
ws.onclose = function(event){
|
|
172
|
+
window.location.reload()
|
|
173
|
+
}
|
|
174
|
+
</script>
|
|
175
|
+
`
|
|
176
|
+
const data = Buffer.from(html, 'utf-8');
|
|
177
|
+
const compressed = Bun.gzipSync(data);
|
|
178
|
+
return new Response(compressed, {status: 200, headers: {'Content-Type': 'text/html', 'x-powered-by': 'Vader',
|
|
179
|
+
'Content-Encoding':'gzip',
|
|
180
|
+
'Accept-Encoding': 'gzip, deflate, br','Connection': 'keep-alive', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache', 'Expires': '0',
|
|
181
|
+
...config?.Router?.headers}})
|
|
155
182
|
}
|
|
156
183
|
return new Response('Not Found', {status: 404})
|
|
157
184
|
}
|
|
@@ -171,7 +198,7 @@ export default {
|
|
|
171
198
|
if(!globalThis.isListening){
|
|
172
199
|
let config = require(process.cwd() + '/vader.config.js').default
|
|
173
200
|
if(process.env.mode === 'production'){
|
|
174
|
-
console.log(
|
|
201
|
+
console.log(`\x1b[32msuccess \x1b[0m- listening on port ${config.env.PORT || 3000}`)
|
|
175
202
|
spawnServer(config)
|
|
176
203
|
}
|
|
177
204
|
config?.env?.SSR ? spawn_ssr_server(config ) : spawnServer(config)
|
package/server/index.js
CHANGED
|
@@ -48,7 +48,21 @@ class Component {
|
|
|
48
48
|
|
|
49
49
|
}
|
|
50
50
|
export async function renderToString(element, args = []) {
|
|
51
|
-
|
|
51
|
+
globalThis.isServer = true
|
|
52
|
+
globalThis.preRender = true
|
|
53
|
+
let data = typeof element === 'function' ? await element(args) : element
|
|
54
|
+
if(data?.tagName === 'html'){
|
|
55
|
+
let body = data.querySelector('body') || new Document().createElement('body')
|
|
56
|
+
let innerHTML = body.innerHTML
|
|
57
|
+
innerHTML = `${innerHTML}`
|
|
58
|
+
body.tagName = 'div'
|
|
59
|
+
body.setAttribute('id', 'root')
|
|
60
|
+
body.innerHTML = innerHTML
|
|
61
|
+
data.removeChild(data.querySelector('body'))
|
|
62
|
+
data.appendChild(body)
|
|
63
|
+
}else if(data){
|
|
64
|
+
data.firstChild.setAttribute('id', 'root')
|
|
65
|
+
}
|
|
52
66
|
let doc = new Document()
|
|
53
67
|
let el = doc.createElement(data)
|
|
54
68
|
|