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/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 with terser
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 watchMode = args.includes("--watch");
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
- return compileDir(inputPath);
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 map = cg.getSourceMap(inputArg);
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
- async function maybeMinify(js) {
246
+ function maybeMinify(js) {
236
247
  if (!minifyOutput) return js;
237
- const result = await minify(js, {
238
- module: true,
239
- compress: true,
240
- mangle: true,
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 = await maybeMinify(result.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
- async function buildOnce() {
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
- const js = await maybeMinify(result.js);
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.push(isFloat ? T.FLOAT : T.INT, n, l, c);
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);