turbo-web 4.2.6 → 4.2.8

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.
Files changed (3) hide show
  1. package/README.md +133 -0
  2. package/dist/turbo.js +61 -2
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # TURBO-WEB FRAMEWORK
2
+
3
+ ## I. OVERVIEW
4
+
5
+ ---
6
+
7
+ Lightweight JavaScript framework based on a book "Build a **Frontend Web Framework** (from scratch)" by **Angel Sola Orbaiceta** with additional features sprinkled on top.
8
+
9
+ [//]: # (Installation instructions.)
10
+ ## II. INSTALLATION
11
+
12
+ ---
13
+ There are 4 ways to utilize this framework: **NPM installation**, **SETUP script** (NODE), **locally** and through **CDN**.
14
+ ### (1) NPM Installation
15
+ To install Turbo-Web framework, run in your terminal ```npm install turbo-web```
16
+
17
+ ### (2) NODE initialization script
18
+ To install the framework with pre-configured templates using CLI, run in your terminal ```npx turbo-web PROJECT_NAME```
19
+ This will create a starting kit: project directory, package.json, index.html + src/ with router.js, main.js and store.js
20
+
21
+ // script exists under bin/turbo-charge.js
22
+
23
+ ### (3) Locally
24
+ To import the framework locally - you must include the bundled file located in ```./framework/runtime/dist/turbo.js``` as imports
25
+
26
+ // This file can be generated using script ```npm run build```
27
+
28
+ Example:
29
+
30
+ ```'import {createApp, defineComponent, h, hFragment,} from '../../../framework/runtime/dist/turbo.js'```
31
+
32
+ ### (4) Using CDN (Content Delivery Network)
33
+ You can import any version of the framework directly into your JS file:
34
+
35
+ ```import {createApp, defineComponent, h, hFragment,} from 'https://unpkg.com/turbo-web'```
36
+
37
+ CDN Version control ( version 1x.2x.3x):
38
+ 1. x MAJOR: @1 or @^1.0.0 - fetches latest compatible minor and path release
39
+ 2. x MINOR: @1.2 or @~1.2.0 - fetches latest compatible patch release of a certain MINOR version 0.X.0
40
+ 3. x PATCH: @1.2.3 - fetches a specific build with full version control
41
+
42
+ ``````import ... from 'https://unpkg.com/turbo-web@2'`````` - will import latest minor + patch version of 2.X.X.
43
+
44
+ [//]: # (A "Getting Started" guide.)
45
+ ## III. GETTING STARTED
46
+
47
+ ---
48
+
49
+ ### [1] IMPORT
50
+ 1. (2.) NPM installation & NODE script: Import from packages:
51
+ > import { createApp, defineComponent, h, hFragment } from 'turbo-web'
52
+
53
+ 3. (4.) Locally & CDN: You must use relative paths (./ or ../):
54
+ > import { createApp, defineComponent, h, hFragment } from './framework/runtime/index.js'
55
+
56
+ ### [2] LAUNCH
57
+
58
+ You can run server using ```npm run serve:examples```.
59
+
60
+ If you change code inside **examples/** make sure to do a hard reset for changes to apply:
61
+ > F12 -> right-click on the REFRESH button -> Empty Cache & Hard Reload OR ( Ctrl + Shift + R )
62
+
63
+
64
+ [//]: # (A detailed explanation of each feature with code examples.)
65
+ ## IV. FEATURES
66
+
67
+ ---
68
+
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"
71
+
72
+ ### [CORE] Feature 2: Virtual DOM
73
+ Inversed Control principle - the user doesn't need to manipulate the DOM directly, the framework creates a lightweight
74
+ JavaScript object tree that represents the UI.
75
+
76
+ ### [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).
78
+
79
+ ### [CORE] Feature 4: Components
80
+ Encapsulation & Reusability principles: making UI building blocks with better combined hierarchical tree structure.
81
+
82
+ ### Feature 5: Client-Side Router (#hash-based)
83
+ 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
85
+
86
+ ### 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
+ ...
89
+
90
+ ### Feature 7: Persistent Storage
91
+ Single Source of Truth principle: App's state is persistent between sessions.
92
+ ...
93
+
94
+ ...
95
+
96
+ ### Feature X: Node-based Initialization through CLI
97
+ Scaffolding setup script that creates base structure with required templates for the App.
98
+
99
+ Script: ```npx turbo-web ${PROJECT_NAME}``` creates "Project" & src directory + main.js, router.js, store.js + package.json
100
+
101
+ [//]: # (Best practices and guidelines for building applications with the framework.)
102
+ ## V. BEST PRACTICES
103
+
104
+ ---
105
+
106
+ ### Routing
107
+
108
+
109
+ ```const routes = [{ path: '/', component: Home }, {}, {}]```
110
+ ```const router = new HashRouter(routes)```
111
+ ```await router.init()```
112
+ ```#isInitialized```
113
+
114
+ ### Components
115
+ ...
116
+
117
+ ### Centralized Store
118
+ Must be initialized in a separate file (store.js).
119
+ ```
120
+ export const store = new Store({
121
+ state: {
122
+ count: 0,
123
+ user: null
124
+ },
125
+ mutations: {
126
+ increment(state, payload = 1) {
127
+ state.count += payload
128
+ },
129
+ setUser(state, user) {
130
+ state.user = user
131
+ }
132
+ }
133
+ ```
package/dist/turbo.js CHANGED
@@ -1050,6 +1050,9 @@ function defineComponent({ render, state, ...methods }) {
1050
1050
  get appContext() {
1051
1051
  return this.#appContext
1052
1052
  }
1053
+ get $http() {
1054
+ return this.appContext?.http;
1055
+ }
1053
1056
  get elements() {
1054
1057
  if (this.#vdom == null) {
1055
1058
  return []
@@ -1172,10 +1175,11 @@ const RouterLink = defineComponent({
1172
1175
  const RouterOutlet = defineComponent({
1173
1176
  state() {
1174
1177
  return {
1175
- matchedRoute: this.appContext.router.matchedRoute,
1178
+ matchedRoute: null,
1176
1179
  }
1177
1180
  },
1178
1181
  onMounted() {
1182
+ this.updateState({ matchedRoute: this.appContext.router.matchedRoute });
1179
1183
  this.boundHandler = this.handleRouteChange.bind(this);
1180
1184
  this.appContext.router.subscribe(this.boundHandler);
1181
1185
  },
@@ -1193,4 +1197,59 @@ const RouterOutlet = defineComponent({
1193
1197
  },
1194
1198
  });
1195
1199
 
1196
- export { DOM_TYPES, HashRouter, RouterLink, RouterOutlet, createApp, defineComponent, h, hFragment, hSlot, hString, nextTick };
1200
+ class Store {
1201
+ #state = {}
1202
+ #initialState = {}
1203
+ #mutations = {}
1204
+ #actions = {}
1205
+ #dispatcher = new Dispatcher()
1206
+ #storageKey = 'turbo_state'
1207
+ constructor({ state = {}, mutations = {}, actions = {} }) {
1208
+ this.#initialState = structuredClone(state);
1209
+ const saved = localStorage.getItem(this.#storageKey);
1210
+ this.#state = saved ? JSON.parse(saved) : state;
1211
+ this.#mutations = mutations;
1212
+ this.#actions = actions;
1213
+ window.addEventListener('storage', (event) => {
1214
+ if (event.key === this.#storageKey && event.newValue) {
1215
+ this.#state = JSON.parse(event.newValue);
1216
+ this.#dispatcher.dispatch('state-change', this.state);
1217
+ }
1218
+ });
1219
+ }
1220
+ get state() {
1221
+ return structuredClone(this.#state)
1222
+ }
1223
+ commit(mutationName, payload) {
1224
+ const mutation = this.#mutations[mutationName];
1225
+ if (typeof mutation !== 'function') {
1226
+ console.warn(`[Store] Mutation "${mutationName}" does not exist.`);
1227
+ return
1228
+ }
1229
+ mutation(this.#state, payload);
1230
+ localStorage.setItem(this.#storageKey, JSON.stringify(this.#state));
1231
+ this.#dispatcher.dispatch('state-change', this.state);
1232
+ }
1233
+ async dispatch(actionName, payload) {
1234
+ const action = this.#actions[actionName];
1235
+ if (typeof action !== 'function') {
1236
+ console.warn(`[Store] Action "${actionName}" does not exist.`);
1237
+ return
1238
+ }
1239
+ const context = {
1240
+ commit: this.commit.bind(this),
1241
+ state: this.state
1242
+ };
1243
+ return await action(context, payload)
1244
+ }
1245
+ subscribe(handler) {
1246
+ return this.#dispatcher.subscribe('state-change', handler)
1247
+ }
1248
+ clear() {
1249
+ localStorage.removeItem(this.#storageKey);
1250
+ this.#state = structuredClone(this.#initialState);
1251
+ this.#dispatcher.dispatch('state-change', this.state);
1252
+ }
1253
+ }
1254
+
1255
+ export { DOM_TYPES, HashRouter, RouterLink, RouterOutlet, Store, createApp, defineComponent, h, hFragment, hSlot, hString, nextTick };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turbo-web",
3
- "version": "4.2.6",
3
+ "version": "4.2.8",
4
4
  "main": "dist/turbo.js",
5
5
  "files": [
6
6
  "dist",