vanillaforge 1.9.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/CHANGELOG.md +466 -0
- package/README.md +198 -0
- package/package.json +91 -0
- package/src/components/base-component.js +925 -0
- package/src/core/component-manager.js +306 -0
- package/src/core/dom-morph.js +234 -0
- package/src/core/event-bus.js +229 -0
- package/src/core/router.js +487 -0
- package/src/core/signal.js +114 -0
- package/src/framework.js +323 -0
- package/src/plugins/alerts/alerts-plugin.js +427 -0
- package/src/plugins/fonts/files/inter.js +4 -0
- package/src/plugins/fonts/files/jetbrains-mono.js +4 -0
- package/src/plugins/fonts/font-manifests.js +53 -0
- package/src/plugins/fonts/fonts-plugin.js +246 -0
- package/src/plugins/icons/default-icons.js +51 -0
- package/src/plugins/icons/icons-plugin.js +130 -0
- package/src/plugins/store/store-plugin.js +127 -0
- package/src/plugins/theme/base-styles.js +58 -0
- package/src/plugins/theme/theme-plugin.js +160 -0
- package/src/utils/decorators.js +51 -0
- package/src/utils/dom.js +40 -0
- package/src/utils/error-handler.js +442 -0
- package/src/utils/framework-debug.js +375 -0
- package/src/utils/logger.js +324 -0
- package/src/utils/notification.js +123 -0
- package/src/utils/performance.js +281 -0
- package/src/utils/storage.js +86 -0
- package/src/utils/sweet-alert.js +84 -0
- package/src/utils/validation.js +70 -0
- package/src/utils/validators.js +129 -0
- package/types/index.d.ts +524 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to VanillaForge will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.9.0] - 2026-06-23
|
|
11
|
+
|
|
12
|
+
This release completes the public API surface and prepares the package for npm
|
|
13
|
+
publication.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
#### Service class re-exports
|
|
18
|
+
- `IconsService`, `ThemeService`, `AlertsService`, and `FontsService` are now exported
|
|
19
|
+
directly from `src/framework.js` alongside their plugin objects, matching the
|
|
20
|
+
existing `StoreService` export. Allows standalone instantiation and TypeScript type
|
|
21
|
+
annotations without importing from internal plugin paths.
|
|
22
|
+
|
|
23
|
+
#### Publish readiness
|
|
24
|
+
- `prepublishOnly` npm script — runs `npm test && npm run lint` automatically before
|
|
25
|
+
any `npm publish`.
|
|
26
|
+
- `package.json` `"files"` field tightened to include only framework source
|
|
27
|
+
(`src/framework.js`, `src/core/`, `src/components/base-component.js`,
|
|
28
|
+
`src/plugins/`, `src/utils/`) and `types/`. Demo files, build scripts, and
|
|
29
|
+
examples are no longer included in the published package.
|
|
30
|
+
- `FRAMEWORK_VERSION` constant bumped to `1.9.0`.
|
|
31
|
+
|
|
32
|
+
## [1.8.0] - 2026-06-23
|
|
33
|
+
|
|
34
|
+
This release adds **route loaders** — async data-fetching that runs before a route
|
|
35
|
+
component mounts, so the first render always has data available.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
#### Route loaders (`src/core/router.js`)
|
|
40
|
+
- `loader` option on a route config object — an async function called before the
|
|
41
|
+
component mounts.
|
|
42
|
+
- Receives `{ params, path }` — the dynamic URL segments and the matched path.
|
|
43
|
+
- Return value is passed to the component as `this.props.data` on the first render.
|
|
44
|
+
- Loader errors are caught and logged; the component still mounts with
|
|
45
|
+
`props.data: undefined` so it can render a graceful fallback.
|
|
46
|
+
- Fully backward-compatible — routes registered as a bare component class continue
|
|
47
|
+
to work unchanged.
|
|
48
|
+
- TypeScript types: `RouteLoader`, `RouteLoaderContext`, and `RouteConfig.loader`
|
|
49
|
+
added to `types/index.d.ts`.
|
|
50
|
+
- `FRAMEWORK_VERSION` bumped to `1.8.0`.
|
|
51
|
+
|
|
52
|
+
#### Tests
|
|
53
|
+
- `tests/router-loaders.test.js` — covers: loader called before mount, return value
|
|
54
|
+
in `props.data`, `params` and `path` passed to loader, loader error handling,
|
|
55
|
+
bare-class routes still work, multiple routes with independent loaders.
|
|
56
|
+
|
|
57
|
+
#### Documentation
|
|
58
|
+
- `docs/roadmap.md` — route loaders moved to "Done (v1.8)".
|
|
59
|
+
- `docs/API.md` — "Route loaders" section with full usage example.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## [1.7.0] - 2026-06-23
|
|
64
|
+
|
|
65
|
+
This release adds the **shared store plugin** — a reactive key/value store accessible
|
|
66
|
+
from any component, no prop-drilling required.
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
#### Store plugin (`src/plugins/store/store-plugin.js`)
|
|
71
|
+
- `storePlugin` — install with `app.use(storePlugin)`.
|
|
72
|
+
- `StoreService.set(key, value)` — writes a value; `Object.is` equality check
|
|
73
|
+
means identical values are silently ignored.
|
|
74
|
+
- `StoreService.get(key)` — returns the current value (`undefined` for unset keys).
|
|
75
|
+
- `StoreService.subscribe(key, fn)` — per-key subscription; handler receives
|
|
76
|
+
`(value, prev)`; returns an unsubscribe function.
|
|
77
|
+
- `StoreService.subscribeAll(fn)` — subscribes to all writes; handler receives
|
|
78
|
+
`(key, value, prev)`.
|
|
79
|
+
- `StoreService.delete(key)` — removes a key and fires change events with
|
|
80
|
+
`value: undefined`.
|
|
81
|
+
- `StoreService.keys()` — returns all stored key names.
|
|
82
|
+
- Emits `'store:change'` and `'store:change:<key>'` on the shared EventBus so any
|
|
83
|
+
code can react without holding a service reference.
|
|
84
|
+
- Exported from `src/framework.js` as `storePlugin` and `StoreService`.
|
|
85
|
+
- Full TypeScript generics in `types/index.d.ts`.
|
|
86
|
+
- `FRAMEWORK_VERSION` bumped to `1.7.0`.
|
|
87
|
+
|
|
88
|
+
#### Tests
|
|
89
|
+
- `tests/store.test.js` — covers: set/get round-trip, Object.is no-op, subscribe
|
|
90
|
+
per-key, unsubscribe, subscribeAll, delete fires events, keys(), EventBus events,
|
|
91
|
+
plugin registration, idempotency.
|
|
92
|
+
|
|
93
|
+
#### Documentation
|
|
94
|
+
- `docs/roadmap.md` — store plugin moved to "Done (v1.7)".
|
|
95
|
+
- `docs/plugins.md` — full "Store plugin" section with cross-component cart example.
|
|
96
|
+
- `docs/API.md` — `storePlugin` quick reference.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## [1.6.0] - 2026-06-23
|
|
101
|
+
|
|
102
|
+
This release adds **signals** — fine-grained reactive primitives that batch updates
|
|
103
|
+
and keep re-renders minimal without requiring `setState()`.
|
|
104
|
+
|
|
105
|
+
### Added
|
|
106
|
+
|
|
107
|
+
#### Signals (`src/core/signal.js`, `src/components/base-component.js`)
|
|
108
|
+
- `Signal<T>` class — a reactive cell; reading `.value` returns the current value;
|
|
109
|
+
`.set(newValue)` updates it and notifies all subscribers.
|
|
110
|
+
- `Object.is` equality check — identical values are ignored, no unnecessary renders.
|
|
111
|
+
- `signal.subscribe(fn)` — returns an unsubscribe function; works standalone with no
|
|
112
|
+
component needed.
|
|
113
|
+
- `this.signal(initialValue)` on `BaseComponent` — creates a `Signal` linked to the
|
|
114
|
+
component. Calling `.set()` schedules a single batched morph re-render via
|
|
115
|
+
microtask; multiple `.set()` calls in the same synchronous block collapse into one.
|
|
116
|
+
- Auto-cleanup: signals created via `this.signal()` are destroyed when the
|
|
117
|
+
component is destroyed, preventing memory leaks.
|
|
118
|
+
- `Signal` exported from `src/framework.js` for standalone use.
|
|
119
|
+
- Full TypeScript generic `Signal<T>` in `types/index.d.ts`.
|
|
120
|
+
- `FRAMEWORK_VERSION` bumped to `1.6.0`.
|
|
121
|
+
|
|
122
|
+
#### Tests
|
|
123
|
+
- `tests/signals.test.js` — covers: read/write, Object.is no-op, subscribe/
|
|
124
|
+
unsubscribe, batched rendering, component signal auto-cleanup on destroy,
|
|
125
|
+
standalone signal, multiple subscribers.
|
|
126
|
+
|
|
127
|
+
#### Documentation
|
|
128
|
+
- `docs/roadmap.md` — signals moved to "Done (v1.6)".
|
|
129
|
+
- `docs/API.md` — "Signals" section with component and standalone examples.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## [1.5.0] - 2026-06-23
|
|
134
|
+
|
|
135
|
+
This release adds the **scaffold CLI** and **TypeScript declaration file** so new
|
|
136
|
+
projects can be created in one command and VS Code provides full type coverage.
|
|
137
|
+
|
|
138
|
+
### Added
|
|
139
|
+
|
|
140
|
+
#### Scaffold CLI (`create-vanillaforge/`)
|
|
141
|
+
- `npx create-vanillaforge my-app` — interactive project scaffolder.
|
|
142
|
+
- `--template=<name>` flag for non-interactive use.
|
|
143
|
+
- Four templates:
|
|
144
|
+
- `minimal` — bare component + router, no plugins.
|
|
145
|
+
- `full` — all first-party plugins pre-installed.
|
|
146
|
+
- `todo-app` — fully-working Todo app matching the examples/ version.
|
|
147
|
+
- `router-app` — multi-route app with params and child components.
|
|
148
|
+
- Each generated project uses a GitHub git dependency + import map; no build step
|
|
149
|
+
needed in development.
|
|
150
|
+
- CLI package lives in `create-vanillaforge/` (monorepo style); entry point:
|
|
151
|
+
`create-vanillaforge/bin/cli.js`.
|
|
152
|
+
- See `docs/cli.md` for the full CLI reference.
|
|
153
|
+
|
|
154
|
+
#### TypeScript declarations (`types/index.d.ts`)
|
|
155
|
+
- Full declaration file covering every exported symbol: `createApp`, `FrameworkApp`,
|
|
156
|
+
`BaseComponent`, `Router`, `EventBus`, `Signal`, all plugin classes
|
|
157
|
+
(`IconsService`, `ThemeService`, `AlertsService`, `FontsService`, `StoreService`),
|
|
158
|
+
all option interfaces, and all constants.
|
|
159
|
+
- `package.json` updated: `"types": "types/index.d.ts"`, `"files"` array includes
|
|
160
|
+
`types/`, `"exports"` map includes the `"types"` condition.
|
|
161
|
+
- VS Code picks up types automatically in JavaScript projects (no extra config
|
|
162
|
+
needed when importing from the package).
|
|
163
|
+
- `FRAMEWORK_VERSION` bumped to `1.5.0`.
|
|
164
|
+
|
|
165
|
+
#### Documentation
|
|
166
|
+
- `docs/cli.md` — full CLI reference: usage, templates, flags, generated layout.
|
|
167
|
+
- `docs/roadmap.md` — scaffold + types moved to "Done (v1.5)".
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## [1.4.0] - 2026-06-22
|
|
172
|
+
|
|
173
|
+
This release adds the **self-hosted fonts plugin** — Inter and JetBrains Mono bundled
|
|
174
|
+
as Latin-subset variable-weight woff2 data URIs. Zero external requests, no Google
|
|
175
|
+
Fonts, no file setup.
|
|
176
|
+
|
|
177
|
+
### Added
|
|
178
|
+
|
|
179
|
+
#### Fonts plugin (`src/plugins/fonts/`)
|
|
180
|
+
- `fontsPlugin` — install with `app.use(fontsPlugin, { families: ['Inter', 'JetBrains Mono'] })`.
|
|
181
|
+
- Inter and JetBrains Mono bundled as Latin-subset, variable-weight woff2 data
|
|
182
|
+
URIs — zero external requests, works out of the box.
|
|
183
|
+
- Weight and style filtering: `{ name: 'Inter', weights: [400, 700], styles: ['normal'] }`.
|
|
184
|
+
For variable fonts `weights` is treated as a `[min, max]` range.
|
|
185
|
+
- `path` option to serve your own font files instead of bundled data URIs.
|
|
186
|
+
- Custom family registration via `FontsService.addFamily(name, manifest)` — returns
|
|
187
|
+
`this` for chaining.
|
|
188
|
+
- Theme token integration: loading Inter updates `--vf-font-sans`; loading JetBrains
|
|
189
|
+
Mono updates `--vf-font-mono` (no-op when `themePlugin` is absent).
|
|
190
|
+
- `FontsService.getFamilies()` — returns CSS family names of all loaded fonts.
|
|
191
|
+
- Idempotent `<style id="vf-fonts">` element — reuses existing element rather than
|
|
192
|
+
duplicating it.
|
|
193
|
+
- `display` option controls `font-display` (default: `'swap'`).
|
|
194
|
+
- Exported from `src/framework.js` as `fontsPlugin` and `FontsService`.
|
|
195
|
+
- `FRAMEWORK_VERSION` bumped to `1.4.0`.
|
|
196
|
+
|
|
197
|
+
#### Tests
|
|
198
|
+
- `tests/fonts.test.js` — covers: style element creation, Inter/JetBrains Mono
|
|
199
|
+
`@font-face` injection, weight range, style filtering, idempotency, `getFamilies()`,
|
|
200
|
+
theme token integration, custom path, `addFamily()`, `fontsPlugin` install.
|
|
201
|
+
|
|
202
|
+
#### Documentation
|
|
203
|
+
- `docs/roadmap.md` — fonts plugin moved to "Done (v1.4)".
|
|
204
|
+
- `docs/plugins.md` — "Built-in fonts plugin" section with full option table and
|
|
205
|
+
`addFamily()` example.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## [1.3.0] - 2026-06-22
|
|
210
|
+
|
|
211
|
+
This release adds the **alerts plugin** — zero-dependency toasts and confirm dialogs.
|
|
212
|
+
Apps no longer need SweetAlert, `window.confirm`, or any other notification library.
|
|
213
|
+
The plugin also replaces the old `Notification` class so the ErrorHandler automatically
|
|
214
|
+
uses the new styled UI.
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
|
|
218
|
+
#### Alerts plugin (`src/plugins/alerts/`)
|
|
219
|
+
- `alertsPlugin` — install with `app.use(alertsPlugin)` or with options.
|
|
220
|
+
- `AlertsService` methods:
|
|
221
|
+
- `success(message)`, `error(message)`, `warning(message)`, `info(message)` —
|
|
222
|
+
show a slide-in toast that auto-dismisses after `duration` ms (default 4000).
|
|
223
|
+
- `confirm(message, opts)` — show a modal dialog; returns a `Promise<boolean>`
|
|
224
|
+
(true = confirmed, false = cancelled / backdrop click). Simultaneously calls
|
|
225
|
+
`opts.onConfirm` / `opts.onCancel` callbacks if provided.
|
|
226
|
+
- Toast options: `duration` (per-call override), type classes (`vf-toast-success`,
|
|
227
|
+
`vf-toast-error`, `vf-toast-warning`, `vf-toast-info`), built-in close button.
|
|
228
|
+
- Confirm options: `title` (optional heading), `confirmText`, `cancelText`,
|
|
229
|
+
`danger` (red confirm button), `onConfirm`, `onCancel`.
|
|
230
|
+
- `maxToasts` cap (default 5) — oldest toast is silently removed when exceeded.
|
|
231
|
+
- Injected styles (`<style id="vf-alerts-styles">`) use `--vf-*` custom properties
|
|
232
|
+
if the theme plugin is installed, with plain-CSS fallbacks if not.
|
|
233
|
+
- Backward-compatible `showToast(message, type)` and `showModal(title, message, opts)`
|
|
234
|
+
methods so the existing `ErrorHandler` works without changes.
|
|
235
|
+
- On install, `app.errorHandler.notification` is re-pointed to the new service,
|
|
236
|
+
so framework errors automatically use the styled toasts and dialogs.
|
|
237
|
+
- `alertsPlugin` exported from `src/framework.js` as part of the public API.
|
|
238
|
+
- `FRAMEWORK_VERSION` bumped to `1.3.0`.
|
|
239
|
+
|
|
240
|
+
#### Tests
|
|
241
|
+
- `tests/alerts.test.js` — 22 tests covering: container creation, style injection,
|
|
242
|
+
de-duplication, all four toast type classes, message rendering, auto-dismiss with
|
|
243
|
+
fake timers, close button, maxToasts trimming, `showToast()` backward compat,
|
|
244
|
+
`confirm()` Promise resolution (confirm / cancel / backdrop), `onConfirm` /
|
|
245
|
+
`onCancel` callbacks, DOM cleanup after close, title and danger-button options,
|
|
246
|
+
`showModal()` backward compat and close button, and full plugin integration
|
|
247
|
+
(registration, ErrorHandler wiring, options pass-through, idempotency).
|
|
248
|
+
|
|
249
|
+
#### Examples
|
|
250
|
+
- `examples/router-app/components/user-card.js` — adds a "Remove" button (trash
|
|
251
|
+
icon) that triggers a danger confirm dialog; on confirmation emits `user:remove`
|
|
252
|
+
and shows a success toast.
|
|
253
|
+
- `examples/router-app/components/users-list.js` — initialises state from the users
|
|
254
|
+
array, listens for `user:remove` on the EventBus, and drops the user from state so
|
|
255
|
+
the list re-renders without that card.
|
|
256
|
+
- `examples/router-app/index.html` — installs `alertsPlugin`; adds `.user-card-remove`
|
|
257
|
+
hover style; updates the subtitle.
|
|
258
|
+
|
|
259
|
+
#### Documentation
|
|
260
|
+
- `docs/roadmap.md` — alerts moved to "Done"; fonts/scaffold/signals renumbered.
|
|
261
|
+
|
|
262
|
+
## [1.2.0] - 2026-06-22
|
|
263
|
+
|
|
264
|
+
This release adds the **CSS/theming plugin** — the first batteries-included styling
|
|
265
|
+
subsystem. Apps now look sensible out of the box without Tailwind, Bootstrap, or any
|
|
266
|
+
external CSS library, while remaining fully themeable.
|
|
267
|
+
|
|
268
|
+
### Added
|
|
269
|
+
|
|
270
|
+
#### Theme plugin (`src/plugins/theme/`)
|
|
271
|
+
- `themePlugin` — install with `app.use(themePlugin)` or
|
|
272
|
+
`app.use(themePlugin, { tokens: { primary: '#6366f1', radius: '8px' } })`.
|
|
273
|
+
- `ThemeService` — creates a `<style id="vf-theme">` in `<head>` and fills it with
|
|
274
|
+
a `:root {}` block of CSS custom properties (`--vf-primary`, `--vf-radius`, …).
|
|
275
|
+
- **20 default tokens:** `primary`, `primaryDark`, `secondary`, `surface`,
|
|
276
|
+
`background`, `text`, `textMuted`, `border`, `danger`, `success`, `warning`,
|
|
277
|
+
`radius`, `radiusSm`, `radiusLg`, `fontSans`, `fontMono`,
|
|
278
|
+
`shadowSm`, `shadowMd`, `shadowLg`, `space`.
|
|
279
|
+
- Token names follow camelCase in JS and `--vf-kebab-case` in CSS
|
|
280
|
+
(`primaryDark` → `--vf-primary-dark`).
|
|
281
|
+
- `setTokens(map)` — merge new values and live-update the injected stylesheet.
|
|
282
|
+
Returns `this` for chaining.
|
|
283
|
+
- `getToken(name)` — read the current resolved value; returns `null` for unknown
|
|
284
|
+
names.
|
|
285
|
+
- `base: false` option — inject only the `:root {}` token block, skip the
|
|
286
|
+
base stylesheet.
|
|
287
|
+
- Idempotent `<style>` element — re-using an existing `#vf-theme` element so a
|
|
288
|
+
second ThemeService (e.g. after `app.provide('theme', …)`) doesn't leave orphans.
|
|
289
|
+
- Base stylesheet (`src/plugins/theme/base-styles.js`) — included by default:
|
|
290
|
+
box-sizing reset, body font/color/background wired to tokens, `.vf-card` surface
|
|
291
|
+
card, `.vf-btn` + `.vf-btn-primary/secondary/danger/success`, `.vf-icon` alignment.
|
|
292
|
+
- `themePlugin` exported from `src/framework.js` as part of the public API.
|
|
293
|
+
- `FRAMEWORK_VERSION` bumped to `1.2.0`.
|
|
294
|
+
|
|
295
|
+
#### Documentation
|
|
296
|
+
- `docs/roadmap.md` — CSS/theming moved to "Done"; alerts/notifications is now next.
|
|
297
|
+
|
|
298
|
+
#### Tests
|
|
299
|
+
- `tests/theme.test.js` — 16 tests covering: style element creation, `:root` block,
|
|
300
|
+
camelCase→kebab conversion, default token values, token overrides, base styles
|
|
301
|
+
inclusion/exclusion, idempotent element reuse, `setTokens()` update and chaining,
|
|
302
|
+
`getToken()` read/update/null, and `themePlugin` integration via `createApp`.
|
|
303
|
+
|
|
304
|
+
#### Examples
|
|
305
|
+
- `examples/router-app/index.html` installs `themePlugin` with custom token overrides
|
|
306
|
+
and replaces hard-coded hex values with `var(--vf-*)` references throughout its
|
|
307
|
+
hand-written stylesheet.
|
|
308
|
+
|
|
309
|
+
## [1.1.0] - 2026-06-22
|
|
310
|
+
|
|
311
|
+
This release lays the **long-term foundation** for a batteries-included framework:
|
|
312
|
+
a plugin/service registry, component composition, and a built-in icons subsystem.
|
|
313
|
+
Every future subsystem (CSS/theming, alerts, fonts) slots into the same plugin API.
|
|
314
|
+
|
|
315
|
+
### Added
|
|
316
|
+
|
|
317
|
+
#### Plugin / service registry (`src/framework.js`)
|
|
318
|
+
- `app.use(plugin, options)` — installs a plugin (a function or `{ name, install }` object).
|
|
319
|
+
Idempotent for named plugins, chainable, available before `app.initialize()`.
|
|
320
|
+
- `app.provide(name, instance)` — registers or replaces a named service in the registry.
|
|
321
|
+
- `app.get(name)` — looks up a service from the registry first, then falls back to registered
|
|
322
|
+
component classes. Fully backward-compatible with existing `app.get('eventBus')` etc.
|
|
323
|
+
- All built-in services (`eventBus`, `logger`, `errorHandler`, `notification`, `validation`,
|
|
324
|
+
`componentManager`, `performanceUtils`, `router`) are now in the registry.
|
|
325
|
+
|
|
326
|
+
#### Component composition (`src/components/base-component.js`, `src/core/dom-morph.js`, `src/core/component-manager.js`)
|
|
327
|
+
- `this.child(ClassOrName, props, key)` — declare a child component inside `getTemplate()`.
|
|
328
|
+
Returns a host placeholder; after the morph, `reconcileChildren()` mounts/updates/destroys
|
|
329
|
+
real child instances to match.
|
|
330
|
+
- `reconcileChildren()` — called automatically after every render. Mounts new children, updates
|
|
331
|
+
props on existing ones (re-renders only when props change), destroys children whose host was
|
|
332
|
+
removed from the template.
|
|
333
|
+
- DOM morph now treats `[data-vf-host]` elements as opaque boundaries — the mounted child's DOM
|
|
334
|
+
is never overwritten by a parent re-render.
|
|
335
|
+
- Event delegation scoped per component — clicks inside a child do not trigger the parent's
|
|
336
|
+
delegated handler.
|
|
337
|
+
- Child instances propagate `app` reference and component resolver down the tree, so
|
|
338
|
+
`this.service()`, `this.icon()`, and `this.child('by-name')` work at any nesting depth.
|
|
339
|
+
- Full teardown: `parent.destroy()` recursively destroys all children, clearing listeners and
|
|
340
|
+
instances with no leaks.
|
|
341
|
+
- `this.service(name)` — reach any plugin service from a component (`null` if absent).
|
|
342
|
+
- `this.icon(name, opts)` — shortcut to the icons service (empty string if absent).
|
|
343
|
+
|
|
344
|
+
#### Built-in icons plugin (`src/plugins/icons/`)
|
|
345
|
+
- `IconsService` — stores and renders inline SVG icons. Methods: `render(name, opts)`,
|
|
346
|
+
`register(name, svg)`, `has(name)`.
|
|
347
|
+
- `iconsPlugin` — install with `app.use(iconsPlugin)` or
|
|
348
|
+
`app.use(iconsPlugin, { icons: { myIcon: '<path .../>' } })`.
|
|
349
|
+
- Ships 20 original inline SVG icons: `check`, `close`, `trash`, `plus`, `minus`, `menu`,
|
|
350
|
+
`chevron-right`, `chevron-left`, `chevron-down`, `chevron-up`, `search`, `info`, `warning`,
|
|
351
|
+
`edit`, `arrow-left`, `arrow-right`, `home`, `user`, `settings`, `external-link`.
|
|
352
|
+
- `render()` options: `size` (default 24), `className`, `title` (adds accessible `<title>` +
|
|
353
|
+
`role="img"`), `color`.
|
|
354
|
+
- Unknown icons log a warning once (not repeatedly) and return an empty string.
|
|
355
|
+
- Exported from `src/framework.js` as part of the public API.
|
|
356
|
+
- Fully replaceable: `app.provide('icons', customService)` overrides the default.
|
|
357
|
+
|
|
358
|
+
#### Documentation
|
|
359
|
+
- `DEVELOPMENT.md` (repo root) — maintainer handbook: vision, resume-after-a-break checklist,
|
|
360
|
+
architecture map, plugin guide, conventions, commands, directory tour, and roadmap pointer.
|
|
361
|
+
- `docs/roadmap.md` — prioritised living roadmap for the next six capabilities (CSS/theming,
|
|
362
|
+
alerts, fonts, fast onboarding, signals, data+store, npm publish).
|
|
363
|
+
- `docs/composition.md` — full guide to component composition: `child()`, keys, props flow,
|
|
364
|
+
child lifecycle, event isolation, nesting, patterns, and gotchas.
|
|
365
|
+
- `docs/plugins.md` — plugin system guide: `use`/`provide`, icons reference, writing your own
|
|
366
|
+
plugin, plugin rules.
|
|
367
|
+
- `docs/README.md` updated with new features and doc links.
|
|
368
|
+
- `README.md` updated: features list, quick-start example with icons, documentation links,
|
|
369
|
+
expanded roadmap section.
|
|
370
|
+
|
|
371
|
+
#### Examples
|
|
372
|
+
- `examples/router-app/components/user-card.js` — new `UserCard` child component with its own
|
|
373
|
+
expand/collapse state, demonstrating stateful children and per-child event isolation.
|
|
374
|
+
- `examples/router-app/components/users-list.js` refactored to use `this.child(UserCard, ...)`,
|
|
375
|
+
with users keyed by id.
|
|
376
|
+
- `examples/router-app/components/user-detail.js` updated to use `this.icon('arrow-left')`.
|
|
377
|
+
- `examples/router-app/index.html` installs `iconsPlugin` and adds card styles.
|
|
378
|
+
|
|
379
|
+
#### Tests
|
|
380
|
+
- `tests/composition.test.js` — child rendering, props flow, keyed identity/reuse, child state
|
|
381
|
+
preservation across parent re-renders, removal/teardown, event isolation, by-name resolution.
|
|
382
|
+
- `tests/plugins.test.js` — `provide()`/`get()` round-trip, `use()` with function and object
|
|
383
|
+
plugins, idempotency, invalid plugin guard, chaining, override behavior.
|
|
384
|
+
- `tests/icons.test.js` — `IconsService.render()` (size/class/title/aria/color), warn-once for
|
|
385
|
+
unknown icons, `register()`/`has()`, `iconsPlugin` install and defaults, `BaseComponent.icon()`
|
|
386
|
+
with and without the plugin installed.
|
|
387
|
+
- Total test count: 88 (all passing).
|
|
388
|
+
|
|
389
|
+
### Changed
|
|
390
|
+
- `src/framework.js`: version bumped to `1.1.0`.
|
|
391
|
+
- `src/core/component-manager.js`: `loadComponentClass()` now wires `instance.app` and
|
|
392
|
+
`instance._resolveComponent` on every mounted route component.
|
|
393
|
+
|
|
394
|
+
## [1.0.1] - 2025-06-15 (production-ready refactor)
|
|
395
|
+
|
|
396
|
+
### Changed
|
|
397
|
+
- **Unified the render pipeline.** `BaseComponent` is now the single owner of
|
|
398
|
+
rendering. The previous double-render (component init + a separate DOMRenderer)
|
|
399
|
+
and its three overlapping event systems were removed; `DOMRenderer` is gone.
|
|
400
|
+
- **Reactive rendering via DOM morphing.** `setState()`/`updateProps()` now patch
|
|
401
|
+
only changed nodes via a new zero-dependency `dom-morph` module, preserving the
|
|
402
|
+
focus and caret of focused inputs and reconciling `data-key` lists in place.
|
|
403
|
+
- **Single declarative event system.** `data-action` (click), `data-change`,
|
|
404
|
+
`data-input`, `data-keydown`, and `data-submit` are delegated once to the
|
|
405
|
+
component root and cleaned up on destroy.
|
|
406
|
+
- Configurable mount container via `createApp({ mountId })` (default `main-content`).
|
|
407
|
+
|
|
408
|
+
### Fixed
|
|
409
|
+
- Router now navigates to the configured `fallback` route on a no-match, so the
|
|
410
|
+
404 component actually renders.
|
|
411
|
+
- Router responds to `router:navigate` events from components.
|
|
412
|
+
- Todo example renders correctly (previously a blank page — it mounted into a
|
|
413
|
+
container the router never used).
|
|
414
|
+
|
|
415
|
+
### Added
|
|
416
|
+
- DOM-morphing renderer (`src/core/dom-morph.js`).
|
|
417
|
+
- Routing example with route params (`examples/router-app/`).
|
|
418
|
+
- Test suite (Vitest + happy-dom) covering morph, components, router, event bus.
|
|
419
|
+
- Flat ESLint config (`eslint.config.js`) and CI workflow.
|
|
420
|
+
|
|
421
|
+
## [1.0.0] - 2025-06-15
|
|
422
|
+
|
|
423
|
+
### Added
|
|
424
|
+
- Initial release of Vanilla JS SPA Framework
|
|
425
|
+
- Component-based architecture with BaseComponent class
|
|
426
|
+
- Client-side routing with history API support
|
|
427
|
+
- Event-driven communication via EventBus
|
|
428
|
+
- Comprehensive error handling and logging system
|
|
429
|
+
- Build system for production deployment
|
|
430
|
+
- Framework debug utilities
|
|
431
|
+
- State management capabilities
|
|
432
|
+
- Form validation utilities
|
|
433
|
+
- SweetAlert integration for notifications
|
|
434
|
+
- Zero external dependencies
|
|
435
|
+
- Full ES module support
|
|
436
|
+
- TypeScript-friendly (JSDoc annotations)
|
|
437
|
+
|
|
438
|
+
### Framework Features
|
|
439
|
+
- **ComponentManager** - Manages component lifecycle and registration
|
|
440
|
+
- **Router** - Handles client-side navigation and route protection
|
|
441
|
+
- **EventBus** - Pub/sub pattern for component communication
|
|
442
|
+
- **Logger** - Configurable logging system with levels
|
|
443
|
+
- **ErrorHandler** - Centralized error handling and reporting
|
|
444
|
+
- **Validator** - Input validation utilities
|
|
445
|
+
- **FrameworkDebug** - Development debugging tools
|
|
446
|
+
|
|
447
|
+
### Examples
|
|
448
|
+
- Interactive counter demo
|
|
449
|
+
- Basic todo application example
|
|
450
|
+
- Component composition examples
|
|
451
|
+
- Routing demonstrations
|
|
452
|
+
|
|
453
|
+
### Documentation
|
|
454
|
+
- Comprehensive README with examples
|
|
455
|
+
- Framework API documentation
|
|
456
|
+
- Getting started guide
|
|
457
|
+
- Best practices guide
|
|
458
|
+
|
|
459
|
+
### Browser Support
|
|
460
|
+
- Modern browsers supporting ES2020+
|
|
461
|
+
- Chrome 80+, Firefox 72+, Safari 14+
|
|
462
|
+
- Edge 80+
|
|
463
|
+
|
|
464
|
+
### Bundle Size
|
|
465
|
+
- Framework core: ~48 KB minified (~14.5 KB gzipped)
|
|
466
|
+
- Full demo bundle (with all utilities): ~56 KB minified
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# 🔥 VanillaForge
|
|
2
|
+
|
|
3
|
+
**A small, zero-dependency JavaScript framework for building Single Page Applications with plain web standards.**
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
|
|
7
|
+
[](#testing)
|
|
8
|
+
|
|
9
|
+
VanillaForge gives you components, client-side routing, and an event bus in a
|
|
10
|
+
few small ES modules — no dependencies and no required build step. It runs
|
|
11
|
+
straight from `src/` in the browser; the build is only for producing an
|
|
12
|
+
optimized bundle.
|
|
13
|
+
|
|
14
|
+
## Why VanillaForge?
|
|
15
|
+
|
|
16
|
+
- **Zero runtime dependencies** — ships as plain ES modules.
|
|
17
|
+
- **Small** — the core is ~14.5 KB min+gzip (~48 KB minified).
|
|
18
|
+
- **Batteries-included** — icons, CSS/theming, alerts, self-hosted fonts, and a
|
|
19
|
+
shared reactive store, all built in. No Font Awesome, Bootstrap, SweetAlert, or
|
|
20
|
+
Google Fonts required — but you can still bring them in if you want.
|
|
21
|
+
- **Component composition** — embed child components directly inside parent
|
|
22
|
+
templates. Each child has isolated state, props, lifecycle, and event handling.
|
|
23
|
+
- **Plugin system** — every subsystem (icons, theme, alerts, fonts, store) is a
|
|
24
|
+
plugin you can install, replace, or skip.
|
|
25
|
+
- **Efficient updates** — re-renders are applied with a tiny DOM-morphing diff,
|
|
26
|
+
so only changed nodes are touched and focused inputs keep their cursor (see
|
|
27
|
+
[How rendering works](#how-rendering-works)).
|
|
28
|
+
- **Client-side routing** — history API, route params (`/users/:id`), and a
|
|
29
|
+
configurable fallback route.
|
|
30
|
+
- **Declarative events** — wire DOM events to methods with `data-*` attributes;
|
|
31
|
+
the framework handles delegation and cleanup.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/Steve-GitCodex/vanillaforge.git
|
|
37
|
+
cd vanillaforge
|
|
38
|
+
npm install
|
|
39
|
+
|
|
40
|
+
# Run the demo (builds to dist/ and serves it)
|
|
41
|
+
npm run dev
|
|
42
|
+
|
|
43
|
+
# Or run an example directly (no build needed)
|
|
44
|
+
npm run example # Todo app
|
|
45
|
+
npm run example:router # Routing + params demo
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Your first component:**
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import { createApp, BaseComponent, iconsPlugin } from './src/framework.js';
|
|
52
|
+
|
|
53
|
+
class HelloWorld extends BaseComponent {
|
|
54
|
+
constructor(eventBus, props = {}) {
|
|
55
|
+
super(eventBus, props);
|
|
56
|
+
this.name = 'hello-world';
|
|
57
|
+
this.state = { count: 0 };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getTemplate() {
|
|
61
|
+
return `
|
|
62
|
+
<div class="hello">
|
|
63
|
+
<h1>Hello, VanillaForge!</h1>
|
|
64
|
+
<p>Clicked ${this.state.count} times.</p>
|
|
65
|
+
<button data-action="inc">
|
|
66
|
+
${this.icon('plus', { size: 16 })} Click me
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getMethods() {
|
|
73
|
+
return { inc: () => this.setState({ count: this.state.count + 1 }) };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const app = createApp({ debug: true });
|
|
78
|
+
app.use(iconsPlugin); // built-in SVG icons
|
|
79
|
+
await app.initialize({ routes: { '/': HelloWorld } });
|
|
80
|
+
await app.start();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The page needs a mount element (default id `main-content`, configurable via
|
|
84
|
+
`createApp({ mountId })`):
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<div id="main-content"></div>
|
|
88
|
+
<script type="module" src="./app.js"></script>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## How rendering works
|
|
92
|
+
|
|
93
|
+
Calling `setState()` re-runs your `getTemplate()` and **morphs** the result onto
|
|
94
|
+
the live DOM instead of replacing `innerHTML`. The morph:
|
|
95
|
+
|
|
96
|
+
- patches only attributes/text/nodes that actually changed;
|
|
97
|
+
- preserves the focus and caret/selection of a focused input, so typing is never
|
|
98
|
+
interrupted by a re-render;
|
|
99
|
+
- reconciles lists by `data-key`, so reordering or removing an item reuses the
|
|
100
|
+
existing DOM nodes instead of rebuilding the list.
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
getTemplate() {
|
|
104
|
+
return `<ul>${this.state.items
|
|
105
|
+
.map((it) => `<li data-key="${it.id}">${it.label}</li>`)
|
|
106
|
+
.join('')}</ul>`;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> Note: a full re-render still re-runs the whole template (then diffs it). Moving
|
|
111
|
+
> to fine-grained, signal-based updates is on the [roadmap](#roadmap).
|
|
112
|
+
|
|
113
|
+
## Declarative events
|
|
114
|
+
|
|
115
|
+
Bind DOM events to `getMethods()` entries with attributes. Each attribute maps to
|
|
116
|
+
exactly one event so a handler fires once:
|
|
117
|
+
|
|
118
|
+
| Attribute | Fires on | Typical use |
|
|
119
|
+
| -------------- | --------- | --------------------------- |
|
|
120
|
+
| `data-action` | `click` | buttons, links |
|
|
121
|
+
| `data-change` | `change` | checkboxes, radios, selects |
|
|
122
|
+
| `data-input` | `input` | text inputs, textareas |
|
|
123
|
+
| `data-keydown` | `keydown` | keyboard shortcuts |
|
|
124
|
+
| `data-submit` | `submit` | forms |
|
|
125
|
+
|
|
126
|
+
Handlers receive `(event, matchedElement)`. Listeners are delegated to the
|
|
127
|
+
component's root element once and cleaned up automatically on destroy.
|
|
128
|
+
|
|
129
|
+
## Examples
|
|
130
|
+
|
|
131
|
+
- [Todo App](examples/todo-app/) — local state, filtering, keyed list, and
|
|
132
|
+
focus-preserving input. Run with `npm run example`.
|
|
133
|
+
- [Routing demo](examples/router-app/) — a list view and a `/users/:id` detail
|
|
134
|
+
view driven by route params. Run with `npm run example:router`.
|
|
135
|
+
|
|
136
|
+
## Testing
|
|
137
|
+
|
|
138
|
+
Tests run on [Vitest](https://vitest.dev) with [happy-dom](https://github.com/capricorn86/happy-dom)
|
|
139
|
+
(dev dependencies only — the framework itself stays dependency-free):
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm test
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Coverage includes the DOM morph (focus/selection preservation, keyed lists), the
|
|
146
|
+
component lifecycle and event delegation, the router, the event bus, and both
|
|
147
|
+
examples.
|
|
148
|
+
|
|
149
|
+
## Build
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm run build # bundle src/app.js + CSS into dist/
|
|
153
|
+
NODE_ENV=production npm run build # minified bundle
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The build uses [esbuild](https://esbuild.github.io/) to bundle and tree-shake,
|
|
157
|
+
and copies/minifies discovered CSS. See [docs/build-system.md](docs/build-system.md).
|
|
158
|
+
|
|
159
|
+
## Documentation
|
|
160
|
+
|
|
161
|
+
- [Components Guide](docs/components.md)
|
|
162
|
+
- [Component Composition](docs/composition.md)
|
|
163
|
+
- [Plugin System & Built-in Icons](docs/plugins.md)
|
|
164
|
+
- [Routing System](docs/router.md)
|
|
165
|
+
- [Event Bus](docs/event-bus.md)
|
|
166
|
+
- [API Reference](docs/API.md)
|
|
167
|
+
- [Build System](docs/build-system.md)
|
|
168
|
+
- [GitHub Pages](docs/github-pages.md)
|
|
169
|
+
- [Roadmap](docs/roadmap.md)
|
|
170
|
+
|
|
171
|
+
For maintainers returning after time away: see [DEVELOPMENT.md](DEVELOPMENT.md).
|
|
172
|
+
|
|
173
|
+
## Browser Support
|
|
174
|
+
|
|
175
|
+
Modern browsers with ES2020+ support: Chrome 80+, Firefox 72+, Safari 14+, Edge 80+.
|
|
176
|
+
|
|
177
|
+
## Roadmap
|
|
178
|
+
|
|
179
|
+
All five built-in plugins (icons, theme, alerts, fonts, store), component
|
|
180
|
+
composition, signals, route loaders, TypeScript types, and the scaffold CLI
|
|
181
|
+
are shipped. What's next:
|
|
182
|
+
|
|
183
|
+
- **npm publish** — stable `2.0.0` release to the npm registry.
|
|
184
|
+
|
|
185
|
+
Full details in [docs/roadmap.md](docs/roadmap.md).
|
|
186
|
+
|
|
187
|
+
## Contributing
|
|
188
|
+
|
|
189
|
+
Issues and pull requests are welcome. Please run `npm test` and `npm run lint`
|
|
190
|
+
before opening a PR.
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT — see [LICENSE](LICENSE).
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
**Author:** Stephen Musyoka · [GitHub](https://github.com/Steve-GitCodex/vanillaforge) · [Issues](https://github.com/Steve-GitCodex/vanillaforge/issues)
|