@vindo/react 0.0.1
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 +12 -0
- package/client.js +95 -0
- package/index.d.ts +14 -0
- package/index.js +224 -0
- package/package.json +23 -0
package/README.md
ADDED
package/client.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @vindo/react
|
|
3
|
+
* Copyright(c) 2025 Ruel Mindo
|
|
4
|
+
* MIT Licensed
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const React = require('node_modules/react')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Http Request
|
|
16
|
+
*/
|
|
17
|
+
function request({path, data}, opts = {}) {
|
|
18
|
+
if(data && typeof data !== 'object') {
|
|
19
|
+
throw TypeError(`Invalid type of 'data'. Expected value of type 'object' but got ${typeof data}'.`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var uuid = window.crypto.randomUUID()
|
|
23
|
+
var opts = {
|
|
24
|
+
...opts,
|
|
25
|
+
headers: {
|
|
26
|
+
...opts.headers,
|
|
27
|
+
'X-Fetch-Request-Token': uuid
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var url = new URL(location.href)
|
|
32
|
+
if(path) {
|
|
33
|
+
url = new URL(path, location.origin)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if(opts.method == 'GET') {
|
|
37
|
+
if(data) {
|
|
38
|
+
for(var i in data) {
|
|
39
|
+
url.searchParams.append(i, data[i])
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return fetch(url, opts).then((res) => {
|
|
45
|
+
return res.headers.get('X-Fetch-Response') == uuid && res.json()
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Http Request
|
|
53
|
+
*/
|
|
54
|
+
exports.http = {
|
|
55
|
+
get(args = {}) {
|
|
56
|
+
return request(args, {...args.headers, method: 'GET'})
|
|
57
|
+
},
|
|
58
|
+
post(args = {}) {
|
|
59
|
+
return request(args, {
|
|
60
|
+
body: JSON.stringify(args.data ?? {}),
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
...args.headers,
|
|
64
|
+
'Content-Type': 'application/json'
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* HTML Holder
|
|
73
|
+
*/
|
|
74
|
+
exports.View = function View({children}) {
|
|
75
|
+
return children
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Find current route
|
|
80
|
+
*/
|
|
81
|
+
exports.HydrateContent = function HydrateContent({data, meta, ...props}) {
|
|
82
|
+
|
|
83
|
+
if(data && React.isValidElement(data.content)) {
|
|
84
|
+
return React.createElement('main', props, data.content)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return React.createElement('main', props, React.Children.map(props.children, (child) => {
|
|
88
|
+
if(!data || !child.props.name) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
if(meta.name == child.props.name) {
|
|
92
|
+
return child.props.component(data.content)
|
|
93
|
+
}
|
|
94
|
+
}))
|
|
95
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module '@vindo/react' {
|
|
2
|
+
export const HTTPResponse: {
|
|
3
|
+
get(cb:Function): void
|
|
4
|
+
post(cb:Function): void
|
|
5
|
+
}
|
|
6
|
+
export function server(): Function;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare module '@vindo/react/client' {
|
|
10
|
+
export const http:any;
|
|
11
|
+
export function View(props): any;
|
|
12
|
+
export function Hydrate(props): any;
|
|
13
|
+
export function HydrateContent(props): any;
|
|
14
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @vindo/react
|
|
3
|
+
* Copyright(c) 2025 Ruel Mindo
|
|
4
|
+
* MIT Licensed
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const path = require('node:path')
|
|
11
|
+
const React = require('node_modules/react')
|
|
12
|
+
const ReactDom = require('node_modules/react-dom/server')
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Shorthand
|
|
16
|
+
*/
|
|
17
|
+
const map = React.Children.map
|
|
18
|
+
const clone = React.cloneElement
|
|
19
|
+
const create = React.createElement
|
|
20
|
+
const isValid = React.isValidElement
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
var data = {}
|
|
24
|
+
var index = require(path.resolve('src/react'))
|
|
25
|
+
if(index.default) {
|
|
26
|
+
index = index.default
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Set data
|
|
32
|
+
* @param {object} el
|
|
33
|
+
* @param {object} meta
|
|
34
|
+
*/
|
|
35
|
+
function set(el, meta) {
|
|
36
|
+
const {children: content, ...props} = el.props
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Use id if no name provided
|
|
40
|
+
*/
|
|
41
|
+
var name = props.name ?? props.id
|
|
42
|
+
var opt = {
|
|
43
|
+
name,
|
|
44
|
+
meta: {name, ...meta}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
switch(el.type.name) {
|
|
48
|
+
case 'View':
|
|
49
|
+
Object.assign(opt, {bundle: true, content: content ?? props})
|
|
50
|
+
break
|
|
51
|
+
default:
|
|
52
|
+
Object.assign(opt, {bundle: false, content: el})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
function children(item) {
|
|
57
|
+
var children = item.props.children
|
|
58
|
+
|
|
59
|
+
if(item.type == 'head') {
|
|
60
|
+
children = head(children, opt)
|
|
61
|
+
}
|
|
62
|
+
if(item.type == 'body') {
|
|
63
|
+
children = body(children, opt)
|
|
64
|
+
}
|
|
65
|
+
return clone(item, item.props, children)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
const app = index({name, content: opt.content, ...meta})
|
|
70
|
+
return {
|
|
71
|
+
name,
|
|
72
|
+
html: html(
|
|
73
|
+
clone(app, {}, map(app.props.children, children))
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set data to first level of children function
|
|
81
|
+
* @param {array} children
|
|
82
|
+
* @param {object} data
|
|
83
|
+
*/
|
|
84
|
+
function inherit(children, data) {
|
|
85
|
+
|
|
86
|
+
return children.map((child, key) => {
|
|
87
|
+
if(typeof child.type == 'function') {
|
|
88
|
+
child = child.type({...child.props, data, meta: data.meta})
|
|
89
|
+
}
|
|
90
|
+
return clone(child, {key})
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Set DOCTYPE
|
|
97
|
+
* @param {object} html
|
|
98
|
+
*/
|
|
99
|
+
function html(obj) {
|
|
100
|
+
return '<!DOCTYPE html>'.concat(ReactDom.renderToString(obj))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Add a bundle script to head
|
|
106
|
+
* @param children
|
|
107
|
+
* @param args
|
|
108
|
+
*/
|
|
109
|
+
function head(children, args) {
|
|
110
|
+
const env = process.env
|
|
111
|
+
|
|
112
|
+
if(!args.bundle) {
|
|
113
|
+
return children
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const queries = new URLSearchParams({
|
|
117
|
+
hash: env.UUID,
|
|
118
|
+
name: args.name,
|
|
119
|
+
port: (env.NODE_ENV == 'dev' || env.NODE_ENV == 'develop' || env.NODE_ENV == 'development') && env.DEV_SERVER_PORT
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
return children.concat(
|
|
123
|
+
create('script', {
|
|
124
|
+
key: 0,
|
|
125
|
+
id: 'bundle',
|
|
126
|
+
type: 'module',
|
|
127
|
+
src: '/bundle.js?'.concat(queries.toString())
|
|
128
|
+
})
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Set meta data to children
|
|
135
|
+
* @param {array} children
|
|
136
|
+
* @param {object} args
|
|
137
|
+
*/
|
|
138
|
+
function body(children, args) {
|
|
139
|
+
|
|
140
|
+
if(typeof children.type == 'function') {
|
|
141
|
+
const type = children.type(args)
|
|
142
|
+
|
|
143
|
+
if(!type) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
if(type.props.children) {
|
|
147
|
+
children = type.props.children
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
data[args.name] = inherit(children, args)
|
|
151
|
+
|
|
152
|
+
return data[args.name]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Client fetch response
|
|
158
|
+
*
|
|
159
|
+
* @param {object} req
|
|
160
|
+
* @param {object} res
|
|
161
|
+
*/
|
|
162
|
+
function HTTPResponse(req, res) {
|
|
163
|
+
const token = req.get('x-fetch-request-token')
|
|
164
|
+
|
|
165
|
+
const response = function(data) {
|
|
166
|
+
if(token) {
|
|
167
|
+
res.json(data, 200, {'X-Fetch-Response': token})
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
get(cb) {
|
|
173
|
+
if(req.method == 'GET')
|
|
174
|
+
response(cb(req.query))
|
|
175
|
+
},
|
|
176
|
+
post(cb) {
|
|
177
|
+
if(req.method == 'POST')
|
|
178
|
+
response(cb(req.body))
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Middleware
|
|
186
|
+
*/
|
|
187
|
+
exports.server = function server() {
|
|
188
|
+
|
|
189
|
+
return function(req, res, next, {env, meta, events, exception}) {
|
|
190
|
+
exports.HTTPResponse = HTTPResponse(req, res)
|
|
191
|
+
/**
|
|
192
|
+
* JSX object
|
|
193
|
+
*/
|
|
194
|
+
if(req.is(env.UUID)) {
|
|
195
|
+
data = res.json(data[req.query.name]) ?? {}
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
events.on('render', function(component) {
|
|
200
|
+
if(req.get('x-fetch-request-token')) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if(isValid(component)) {
|
|
205
|
+
var data = set(component, meta)
|
|
206
|
+
if(!data) {
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Return nothing if the request not matched with the component.
|
|
211
|
+
*/
|
|
212
|
+
const errors = Object.keys(exception.statuses).concat('error')
|
|
213
|
+
if(data.name && req.name) {
|
|
214
|
+
if(req.route.back && data.name !== req.name && !errors.includes(data.name)) {
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return data
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
next()
|
|
223
|
+
}
|
|
224
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vindo/react",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React SSR",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"types": "./index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/rmindo/vindo.git",
|
|
14
|
+
"directory": "packages/react"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/rmindo/vindo/tree/main/packages/react#readme",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"react"
|
|
19
|
+
],
|
|
20
|
+
"author": "Ruel Mindo",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {}
|
|
23
|
+
}
|