gofront 0.0.7 → 0.0.9
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 +104 -18
- package/package.json +9 -4
- package/src/codegen/expressions.js +351 -6
- package/src/codegen/source-map.js +8 -5
- package/src/codegen.js +329 -4
- package/src/compiler.js +39 -4
- package/src/dev-server.js +1 -0
- package/src/index.js +10 -6
- package/src/lexer.js +14 -14
- package/src/parser/expressions.js +3 -2
- package/src/parser/statements.js +5 -1
- package/src/parser.js +8 -1
- package/src/resolver.js +29 -0
- package/src/templ-lexer.js +737 -0
- package/src/templ-parser.js +235 -0
- package/src/typechecker/stdlib.js +305 -2
- package/src/typechecker/types.js +6 -18
- package/src/typechecker.js +30 -2
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@ quite felt like home either.
|
|
|
7
7
|
So I built GoFront. Go syntax and type safety, compiling to plain ES modules. One language
|
|
8
8
|
front and back, no runtime, no framework, no tsconfig.json.
|
|
9
9
|
|
|
10
|
+
With built-in support for declarative DOM rendering via the [`gom`](https://www.gomponents.com) standard library, JSX-like [`.templ`](https://templ.guide/) files, and seamless integration with external JavaScript libraries through TypeScript definition files (`.d.ts`), GoFront is designed specifically as a frontend development target. Build complex, reactive user interfaces entirely in Go.
|
|
11
|
+
|
|
10
12
|
Probably not useful. Definitely fun to build.
|
|
11
13
|
|
|
12
14
|
```go
|
|
@@ -61,6 +63,12 @@ source text (.go files)
|
|
|
61
63
|
→ Parser recursive-descent → AST
|
|
62
64
|
→ Type Checker annotate AST with types + collect errors
|
|
63
65
|
→ Code Gen AST → JavaScript string
|
|
66
|
+
|
|
67
|
+
source text (.templ files)
|
|
68
|
+
→ TemplLexer dual-mode: Go mode for declarations, HTML mode inside templ bodies
|
|
69
|
+
→ TemplParser extends Parser; produces TemplDecl AST nodes with TemplNode children
|
|
70
|
+
→ Type Checker registers each templ component as func(...) gom.Node
|
|
71
|
+
→ Code Gen TemplDecl → direct DOM calls (createElement / setAttribute / appendChild)
|
|
64
72
|
```
|
|
65
73
|
|
|
66
74
|
### 1. Lexer (`src/lexer.js`)
|
|
@@ -101,8 +109,8 @@ Walks the typed AST and emits clean, readable JavaScript. No intermediate repres
|
|
|
101
109
|
— the codegen writes directly to an output buffer with indentation tracking.
|
|
102
110
|
|
|
103
111
|
Runtime helpers (`__len`, `__append`, `__s`, `__sprintf`, `__cmul`, `__cdiv`, `__error`,
|
|
104
|
-
`__errorIs`) are tree-shaken: only emitted
|
|
105
|
-
maps are supported via VLQ-encoded mappings.
|
|
112
|
+
`__errorIs`, `__timeFmt`, `__timeParse`, `__pathClean`) are tree-shaken: only emitted
|
|
113
|
+
when actually used. Optional inline source maps are supported via VLQ-encoded mappings.
|
|
106
114
|
|
|
107
115
|
---
|
|
108
116
|
|
|
@@ -184,7 +192,7 @@ The type checker enforces correctness; JavaScript doesn't need to know.
|
|
|
184
192
|
|
|
185
193
|
## Examples — Todo App
|
|
186
194
|
|
|
187
|
-
There are
|
|
195
|
+
There are four example apps — all implement the same todo app to show different aspects
|
|
188
196
|
of GoFront.
|
|
189
197
|
|
|
190
198
|
### Simple (vanilla DOM)
|
|
@@ -244,6 +252,60 @@ The reactive example covers the full reactive.js API surface:
|
|
|
244
252
|
| Component instance | `signal`, `effect`, `on` (auto-cleanup event listeners) |
|
|
245
253
|
| Scan attributes | `data-model`, `data-text`, `data-html`, `data-if`, `data-visible`, `data-class-*`, `data-attr-*`, `data-bool-*`, `data-on-*`, `data-ref` |
|
|
246
254
|
|
|
255
|
+
### templ (template files + direct DOM codegen)
|
|
256
|
+
|
|
257
|
+
Same todo app using GoFront's `.templ` file format — a JSX-like syntax embedded directly
|
|
258
|
+
in `.go` packages. `templ` declarations compile to direct `document.createElement` calls
|
|
259
|
+
with no intermediate representation, producing `{Mount(p){}}` objects that are fully
|
|
260
|
+
compatible with the `gom.Node` interface.
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
example/templ/
|
|
264
|
+
src/
|
|
265
|
+
render.templ ← UI as templ declarations: TodoItem, TodoList, Header, InputRow, StatsBar, AppView, …
|
|
266
|
+
render.go ← helper functions (todoItemClass, filterLabel, …) + render() entry point
|
|
267
|
+
types.go ← Todo struct, filter/priority constants
|
|
268
|
+
store.go ← state, mutations, localStorage persistence
|
|
269
|
+
styles.go ← appStyles() CSS string
|
|
270
|
+
main.go ← event setup, entry point
|
|
271
|
+
utils/ ← Filter[T], Plural, HasText
|
|
272
|
+
app.js ← generated output
|
|
273
|
+
index.html ← HTML shell
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`.templ` syntax at a glance:
|
|
277
|
+
|
|
278
|
+
```go
|
|
279
|
+
templ TodoItem(t Todo) {
|
|
280
|
+
<li class={ todoItemClass(t) } draggable="true" data-id={ t.id }>
|
|
281
|
+
<input type="checkbox" checked?={ t.done }/>
|
|
282
|
+
<span class="todo-text">{ t.text }</span>
|
|
283
|
+
if t.isUrgent() {
|
|
284
|
+
<span class="badge">urgent</span>
|
|
285
|
+
}
|
|
286
|
+
</li>
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
templ AppView() {
|
|
290
|
+
<div class="card">
|
|
291
|
+
@Header(highCount(), syncMsg, syncCls)
|
|
292
|
+
@TodoList(visibleTodos())
|
|
293
|
+
</div>
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Key syntax features:
|
|
298
|
+
- `{ expr }` — interpolate any Go expression (strings auto-cast, others use `String()`)
|
|
299
|
+
- `attr={ expr }` — dynamic attribute value (expression)
|
|
300
|
+
- `attr?={ expr }` — boolean attribute (present/absent based on truthiness)
|
|
301
|
+
- `@Component(args)` — call another templ component and mount it as a child
|
|
302
|
+
- `if cond { } else if cond { } else { }` — conditional rendering (arbitrary depth)
|
|
303
|
+
- `for _, v := range slice { }` — loop rendering inside template bodies
|
|
304
|
+
- `switch expr { case v: ... default: ... }` — switch rendering inside template bodies
|
|
305
|
+
- `@templ.Raw(htmlStr)` — inject raw/trusted HTML (uses `insertAdjacentHTML`)
|
|
306
|
+
- Components are called like regular functions (`AppView()`, `TodoItem(t)`) and return
|
|
307
|
+
a `gom.Node`-compatible object, so `gom.Mount("#app", AppView())` works directly.
|
|
308
|
+
|
|
247
309
|
### gom (stdlib component library + todo app)
|
|
248
310
|
|
|
249
311
|
Browser-native declarative DOM components inspired by
|
|
@@ -299,12 +361,19 @@ declarative node-tree pattern (no `innerHTML`, no `querySelector`), and full fea
|
|
|
299
361
|
parity with the other examples (priority mode, validation, localStorage persistence,
|
|
300
362
|
sync status, drag-and-drop reordering).
|
|
301
363
|
|
|
364
|
+
The templ example additionally demonstrates: **`.templ` file compilation**, template
|
|
365
|
+
components with parameters, `{ expr }` interpolation, `attr?={}` conditional boolean
|
|
366
|
+
attributes, `@Component()` calls inside templates, `if / else if / else` chains,
|
|
367
|
+
`switch` blocks, `for range` loops, and `@templ.Raw()` for trusted HTML injection —
|
|
368
|
+
all inside template bodies.
|
|
369
|
+
|
|
302
370
|
### Build and run
|
|
303
371
|
|
|
304
372
|
```sh
|
|
305
373
|
npm run build:simple # → example/simple/app.js
|
|
306
374
|
npm run build:reactive # → example/reactive/app.js
|
|
307
375
|
npm run build:gom # → example/gom/app.js
|
|
376
|
+
npm run build:templ # → example/templ/app.js
|
|
308
377
|
# open the respective index.html in a browser
|
|
309
378
|
```
|
|
310
379
|
|
|
@@ -377,7 +446,10 @@ func greet(name string) {
|
|
|
377
446
|
console.log("Hello, " + name)
|
|
378
447
|
}
|
|
379
448
|
|
|
380
|
-
greet(42)
|
|
449
|
+
greet(42)
|
|
450
|
+
// → Type error in src/main.go at line 5:7: cannot use int as string
|
|
451
|
+
// 5 | greet(42)
|
|
452
|
+
// ^
|
|
381
453
|
```
|
|
382
454
|
|
|
383
455
|
External TypeScript type definitions are supported via `js:` imports:
|
|
@@ -464,21 +536,24 @@ signatures into GoFront's internal type representation.
|
|
|
464
536
|
|
|
465
537
|
| Package | Functions |
|
|
466
538
|
|---|---|
|
|
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 |
|
|
468
|
-
| `strings` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `LastIndex`, `Count`, `Repeat`, `Replace`, `ReplaceAll`, `ToUpper`, `ToLower`, `TrimSpace`, `Trim`, `TrimPrefix`, `TrimSuffix`, `TrimLeft`, `TrimRight`, `Split`, `Join`, `EqualFold
|
|
469
|
-
| `bytes` | `Contains`, `HasPrefix`, `HasSuffix`, `Index`, `Join`, `Split`, `Replace`, `ToUpper`, `ToLower`, `TrimSpace`, `Equal`, `Count`, `Repeat
|
|
470
|
-
| `strconv` | `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `FormatFloat`, `ParseFloat`, `ParseInt`, `ParseBool` |
|
|
471
|
-
| `sort` | `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted` |
|
|
472
|
-
| `math` | `Abs`, `Floor`, `Ceil`, `Round`, `Sqrt`, `Cbrt`, `Pow`, `Log`, `Log2`, `Log10`,
|
|
539
|
+
| `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` |
|
|
540
|
+
| `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 |
|
|
541
|
+
| `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 |
|
|
542
|
+
| `strconv` | `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `FormatFloat`, `ParseFloat`, `ParseInt`, `ParseBool`, `Quote`, `Unquote`, `AppendInt`, `AppendFloat` |
|
|
543
|
+
| `sort` | `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted`, `Search`, `IntsAreSorted`, `Float64sAreSorted`, `StringsAreSorted` |
|
|
544
|
+
| `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` |
|
|
545
|
+
| `math/rand` | `Intn`, `Float64`, `Float32`, `Int`, `Int63`, `Int63n`, `Int31`, `Int31n`, `Seed` (no-op), `Shuffle`, `Perm` |
|
|
473
546
|
| `errors` | `New`, `Is`, `Unwrap` — custom error types via interface satisfaction |
|
|
474
|
-
| `time` | `Now`, `Since`, `Sleep`
|
|
547
|
+
| `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 |
|
|
475
548
|
| `html` | `EscapeString`, `UnescapeString` |
|
|
476
549
|
| `maps` | `Keys`, `Values`, `Clone`, `Copy`, `Equal`, `EqualFunc`, `Delete`, `DeleteFunc` |
|
|
477
550
|
| `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` |
|
|
478
551
|
| `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. |
|
|
479
552
|
| `unicode` | `IsLetter`, `IsDigit`, `IsSpace`, `IsUpper`, `IsLower`, `IsPunct`, `IsControl`, `IsPrint`, `IsGraphic`, `ToUpper`, `ToLower` |
|
|
553
|
+
| `unicode/utf8` | `RuneCountInString`, `RuneLen`, `ValidString`, `ValidRune`, `DecodeRuneInString`, `DecodeLastRuneInString`, `FullRuneInString`; constants `RuneError`, `MaxRune`, `UTFMax` |
|
|
554
|
+
| `path` | `Base`, `Dir`, `Ext`, `Join`, `Clean`, `IsAbs`, `Split`, `Match`; `"path/filepath"` is an alias |
|
|
480
555
|
| `os` | `Exit`, `Args`, `Getenv` |
|
|
481
|
-
| `io` | `Writer`, `Reader`, `ReadWriter`, `Closer` interface types
|
|
556
|
+
| `io` | `Writer`, `Reader`, `ReadWriter`, `Closer` interface types; `ReadAll`, `EOF`, `Discard`, `WriteString` |
|
|
482
557
|
| `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`, …) |
|
|
483
558
|
|
|
484
559
|
### Packages & imports
|
|
@@ -493,6 +568,7 @@ signatures into GoFront's internal type representation.
|
|
|
493
568
|
| Unused import detection | ✓ |
|
|
494
569
|
| External `.d.ts` types | ✓ |
|
|
495
570
|
| npm package type resolution | ✓ |
|
|
571
|
+
| `.templ` files in packages | ✓ — mix `.go` and `.templ` files freely; templ components visible across the whole package |
|
|
496
572
|
|
|
497
573
|
---
|
|
498
574
|
|
|
@@ -558,11 +634,21 @@ Design documents for planned features are organised by release under `docs/v*/`
|
|
|
558
634
|
## Tests
|
|
559
635
|
|
|
560
636
|
```sh
|
|
561
|
-
npm test
|
|
637
|
+
npm run test:unit # unit tests only (~1100 tests, no browser required)
|
|
638
|
+
npm run test:e2e # E2E tests (Playwright, headless Chromium)
|
|
639
|
+
npm test # both
|
|
562
640
|
```
|
|
563
641
|
|
|
564
|
-
|
|
565
|
-
`.d.ts`, npm resolver, multi-file compilation, embedded structs, string
|
|
566
|
-
iteration order, integer overflow semantics, unused variable detection,
|
|
567
|
-
detection, semantic difference verification, stdlib shim packages, generics
|
|
568
|
-
|
|
642
|
+
**Unit tests** (~1100) cover language features, type errors, edge cases, DOM (jsdom),
|
|
643
|
+
external `.d.ts`, npm resolver, multi-file compilation, embedded structs, string
|
|
644
|
+
formatting, map iteration order, integer overflow semantics, unused variable detection,
|
|
645
|
+
unused import detection, semantic difference verification, stdlib shim packages, generics,
|
|
646
|
+
and `.templ` file compilation (element rendering, interpolation, boolean attrs, component
|
|
647
|
+
calls, `if/else/else-if` chains, `for range`, `switch/case/default`, `@templ.Raw()` raw
|
|
648
|
+
HTML injection, mixed `.go`+`.templ` packages).
|
|
649
|
+
|
|
650
|
+
**E2E tests** (~104, Playwright) run all four example apps in a real browser and verify
|
|
651
|
+
CRUD, filtering, priority mode, persistence (reload), drag-and-drop reordering, and sync
|
|
652
|
+
status. Per-app suites check app-specific behaviour: scoped styles, stats bar, loading
|
|
653
|
+
placeholder, `gom.If` conditional rendering, and templ-specific features (`if/else`
|
|
654
|
+
priority hint, `for` loop rendering, conditional bool attributes).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gofront",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"author": "Luuk van Venrooij",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,14 +34,19 @@
|
|
|
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
36
|
"build:gom": "node src/index.js example/gom/src -o example/gom/app.js",
|
|
37
|
-
"
|
|
37
|
+
"build:templ": "node src/index.js example/templ/src -o example/templ/app.js",
|
|
38
|
+
"test": "npm run test:unit && npm run test:e2e",
|
|
39
|
+
"test:unit": "node test/unit/run.js",
|
|
40
|
+
"test:e2e": "playwright test",
|
|
38
41
|
"format": "biome format --write .",
|
|
39
|
-
"check": "biome check .",
|
|
42
|
+
"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 && node src/index.js example/templ/src --check",
|
|
40
43
|
"prepare": "husky"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
43
46
|
"@biomejs/biome": "^2.4.12",
|
|
47
|
+
"@playwright/test": "^1.59.1",
|
|
44
48
|
"husky": "^9.1.7",
|
|
45
|
-
"jsdom": "^29.0.2"
|
|
49
|
+
"jsdom": "^29.0.2",
|
|
50
|
+
"serve": "^14.2.6"
|
|
46
51
|
}
|
|
47
52
|
}
|