gofront 0.0.1 → 0.0.3
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 +357 -33
- package/package.json +7 -3
- package/src/codegen/expressions.js +804 -0
- package/src/codegen/source-map.js +45 -0
- package/src/codegen/statements.js +406 -0
- package/src/codegen.js +50 -851
- package/src/compiler.js +44 -23
- package/src/dts-parser.js +0 -4
- package/src/index.js +11 -20
- package/src/lexer.js +170 -12
- package/src/parser/expressions.js +389 -0
- package/src/parser/statements.js +355 -0
- package/src/parser/types.js +140 -0
- package/src/parser.js +57 -745
- package/src/resolver.js +1 -1
- package/src/typechecker/expressions.js +359 -0
- package/src/typechecker/statements.js +250 -0
- package/src/typechecker/types.js +226 -0
- package/src/typechecker.js +390 -647
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
## About
|
|
2
2
|
|
|
3
3
|
I love Go for the backend — simple, type-safe, no nonsense. I like JavaScript for the
|
|
4
4
|
frontend — runs everywhere, no setup. What I don't like is JavaScript's loose typing. And
|
|
@@ -7,7 +7,7 @@ I don't like TypeScript either. I know, controversial. It's fine, I just don't e
|
|
|
7
7
|
So I built GoFront. Go syntax, type safety, compiles to plain ES modules. Same language
|
|
8
8
|
front and back, no runtime, no framework, no tsconfig.json.
|
|
9
9
|
|
|
10
|
-
Is this useful? Probably
|
|
10
|
+
Is this useful? Probably not, but it was fun to build:D
|
|
11
11
|
|
|
12
12
|
```go
|
|
13
13
|
package main
|
|
@@ -46,40 +46,210 @@ highlighting, bracket matching, and indentation rules without any extra configur
|
|
|
46
46
|
npm install -g gofront
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Requires Node.js
|
|
49
|
+
Requires Node.js 20+.
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
53
|
-
##
|
|
53
|
+
## How it works
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
GoFront is a four-stage compiler written in pure Node.js (no dependencies). Every stage
|
|
56
|
+
operates on the same AST (abstract syntax tree), running in a single pass per stage:
|
|
57
57
|
|
|
58
58
|
```
|
|
59
|
-
|
|
59
|
+
source text (.go files)
|
|
60
|
+
→ Lexer tokenize + Go-style semicolon insertion
|
|
61
|
+
→ Parser recursive-descent → AST
|
|
62
|
+
→ Type Checker annotate AST with types + collect errors
|
|
63
|
+
→ Code Gen AST → JavaScript string
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 1. Lexer (`src/lexer.js`)
|
|
67
|
+
|
|
68
|
+
Tokenises the source into a stream of tokens. Implements Go's semicolon insertion rules:
|
|
69
|
+
a semicolon is automatically inserted after a line's final token if that token is an
|
|
70
|
+
identifier, literal, `)`, `]`, `}`, or certain keywords (`return`, `break`, `continue`,
|
|
71
|
+
`fallthrough`). This is why Go doesn't need explicit semicolons — and neither does
|
|
72
|
+
GoFront.
|
|
73
|
+
|
|
74
|
+
### 2. Parser (`src/parser.js`)
|
|
75
|
+
|
|
76
|
+
A hand-written recursive-descent parser. No parser generators, no grammar files — just
|
|
77
|
+
straightforward top-down parsing. Produces an AST where every node is a plain JS object
|
|
78
|
+
with a `kind` field (`"FuncDecl"`, `"IfStmt"`, `"BinaryExpr"`, etc.).
|
|
79
|
+
|
|
80
|
+
Operator precedence is handled via a Pratt-style expression parser with numeric
|
|
81
|
+
precedence levels.
|
|
82
|
+
|
|
83
|
+
### 3. Type Checker (`src/typechecker.js`)
|
|
84
|
+
|
|
85
|
+
Three-pass type checker operating on the AST:
|
|
86
|
+
|
|
87
|
+
1. **Pass 1 — collect types**: register all `type` declarations (structs, interfaces,
|
|
88
|
+
named types) so they can be referenced before definition.
|
|
89
|
+
2. **Pass 2 — collect functions & vars**: register function signatures and package-level
|
|
90
|
+
variables. Resolve embedded struct fields and promote methods.
|
|
91
|
+
3. **Pass 3 — check bodies**: walk every function body, infer expression types, verify
|
|
92
|
+
assignments and call arguments, and report errors with source location.
|
|
93
|
+
|
|
94
|
+
Types are plain JS objects (`{ kind: "basic", name: "int" }`, `{ kind: "slice",
|
|
95
|
+
elem: ... }`, etc.). The special `any` type acts as a recovery/escape hatch — any
|
|
96
|
+
operation on it is silently permitted, preventing cascading errors.
|
|
97
|
+
|
|
98
|
+
### 4. Code Generator (`src/codegen.js`)
|
|
99
|
+
|
|
100
|
+
Walks the typed AST and emits clean, readable JavaScript. No intermediate representation
|
|
101
|
+
— the codegen writes directly to an output buffer with indentation tracking.
|
|
102
|
+
|
|
103
|
+
Runtime helpers (`__len`, `__append`, `__s`, `__sprintf`) are tree-shaken: only emitted
|
|
104
|
+
when actually used. Optional inline source maps are supported via VLQ-encoded mappings.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Go → JavaScript mapping
|
|
109
|
+
|
|
110
|
+
Every Go construct compiles to a specific JavaScript pattern. The output is designed to be
|
|
111
|
+
readable and debuggable — no name mangling, no opaque wrappers.
|
|
112
|
+
|
|
113
|
+
### Data structures
|
|
114
|
+
|
|
115
|
+
| Go | JavaScript | Notes |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `struct` | ES6 `class` | Single destructured-object constructor: `new Point({ X: 1, Y: 2 })` |
|
|
118
|
+
| Methods | Class instance methods | Receiver is `this` |
|
|
119
|
+
| Embedded structs | Flattened fields + delegation stubs | `Greet(...a) { return Base.prototype.Greet.call(this, ...a); }` |
|
|
120
|
+
| `[]T` (slice) | `Array` | `append` → spread, `len` → `.length` |
|
|
121
|
+
| `map[K]V` | Plain object `{}` | Key access via `[]`, iteration via `Object.entries()` |
|
|
122
|
+
| `nil` | `null` | |
|
|
123
|
+
| `error` | `string` | `error("msg")` → `"msg"`, `.Error()` → the string itself |
|
|
124
|
+
| Pointers (`*T`) | `{ value: T }` | `new(T)` allocates a boxed zero value |
|
|
125
|
+
|
|
126
|
+
### Functions
|
|
127
|
+
|
|
128
|
+
| Go | JavaScript |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `func f(a int) int` | `function f(a)` |
|
|
131
|
+
| Multiple returns `return a, b` | `return [a, b]` — destructured at call site: `let [a, b] = f()` |
|
|
132
|
+
| Named returns | Variables pre-declared; bare `return` returns them |
|
|
133
|
+
| Variadic `func f(xs ...int)` | `function f(...xs)` |
|
|
134
|
+
| `func` literal (closure) | Arrow or function expression |
|
|
135
|
+
| `async func` / `await` | `async function` / `await` |
|
|
136
|
+
| `init()` | Emitted as `(function() { ... })()` immediately-invoked |
|
|
137
|
+
| `defer` | `try { ... } finally { deferred() }` |
|
|
138
|
+
|
|
139
|
+
### Control flow
|
|
140
|
+
|
|
141
|
+
| Go | JavaScript |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `for init; cond; post {}` | `for (init; cond; post) {}` |
|
|
144
|
+
| `for cond {}` | `while (cond) {}` |
|
|
145
|
+
| `for {}` | `while (true) {}` |
|
|
146
|
+
| `for i, v := range slice` | `for (const [i, v] of arr.entries())` |
|
|
147
|
+
| `for k, v := range map` | `for (const [k, v] of Object.entries(m))` |
|
|
148
|
+
| `for i, ch := range str` | `for (const [i, ch] of Array.from(s).entries())` |
|
|
149
|
+
| `for i := range n` | `for (let i = 0; i < n; i++)` |
|
|
150
|
+
| `switch` / `fallthrough` | `switch` / case fall-through |
|
|
151
|
+
| `switch v := x.(type)` | `if/else if` with `typeof` / `instanceof` checks |
|
|
152
|
+
| `panic(msg)` | `throw new Error(msg)` |
|
|
153
|
+
| `recover()` | Captured in `defer` via `try/catch` |
|
|
154
|
+
|
|
155
|
+
### Builtins
|
|
156
|
+
|
|
157
|
+
| Go | JavaScript |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `len(x)` | `__len(x)` (tree-shaken helper) or `Object.keys(x).length` for maps |
|
|
160
|
+
| `cap(x)` | `x.length` |
|
|
161
|
+
| `append(s, elems...)` | `[...s, ...elems]` |
|
|
162
|
+
| `copy(dst, src)` | Inline splice helper |
|
|
163
|
+
| `make([]T, n)` | `new Array(n).fill(zero)` |
|
|
164
|
+
| `make(map[K]V)` | `{}` |
|
|
165
|
+
| `delete(m, k)` | `delete m[k]` |
|
|
166
|
+
| `new(T)` | `{ value: zeroOf(T) }` |
|
|
167
|
+
| `min(a, b, ...)` | `Math.min(a, b, ...)` |
|
|
168
|
+
| `max(a, b, ...)` | `Math.max(a, b, ...)` |
|
|
169
|
+
| `clear(x)` | `.length = 0` (slice) or delete-loop (map) |
|
|
170
|
+
| `print` / `println` | `console.log` |
|
|
171
|
+
| `fmt.Sprintf` | `__sprintf` tree-shaken helper |
|
|
172
|
+
| `strings.*` | JS string methods (`.includes`, `.startsWith`, `.split`, etc.) |
|
|
173
|
+
| `strconv.*` | `String()`, `parseInt()`, `Number()` |
|
|
174
|
+
| `sort.Slice` | `Array.prototype.sort()` with comparator wrapping |
|
|
175
|
+
| `math.*` | `Math.*` methods and constants |
|
|
176
|
+
| `errors.New` | Identity (returns string) |
|
|
177
|
+
| `time.Now()` | `Date.now()` |
|
|
178
|
+
|
|
179
|
+
### Type system at runtime
|
|
180
|
+
|
|
181
|
+
All type checking happens at compile time. At runtime, types are erased — there are no
|
|
182
|
+
type tags, no reflection, no runtime overhead. Sized integers (`int8`–`int64`,
|
|
183
|
+
`uint8`–`uint64`) are all `number` at runtime. `float32` is `number`. The type checker
|
|
184
|
+
enforces correctness; JavaScript doesn't need to know.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Examples — Todo App
|
|
189
|
+
|
|
190
|
+
There are two example apps — both implement the same todo app to show different aspects
|
|
191
|
+
of GoFront.
|
|
192
|
+
|
|
193
|
+
### Simple (vanilla DOM)
|
|
194
|
+
|
|
195
|
+
The default example. Zero dependencies, clean 1:1 compiled JS output. Showcases GoFront's
|
|
196
|
+
core language features with straightforward DOM manipulation.
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
example/simple/
|
|
60
200
|
src/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
201
|
+
types.go ← Todo struct · FilterAll/Active/Completed iota · Priority iota
|
|
202
|
+
store.go ← state as plain variables · add/toggle/remove/clear · visibleTodos()
|
|
203
|
+
· stats() with named returns · defer/recover · async persistence
|
|
204
|
+
render.go ← render() updates DOM via innerHTML · renderTodo() · renderFilterBar()
|
|
205
|
+
styles.go ← injectStyles() creates <style> element with all CSS
|
|
206
|
+
main.go ← createApp() builds DOM shell · setupEvents() · event delegation
|
|
207
|
+
utils/
|
|
208
|
+
utils.go ← Plural() — cross-package import demo
|
|
209
|
+
browser.d.ts ← minimal external type declarations (sleep)
|
|
210
|
+
index.html ← bare HTML shell, loads app.js as ES module
|
|
211
|
+
app.js ← generated output
|
|
71
212
|
```
|
|
72
213
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
214
|
+
### Reactive (signals + d.ts imports)
|
|
215
|
+
|
|
216
|
+
Same app rebuilt with [reactive.js](https://github.com/seriva/microtastic), a tiny
|
|
217
|
+
signals-based reactive framework. Demonstrates how GoFront integrates with external JS
|
|
218
|
+
libraries via `.d.ts` type declarations — the GoFront code uses typed `Signal` values,
|
|
219
|
+
`Signals.create()`, `Signals.computed()`, `Signals.batch()`, and `Reactive.bind()`,
|
|
220
|
+
all described in a hand-written `browser.d.ts` shim.
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
example/reactive/
|
|
224
|
+
src/
|
|
225
|
+
types.go ← Todo, Stats, AppElements structs
|
|
226
|
+
store.go ← Signal-typed state · Signals.create/computed/batch
|
|
227
|
+
render.go ← createAppShell() returns AppElements · ctx.computed/ctx.bind
|
|
228
|
+
styles.go ← injectStyles()
|
|
229
|
+
main.go ← wires everything together
|
|
230
|
+
utils/
|
|
231
|
+
utils.go ← Plural()
|
|
232
|
+
browser.d.ts ← Signal interface · Signals/Reactive/ComponentContext namespaces
|
|
233
|
+
reactive.js ← signals framework (from microtastic)
|
|
234
|
+
index.html ← loads reactive.js + app.js
|
|
235
|
+
app.js ← generated output
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Features demonstrated
|
|
239
|
+
|
|
240
|
+
Both apps cover: structs & methods, iota constants, named return values, closures,
|
|
241
|
+
slices, `for range`, `switch`, cross-package imports, multi-file same-package compilation,
|
|
242
|
+
`async`/`await`, `defer`/`recover`, localStorage persistence, and DOM APIs.
|
|
76
243
|
|
|
77
|
-
|
|
244
|
+
The reactive example additionally demonstrates: external `.d.ts` type imports,
|
|
245
|
+
`declare namespace` patterns for typing JS libraries, and reactive UI updates via signals.
|
|
246
|
+
|
|
247
|
+
### Build and run
|
|
78
248
|
|
|
79
249
|
```sh
|
|
80
|
-
|
|
81
|
-
# →
|
|
82
|
-
# open
|
|
250
|
+
npm run build:simple # → example/simple/app.js
|
|
251
|
+
npm run build:reactive # → example/reactive/app.js
|
|
252
|
+
# open the respective index.html in a browser
|
|
83
253
|
```
|
|
84
254
|
|
|
85
255
|
---
|
|
@@ -105,7 +275,15 @@ gofront --help / -h print this help
|
|
|
105
275
|
|
|
106
276
|
## Multi-file packages
|
|
107
277
|
|
|
108
|
-
All `.go` files in a directory share the same namespace and are compiled as one unit
|
|
278
|
+
All `.go` files in a directory share the same namespace and are compiled as one unit.
|
|
279
|
+
The compiler (`src/compiler.js`) orchestrates this:
|
|
280
|
+
|
|
281
|
+
1. Parse each `.go` file in the directory into a separate AST.
|
|
282
|
+
2. Resolve imports: `js:` d.ts files, npm packages (via `node_modules/` and `@types/`),
|
|
283
|
+
and local GoFront sub-packages (`import "./subpkg"`) which are compiled recursively.
|
|
284
|
+
3. Run the type checker across all ASTs as a single unit — types, functions, and
|
|
285
|
+
variables declared in one file are visible in all other files of the same package.
|
|
286
|
+
4. Generate code for each AST and concatenate. Sub-package code is inlined as a preamble.
|
|
109
287
|
|
|
110
288
|
```
|
|
111
289
|
myapp/
|
|
@@ -115,7 +293,7 @@ myapp/
|
|
|
115
293
|
```
|
|
116
294
|
|
|
117
295
|
```sh
|
|
118
|
-
|
|
296
|
+
gofront myapp -o myapp/bundle.js
|
|
119
297
|
```
|
|
120
298
|
|
|
121
299
|
Cross-package imports are supported via relative paths:
|
|
@@ -132,7 +310,8 @@ func main() {
|
|
|
132
310
|
|
|
133
311
|
## Type checking
|
|
134
312
|
|
|
135
|
-
GoFront performs static type checking before emitting any code, accurately tracking source
|
|
313
|
+
GoFront performs static type checking before emitting any code, accurately tracking source
|
|
314
|
+
locations in error messages across multiple files:
|
|
136
315
|
|
|
137
316
|
```go
|
|
138
317
|
func greet(name string) {
|
|
@@ -149,6 +328,10 @@ import "js:./dom.d.ts"
|
|
|
149
328
|
```
|
|
150
329
|
|
|
151
330
|
npm package types are resolved automatically from `node_modules/` and `@types/`.
|
|
331
|
+
The resolver (`src/resolver.js`) walks up the directory tree to find `node_modules`,
|
|
332
|
+
checks `package.json` `"types"` / `"typings"` fields, falls back to `index.d.ts`,
|
|
333
|
+
then tries `@types/`. The `.d.ts` parser (`src/dts-parser.js`) extracts type
|
|
334
|
+
signatures into GoFront's internal type representation.
|
|
152
335
|
|
|
153
336
|
---
|
|
154
337
|
|
|
@@ -158,38 +341,176 @@ npm package types are resolved automatically from `node_modules/` and `@types/`.
|
|
|
158
341
|
|---|---|
|
|
159
342
|
| Variables (`var`, `:=` short re-declaration) | ✓ |
|
|
160
343
|
| Constants (`const`, `iota`) | ✓ |
|
|
344
|
+
| `if` / `else if` / `else` | ✓ |
|
|
345
|
+
| `if` with init statement (`if err := f(); err != nil`) | ✓ |
|
|
346
|
+
| Type definitions (`type MyType ...`) | ✓ |
|
|
161
347
|
| Functions, multiple returns | ✓ |
|
|
162
348
|
| Named return values | ✓ |
|
|
163
349
|
| Variadic parameters | ✓ |
|
|
164
350
|
| `init()` functions | ✓ |
|
|
165
351
|
| Structs + methods | ✓ |
|
|
352
|
+
| Anonymous struct types (`struct { Name string }`) | ✓ — compile to plain JS objects (no class emitted) |
|
|
166
353
|
| Embedded structs | ✓ |
|
|
167
354
|
| Pointer receivers | ✓ |
|
|
168
355
|
| Interfaces | ✓ |
|
|
356
|
+
| Interface embedding | ✓ |
|
|
169
357
|
| Closures | ✓ |
|
|
170
358
|
| Slices (`append`, `len`, `make`) | ✓ |
|
|
171
359
|
| Maps (`make`, `delete`, comma-ok) | ✓ — note: iteration order is insertion-order (JS), not randomised (Go) |
|
|
172
360
|
| `for`, `for range`, `for {}` | ✓ |
|
|
173
361
|
| `range` over string | ✓ |
|
|
174
362
|
| `switch` / `fallthrough` | ✓ |
|
|
363
|
+
| `switch` with init statement (`switch x := f(); x {}`) | ✓ |
|
|
175
364
|
| `new(T)` | ✓ |
|
|
176
365
|
| Type conversions | ✓ |
|
|
177
366
|
| Type assertions (`x.(T)`) | ✓ |
|
|
178
|
-
|
|
|
179
|
-
|
|
|
367
|
+
| Type switch (`switch x.(type)`, `switch v := x.(type)`) | ✓ — compiles to `if/else if` with `typeof` / `instanceof` checks |
|
|
368
|
+
| Slice expressions (`s[lo:hi]`, `s[lo:hi:max]`) | ✓ — three-index form is parsed; `max` is ignored at runtime (JS has no capacity) |
|
|
369
|
+
| `break` / `continue` / labeled `break` / labeled `continue` | ✓ |
|
|
180
370
|
| `cap()`, `copy()`, `panic()` | ✓ |
|
|
181
|
-
|
|
|
182
|
-
|
|
|
371
|
+
| Variadic spread (`f(slice...)`, `append(a, b...)`) | ✓ |
|
|
372
|
+
| Bitwise operators (`&`, `\|`, `^`, `&^`, `<<`, `>>`) | ✓ — `&^` (bit clear) compiles to `& ~` |
|
|
373
|
+
| Compound assignment (`+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `\|=`, `^=`, `<<=`, `>>=`) | ✓ |
|
|
183
374
|
| Increment / decrement (`i++`, `i--`) | ✓ |
|
|
184
375
|
| Raw string literals (backticks) | ✓ |
|
|
376
|
+
| Rune / char literals (`'a'`, `'\n'`) | ✓ — compiled to integer char codes |
|
|
185
377
|
| `defer` | ✓ |
|
|
378
|
+
| `panic()` / `recover()` | ✓ |
|
|
379
|
+
| `print` / `println` builtins | ✓ — compile to `console.log` |
|
|
380
|
+
| Pointer operators (`&`, `*`) | ✓ |
|
|
186
381
|
| `error` type / `error("msg")` / `.Error()` | ✓ |
|
|
187
382
|
| `async func` / `await` expressions | ✓ |
|
|
188
|
-
| `fmt.Sprintf` / `fmt.Printf` / `fmt.Println` / `fmt.Errorf` | ✓ |
|
|
383
|
+
| `fmt.Sprintf` / `fmt.Printf` / `fmt.Println` / `fmt.Print` / `fmt.Errorf` | ✓ — expanded: `%t`, `%x`, `%X`, `%o`, `%b`, `%q`, `%e`, `%g`, width/precision |
|
|
384
|
+
| `strings` package | ✓ — 20 functions: `Contains`, `HasPrefix`, `Split`, `Join`, `ToUpper`, `ToLower`, `TrimSpace`, etc. |
|
|
385
|
+
| `strconv` package | ✓ — `Itoa`, `Atoi`, `FormatBool`, `FormatInt`, `ParseFloat`, `ParseInt`, `ParseBool` |
|
|
386
|
+
| `sort` package | ✓ — `Ints`, `Float64s`, `Strings`, `Slice`, `SliceStable`, `SliceIsSorted` |
|
|
387
|
+
| `math` package | ✓ — `Sqrt`, `Pow`, `Abs`, `Floor`, `Ceil`, `Round`, `Log`, trig functions + `Pi`, `E`, `MaxInt` constants |
|
|
388
|
+
| `errors` package | ✓ — `errors.New` (returns plain string) |
|
|
389
|
+
| `time` package (partial) | ✓ — `Now`, `Since`, `Sleep` + `Millisecond`, `Second`, `Minute`, `Hour` constants |
|
|
189
390
|
| External `.d.ts` types | ✓ |
|
|
190
391
|
| npm package types | ✓ |
|
|
191
392
|
| Multi-file packages | ✓ |
|
|
192
393
|
| Cross-package imports | ✓ |
|
|
394
|
+
| Import aliases (`import m "./pkg"`) | ✓ |
|
|
395
|
+
| Sized integer types (`uint`, `int8`–`int64`, `uint8`–`uint64`, `float32`) | ✓ — mapped to `int` / `float64` at runtime |
|
|
396
|
+
| Struct field tags (`` `json:"name"` ``) | ✓ — parsed and ignored (no reflection) |
|
|
397
|
+
| `[]byte(s)` conversion | ✓ — produces UTF-8 byte values via `TextEncoder` |
|
|
398
|
+
| `[]rune(s)` conversion | ✓ — produces Unicode code points via `Array.from` |
|
|
399
|
+
| `[...]T{...}` array length inference | ✓ |
|
|
400
|
+
| Side-effect imports (`import _ "pkg"`) | ✓ — dependency code is bundled; package namespace is not exposed |
|
|
401
|
+
| Dot imports (`import . "pkg"`) | ✓ — merges package exports into the current scope (no qualifier needed) |
|
|
402
|
+
| Unused local import detection | ✓ — unused cross-package imports are type errors (matching Go); `import _` exempt |
|
|
403
|
+
| `min()` / `max()` builtins | ✓ — compiles to `Math.min` / `Math.max` |
|
|
404
|
+
| `clear()` builtin | ✓ — zeroes slice length or deletes all map keys |
|
|
405
|
+
| `range` over integer (`for i := range n`) | ✓ — Go 1.22; also supports `for range n` |
|
|
406
|
+
| Type aliases (`type A = B`) | ✓ — transparent alias; no conversion needed between alias and original |
|
|
407
|
+
| Numeric literal separators (`1_000_000`) | ✓ — underscores stripped at lex time |
|
|
408
|
+
| Binary / octal literals (`0b1010`, `0o777`) | ✓ — passed through to JS which supports them natively |
|
|
409
|
+
| Hex float literals (`0x1.8p1`) | ✓ — evaluated at lex time to a decimal value |
|
|
410
|
+
| Untyped constants | ✓ — `const x = 5` produces an untyped int that coerces to `int` or `float64` on use (Go spec §Constants) |
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Go Compatibility
|
|
415
|
+
|
|
416
|
+
GoFront implements a practical subset of the
|
|
417
|
+
[Go Language Specification](https://go.dev/ref/spec) (go1.26). It is not aiming for
|
|
418
|
+
byte-level parity — it is a Go-inspired language for the JavaScript platform. This
|
|
419
|
+
section explains exactly where GoFront matches Go, where it intentionally diverges, and
|
|
420
|
+
where JS fundamentally prevents matching Go semantics.
|
|
421
|
+
|
|
422
|
+
### What matches Go
|
|
423
|
+
|
|
424
|
+
The core language surface is well covered. All of these compile and behave as a Go
|
|
425
|
+
developer would expect:
|
|
426
|
+
|
|
427
|
+
- **Declarations**: `var`, `:=` (with re-declaration), `const` (with `iota`), `type`,
|
|
428
|
+
`func`, methods, `init()`.
|
|
429
|
+
- **Control flow**: `if`/`else` (with init statement), `for` (classic, condition-only,
|
|
430
|
+
infinite, `range`), `switch`/`case`/`default`/`fallthrough`, type switches, labeled
|
|
431
|
+
`break`/`continue`, `defer`, `panic`/`recover`.
|
|
432
|
+
- **Types**: `int`, `float64`, `string`, `bool`, sized integers (`int8`–`int64`,
|
|
433
|
+
`uint8`–`uint64`), `float32`, `byte`, `rune`, `error`, slices, maps, arrays, structs,
|
|
434
|
+
interfaces (with embedding), pointers (`*T`, `&x`), type aliases, function types.
|
|
435
|
+
- **Expressions**: all arithmetic, comparison, logical, and bitwise operators (including
|
|
436
|
+
`&^`), compound assignment, increment/decrement, type conversions, type assertions
|
|
437
|
+
(plain and comma-ok), slice expressions (`a[lo:hi]`, `a[lo:hi:max]`), composite
|
|
438
|
+
literals, `[...]T{...}` inference, rune literals, raw string literals, variadic
|
|
439
|
+
spread, blank identifier `_`.
|
|
440
|
+
- **Builtins**: `len`, `cap`, `append`, `copy`, `make`, `delete`, `new`, `clear`, `min`,
|
|
441
|
+
`max`, `print`, `println`, `panic`, `recover`, `fmt.Sprintf`/`Printf`/`Println`/`Errorf`.
|
|
442
|
+
- **Standard library shims**: `strings` (20 functions), `strconv` (8 functions),
|
|
443
|
+
`sort` (6 functions), `math` (20 functions + 6 constants), `errors.New`,
|
|
444
|
+
`time.Now`/`Since`/`Sleep` + duration constants.
|
|
445
|
+
- **Packages**: multi-file packages, cross-package imports, import aliases,
|
|
446
|
+
dot imports, blank imports, unused import detection.
|
|
447
|
+
|
|
448
|
+
### GoFront extensions (not in Go)
|
|
449
|
+
|
|
450
|
+
These features are intentional additions for the JavaScript platform:
|
|
451
|
+
|
|
452
|
+
| Feature | Purpose |
|
|
453
|
+
|---|---|
|
|
454
|
+
| `async func` / `await` | First-class async syntax for frontend work. |
|
|
455
|
+
| Browser globals (`document`, `console`, etc.) | Predeclared as `any` for practical DOM access. |
|
|
456
|
+
| `.d.ts` type imports (`import "js:./types.d.ts"`) | Type-safe interop with JavaScript libraries. |
|
|
457
|
+
| npm package resolution | Import types from `node_modules/` and `@types/` automatically. |
|
|
458
|
+
|
|
459
|
+
### What's not implemented
|
|
460
|
+
|
|
461
|
+
| Feature | Reason | Prospect |
|
|
462
|
+
|---|---|---|
|
|
463
|
+
| Generics (`[T any]`) | Touches every compiler stage. Large effort, limited runtime payoff on a JS target. | Feasible — highest-priority future feature. |
|
|
464
|
+
| Range over iterator functions (Go 1.23) | `func(yield func(K, V) bool)` protocol. | Feasible. |
|
|
465
|
+
| Complex numbers (`complex64`, `complex128`, `3i`) | Would need a runtime complex type. Rarely used in frontend. | Feasible but low priority. |
|
|
466
|
+
| `goto` | No clean JS translation. Rare in idiomatic Go. | Not planned. |
|
|
467
|
+
| Method expressions (`T.Method`) | Requires function-wrapping with explicit receiver. | Feasible. |
|
|
468
|
+
| Goroutines / channels / `select` | Go's concurrency model has no JS equivalent. A userland scheduler defeats the "no runtime" goal. | Out of scope. |
|
|
469
|
+
| `unsafe`, `reflect`, `cgo` | Require memory model or runtime type metadata that JS cannot provide. | Out of scope. |
|
|
470
|
+
|
|
471
|
+
### Semantic differences
|
|
472
|
+
|
|
473
|
+
These features are implemented but behave differently due to fundamental JS runtime
|
|
474
|
+
constraints. These are **not bugs** — they are deliberate trade-offs documented here so
|
|
475
|
+
you know exactly what to expect.
|
|
476
|
+
|
|
477
|
+
| Feature | GoFront | Go | Why |
|
|
478
|
+
|---|---|---|---|
|
|
479
|
+
| Map iteration order | Insertion-order (`Object.entries`) | Randomised | JS objects preserve insertion order. |
|
|
480
|
+
| Integer overflow | IEEE 754 float64 semantics | Wraps at type boundary (e.g. `int32`) | All JS numbers are float64. |
|
|
481
|
+
| Integer precision | Safe up to 2⁵³ | Full width per type (`int64` = 64 bits) | JS `number` limitation. |
|
|
482
|
+
| `cap()` | Always equals `len()` | May exceed `len()` | JS arrays have no separate capacity. |
|
|
483
|
+
| Fixed-size arrays (`[n]T`) | Plain JS arrays (no length enforcement) | Fixed at compile time | Runtime enforcement adds overhead for no JS benefit. |
|
|
484
|
+
| `nil` | Maps to `null` | Typed nil (distinct per type) | JS has no typed nil concept. |
|
|
485
|
+
| `rune` / `byte` | Treated as `int` | Distinct types with UTF-8 encoding | JS strings are UTF-16. |
|
|
486
|
+
| `range` over string | JS characters (UTF-16 via `Array.from`) | Runes (UTF-8 code points) | UTF-16 vs UTF-8 mismatch. |
|
|
487
|
+
| `len()` on strings | JS `.length` (UTF-16 code units) | Byte count (UTF-8) | Matching Go would require `TextEncoder` on every call. |
|
|
488
|
+
| `error` type | Plain string | Interface `{ Error() string }` | Practical simplification for JS. |
|
|
489
|
+
| `defer` | `try`/`finally` with a defer stack | Runtime stack unwinding | Covers most cases but not identical to Go internals. |
|
|
490
|
+
| Struct field tags | Parsed, silently discarded | Available via `reflect` | No reflection = no use for tag values. |
|
|
491
|
+
| Pointers (`&x`, `*p`) | Syntax accepted, semantically transparent | True memory indirection | JS has no pointer model. |
|
|
492
|
+
| Three-index slice (`a[lo:hi:max]`) | `max` is parsed but ignored | Sets result capacity | JS arrays have no capacity. |
|
|
493
|
+
| Exported / unexported | Uppercase names exported across packages | Access enforced per-identifier | Cross-package access to lowercase names is not yet an error. |
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Roadmap
|
|
498
|
+
|
|
499
|
+
These are the open items roughly in priority order. GoFront's guiding principle is
|
|
500
|
+
**no runtime** — features that need a scheduler, runtime type descriptors, or raw memory
|
|
501
|
+
access are out of scope.
|
|
502
|
+
|
|
503
|
+
| Feature | Difficulty | Notes |
|
|
504
|
+
|---|---|---|
|
|
505
|
+
| Generics (`[T any]`) | High | Biggest modern Go feature gap. Touches every compiler stage but has low runtime pressure. |
|
|
506
|
+
| Range over iterator functions | Medium | Go 1.23 `func(yield func(K, V) bool)` protocol. Low runtime pressure. |
|
|
507
|
+
| Complex number types | Medium | New type kind + builtins (`complex`, `real`, `imag`). Shimmed with a two-field object. |
|
|
508
|
+
| Richer error values | Medium | Move `error` from plain string to an interface-like value. |
|
|
509
|
+
| Better array semantics | Medium | Arrays are currently indistinguishable from slices at runtime. |
|
|
510
|
+
| Better pointer model | High | Current `{ value: T }` boxing is useful but shallow. |
|
|
511
|
+
|
|
512
|
+
Explicitly **out of scope**: goroutines, channels, `select`, `unsafe`, full `reflect`,
|
|
513
|
+
`cgo`, exact integer overflow, Go-equivalent map semantics.
|
|
193
514
|
|
|
194
515
|
---
|
|
195
516
|
|
|
@@ -199,4 +520,7 @@ npm package types are resolved automatically from `node_modules/` and `@types/`.
|
|
|
199
520
|
npm test
|
|
200
521
|
```
|
|
201
522
|
|
|
202
|
-
|
|
523
|
+
611 tests covering language features, type errors, edge cases, DOM (jsdom), external
|
|
524
|
+
`.d.ts`, npm resolver, multi-file compilation, embedded structs, string formatting, map
|
|
525
|
+
iteration order, integer overflow semantics, unused variable detection, unused import
|
|
526
|
+
detection, semantic difference verification, stdlib shim packages, and both example apps.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gofront",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"author": "Luuk van Venrooij",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,14 +21,18 @@
|
|
|
21
21
|
"type-safe"
|
|
22
22
|
],
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
24
|
+
"node": ">=20"
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
|
+
"files": [
|
|
28
|
+
"src/"
|
|
29
|
+
],
|
|
27
30
|
"bin": {
|
|
28
31
|
"gofront": "./src/index.js"
|
|
29
32
|
},
|
|
30
33
|
"scripts": {
|
|
31
|
-
"build": "node src/index.js",
|
|
34
|
+
"build:simple": "node src/index.js example/simple/src -o example/simple/app.js",
|
|
35
|
+
"build:reactive": "node src/index.js example/reactive/src -o example/reactive/app.js",
|
|
32
36
|
"test": "node test/run.js",
|
|
33
37
|
"format": "biome format --write .",
|
|
34
38
|
"check": "biome check ."
|