gofront 0.0.6 → 0.0.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.
package/README.md CHANGED
@@ -101,8 +101,8 @@ Walks the typed AST and emits clean, readable JavaScript. No intermediate repres
101
101
  — the codegen writes directly to an output buffer with indentation tracking.
102
102
 
103
103
  Runtime helpers (`__len`, `__append`, `__s`, `__sprintf`, `__cmul`, `__cdiv`, `__error`,
104
- `__errorIs`) are tree-shaken: only emitted when actually used. Optional inline source
105
- maps are supported via VLQ-encoded mappings.
104
+ `__errorIs`, `__timeFmt`, `__timeParse`, `__pathClean`) are tree-shaken: only emitted
105
+ when actually used. Optional inline source maps are supported via VLQ-encoded mappings.
106
106
 
107
107
  ---
108
108
 
@@ -184,7 +184,7 @@ The type checker enforces correctness; JavaScript doesn't need to know.
184
184
 
185
185
  ## Examples — Todo App
186
186
 
187
- There are two example apps — both implement the same todo app to show different aspects
187
+ There are three example apps — all implement the same todo app to show different aspects
188
188
  of GoFront.
189
189
 
190
190
  ### Simple (vanilla DOM)
@@ -202,7 +202,7 @@ example/simple/
202
202
  styles.go ← injectStyles() creates <style> element with all CSS
203
203
  main.go ← createApp() builds DOM shell · setupEvents() · event delegation
204
204
  utils/
205
- utils.go ← Plural() · generic Filter/Map — cross-package import demo
205
+ utils.go ← Plural() · generic Filter — cross-package import demo
206
206
  browser.d.ts ← minimal external type declarations (sleep)
207
207
  index.html ← bare HTML shell, loads app.js as ES module
208
208
  app.js ← generated output
@@ -212,41 +212,99 @@ example/simple/
212
212
 
213
213
  Same app rebuilt with [reactive.js](https://github.com/seriva/microtastic), a tiny
214
214
  signals-based reactive framework. Demonstrates how GoFront integrates with external JS
215
- libraries via `.d.ts` type declarations — the GoFront code uses typed `Signal` values,
216
- `Signals.create()`, `Signals.computed()`, `Signals.batch()`, and `Reactive.bind()`,
217
- all described in a hand-written `browser.d.ts` shim.
215
+ libraries via `.d.ts` type declarations — the entire reactive API surface is typed via a
216
+ hand-written `browser.d.ts` shim and exercised from GoFront source.
218
217
 
219
218
  ```
220
219
  example/reactive/
221
220
  src/
222
221
  types.go ← Todo, Stats, AppElements structs
223
- store.go ← Signal-typed state · Signals.create/computed/batch
224
- render.go ← createAppShell() returns AppElements · ctx.computed/ctx.bind
225
- styles.go ← injectStyles()
226
- main.go ← wires everything together
222
+ store.go ← reactive state: Signals.create/computed/computedAsync/batch/update
223
+ render.go ← Reactive.Component lifecycle · ctx.bind* · setupStatsBar
224
+ styles.go ← per-section cssClass() scoped styles
225
+ main.go ← async boot, Reactive.mount loading placeholder, signal.subscribe/once
227
226
  utils/
228
- utils.go ← Plural() · generic Filter/Map
229
- browser.d.ts ← Signal interface · Signals/Reactive/ComponentContext namespaces
227
+ utils.go ← Plural() · generic Filter
228
+ browser.d.ts ← Signal · ComponentContext · Signals/Reactive namespaces · htmlTag
230
229
  reactive.js ← signals framework (from microtastic)
231
- index.html ← loads reactive.js + app.js
230
+ index.html ← loads reactive.js, exposes helpers, imports app.js
232
231
  app.js ← generated output
233
232
  ```
234
233
 
234
+ The reactive example covers the full reactive.js API surface:
235
+
236
+ | Category | Features used |
237
+ |---|---|
238
+ | HTML helpers | `html` tagged template (via `htmlTag`), `trusted`, `join` |
239
+ | CSS | `cssClass` scoped styles with nested `&` selectors |
240
+ | Signals | `create`, `computed`, `computedAsync`, `batch`, `get`, `peek`, `set`, `update`, `subscribe`, `once` |
241
+ | Reactive namespace | `mount` (loading placeholder), `createComponent`, `scan` |
242
+ | Component context | `bind`, `bindAttr`, `bindBoolAttr`, `bindClass`, `bindText`, `bindStyle`, `bindMultiple`, `computed` |
243
+ | Component lifecycle | `state`, `template`, `styles`, `mount`, `mountTo`, `appendTo`, `refs` (via `data-ref`) |
244
+ | Component instance | `signal`, `effect`, `on` (auto-cleanup event listeners) |
245
+ | Scan attributes | `data-model`, `data-text`, `data-html`, `data-if`, `data-visible`, `data-class-*`, `data-attr-*`, `data-bool-*`, `data-on-*`, `data-ref` |
246
+
247
+ ### gom (stdlib component library + todo app)
248
+
249
+ Browser-native declarative DOM components inspired by
250
+ [gomponents](https://www.gomponents.com). `gom` is a **built-in stdlib package** — no
251
+ import path needed, available in every GoFront program just like `fmt` or `strings`. It
252
+ uses **methods on named non-struct types** so that plain functions and slices can satisfy
253
+ the `Node` interface without any struct boilerplate.
254
+
255
+ ```
256
+ example/gom/
257
+ src/
258
+ types.go ← Todo struct, filter/priority constants
259
+ store.go ← state, mutations, localStorage persistence
260
+ render.go ← gom node builders (pure node tree, no innerHTML)
261
+ styles.go ← appStyles() CSS string
262
+ main.go ← event setup, submitInput, entry point
263
+ utils/ ← Filter[T], Plural, HasText
264
+ app.js ← generated output
265
+ index.html ← HTML shell
266
+ ```
267
+
268
+ Key types and functions (all accessed as `gom.*`, no import needed):
269
+ - `gom.Node` — interface with a single `Mount(parent any)` method
270
+ - `gom.NodeFunc` — `type NodeFunc func(parent any)` with `Mount` method; any function becomes a `Node`
271
+ - `gom.Group` — `type Group []Node` with `Mount` method; composes children in order
272
+ - `gom.El(tag, children...)` — creates an element node
273
+ - `gom.Text(s)` — creates a text node
274
+ - `gom.Attr(name, val)` — generic attribute node; named shortcuts: `gom.Class`, `gom.Href`, `gom.Type`, etc.
275
+ - `gom.If(cond, node)` — conditionally renders a node
276
+ - `gom.Map(slice, fn)` — maps a slice to a `Group` of nodes
277
+ - `gom.Style(css)` — returns a `Node` that injects a `<style>` element; styles are part of the node tree
278
+ - `gom.Mount(selector, n)` — mounts a node into the DOM, replacing the element's content
279
+ - `gom.MountTo(selector, n)` — appends a node without clearing; used to inject styles into `<head>`
280
+ - HTML element helpers: `gom.Div`, `gom.Span`, `gom.Button`, `gom.Input`, `gom.Ul`, `gom.Li`, … (full HTML element set)
281
+
235
282
  ### Features demonstrated
236
283
 
237
- Both apps cover: structs & methods, iota constants, named return values, closures,
238
- slices, `for range`, `switch`, cross-package imports, multi-file same-package compilation,
239
- `async`/`await`, `defer`/`recover`, localStorage persistence, DOM APIs, and generic
240
- utility functions (`Filter[T]`, `Map[T, U]`).
284
+ The simple and reactive examples cover: structs & methods, iota constants, named return
285
+ values, closures, slices, `for range`, `switch`, cross-package imports, multi-file
286
+ same-package compilation, `async`/`await`, `defer`/`recover`, localStorage persistence,
287
+ DOM APIs, and generic utility functions (`Filter[T]`, `Map[T, U]`).
241
288
 
242
289
  The reactive example additionally demonstrates: external `.d.ts` type imports,
243
- `declare namespace` patterns for typing JS libraries, and reactive UI updates via signals.
290
+ `declare namespace` patterns for typing JS libraries, the full reactive signal graph
291
+ (source signals → computed signals → async computed → DOM bindings), reactive UI with
292
+ no `querySelector` or `getElementById` in application code, and `Reactive.Component`
293
+ as the primary composition unit (state, template, styles, mount lifecycle, `data-ref`
294
+ element refs, auto-cleanup event listeners).
295
+
296
+ The gom example additionally demonstrates: **the `gom` built-in stdlib package**,
297
+ styles as nodes (`gom.Style` + `gom.MountTo`), `gom.Map` for list rendering, the
298
+ declarative node-tree pattern (no `innerHTML`, no `querySelector`), and full feature
299
+ parity with the other examples (priority mode, validation, localStorage persistence,
300
+ sync status, drag-and-drop reordering).
244
301
 
245
302
  ### Build and run
246
303
 
247
304
  ```sh
248
305
  npm run build:simple # → example/simple/app.js
249
306
  npm run build:reactive # → example/reactive/app.js
307
+ npm run build:gom # → example/gom/app.js
250
308
  # open the respective index.html in a browser
251
309
  ```
252
310
 
@@ -367,6 +425,7 @@ signatures into GoFront's internal type representation.
367
425
  | `error` type | ✓ — interface `{ Error() string }`; custom error types, `errors.Is`/`Unwrap`, `%w` wrapping |
368
426
  | Complex numbers (`complex64`, `complex128`, `3i`) | ✓ — `complex()`, `real()`, `imag()` builtins; `__cmul`/`__cdiv` helpers |
369
427
  | Type definitions, type aliases (`type A = B`) | ✓ |
428
+ | Methods on named non-struct types | ✓ — `type T func(...)` or `type T []E` with methods; emitted as ES6 wrapper classes; satisfies interfaces |
370
429
  | Type conversions, type assertions (plain & comma-ok) | ✓ |
371
430
  | Type switch (`switch v := x.(type)`) | ✓ — compiles to `if/else if` with `typeof` / `instanceof` |
372
431
  | Sized integers (`int8`–`int64`, `uint8`–`uint64`, `float32`) | ✓ — mapped to `number` at runtime |
@@ -405,20 +464,25 @@ signatures into GoFront's internal type representation.
405
464
 
406
465
  | Package | Functions |
407
466
  |---|---|
408
- | `fmt` | `Sprintf`, `Printf`, `Println`, `Print`, `Errorf`, `Fprintf`, `Fprintln`, `Fprint` — format verbs: `%v`, `%d`, `%s`, `%t`, `%x`, `%o`, `%b`, `%q`, `%e`, `%g`, `%w`, width/precision |
409
- | `strings` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `LastIndex`, `Count`, `Repeat`, `Replace`, `ReplaceAll`, `ToUpper`, `ToLower`, `TrimSpace`, `Trim`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `Split`, `Join`, `EqualFold`; **`Builder`** type (`WriteString`, `WriteByte`, `WriteRune`, `Write`, `String`, `Len`, `Reset`, `Grow`) |
410
- | `bytes` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `Join`, `Split`, `Replace`, `ToUpper`, `ToLower`, `TrimSpace`, `Equal`, `Count`, `Repeat`; **`Buffer`** type (`WriteString`, `WriteByte`, `Write`, `String`, `Bytes`, `Len`, `Reset`) |
411
- | `strconv` | `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `FormatFloat`, `ParseFloat`, `ParseInt`, `ParseBool` |
412
- | `sort` | `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted` |
413
- | `math` | `Abs`, `Floor`, `Ceil`, `Round`, `Sqrt`, `Cbrt`, `Pow`, `Log`, `Log2`, `Log10`, trig functions + `Pi`, `E`, `MaxFloat64`, `MaxInt`, `MinInt` |
467
+ | `fmt` | `Sprintf`, `Printf`, `Println`, `Print`, `Errorf`, `Fprintf`, `Fprintln`, `Fprint` — format verbs: `%v`, `%d`, `%s`, `%t`, `%x`, `%o`, `%b`, `%q`, `%e`, `%g`, `%w`, width/precision; **scanning**: `Sscan`, `Sscanln`, `Sscanf` |
468
+ | `strings` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `LastIndex`, `Count`, `Repeat`, `Replace`, `ReplaceAll`, `ToUpper`, `ToLower`, `TrimSpace`, `Trim`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `Split`, `Join`, `EqualFold`, `Fields`, `Cut`, `CutPrefix`, `CutSuffix`, `SplitN`, `SplitAfter`, `SplitAfterN`, `IndexAny`, `LastIndexAny`, `ContainsAny`, `ContainsRune`, `IndexRune`, `IndexByte`, `LastIndexByte`, `Map`, `Title`, `ToTitle`, `TrimFunc`, `TrimLeftFunc`, `TrimRightFunc`, `IndexFunc`, `LastIndexFunc`, `NewReplacer`; **`Builder`** type; **`NewReader`** → reader shim |
469
+ | `bytes` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `Join`, `Split`, `Replace`, `ToUpper`, `ToLower`, `TrimSpace`, `Equal`, `Count`, `Repeat`, `ReplaceAll`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `TrimFunc`, `IndexByte`, `LastIndex`, `LastIndexByte`, `Fields`, `Cut`, `ContainsAny`, `ContainsRune`, `Map`, `SplitN`; **`Buffer`** type; **`NewReader`** → reader shim |
470
+ | `strconv` | `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `FormatFloat`, `ParseFloat`, `ParseInt`, `ParseBool`, `Quote`, `Unquote`, `AppendInt`, `AppendFloat` |
471
+ | `sort` | `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted`, `Search`, `IntsAreSorted`, `Float64sAreSorted`, `StringsAreSorted` |
472
+ | `math` | `Abs`, `Floor`, `Ceil`, `Round`, `Sqrt`, `Cbrt`, `Pow`, `Log`, `Log2`, `Log10`, `Sin`, `Cos`, `Tan`, `Atan`, `Atan2`, `Asin`, `Acos`, `Exp`, `Exp2`, `Trunc`, `Hypot`, `Signbit`, `Copysign`, `Dim`, `Remainder`, `Min`, `Max`, `Mod`, `Inf`, `IsNaN`, `IsInf`, `NaN` + `Pi`, `E`, `MaxFloat64`, `SmallestNonzeroFloat64`, `MaxInt`, `MinInt` |
473
+ | `math/rand` | `Intn`, `Float64`, `Float32`, `Int`, `Int63`, `Int63n`, `Int31`, `Int31n`, `Seed` (no-op), `Shuffle`, `Perm` |
414
474
  | `errors` | `New`, `Is`, `Unwrap` — custom error types via interface satisfaction |
415
- | `time` | `Now`, `Since`, `Sleep` + `Millisecond`, `Second`, `Minute`, `Hour` constants |
475
+ | `time` | `Now` → `time.Time`, `Since`, `Sleep`, `Parse`, `Unix`, `Date`; **`time.Time`** methods: `Format`, `String`, `Year`, `Month`, `Day`, `Hour`, `Minute`, `Second`, `Weekday`, `Unix`, `UnixMilli`, `Add`, `Sub`, `Before`, `After`, `Equal`; layout constants: `RFC3339`, `RFC3339Nano`, `DateOnly`, `TimeOnly`, `DateTime`; duration constants: `Millisecond`, `Second`, `Minute`, `Hour`; month/weekday constants |
416
476
  | `html` | `EscapeString`, `UnescapeString` |
417
477
  | `maps` | `Keys`, `Values`, `Clone`, `Copy`, `Equal`, `EqualFunc`, `Delete`, `DeleteFunc` |
418
478
  | `slices` | `Contains`, `Index`, `Equal`, `Compare`, `Sort`, `SortFunc`, `SortStableFunc`, `IsSorted`, `IsSortedFunc`, `Reverse`, `Max`, `Min`, `MaxFunc`, `MinFunc`, `Clone`, `Compact`, `CompactFunc`, `Concat`, `Delete`, `DeleteFunc`, `Insert`, `Replace`, `Grow`, `Clip` |
419
479
  | `regexp` | `MustCompile`, `Compile`, `MatchString`, `QuoteMeta`; **`*Regexp`** methods: `MatchString`, `FindString`, `FindStringIndex`, `FindAllString`, `FindStringSubmatch`, `FindAllStringSubmatch`, `ReplaceAllString`, `ReplaceAllLiteralString`, `Split`, `String`. Inline flags (`(?i)`, `(?m)`, `(?s)`) are extracted automatically into the JS `RegExp` constructor. |
420
480
  | `unicode` | `IsLetter`, `IsDigit`, `IsSpace`, `IsUpper`, `IsLower`, `IsPunct`, `IsControl`, `IsPrint`, `IsGraphic`, `ToUpper`, `ToLower` |
481
+ | `unicode/utf8` | `RuneCountInString`, `RuneLen`, `ValidString`, `ValidRune`, `DecodeRuneInString`, `DecodeLastRuneInString`, `FullRuneInString`; constants `RuneError`, `MaxRune`, `UTFMax` |
482
+ | `path` | `Base`, `Dir`, `Ext`, `Join`, `Clean`, `IsAbs`, `Split`, `Match`; `"path/filepath"` is an alias |
421
483
  | `os` | `Exit`, `Args`, `Getenv` |
484
+ | `io` | `Writer`, `Reader`, `ReadWriter`, `Closer` interface types; `ReadAll`, `EOF`, `Discard`, `WriteString` |
485
+ | `gom` | Browser-native declarative DOM component library. **Types**: `Node` (interface), `NodeFunc`, `Group`. **Core**: `El(tag, children...)`, `Text(s)`, `Mount(sel, node)`, `MountTo(sel, node)`. **Attributes**: `Attr`, `Class`, `Href`, `Type`, `Src`, `Placeholder`, `DataAttr`, `Style`, `For`, `Name`, `Value`, `Target`, `Rel`, `Alt`, `Title`, `Draggable`, `Role`, `AriaLabel`, `StyleAttr`; boolean: `Disabled`, `Checked`, `Selected`, `Readonly`. **Logic**: `If(cond, node)`, `Map(slice, fn)`. **Elements**: full HTML element set (`Div`, `Span`, `Button`, `Input`, `Ul`, `Li`, `Table`, `Form`, `Img`, `A`, `H1`–`H6`, …) |
422
486
 
423
487
  ### Packages & imports
424
488
 
@@ -490,18 +554,25 @@ you know exactly what to expect.
490
554
 
491
555
  See [`docs/ROADMAP.md`](docs/ROADMAP.md) for the full roadmap and release history.
492
556
  Design documents for planned features are organised by release under `docs/v*/`
493
- (e.g. [`docs/v0.0.5/`](docs/v0.0.5/)).
557
+ (e.g. [`docs/v0.0.8/`](docs/v0.0.8/), [`docs/v0.0.9/`](docs/v0.0.9/)).
494
558
 
495
559
  ---
496
560
 
497
561
  ## Tests
498
562
 
499
563
  ```sh
500
- npm test
564
+ npm run test:unit # unit tests only (~1076 tests, no browser required)
565
+ npm run test:e2e # E2E tests (Playwright, headless Chromium)
566
+ npm test # both
501
567
  ```
502
568
 
503
- 940 tests covering language features, type errors, edge cases, DOM (jsdom), external
504
- `.d.ts`, npm resolver, multi-file compilation, embedded structs, string formatting, map
505
- iteration order, integer overflow semantics, unused variable detection, unused import
506
- detection, semantic difference verification, stdlib shim packages, generics (type params,
507
- inference, constraints), and both example apps.
569
+ **Unit tests** (1076) cover language features, type errors, edge cases, DOM (jsdom),
570
+ external `.d.ts`, npm resolver, multi-file compilation, embedded structs, string
571
+ formatting, map iteration order, integer overflow semantics, unused variable detection,
572
+ unused import detection, semantic difference verification, stdlib shim packages, and
573
+ generics.
574
+
575
+ **E2E tests** (74, Playwright) run all three example apps in a real browser and verify
576
+ CRUD, filtering, priority mode, persistence (reload), drag-and-drop reordering, and sync
577
+ status. Per-app suites check app-specific behaviour: scoped styles, stats bar, loading
578
+ placeholder, and `gom.If` conditional rendering.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gofront",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "author": "Luuk van Venrooij",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,12 +33,19 @@
33
33
  "scripts": {
34
34
  "build:simple": "node src/index.js example/simple/src -o example/simple/app.js",
35
35
  "build:reactive": "node src/index.js example/reactive/src -o example/reactive/app.js",
36
- "test": "node test/run.js",
36
+ "build:gom": "node src/index.js example/gom/src -o example/gom/app.js",
37
+ "test": "npm run test:unit && npm run test:e2e",
38
+ "test:unit": "node test/unit/run.js",
39
+ "test:e2e": "playwright test",
37
40
  "format": "biome format --write .",
38
- "check": "biome check ."
41
+ "check": "biome check . && node src/index.js example/simple/src --check && node src/index.js example/reactive/src --check && node src/index.js example/gom/src --check",
42
+ "prepare": "husky"
39
43
  },
40
44
  "devDependencies": {
41
- "@biomejs/biome": "^2.4.11",
42
- "jsdom": "^29.0.2"
45
+ "@biomejs/biome": "^2.4.12",
46
+ "@playwright/test": "^1.59.1",
47
+ "husky": "^9.1.7",
48
+ "jsdom": "^29.0.2",
49
+ "serve": "^14.2.6"
43
50
  }
44
51
  }