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 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
- [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/vaderjs.svg?style=flat)](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
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2016",
4
- "module": "commonjs",
5
- "allowJs": true,
6
- "checkJs": true
7
-
8
- },
9
- "include": [
10
- "**/*.js"
11
- ],
12
- "exclude": [
13
- "node_modules",
14
- "dist",
15
- "website"
16
- ],
17
- "compileOnSave": true,
18
- }
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;