gofront 1.5.1 → 1.6.1
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 +174 -466
- package/package.json +16 -20
- package/src/backend/js/expressions.js +288 -128
- package/src/backend/js/index.js +138 -56
- package/src/backend/js/runtime.js +1055 -53
- package/src/backend/js/source-map.js +32 -0
- package/src/backend/js/statements.js +151 -83
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +22 -3
- package/src/backend/js/stdlib/fmt.js +95 -20
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +19 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/maps.js +1 -1
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +20 -6
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/backend/js/stdlib/slices.js +53 -27
- package/src/backend/js/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +52 -11
- package/src/backend/js/stdlib/strings.js +152 -16
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/testing.js +2 -2
- package/src/backend/js/stdlib/time.js +93 -22
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/js/templ.js +1 -1
- package/src/backend/wasm/boundary.js +1677 -58
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +1230 -0
- package/src/backend/wasm/emit-exprs.js +2096 -0
- package/src/backend/wasm/emit-maps.js +1999 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +13 -0
- package/src/backend/wasm/emit-stmts.js +2466 -0
- package/src/backend/wasm/emit.js +656 -5031
- package/src/backend/wasm/encode.js +259 -85
- package/src/backend/wasm/glue.js +19 -5
- package/src/backend/wasm/index.js +2432 -86
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +236 -0
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +40 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +312 -36
- package/src/backend/wasm/wat.js +49 -2
- package/src/cli-core.js +276 -73
- package/src/compiler.js +162 -25
- package/src/dts-parser.js +79 -126
- package/src/index.js +88 -121
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +4 -6
- package/src/lower/captures.js +12 -23
- package/src/lower/escape.js +4 -9
- package/src/lower/functions.js +277 -5
- package/src/lower/globals.js +7 -15
- package/src/lower/index.js +5 -2
- package/src/lower/ownership.js +35 -19
- package/src/lower/range.js +2 -5
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/types.js +1 -2
- package/src/resolver.js +3 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/test-runner.js +20 -0
- package/src/typechecker/assignability.js +123 -14
- package/src/typechecker/expressions.js +244 -4
- package/src/typechecker/index.js +27 -1
- package/src/typechecker/resolve.js +71 -12
- package/src/typechecker/statements.js +0 -7
- package/src/typechecker/stdlib/core.js +292 -103
- package/src/typechecker/stdlib/extended.js +471 -4
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +545 -8
- package/src/vendor.js +2 -2
|
@@ -261,8 +261,7 @@ export const declarationParseMethods = {
|
|
|
261
261
|
},
|
|
262
262
|
|
|
263
263
|
parseVarSpec() {
|
|
264
|
-
const names =
|
|
265
|
-
while (this.match(T.COMMA)) names.push(this.expect(T.IDENT).value);
|
|
264
|
+
const names = this.parseIdentList();
|
|
266
265
|
let type = null;
|
|
267
266
|
let value = null;
|
|
268
267
|
if (
|
|
@@ -284,8 +283,7 @@ export const declarationParseMethods = {
|
|
|
284
283
|
let prevRawExprs = null; // raw AST (with iota Idents) from the previous spec
|
|
285
284
|
let prevType = null;
|
|
286
285
|
while (!this.check(T.RPAREN) && !this.check(T.EOF)) {
|
|
287
|
-
const names =
|
|
288
|
-
while (this.match(T.COMMA)) names.push(this.expect(T.IDENT).value);
|
|
286
|
+
const names = this.parseIdentList();
|
|
289
287
|
let type = null;
|
|
290
288
|
let rawExprs; // before iota substitution
|
|
291
289
|
if (this.check(T.ASSIGN)) {
|
|
@@ -315,8 +313,7 @@ export const declarationParseMethods = {
|
|
|
315
313
|
}
|
|
316
314
|
this.expect(T.RPAREN);
|
|
317
315
|
} else {
|
|
318
|
-
const names =
|
|
319
|
-
while (this.match(T.COMMA)) names.push(this.expect(T.IDENT).value);
|
|
316
|
+
const names = this.parseIdentList();
|
|
320
317
|
let type = null;
|
|
321
318
|
if (!this.check(T.ASSIGN)) type = this.parseType();
|
|
322
319
|
this.expect(T.ASSIGN);
|
|
@@ -25,6 +25,8 @@ const PREC = {
|
|
|
25
25
|
[T.AND_NOT]: 5,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
const UNARY_OPS = new Set([T.NOT, T.MINUS, T.PLUS, T.CARET, T.AMP, T.STAR]);
|
|
29
|
+
|
|
28
30
|
const TYPE_KEYWORDS = new Set([
|
|
29
31
|
"int",
|
|
30
32
|
"int8",
|
|
@@ -128,14 +130,7 @@ export const expressionParserMethods = {
|
|
|
128
130
|
|
|
129
131
|
parseUnary(noCompLit = false) {
|
|
130
132
|
const t = this.peek();
|
|
131
|
-
if (
|
|
132
|
-
t.type === T.NOT ||
|
|
133
|
-
t.type === T.MINUS ||
|
|
134
|
-
t.type === T.PLUS ||
|
|
135
|
-
t.type === T.CARET ||
|
|
136
|
-
t.type === T.AMP ||
|
|
137
|
-
t.type === T.STAR
|
|
138
|
-
) {
|
|
133
|
+
if (UNARY_OPS.has(t.type)) {
|
|
139
134
|
const op = this.advance().value;
|
|
140
135
|
return { kind: "UnaryExpr", op, operand: this.parseUnary(noCompLit) };
|
|
141
136
|
}
|
package/src/parser/index.js
CHANGED
|
@@ -73,9 +73,18 @@ export class Parser {
|
|
|
73
73
|
return this.peek2().type === type;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
match(...
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
match(t1, t2, t3, ...rest) {
|
|
77
|
+
const cur = this.peek().type;
|
|
78
|
+
if (
|
|
79
|
+
cur === t1 ||
|
|
80
|
+
(t2 !== undefined && cur === t2) ||
|
|
81
|
+
(t3 !== undefined && cur === t3)
|
|
82
|
+
) {
|
|
83
|
+
this.advance();
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
for (let i = 0; i < rest.length; i++) {
|
|
87
|
+
if (cur === rest[i]) {
|
|
79
88
|
this.advance();
|
|
80
89
|
return true;
|
|
81
90
|
}
|
|
@@ -83,6 +92,12 @@ export class Parser {
|
|
|
83
92
|
return false;
|
|
84
93
|
}
|
|
85
94
|
|
|
95
|
+
parseIdentList() {
|
|
96
|
+
const names = [this.expect(T.IDENT).value];
|
|
97
|
+
while (this.match(T.COMMA)) names.push(this.expect(T.IDENT).value);
|
|
98
|
+
return names;
|
|
99
|
+
}
|
|
100
|
+
|
|
86
101
|
expect(type) {
|
|
87
102
|
if (!this.check(type))
|
|
88
103
|
throw new ParseError(`expected '${type}'`, this.peek());
|
package/src/parser/statements.js
CHANGED
|
@@ -15,6 +15,20 @@ const PARSE_STMT_DELEGATE = {
|
|
|
15
15
|
[T.LBRACE]: "parseBlock",
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
const ASSIGN_OPS = new Set([
|
|
19
|
+
T.ASSIGN,
|
|
20
|
+
T.PLUS_ASSIGN,
|
|
21
|
+
T.MINUS_ASSIGN,
|
|
22
|
+
T.STAR_ASSIGN,
|
|
23
|
+
T.SLASH_ASSIGN,
|
|
24
|
+
T.PERCENT_ASSIGN,
|
|
25
|
+
T.AMP_ASSIGN,
|
|
26
|
+
T.PIPE_ASSIGN,
|
|
27
|
+
T.CARET_ASSIGN,
|
|
28
|
+
T.LSHIFT_ASSIGN,
|
|
29
|
+
T.RSHIFT_ASSIGN,
|
|
30
|
+
]);
|
|
31
|
+
|
|
18
32
|
export const statementParserMethods = {
|
|
19
33
|
parseBlock() {
|
|
20
34
|
this.expect(T.LBRACE);
|
|
@@ -206,22 +220,13 @@ export const statementParserMethods = {
|
|
|
206
220
|
this.expect(T.LBRACE);
|
|
207
221
|
const cases = [];
|
|
208
222
|
while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const list = this.parseExprList();
|
|
212
|
-
this.expect(T.COLON);
|
|
213
|
-
cases.push({ kind: "CaseClause", list, stmts: this._parseCaseBody() });
|
|
214
|
-
} else if (this.check(T.DEFAULT)) {
|
|
215
|
-
this.advance();
|
|
216
|
-
this.expect(T.COLON);
|
|
217
|
-
cases.push({
|
|
218
|
-
kind: "CaseClause",
|
|
219
|
-
list: null,
|
|
220
|
-
stmts: this._parseCaseBody(),
|
|
221
|
-
});
|
|
222
|
-
} else {
|
|
223
|
+
const isCase = this.match(T.CASE);
|
|
224
|
+
if (!isCase && !this.match(T.DEFAULT)) {
|
|
223
225
|
this.err("Expected case or default");
|
|
224
226
|
}
|
|
227
|
+
const list = isCase ? this.parseExprList() : null;
|
|
228
|
+
this.expect(T.COLON);
|
|
229
|
+
cases.push({ kind: "CaseClause", list, stmts: this._parseCaseBody() });
|
|
225
230
|
}
|
|
226
231
|
this.expect(T.RBRACE);
|
|
227
232
|
return { kind: "SwitchStmt", init, tag, cases };
|
|
@@ -244,18 +249,13 @@ export const statementParserMethods = {
|
|
|
244
249
|
this.expect(T.LBRACE);
|
|
245
250
|
const cases = [];
|
|
246
251
|
while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const types = this._parseTypeSwitchTypeList();
|
|
250
|
-
this.expect(T.COLON);
|
|
251
|
-
cases.push({ types, stmts: this._parseCaseBody() });
|
|
252
|
-
} else if (this.check(T.DEFAULT)) {
|
|
253
|
-
this.advance();
|
|
254
|
-
this.expect(T.COLON);
|
|
255
|
-
cases.push({ types: null, stmts: this._parseCaseBody() });
|
|
256
|
-
} else {
|
|
252
|
+
const isCase = this.match(T.CASE);
|
|
253
|
+
if (!isCase && !this.match(T.DEFAULT)) {
|
|
257
254
|
this.err("Expected case or default");
|
|
258
255
|
}
|
|
256
|
+
const types = isCase ? this._parseTypeSwitchTypeList() : null;
|
|
257
|
+
this.expect(T.COLON);
|
|
258
|
+
cases.push({ types, stmts: this._parseCaseBody() });
|
|
259
259
|
}
|
|
260
260
|
this.expect(T.RBRACE);
|
|
261
261
|
return { kind: "TypeSwitchStmt", assign, expr, cases };
|
|
@@ -305,20 +305,7 @@ export const statementParserMethods = {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
// x = expr or x += expr etc.
|
|
308
|
-
|
|
309
|
-
T.ASSIGN,
|
|
310
|
-
T.PLUS_ASSIGN,
|
|
311
|
-
T.MINUS_ASSIGN,
|
|
312
|
-
T.STAR_ASSIGN,
|
|
313
|
-
T.SLASH_ASSIGN,
|
|
314
|
-
T.PERCENT_ASSIGN,
|
|
315
|
-
T.AMP_ASSIGN,
|
|
316
|
-
T.PIPE_ASSIGN,
|
|
317
|
-
T.CARET_ASSIGN,
|
|
318
|
-
T.LSHIFT_ASSIGN,
|
|
319
|
-
T.RSHIFT_ASSIGN,
|
|
320
|
-
];
|
|
321
|
-
if (assignOps.includes(t.type)) {
|
|
308
|
+
if (ASSIGN_OPS.has(t.type)) {
|
|
322
309
|
const op = this.advance().value;
|
|
323
310
|
const rhs = this.parseExprList();
|
|
324
311
|
return { kind: "AssignStmt", lhs: [first], op, rhs };
|
package/src/parser/types.js
CHANGED
|
@@ -175,8 +175,7 @@ export const typeParserMethods = {
|
|
|
175
175
|
this.expect(T.LBRACE);
|
|
176
176
|
const fields = [];
|
|
177
177
|
while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
|
|
178
|
-
const names =
|
|
179
|
-
while (this.match(T.COMMA)) names.push(this.expect(T.IDENT).value);
|
|
178
|
+
const names = this.parseIdentList();
|
|
180
179
|
// Check if this is an embedded field (just a type name)
|
|
181
180
|
// e.g., type T struct { Base }
|
|
182
181
|
if (this.check(T.SEMICOLON) || this.check(T.RBRACE)) {
|
package/src/resolver.js
CHANGED
|
@@ -25,31 +25,7 @@ function findNodeModules(startDir) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
const BUILTIN_PACKAGES = new Set([
|
|
30
|
-
"fmt",
|
|
31
|
-
"strings",
|
|
32
|
-
"bytes",
|
|
33
|
-
"strconv",
|
|
34
|
-
"sort",
|
|
35
|
-
"math",
|
|
36
|
-
"math/bits",
|
|
37
|
-
"math/rand",
|
|
38
|
-
"errors",
|
|
39
|
-
"time",
|
|
40
|
-
"unicode",
|
|
41
|
-
"unicode/utf8",
|
|
42
|
-
"os",
|
|
43
|
-
"slices",
|
|
44
|
-
"html",
|
|
45
|
-
"io",
|
|
46
|
-
"gom",
|
|
47
|
-
"maps",
|
|
48
|
-
"regexp",
|
|
49
|
-
"path",
|
|
50
|
-
"path/filepath",
|
|
51
|
-
"testing",
|
|
52
|
-
]);
|
|
28
|
+
import { STDLIB_PACKAGES } from "./typechecker/types.js";
|
|
53
29
|
|
|
54
30
|
// Returns true for local relative paths (start with ./ or ../)
|
|
55
31
|
export function isLocalPath(importPath) {
|
|
@@ -57,7 +33,7 @@ export function isLocalPath(importPath) {
|
|
|
57
33
|
}
|
|
58
34
|
|
|
59
35
|
export function isBuiltinPackage(importPath) {
|
|
60
|
-
return
|
|
36
|
+
return STDLIB_PACKAGES.has(importPath);
|
|
61
37
|
}
|
|
62
38
|
|
|
63
39
|
// Resolve a local relative import path to an absolute directory of .go files.
|
|
@@ -72,7 +48,7 @@ export function resolveGwDir(importPath, fromFile) {
|
|
|
72
48
|
return gwFiles.length > 0 ? dir : null;
|
|
73
49
|
}
|
|
74
50
|
|
|
75
|
-
|
|
51
|
+
function resolveImport(importPath, fromFile) {
|
|
76
52
|
const fromDir = dirname(resolve(fromFile));
|
|
77
53
|
const nodeModules = findNodeModules(fromDir);
|
|
78
54
|
|
package/src/templ-lexer.js
CHANGED
|
@@ -118,17 +118,15 @@ export class TemplLexer {
|
|
|
118
118
|
continue;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
// Detect `templ` keyword at identifier boundaries
|
|
121
|
+
// Detect `templ` or `css` keyword at identifier boundaries
|
|
122
122
|
if (this._matchKeyword("templ")) {
|
|
123
123
|
this.tokens.push(new Token(TT.TEMPL_KW, "templ", this.line, this.col));
|
|
124
|
-
this.
|
|
124
|
+
this._lexBlockDecl("templ");
|
|
125
125
|
continue;
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
// Detect `css` keyword at identifier boundaries
|
|
129
127
|
if (this._matchKeyword("css")) {
|
|
130
128
|
this.tokens.push(new Token(TT.CSS_KW, "css", this.line, this.col));
|
|
131
|
-
this.
|
|
129
|
+
this._lexBlockDecl("css");
|
|
132
130
|
continue;
|
|
133
131
|
}
|
|
134
132
|
|
|
@@ -201,24 +199,25 @@ export class TemplLexer {
|
|
|
201
199
|
for (const t of this._goTokens(chunk)) this.tokens.push(t);
|
|
202
200
|
}
|
|
203
201
|
|
|
204
|
-
// ── Templ declaration lexing
|
|
202
|
+
// ── Templ / CSS declaration lexing ───────────────────────────
|
|
205
203
|
|
|
206
|
-
// Called right after consuming `templ` keyword.
|
|
207
|
-
// Lexes: Name(params) { <
|
|
208
|
-
|
|
204
|
+
// Called right after consuming `templ` or `css` keyword.
|
|
205
|
+
// Lexes: Name(params) { <body> }
|
|
206
|
+
_lexBlockDecl(kw) {
|
|
209
207
|
this.skipWhitespace();
|
|
210
208
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
209
|
+
const name = this._readWord();
|
|
210
|
+
if (!name) {
|
|
211
|
+
this.err(
|
|
212
|
+
`expected ${kw === "templ" ? "component name" : "name"} after '${kw}'`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
216
215
|
this.tokens.push(new Token(T.IDENT, name, this.line, this.col));
|
|
217
216
|
|
|
218
217
|
this.skipWhitespace();
|
|
219
218
|
|
|
220
219
|
// Parameter list ( ... ) — lex as Go
|
|
221
|
-
if (this.peek() !== "(") this.err(
|
|
220
|
+
if (this.peek() !== "(") this.err(`expected '(' after ${kw} name`);
|
|
222
221
|
const paramStart = this.pos;
|
|
223
222
|
this._skipBalanced("(", ")");
|
|
224
223
|
const paramSrc = this.src.slice(paramStart, this.pos);
|
|
@@ -227,43 +226,12 @@ export class TemplLexer {
|
|
|
227
226
|
this.skipWhitespace();
|
|
228
227
|
|
|
229
228
|
// Opening { of body
|
|
230
|
-
if (this.peek() !== "{") this.err(
|
|
229
|
+
if (this.peek() !== "{") this.err(`expected '{' after ${kw} params`);
|
|
231
230
|
this.advance(); // consume {
|
|
232
231
|
this.tokens.push(new Token(T.LBRACE, "{", this.line, this.col));
|
|
233
232
|
|
|
234
|
-
|
|
235
|
-
this.
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ── CSS declaration lexing ───────────────────────────────────
|
|
239
|
-
|
|
240
|
-
_lexCssDecl() {
|
|
241
|
-
this.skipWhitespace();
|
|
242
|
-
|
|
243
|
-
// Name (identifier)
|
|
244
|
-
const nameStart = this.pos;
|
|
245
|
-
while (!this.eof() && /\w/.test(this.peek())) this.advance();
|
|
246
|
-
const name = this.src.slice(nameStart, this.pos);
|
|
247
|
-
if (!name) this.err("expected name after 'css'");
|
|
248
|
-
this.tokens.push(new Token(T.IDENT, name, this.line, this.col));
|
|
249
|
-
|
|
250
|
-
this.skipWhitespace();
|
|
251
|
-
|
|
252
|
-
// Parameter list (balanced parens)
|
|
253
|
-
if (this.peek() !== "(") this.err("expected '(' after css name");
|
|
254
|
-
const paramStart = this.pos;
|
|
255
|
-
this._skipBalanced("(", ")");
|
|
256
|
-
const paramSrc = this.src.slice(paramStart, this.pos);
|
|
257
|
-
for (const t of this._goTokens(paramSrc)) this.tokens.push(t);
|
|
258
|
-
|
|
259
|
-
this.skipWhitespace();
|
|
260
|
-
|
|
261
|
-
// Opening { of body
|
|
262
|
-
if (this.peek() !== "{") this.err("expected '{' after css params");
|
|
263
|
-
this.advance(); // consume {
|
|
264
|
-
this.tokens.push(new Token(T.LBRACE, "{", this.line, this.col));
|
|
265
|
-
|
|
266
|
-
this._lexCssBody();
|
|
233
|
+
if (kw === "templ") this._lexHtmlBody(1);
|
|
234
|
+
else this._lexCssBody();
|
|
267
235
|
}
|
|
268
236
|
|
|
269
237
|
_skipCssString(quote) {
|
|
@@ -660,7 +628,7 @@ export class TemplLexer {
|
|
|
660
628
|
// ── HTML text content ─────────────────────────────────────────
|
|
661
629
|
|
|
662
630
|
_lexHtmlText() {
|
|
663
|
-
|
|
631
|
+
const start = this.pos;
|
|
664
632
|
const line = this.line;
|
|
665
633
|
while (!this.eof()) {
|
|
666
634
|
const c = this.peek();
|
|
@@ -674,9 +642,9 @@ export class TemplLexer {
|
|
|
674
642
|
(c === "d" && this._peekKeyword("default"))
|
|
675
643
|
)
|
|
676
644
|
break;
|
|
677
|
-
|
|
645
|
+
this.advance();
|
|
678
646
|
}
|
|
679
|
-
const trimmed =
|
|
647
|
+
const trimmed = this.src.slice(start, this.pos).trim();
|
|
680
648
|
if (trimmed)
|
|
681
649
|
this.tokens.push(new Token(TT.HTML_TEXT, trimmed, line, this.col));
|
|
682
650
|
}
|
|
@@ -729,17 +697,15 @@ export class TemplLexer {
|
|
|
729
697
|
}
|
|
730
698
|
|
|
731
699
|
_readHtmlName() {
|
|
732
|
-
|
|
733
|
-
while (!this.eof() && /[a-zA-Z0-9_-]/.test(this.peek()))
|
|
734
|
-
|
|
735
|
-
return name;
|
|
700
|
+
const start = this.pos;
|
|
701
|
+
while (!this.eof() && /[a-zA-Z0-9_-]/.test(this.peek())) this.advance();
|
|
702
|
+
return this.src.slice(start, this.pos);
|
|
736
703
|
}
|
|
737
704
|
|
|
738
705
|
_readHtmlAttrName() {
|
|
739
|
-
|
|
740
|
-
while (!this.eof() && /[a-zA-Z0-9_:.-]/.test(this.peek()))
|
|
741
|
-
|
|
742
|
-
return name;
|
|
706
|
+
const start = this.pos;
|
|
707
|
+
while (!this.eof() && /[a-zA-Z0-9_:.-]/.test(this.peek())) this.advance();
|
|
708
|
+
return this.src.slice(start, this.pos);
|
|
743
709
|
}
|
|
744
710
|
|
|
745
711
|
_readQuotedString() {
|
|
@@ -756,9 +722,9 @@ export class TemplLexer {
|
|
|
756
722
|
}
|
|
757
723
|
|
|
758
724
|
_readWord() {
|
|
759
|
-
|
|
760
|
-
while (!this.eof() && /\w/.test(this.peek()))
|
|
761
|
-
return
|
|
725
|
+
const start = this.pos;
|
|
726
|
+
while (!this.eof() && /\w/.test(this.peek())) this.advance();
|
|
727
|
+
return this.src.slice(start, this.pos);
|
|
762
728
|
}
|
|
763
729
|
|
|
764
730
|
_skipToOpenBrace() {
|
package/src/templ-parser.js
CHANGED
|
@@ -48,55 +48,31 @@ export class TemplParser extends Parser {
|
|
|
48
48
|
|
|
49
49
|
// ── CssDecl ───────────────────────────────────────────────────
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
_parseDecl(kind, bodyFn) {
|
|
52
52
|
const _line = this.peek().line;
|
|
53
|
-
this.advance();
|
|
54
|
-
|
|
53
|
+
this.advance();
|
|
55
54
|
const name = this.expect(T.IDENT).value;
|
|
56
|
-
|
|
57
|
-
// Parse parameter list (standard Go params: LPAREN...RPAREN)
|
|
58
55
|
const params = this.parseParamList();
|
|
59
|
-
|
|
60
|
-
// Optional semicolon between ) and {
|
|
61
56
|
this.semi();
|
|
62
|
-
|
|
63
|
-
// Consume opening {
|
|
64
57
|
this.expect(T.LBRACE);
|
|
65
|
-
|
|
66
|
-
// Consume CSS_BODY
|
|
67
|
-
const bodyToken = this.expect(TT.CSS_BODY);
|
|
68
|
-
const cssText = bodyToken.value;
|
|
69
|
-
|
|
70
|
-
// Consume closing }
|
|
71
|
-
this.expect(T.RBRACE);
|
|
58
|
+
const extra = bodyFn();
|
|
72
59
|
this.semi();
|
|
73
|
-
|
|
74
|
-
return { kind: "CssDecl", name, params, cssText, _line };
|
|
60
|
+
return { kind, name, params, ...extra, _line };
|
|
75
61
|
}
|
|
76
62
|
|
|
77
|
-
|
|
63
|
+
parseCssDecl() {
|
|
64
|
+
return this._parseDecl("CssDecl", () => {
|
|
65
|
+
const bodyToken = this.expect(TT.CSS_BODY);
|
|
66
|
+
const cssText = bodyToken.value;
|
|
67
|
+
this.expect(T.RBRACE);
|
|
68
|
+
return { cssText };
|
|
69
|
+
});
|
|
70
|
+
}
|
|
78
71
|
|
|
79
72
|
parseTemplDecl() {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const name = this.expect(T.IDENT).value;
|
|
84
|
-
|
|
85
|
-
// Parse parameter list (standard Go params: LPAREN...RPAREN)
|
|
86
|
-
const params = this.parseParamList();
|
|
87
|
-
|
|
88
|
-
// Optional semicolon between ) and {
|
|
89
|
-
this.semi();
|
|
90
|
-
|
|
91
|
-
// Consume the opening { of the HTML body
|
|
92
|
-
this.expect(T.LBRACE);
|
|
93
|
-
|
|
94
|
-
// Parse the HTML body; parseHtmlChildren consumes the closing RBRACE
|
|
95
|
-
const body = this.parseHtmlChildren();
|
|
96
|
-
|
|
97
|
-
this.semi();
|
|
98
|
-
|
|
99
|
-
return { kind: "TemplDecl", name, params, body, _line };
|
|
73
|
+
return this._parseDecl("TemplDecl", () => ({
|
|
74
|
+
body: this.parseHtmlChildren(),
|
|
75
|
+
}));
|
|
100
76
|
}
|
|
101
77
|
|
|
102
78
|
// ── HTML node parsing ─────────────────────────────────────────
|
|
@@ -229,14 +205,15 @@ export class TemplParser extends Parser {
|
|
|
229
205
|
!this.check(T.RBRACE) &&
|
|
230
206
|
!this.check(T.EOF)
|
|
231
207
|
) {
|
|
232
|
-
if (this.check(TT.TEMPL_CASE)) {
|
|
208
|
+
if (this.check(TT.TEMPL_CASE) || this.check(TT.TEMPL_DEFAULT)) {
|
|
209
|
+
const isDefault = this.check(TT.TEMPL_DEFAULT);
|
|
233
210
|
const caseTok = this.advance();
|
|
234
211
|
const body = this.parseTemplCaseBody();
|
|
235
|
-
cases.push({
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
212
|
+
cases.push({
|
|
213
|
+
kind: "TemplCase",
|
|
214
|
+
caseTokens: isDefault ? null : caseTok.value,
|
|
215
|
+
body,
|
|
216
|
+
});
|
|
240
217
|
} else {
|
|
241
218
|
this.advance(); // skip unexpected
|
|
242
219
|
}
|
package/src/test-runner.js
CHANGED
|
@@ -14,6 +14,19 @@ import {
|
|
|
14
14
|
gwFilesIn,
|
|
15
15
|
} from "./compiler.js";
|
|
16
16
|
|
|
17
|
+
// WasmGC + exception handling (what the wasm backend emits) ship enabled by
|
|
18
|
+
// default from Node 22 (V8 11.9+). Older runtimes fail at instantiate time
|
|
19
|
+
// with an opaque CompileError, so check up front and explain.
|
|
20
|
+
export const MIN_WASM_NODE_MAJOR = 22;
|
|
21
|
+
|
|
22
|
+
export function checkWasmRuntime(version = process.version) {
|
|
23
|
+
const major = Number.parseInt(version.replace(/^v/, ""), 10);
|
|
24
|
+
if (Number.isNaN(major) || major >= MIN_WASM_NODE_MAJOR) return;
|
|
25
|
+
throw new Error(
|
|
26
|
+
`running wasm packages requires Node ${MIN_WASM_NODE_MAJOR}+ (WasmGC); found ${version}\n hint: upgrade Node, or compile with --js-only to run the JS backend instead`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
export function isTestFunc(decl) {
|
|
18
31
|
if (decl.kind !== "FuncDecl") return false;
|
|
19
32
|
if (!decl.name.startsWith("Test")) return false;
|
|
@@ -382,6 +395,13 @@ export async function runTests(targetDir, options = {}) {
|
|
|
382
395
|
|
|
383
396
|
// `wasm` packages run inside the linked app.wasm; `js` packages that import
|
|
384
397
|
// wasm packages need the linked module too (hybrid packages).
|
|
398
|
+
if (compiled.target === "both" || compiled.wasm) {
|
|
399
|
+
try {
|
|
400
|
+
checkWasmRuntime();
|
|
401
|
+
} catch (err) {
|
|
402
|
+
return reportBuildFailure(err, resolvedDir, options);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
385
405
|
if (compiled.target === "both") {
|
|
386
406
|
return runDualTarget(compiled, resolvedDir, runHarness, options);
|
|
387
407
|
}
|