gofront 0.0.4 → 0.0.6
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 +123 -139
- package/package.json +2 -3
- package/src/codegen/expressions.js +778 -253
- package/src/codegen/source-map.js +13 -5
- package/src/codegen/statements.js +193 -6
- package/src/codegen.js +115 -15
- package/src/compiler.js +10 -3
- package/src/dev-server.js +83 -0
- package/src/dts-parser.js +1 -1
- package/src/index.js +47 -16
- package/src/lexer.js +24 -1
- package/src/minifier.js +698 -0
- package/src/parser/expressions.js +115 -15
- package/src/parser/statements.js +4 -4
- package/src/parser/types.js +108 -3
- package/src/parser.js +57 -4
- package/src/typechecker/expressions.js +279 -10
- package/src/typechecker/statements.js +64 -3
- package/src/typechecker/types.js +96 -10
- package/src/typechecker.js +698 -16
package/src/index.js
CHANGED
|
@@ -26,12 +26,13 @@ import { createRequire } from "node:module";
|
|
|
26
26
|
const _require = createRequire(import.meta.url);
|
|
27
27
|
const { version } = _require("../package.json");
|
|
28
28
|
|
|
29
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
30
|
-
import { minify } from "terser";
|
|
29
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
31
30
|
import { CodeGen } from "./codegen.js";
|
|
32
31
|
import { compileDir } from "./compiler.js";
|
|
32
|
+
import { createDevServer, liveReloadClient } from "./dev-server.js";
|
|
33
33
|
import { parseDts } from "./dts-parser.js";
|
|
34
34
|
import { Lexer } from "./lexer.js";
|
|
35
|
+
import { minify } from "./minifier.js";
|
|
35
36
|
import { Parser } from "./parser.js";
|
|
36
37
|
import { isLocalPath, resolveAll, resolveGwDir } from "./resolver.js";
|
|
37
38
|
import { TypeChecker } from "./typechecker.js";
|
|
@@ -55,8 +56,11 @@ Usage:
|
|
|
55
56
|
gofront <input> -o out.js Compile and write to file
|
|
56
57
|
gofront <input> --check Type-check only
|
|
57
58
|
gofront <input> --watch Watch for changes and recompile
|
|
59
|
+
gofront <input> -o out.js --serve Watch + serve with live reload (default port 3000)
|
|
60
|
+
gofront <input> -o out.js --serve --port 8080 Use a custom port
|
|
58
61
|
gofront <input> --source-map Append inline source map to output
|
|
59
|
-
gofront <input> --minify Minify output
|
|
62
|
+
gofront <input> --minify Minify output
|
|
63
|
+
gofront <input> --minify --mangle Minify and mangle identifiers
|
|
60
64
|
gofront <file.go> --ast Dump AST (debug)
|
|
61
65
|
gofront <file.go> --tokens Dump tokens (debug)
|
|
62
66
|
gofront init [dir] Scaffold a new GoFront project
|
|
@@ -119,8 +123,12 @@ const checkOnly = args.includes("--check");
|
|
|
119
123
|
const dumpAst = args.includes("--ast");
|
|
120
124
|
const dumpTokens = args.includes("--tokens");
|
|
121
125
|
const sourceMap = args.includes("--source-map");
|
|
122
|
-
const
|
|
126
|
+
const serveMode = args.includes("--serve");
|
|
127
|
+
const watchMode = args.includes("--watch") || serveMode;
|
|
123
128
|
const minifyOutput = args.includes("--minify");
|
|
129
|
+
const mangleOutput = args.includes("--mangle");
|
|
130
|
+
const portFlag = args.indexOf("--port");
|
|
131
|
+
const servePort = portFlag !== -1 ? parseInt(args[portFlag + 1], 10) : 3000;
|
|
124
132
|
|
|
125
133
|
// ── Determine input mode ─────────────────────────────────────
|
|
126
134
|
|
|
@@ -137,7 +145,8 @@ try {
|
|
|
137
145
|
|
|
138
146
|
function runCompile() {
|
|
139
147
|
if (isDir) {
|
|
140
|
-
|
|
148
|
+
const outputDir = outputFile ? dirname(resolve(outputFile)) : resolve(".");
|
|
149
|
+
return compileDir(inputPath, { sourceMap, outputDir });
|
|
141
150
|
}
|
|
142
151
|
|
|
143
152
|
// Single-file mode
|
|
@@ -221,7 +230,9 @@ function runCompile() {
|
|
|
221
230
|
let js = cg.generate(ast);
|
|
222
231
|
|
|
223
232
|
if (sourceMap) {
|
|
224
|
-
const
|
|
233
|
+
const outputDir = outputFile ? dirname(resolve(outputFile)) : resolve(".");
|
|
234
|
+
const srcPath = relative(outputDir, inputPath);
|
|
235
|
+
const map = cg.getSourceMap(srcPath);
|
|
225
236
|
const b64 = Buffer.from(map).toString("base64");
|
|
226
237
|
js += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
|
|
227
238
|
}
|
|
@@ -232,14 +243,13 @@ function runCompile() {
|
|
|
232
243
|
|
|
233
244
|
// ── Minify helper ────────────────────────────────────────────
|
|
234
245
|
|
|
235
|
-
|
|
246
|
+
function maybeMinify(js) {
|
|
236
247
|
if (!minifyOutput) return js;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
});
|
|
242
|
-
return result.code;
|
|
248
|
+
if (sourceMap) {
|
|
249
|
+
console.error("gofront: --source-map and --minify cannot be used together");
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
return minify(js, { mangle: mangleOutput });
|
|
243
253
|
}
|
|
244
254
|
|
|
245
255
|
// ── Single-shot mode ─────────────────────────────────────────
|
|
@@ -262,7 +272,7 @@ if (!watchMode) {
|
|
|
262
272
|
|
|
263
273
|
let js;
|
|
264
274
|
try {
|
|
265
|
-
js =
|
|
275
|
+
js = maybeMinify(result.js);
|
|
266
276
|
} catch (e) {
|
|
267
277
|
console.error(`gofront: minify failed: ${e.message}`);
|
|
268
278
|
process.exit(1);
|
|
@@ -290,11 +300,31 @@ function timestamp() {
|
|
|
290
300
|
return new Date().toLocaleTimeString();
|
|
291
301
|
}
|
|
292
302
|
|
|
293
|
-
|
|
303
|
+
// Start dev server before first build so the browser can connect immediately
|
|
304
|
+
let devServer = null;
|
|
305
|
+
if (serveMode) {
|
|
306
|
+
if (!outputFile) {
|
|
307
|
+
console.error("gofront: --serve requires -o <output file>");
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
const serveDir = dirname(resolve(outputFile));
|
|
311
|
+
devServer = createDevServer(serveDir, servePort);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function buildOnce() {
|
|
294
315
|
try {
|
|
295
316
|
const startMs = performance.now();
|
|
296
317
|
const result = runCompile();
|
|
297
|
-
|
|
318
|
+
let js = maybeMinify(result.js);
|
|
319
|
+
if (serveMode) {
|
|
320
|
+
// Insert live reload client before the source map comment so it stays last
|
|
321
|
+
const smIdx = js.lastIndexOf("\n//# sourceMappingURL=");
|
|
322
|
+
if (smIdx !== -1) {
|
|
323
|
+
js = `${js.slice(0, smIdx)}\n${liveReloadClient}${js.slice(smIdx)}`;
|
|
324
|
+
} else {
|
|
325
|
+
js += `\n${liveReloadClient}`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
298
328
|
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
299
329
|
if (outputFile) {
|
|
300
330
|
writeFileSync(outputFile, `${js}\n`);
|
|
@@ -307,6 +337,7 @@ async function buildOnce() {
|
|
|
307
337
|
console.log(js);
|
|
308
338
|
console.error(`[${timestamp()}] gofront: OK (${elapsedMs}ms)`);
|
|
309
339
|
}
|
|
340
|
+
devServer?.notify();
|
|
310
341
|
} catch (e) {
|
|
311
342
|
console.error(`[${timestamp()}] gofront: ERROR`);
|
|
312
343
|
for (const line of e.message.split("\n")) console.error(` ${line}`);
|
package/src/lexer.js
CHANGED
|
@@ -8,6 +8,8 @@ export const T = {
|
|
|
8
8
|
STRING: "STRING",
|
|
9
9
|
IDENT: "IDENT",
|
|
10
10
|
|
|
11
|
+
IMAG: "IMAG",
|
|
12
|
+
|
|
11
13
|
// Keywords
|
|
12
14
|
FUNC: "func",
|
|
13
15
|
VAR: "var",
|
|
@@ -72,6 +74,7 @@ export const T = {
|
|
|
72
74
|
PIPE: "|",
|
|
73
75
|
CARET: "^",
|
|
74
76
|
AND_NOT: "&^",
|
|
77
|
+
TILDE: "~",
|
|
75
78
|
LSHIFT: "<<",
|
|
76
79
|
RSHIFT: ">>",
|
|
77
80
|
ELLIPSIS: "...",
|
|
@@ -97,6 +100,7 @@ const SEMI_TRIGGERS = new Set([
|
|
|
97
100
|
T.IDENT,
|
|
98
101
|
T.INT,
|
|
99
102
|
T.FLOAT,
|
|
103
|
+
T.IMAG,
|
|
100
104
|
T.STRING,
|
|
101
105
|
T.RPAREN,
|
|
102
106
|
T.RBRACE,
|
|
@@ -297,10 +301,20 @@ export class Lexer {
|
|
|
297
301
|
}
|
|
298
302
|
|
|
299
303
|
readRawString() {
|
|
304
|
+
const l = this.line,
|
|
305
|
+
c = this.col;
|
|
300
306
|
this.advance(); // opening `
|
|
301
307
|
let s = "";
|
|
302
308
|
while (this.pos < this.src.length && this.peek() !== "`")
|
|
303
309
|
s += this.advance();
|
|
310
|
+
if (this.pos >= this.src.length)
|
|
311
|
+
throw new LexError(
|
|
312
|
+
"Unterminated raw string",
|
|
313
|
+
l,
|
|
314
|
+
c,
|
|
315
|
+
this.filename,
|
|
316
|
+
this.src,
|
|
317
|
+
);
|
|
304
318
|
this.advance(); // closing `
|
|
305
319
|
return s;
|
|
306
320
|
}
|
|
@@ -501,7 +515,13 @@ export class Lexer {
|
|
|
501
515
|
// Numbers
|
|
502
516
|
if (/[0-9]/.test(ch)) {
|
|
503
517
|
const { n, isFloat } = this.readNumber();
|
|
504
|
-
this.
|
|
518
|
+
if (this.src[this.pos] === "i") {
|
|
519
|
+
this.pos++;
|
|
520
|
+
this.col++;
|
|
521
|
+
this.push(T.IMAG, n, l, c);
|
|
522
|
+
} else {
|
|
523
|
+
this.push(isFloat ? T.FLOAT : T.INT, n, l, c);
|
|
524
|
+
}
|
|
505
525
|
continue;
|
|
506
526
|
}
|
|
507
527
|
|
|
@@ -574,6 +594,9 @@ export class Lexer {
|
|
|
574
594
|
if (this.match("=")) this.push(T.CARET_ASSIGN, "^=", l, c);
|
|
575
595
|
else this.push(T.CARET, "^", l, c);
|
|
576
596
|
break;
|
|
597
|
+
case "~":
|
|
598
|
+
this.push(T.TILDE, "~", l, c);
|
|
599
|
+
break;
|
|
577
600
|
case ":":
|
|
578
601
|
if (this.match("=")) this.push(T.DEFINE, ":=", l, c);
|
|
579
602
|
else this.push(T.COLON, ":", l, c);
|