vaderjs 1.3.6 → 1.3.7-alpha-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 +167 -0
- package/logo.png +0 -0
- package/package.json +15 -23
- package/runtime/router.js +261 -0
- package/runtime/static/index.html +21 -0
- package/vader +0 -0
- package/.vscode/settings.json +0 -5
- package/.vscode/vaderjs.autosense.json +0 -5
- package/index.js +0 -12
- package/jsconfig.json +0 -17
- package/readme.md +0 -262
- package/ts.config.json +0 -1
- package/tsconfig.json +0 -18
- package/vader-min.js +0 -1
- package/vader.js +0 -1141
- package/vaderRouter.js +0 -231
- package/worker-min.js +0 -9
- package/worker.js +0 -325
package/readme.md
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<a href="https://vader-js.pages.dev">
|
|
3
|
-
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="/icon.jpeg">
|
|
5
|
-
<img src="logo.png" height="128">
|
|
6
|
-
</picture>
|
|
7
|
-
<h1 align="center">Vader.js</h1>
|
|
8
|
-
</a>
|
|
9
|
-
</p>
|
|
10
|
-
|
|
11
|
-
# VaderJS: A Reactive Framework for SPAs
|
|
12
|
-
|
|
13
|
-
[](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) [](https://www.npmjs.com/package/vaderjs)
|
|
14
|
-
|
|
15
|
-
VaderJS is powerful component based reactive library for spa inspired by react.js
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## Get Started
|
|
19
|
-
|
|
20
|
-
1. Install VaderJS:
|
|
21
|
-
|
|
22
|
-
```sh
|
|
23
|
-
npm install vaderjs
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
or
|
|
27
|
-
|
|
28
|
-
```html
|
|
29
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/vaderjs@latest/index.js" ></script>
|
|
30
|
-
<script type="module" src="https://unpkg.com/vaderjs@latest/index.js">
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
2. Import components and utilities into your project.
|
|
34
|
-
|
|
35
|
-
- Heres an example import map
|
|
36
|
-
|
|
37
|
-
```html
|
|
38
|
-
<script type="importmap">
|
|
39
|
-
{
|
|
40
|
-
"imports":{
|
|
41
|
-
"vaderjs":"./dist/vader/index.js",
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
</script>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
- Then you can import like this
|
|
48
|
-
|
|
49
|
-
```js
|
|
50
|
-
import Vader, { VaderRouter, include } from 'vaderjs'
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
3. Use VaderJS features for routing, state management, auth, and more.
|
|
54
|
-
|
|
55
|
-
4. Create dynamic SPAs with enhanced user experiences.
|
|
56
|
-
|
|
57
|
-
5. Type checking / testing
|
|
58
|
-
- Vader has jsdoc annotations built in but also allows ts using the tsconfig
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npm run test // validate your code
|
|
62
|
-
```
|
|
63
|
-
## Key Features
|
|
64
|
-
|
|
65
|
-
### Client Fly Rendering
|
|
66
|
-
|
|
67
|
-
Vaderjs allows you to render your components & manipulate worker side vs main thread. This allows for faster page speeds and better user experience.
|
|
68
|
-
|
|
69
|
-
```javascript
|
|
70
|
-
import Vader from "vaderjs";
|
|
71
|
-
class Home extends Vader.Component {
|
|
72
|
-
constructor() {
|
|
73
|
-
super();
|
|
74
|
-
this.cfr = true; // enable client fly rendering - this is optionals
|
|
75
|
-
}
|
|
76
|
-
render() {
|
|
77
|
-
return this.html(`<div>Hello World</div>`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Declarative Routing
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
import VaderRouter from "../dist/vader/vaderRouter.js";
|
|
86
|
-
import { Mycomponent} from "../src/pages/Home.js";
|
|
87
|
-
|
|
88
|
-
const app = new VaderRouter('/');
|
|
89
|
-
|
|
90
|
-
app.get("/", async (req, res)=>{
|
|
91
|
-
res.send('#root', await new Home().render())
|
|
92
|
-
})
|
|
93
|
-
app.get('/docs/:page/*', async (req, res)=>{
|
|
94
|
-
// page and asterisk route use req.params for params and req.params[0] to get the asterisk
|
|
95
|
-
// you can get queries from the url using req.query!
|
|
96
|
-
})
|
|
97
|
-
const middleware = (req, res)=>{
|
|
98
|
-
req.time = Date.now()
|
|
99
|
-
}
|
|
100
|
-
app.use(middleware) // use middlewares
|
|
101
|
-
|
|
102
|
-
app.listen(3000, ()=>{
|
|
103
|
-
console.log('listening on port 3000')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
### State Management
|
|
110
|
-
|
|
111
|
-
```javascript
|
|
112
|
-
import Vader from "vaderjs"
|
|
113
|
-
|
|
114
|
-
class MyApp extends Vader.Component{
|
|
115
|
-
contructor(){
|
|
116
|
-
super()
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
render(){
|
|
121
|
-
const [state, setState] = this.useState('state', 0, ()=>{
|
|
122
|
-
// this is a callback that is ran on state change!
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
let myfunc = this.$Function(function fn(){
|
|
126
|
-
setState(state + 1)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
this.useEffect(()=>{
|
|
130
|
-
// this is a callback that is ran on component mount
|
|
131
|
-
}, [])
|
|
132
|
-
|
|
133
|
-
return this.html(`
|
|
134
|
-
<p>count ${state} </p>
|
|
135
|
-
<button onclick="${myfunc}">Change State by 1</button>
|
|
136
|
-
`)
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Signals
|
|
143
|
-
|
|
144
|
-
Signals are a way to communicate between components. Signals are similar to events in React.js. Signals are useful for global state management and component communication.
|
|
145
|
-
|
|
146
|
-
- This is new as of v1.1.2
|
|
147
|
-
|
|
148
|
-
```javascript
|
|
149
|
-
|
|
150
|
-
let count = this.signal('count', 0)
|
|
151
|
-
|
|
152
|
-
let increment = this.$Function(function increment(){
|
|
153
|
-
count.set(count.get() + 1)
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
count.subscribe( (detail)=>{
|
|
157
|
-
console.log(detail)
|
|
158
|
-
}, true) // true means it will run on once
|
|
159
|
-
|
|
160
|
-
// call the signal
|
|
161
|
-
count.call()
|
|
162
|
-
|
|
163
|
-
count.cleanup() // cleans up the signal
|
|
164
|
-
|
|
165
|
-
count.get() // returns the signal detail
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
```
|
|
170
|
-
- Signals also allow you to share state between scopes
|
|
171
|
-
|
|
172
|
-
```javascript
|
|
173
|
-
window.addEventListener('signalDispatch', (e)=>{
|
|
174
|
-
console.log(e.detail)
|
|
175
|
-
})
|
|
176
|
-
````
|
|
177
|
-
|
|
178
|
-
### Function Binding
|
|
179
|
-
|
|
180
|
-
```javascript
|
|
181
|
-
|
|
182
|
-
const fn = this.$Function(function fn() {
|
|
183
|
-
console.log("Hello World");
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
return html(`<button onclick="${fn}">Click Me</button>`)
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Authentication & Authorization
|
|
190
|
-
|
|
191
|
-
```javascript
|
|
192
|
-
const auth = this.useAuth({
|
|
193
|
-
rulesets: rules,
|
|
194
|
-
user: currentUser
|
|
195
|
-
});
|
|
196
|
-
if (auth.can('edit')) {
|
|
197
|
-
// Display edit button
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
### Simplified Component Creation
|
|
203
|
-
|
|
204
|
-
```javascript
|
|
205
|
-
import Vader from 'vaderjs';
|
|
206
|
-
|
|
207
|
-
export class App extends Vader.Component{
|
|
208
|
-
constructor(){
|
|
209
|
-
super('App')
|
|
210
|
-
}
|
|
211
|
-
render(){
|
|
212
|
-
return html`<div>Hello World</div>`
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## Include views
|
|
218
|
-
|
|
219
|
-
As of v1.1.0 - Vader allows you to include html files as templates
|
|
220
|
-
|
|
221
|
-
```html
|
|
222
|
-
// views/app.vjs
|
|
223
|
-
<body>
|
|
224
|
-
<div>Hello World</div>
|
|
225
|
-
<h1>{{title}}</h1>
|
|
226
|
-
<slot id="app" />
|
|
227
|
-
</body>
|
|
228
|
-
```
|
|
229
|
-
```html
|
|
230
|
-
// pages/app.html
|
|
231
|
-
<include src="views/app.vjs"/>
|
|
232
|
-
<body>
|
|
233
|
-
<app tittle="this is a component">
|
|
234
|
-
<div> this is apps children! </div>
|
|
235
|
-
</app>
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
```js
|
|
240
|
-
// home.js
|
|
241
|
-
import Vader from "vaderjs";
|
|
242
|
-
|
|
243
|
-
class Home extends Vader.Component {
|
|
244
|
-
constructor() {
|
|
245
|
-
super();
|
|
246
|
-
}
|
|
247
|
-
render() {
|
|
248
|
-
return this.html(include("pages/app.html"));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
## License
|
|
257
|
-
|
|
258
|
-
VaderJS is released under the MIT License. See the [LICENSE](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) file for details.
|
|
259
|
-
|
|
260
|
-
## Join the Community
|
|
261
|
-
|
|
262
|
-
Connect with the VaderJS community on [GitHub](https://github.com/Postr-Inc/Vader.js). Contribute, share feedback, and improve VaderJS for SPA development.
|
package/ts.config.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
package/tsconfig.json
DELETED
package/vader-min.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
let dom=[],states={},worker=new Worker(new URL("./worker.js",import.meta.url));export const useRef=t=>{let e=document.querySelector(`[ref="${t}"]`),s=t=>{let s=new DOMParser().parseFromString(t,"text/html"),n=s.body.firstChild;if(e){let i=!n.isEqualNode(e);i&&e.parentNode.replaceChild(n,e)}};return{current:e,update:s}};let components=[];export class Component{constructor(){this.states={},this.name=this.constructor.name,this.executedEffects={},this.storedProps={},this.componentMounted=!1,this.hasMounted=!1,this.$_signal_subscribers=[],this.$_signal_subscribers_ran=[],this.effects={},this.$_useStore_subscribers=[],this.init(),this.Componentcontent=null,this.$_signal_dispatch_event=new CustomEvent("SignalDispatch",{detail:{hasUpdated:!1,state:null}}),this.$_signal_dispatch_cleanup_event=new CustomEvent("Signal_Cleanup_Dispatch",{detail:{state:null,lastState:null}}),this.snapshots=[],this.dom=[],this.cfr=!1}adapter(t){}init(){this.registerComponent()}registerComponent(){components.push(this)}setState(t,e){this.states[t]=e,this.updateComponent()}unmount(){this.componentMounted=!1,this.componentWillUnmount(),document.querySelector(`[data-component="${this.name}"]`).remove(),document.querySelector(`[data-component="${this.name}"]`)||(components=components.filter(t=>t.name!==this.name))}componentUpdate(t,e,s){}componentDidMount(){}componentWillUnmount(){}signal=(t,e)=>{let s=!1,[n,i]=this.useState(t,e,()=>{if(this.$_signal_subscribers.length>0){for(var t=0;t<this.$_signal_subscribers.length;t++)if(!s){if(this.$_signal_subscribers[t].runonce&&this.$_signal_subscribers_ran.includes(this.$_signal_subscribers[t]))break;this.$_signal_subscribers[t].function(n),this.$_signal_subscribers_ran.push(this.$_signal_subscribers[t]);return}}else this.$_signal_dispatch_event.detail.hasUpdated=!0,this.$_signal_dispatch_event.detail.state=n,window.dispatchEvent(this.$_signal_dispatch_event)});return this.$_signal_subscribe=(t,e)=>(this.$_signal_subscribers.push({function:t,runonce:e}),t),this.$_signal_cleanup=t=>{this.lastState=n,this.$_signal_subscribers=this.$_signal_subscribers.filter(e=>e.function!==t),this.$_signal_dispatch_cleanup_event.detail.state=this.states,this.$_signal_dispatch_cleanup_event.detail.lastState=this.lastState,window.dispatchEvent(this.$_signal_dispatch_event)},this.$_signal_dispatch=()=>{for(var t=0;t<this.$_signal_subscribers.length&&!(this.$_signal_subscribers[t].runonce&&this.$_signal_subscribers_ran.includes(this.$_signal_subscribers[t]));t++)this.$_signal_subscribers[t].function(n),this.$_signal_subscribers_ran.push(this.$_signal_subscribers[t])},this.$_signal_get=()=>n,this.$_signal_call=()=>{s=!0,this.$_signal_dispatch()},this.$_signal_set=t=>{i(t)},{subscribe:this.$_signal_subscribe,cleanup:this.$_signal_cleanup,dispatch:this.$_signal_dispatch,call:this.$_signal_call,set:this.$_signal_set,get:this.$_signal_get}};useAuth(t){let e=t.rulesets;if(!e)throw Error("No rulesets provided");let s=t.user,n={can(t){let n=!1;return e.forEach(e=>{e.action===t&&e.condition(s)&&(n=!0)}),n},hasRole:t=>s.role&&s.role.includes(t),canWithRole:(t,e)=>n.can(t)&&n.hasRole(e),assignRule(t){e.some(e=>e.action===t.action)||e.push(t)},revokeRule(t){e=e.filter(e=>e.action!==t)},canAnyOf:t=>t.some(t=>n.can(t)),canAllOf:t=>t.every(t=>n.can(t)),canGroup:(t,e="any")=>"any"===e?n.canAnyOf(t):n.canAllOf(t)};return n}useReducer(t,e,s){return this.states[t]||(this.states[t]=s),[this.states[t],s=>{this.states[t]=e(s),this.updateComponent()}]}runEffects(){Object.keys(this.effects).forEach(t=>{this.effects[t].forEach(t=>{this.executedEffects[t]||(t(),this.executedEffects[t]=!0)})})}useSyncStore(t,e){let[s,n]=this.useState(t,e||localStorage.getItem(`$_vader_${t}`,e=>{localStorage.setItem(`$_vader_${t}`,JSON.stringify(e)),this.$_useStore_subscribers.forEach(t=>{t(e)})})||{}),i=t=>s[t],r=(t,e)=>{let i={...s,[t]:e};n(i)},a=t=>this.$_useStore_subscribers.push(t),o=t=>{let e=s;delete e[t],n(e)};return{getField:i,setField:r,subscribe:a,clear:o}}useState(t,e,s=null){return this.states[t]||(this.states[t]=e),[this.states[t],e=>{this.states[t]=e,this.updateComponent(),"function"==typeof s&&s()}]}useRef(t){let e=this.dom[t],s=t=>{let s=new DOMParser().parseFromString(t,"text/html"),n=s.body.firstChild;if(e){let i=!n.isEqualNode(e);i&&e.parentNode.replaceChild(n,e)}};return{current:e,update:s}}useEffect(t,e){return this.effects[this.name]||(this.effects[this.name]=[]),this.effects[this.name].push(t),e.length>0?e.forEach(t=>{if(t.set)throw Error("signal found, do not use effect and signals at the same time - signals are more efficient")}):this.hasMounted||(t(),this.hasMounted=!0),{cleanup:()=>{this.effects[this.name]=this.effects[this.name].filter(e=>e!==t)}}}$Function(t){let e=t.name;return e||(e="anonymous"+Math.floor(1e17*Math.random())),window[e]=t,`window.${e}()`}updateComponent(){let t=document.createDocumentFragment();Object.keys(components).forEach(async e=>{let{name:s}=components[e],n=document.querySelector(`[data-component="${s}"]`),i=new Date().getTime(),r={name:s,time:i,prev_state:this.states,prev_props:this.storedProps,content:n.innerHTML};if(!n)return;let a=this.useState.bind(this),o=this.useEffect.bind(this),h=this.useReducer.bind(this),l=this.useAuth.bind(this),u=this.useSyncStore.bind(this),c=this.signal.bind(this),d=this.$Function.bind(this),p=this.useRef.bind(this),f=Function("useState","useEffect","useAuth","useReducer","useSyncStore","signal","$Function","dom","render","state","useRef","return`"+await this.render()+"`")(a,o,l,h,u,c,d,dom,this.render,this.states,p);new DOMParser().parseFromString(f,"text/html").body.innerHTML!==n.innerHTML&&(this.snapshots.length>0?this.snapshots[this.snapshots.length-1]!==r&&this.snapshots.push(r):this.snapshots.push(r),this.componentUpdate(r.prev_state,r.prev_props,r.content),t.appendChild(document.createRange().createContextualFragment(f)),n.innerHTML="",n.replaceWith(t),this.runEffects())})}validateClassName(t){return/^[a-zA-Z0-9-_]+$/.test(t)}html(t,...e){let s=setInterval(()=>{document.querySelector(`[data-component="${this.name}"]`)&&(clearInterval(s),this.componentMounted=!0,document.querySelector(`[data-component="${this.name}"]`)?.querySelectorAll("*").forEach(t=>{t.hasAttribute("ref")&&(this.dom[t.getAttribute("ref")]=t)}),this.componentDidMount())},100),n=document.createElement("script");return n.setAttribute("type","text/javascript"),n.setAttribute("data-component-script",this.name),worker.postMessage({strings:t,args:e,location:window.location.origin+window.location.pathname.replace(/\/[^\/]*$/,"")+"/public/",name:this.name}),new Promise((t,e)=>{worker.onmessage=e=>{if(e.data.error)throw Error(e.data.error);let s=this.dom,n=e.data.js,i=e.data.template,r=this.useState.bind(this),a=this.useEffect.bind(this),o=this.useReducer.bind(this),h=this.useAuth.bind(this),l=this.useSyncStore.bind(this),u=this.signal.bind(this),c=this.$Function.bind(this),d=this.states,p=this.useRef.bind(this),f=setInterval(()=>{this.componentMounted&&(clearInterval(f),Function("useState","useEffect","useAuth","useReducer","useSyncStore","signal","$Function","dom","render","state","useRef",n)(r,a,h,o,l,u,c,s,this.render,this.states,p))},100);t(handletemplate(Function("useRef","state","signal","useState","useReducer","useAuth","useSyncStore","useRef","$Function","return`"+i+"`")(p,d,u,r,o,h,l,p,c)))},worker.onerror=t=>{e(t)}})}async render(t){}}let Vader={Component:Component,useRef:useRef};export const component=()=>new Component;export const rf=(t,e)=>{window[t]=e};let cache={};async function handletemplate(t){let e=new DOMParser().parseFromString(t,"text/html"),s=e.documentElement.querySelectorAll("*");if(s.length>0){for(var n=0;n<s.length;n++)if("INCLUDE"===s[n].nodeName){if(!s[n].getAttribute("src")||""===s[n].getAttribute("src"))throw Error("Include tag must have src attribute");let i=s[n].getAttribute("src")?.split("/").pop()?.split(".")[0],r=await include(s[n].getAttribute("src"));r=r.replace(/`/g,"\\`"),cache[s[n].getAttribute("src")]=r,r=Function(`return \`${r}\`;`)();let a=new DOMParser().parseFromString(r,"text/html");a.querySelectorAll("include").forEach(t=>{t.remove()});e.querySelectorAll(i).forEach(t=>{if(t.attributes.length>0)for(var s=0;s<t.attributes.length;s++){let n="{{"+t.attributes[s].name+"}}";a.body.innerHTML.includes(n)&&(a.body.innerHTML=a.body.innerHTML.replaceAll(n,t.attributes[s].value))}if(t.children.length>0&&a.body.querySelector("slot"))for(var s=0;s<t.children.length;s++)a.body.querySelectorAll("slot").forEach(e=>{let n=e.getAttribute("id");(t.hasAttribute("key")&&t.getAttribute("key")===n||!t.hasAttribute("key")&&t.nodeName===n)&&t.children[s].innerHTML.length>0&&(e.outerHTML=t.children[s].innerHTML)});e.body.querySelectorAll("include").forEach(t=>{t.remove()}),e.body.outerHTML=e.body.outerHTML.replace(/`/g,"\\`"),e.body.outerHTML=e.body.outerHTML.replace(t.outerHTML,Function(`return \`${a.body.outerHTML}\`;`)())})}}return e.body.outerHTML=e.body.outerHTML.replace(/`/g,"\\`"),t=Function(`return \`${e.body.outerHTML}\`;`)()}export const include=async t=>(!t.startsWith("/")||t.includes("/src/")||document.documentElement.outerHTML.trim().includes("<!-- #vader-disable_relative-paths -->")||(t="/src/"+t),cache[t])?cache[t]:fetch(`./${t}`).then(e=>{if(404===e.status)throw Error(`No file found at ${t}`);return e.text()}).then(async e=>(cache[t]=e,e));export default Vader;
|