turbo-web 4.2.10 → 4.3.0

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 CHANGED
@@ -67,34 +67,45 @@ If you change code inside **examples/** make sure to do a hard reset for changes
67
67
  ---
68
68
 
69
69
  ### [CORE] Feature 1: Declarative UI
70
- Using declarative methods to define final UI instead of manually handling DOM elements. "What the UI looks like vs step-by-step imperative programming"
70
+ **Abstraction** principle: Using declarative methods to define final UI instead of manually handling DOM elements. "What the UI looks like vs step-by-step imperative programming"
71
+
72
+ ```component.js```
71
73
 
72
74
  ### [CORE] Feature 2: Virtual DOM
73
- Inversed Control principle - the user doesn't need to manipulate the DOM directly, the framework creates a lightweight
75
+ **Inversed Control** principle - the user doesn't need to manipulate the DOM directly, the framework creates a lightweight
74
76
  JavaScript object tree that represents the UI.
75
77
 
78
+ ```patch-dom.js```
79
+
76
80
  ### [CORE] Feature 3: Reconciliation Algorithm
77
- Inversed Control principle - optimizing DOM manipulations by the process of diffing and patching only the required nodes (improves DOM re-rendering & re-painting).
81
+ **Inversed Control** principle - optimizing DOM manipulations by the process of diffing and patching only the required nodes (improves DOM re-rendering & re-painting).
78
82
 
83
+ ```patch-dom.js```
79
84
  ### [CORE] Feature 4: Components
80
- Encapsulation & Reusability principles: making UI building blocks with better combined hierarchical tree structure.
85
+ **Encapsulation & Reusability** principles: making UI building blocks with better combined hierarchical tree structure.
81
86
 
87
+ ```component.js // building scheme: h(tagOrComponent, { attributes, class, style, on: events }, [children])```
82
88
  ### Feature 5: Client-Side Router (#hash-based)
83
89
  Allows for the **SPA design** (Single Page Application) with route guards (fn: checkNavigation) and a common "catch-all route" (cases: route not found). Based on a hash part of the URL, implemented with regex pattern matching by simulating URL path, parameters and query as part of the hash fragment itself.
84
- // router.js, route-matchers.js
90
+
91
+ ```router.js, route-matchers.js```
85
92
 
86
93
  ### Feature 6: Centralized Store
87
- Separation of Concerns & Single Source of Truth principles: Allows for a global state handling and eliminates the need for prop drilling.
88
- ...
94
+ **Separation of Concerns** & **Single Source of Truth** principles: Allows for a global state handling and eliminates the need for prop drilling.
95
+ ```store.js```
89
96
 
90
97
  ### Feature 7: Persistent Storage
91
- Single Source of Truth principle: App's state is persistent between sessions.
92
- ...
98
+ **Single Source of Truth** principle: App's state is persistent between sessions.
99
+
100
+ ```store.js```
101
+
102
+ ### Feature 8: built-in HTTP module.
103
+ **Abstraction** principle: simplifies development by abstracting imperative logic in HTTP request handling.
93
104
 
94
- ...
105
+ ```http.js```
95
106
 
96
107
  ### Feature X: Node-based Initialization through CLI
97
- Scaffolding setup script that creates base structure with required templates for the App.
108
+ Scaffolding setup script that creates base structure with required templates for the App
98
109
 
99
110
  Script: ```npx turbo-web ${PROJECT_NAME}``` creates "Project" & src directory + main.js, router.js, store.js + package.json
100
111
 
@@ -103,31 +114,80 @@ Script: ```npx turbo-web ${PROJECT_NAME}``` creates "Project" & src directory +
103
114
 
104
115
  ---
105
116
 
106
- ### Routing
117
+ ### [1] Router
118
+ Define Routes with Guards - Use the beforeEnter guard to protect routes (e.g., authentication).
119
+ >The router supports asynchronous guards that can either return a boolean or redirect to a different path string
107
120
 
121
+ ```
122
+ beforeEnter: async (from, to, params, query) => {
123
+ const isAuthenticated = localStorage.getItem('auth_token');
124
+ if (!isAuthenticated) return '/login'; // Redirects to login
125
+ return true; // allows navigation
126
+ }
127
+ ```
108
128
 
109
- ```const routes = [{ path: '/', component: Home }, {}, {}]```
110
- ```const router = new HashRouter(routes)```
111
- ```await router.init()```
112
- ```#isInitialized```
129
+ ### [2] RouterLink
130
+ Always use the built-in RouterLink component instead of standard anchor tags ```(<a href="...">)``` for internal navigation.
113
131
 
114
- ### Components
115
- ...
132
+ ```
133
+ export const Navbar = defineComponent({
134
+ render() {
135
+ return h('nav', {}, [
136
+ h(RouterLink, { to: '/' }, ['Home']),
137
+ h(RouterLink, { to: '/dashboard' }, ['Dashboard'])
138
+ ]);
139
+ }
140
+ });
141
+ ```
116
142
 
117
- ### Centralized Store
118
- Must be initialized in a separate file (store.js).
143
+ ### [3] Components
144
+ Never mutate this.state directly. Always use this.updateState() to trigger the virtual DOM patching process.
145
+ > State initialization should be done via the state() function
119
146
  ```
120
- export const store = new Store({
121
- state: {
122
- count: 0,
123
- user: null
147
+ increment() {
148
+ // Correct: Triggers VDOM diffing and patching
149
+ this.updateState({ count: this.state.count + 1 });
150
+
151
+ // Incorrect: Will not update the UI
152
+ // this.state.count++;
153
+ }
154
+ ```
155
+
156
+ ### [4] Centralized Store
157
+ Mutations must be strictly synchronous as they immediately write to localStorage and trigger state-change events. Use actions for asynchronous operations like HTTP requests, and have the action commit the mutation.
158
+
159
+ ```
160
+ mutations: {
161
+ setLoading(state, status) {
162
+ state.isLoading = status; // Synchronous
124
163
  },
125
- mutations: {
126
- increment(state, payload = 1) {
127
- state.count += payload
128
- },
129
- setUser(state, user) {
130
- state.user = user
131
- }
164
+ setUsers(state, users) {
165
+ state.users = users; // Synchronous
166
+ }
167
+ },
168
+ actions: {
169
+ async fetchUsers({ commit, state }) {
170
+ commit('setLoading', true);
171
+ // Async operation
172
+ const response = await fetch('https://api.example.com/users');
173
+ const data = await response.json();
174
+ commit('setUsers', data);
175
+ commit('setLoading', false);
132
176
  }
177
+ ```
178
+
179
+ ### [5] Subscribe to Store Changes in Components
180
+ To make a component react to global state changes, subscribe to the store in onMounted and trigger a local re-render.
181
+ > You must unsubscribe in ```onUnmounted```!!!.
182
+
183
+ ```
184
+ onMounted() {
185
+ this.unsubscribe = this.appContext.store.subscribe(() => {
186
+ this.updateState({}); // Force re-render
187
+ });
188
+ this.appContext.store.dispatch('fetchUsers');
189
+ },
190
+ onUnmounted() {
191
+ if (this.unsubscribe) this.unsubscribe();
192
+ },
133
193
  ```
@@ -41,17 +41,47 @@ fs.writeFileSync(
41
41
 
42
42
  // defines the boilerplate file contents
43
43
  const storeContent = `import { Store } from 'turbo-web';
44
- export const appStore = new Store({ state: { count: 0 } });`;
45
44
 
46
- const routerContent = `import { HashRouter } from 'turbo-web';
47
- export const router = new HashRouter([{ path: '/', component: null }]);`;
45
+ export const appStore = new Store({
46
+ state: {
47
+ count: 0
48
+ },
49
+ mutations: {
50
+ increment(state) {
51
+ state.count++;
52
+ }
53
+ }
54
+ });`;
55
+
56
+ const routerContent = `import { HashRouter, defineComponent, h } from 'turbo-web';
57
+
58
+ const Home = defineComponent({
59
+ render() {
60
+ return h('h2', {}, ['Welcome to Home Page']);
61
+ }
62
+ });
63
+
64
+ export const router = new HashRouter([
65
+ { path: '/', component: Home }
66
+ ]);`;
48
67
 
49
- const mainContent = `import { createApp, h } from 'turbo-web';
68
+ const mainContent = `import { createApp, defineComponent, h, RouterOutlet } from 'turbo-web';
50
69
  import { router } from './router.js';
51
70
  import { appStore } from './store.js';
52
71
 
53
- const App = { render: () => h('div', {}, ['Turbo Power!']) };
54
- createApp(App, {}, { router, store: appStore }).mount(document.body);`;
72
+ const App = defineComponent({
73
+ render() {
74
+ return h('div', { style: { padding: '20px', fontFamily: 'sans-serif' } }, [
75
+ h('h1', {}, ['Turbo Power! 🚀']),
76
+ h('hr'),
77
+ // RouterOutlet renders the components defined in router.js
78
+ h(RouterOutlet)
79
+ ]);
80
+ }
81
+ });
82
+
83
+ const app = createApp(App, {}, { router, store: appStore });
84
+ app.mount(document.body);`;
55
85
 
56
86
  // write the files to the user's new project directory
57
87
  fs.writeFileSync(path.join(srcPath, 'store.js'), storeContent);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turbo-web",
3
- "version": "4.2.10",
3
+ "version": "4.3.0",
4
4
  "main": "dist/turbo.js",
5
5
  "files": [
6
6
  "dist",