gofront 0.0.9 → 1.0.0

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.
Files changed (50) hide show
  1. package/README.md +6 -6
  2. package/package.json +7 -4
  3. package/src/codegen/expressions.js +520 -1717
  4. package/src/codegen/index.js +576 -0
  5. package/src/codegen/runtime.js +151 -0
  6. package/src/codegen/statements.js +305 -296
  7. package/src/codegen/stdlib/builder.js +36 -0
  8. package/src/codegen/stdlib/bytes.js +62 -0
  9. package/src/codegen/stdlib/errors.js +17 -0
  10. package/src/codegen/stdlib/fmt.js +101 -0
  11. package/src/codegen/stdlib/gom.js +152 -0
  12. package/src/codegen/stdlib/html.js +17 -0
  13. package/src/codegen/stdlib/index.js +95 -0
  14. package/src/codegen/stdlib/io.js +33 -0
  15. package/src/codegen/stdlib/maps.js +42 -0
  16. package/src/codegen/stdlib/math.js +53 -0
  17. package/src/codegen/stdlib/os.js +10 -0
  18. package/src/codegen/stdlib/path.js +31 -0
  19. package/src/codegen/stdlib/rand.js +24 -0
  20. package/src/codegen/stdlib/regexp.js +51 -0
  21. package/src/codegen/stdlib/slices.js +48 -0
  22. package/src/codegen/stdlib/sort.js +28 -0
  23. package/src/codegen/stdlib/strconv.js +30 -0
  24. package/src/codegen/stdlib/strings.js +83 -0
  25. package/src/codegen/stdlib/time.js +54 -0
  26. package/src/codegen/stdlib/unicode.js +22 -0
  27. package/src/codegen/stdlib/utf8.js +23 -0
  28. package/src/codegen/templ.js +281 -0
  29. package/src/compiler.js +80 -74
  30. package/src/dts-parser.js +355 -371
  31. package/src/index.js +13 -49
  32. package/src/lexer.js +186 -272
  33. package/src/minifier.js +305 -318
  34. package/src/parser/expressions.js +51 -34
  35. package/src/{parser.js → parser/index.js} +4 -4
  36. package/src/parser/statements.js +24 -34
  37. package/src/parser/types.js +31 -29
  38. package/src/templ-parser.js +2 -2
  39. package/src/tokens.js +109 -0
  40. package/src/typechecker/assignability.js +258 -0
  41. package/src/typechecker/expressions.js +559 -549
  42. package/src/typechecker/index.js +555 -0
  43. package/src/typechecker/resolve.js +421 -0
  44. package/src/typechecker/statements.js +267 -280
  45. package/src/typechecker/stdlib/core.js +592 -0
  46. package/src/typechecker/stdlib/extended.js +332 -0
  47. package/src/typechecker/stdlib.js +9 -1060
  48. package/src/typechecker/termination.js +83 -0
  49. package/src/codegen.js +0 -882
  50. package/src/typechecker.js +0 -1299
package/src/compiler.js CHANGED
@@ -17,14 +17,14 @@
17
17
 
18
18
  import { readdirSync, readFileSync, statSync } from "node:fs";
19
19
  import { basename, dirname, join, relative, resolve } from "node:path";
20
- import { CodeGen } from "./codegen.js";
20
+ import { CodeGen } from "./codegen/index.js";
21
21
  import { parseDts } from "./dts-parser.js";
22
22
  import { Lexer } from "./lexer.js";
23
- import { Parser } from "./parser.js";
23
+ import { Parser } from "./parser/index.js";
24
24
  import { isLocalPath, resolveAll, resolveGwDir } from "./resolver.js";
25
25
  import { TemplLexer } from "./templ-lexer.js";
26
26
  import { TemplParser } from "./templ-parser.js";
27
- import { TypeChecker } from "./typechecker.js";
27
+ import { TypeChecker } from "./typechecker/index.js";
28
28
 
29
29
  // ── Parse cache ──────────────────────────────────────────────
30
30
 
@@ -41,29 +41,20 @@ export function parseCacheSize() {
41
41
 
42
42
  // ── Helpers ──────────────────────────────────────────────────
43
43
 
44
- function parseFile(filePath) {
44
+ function parseGoFrontFile(filePath) {
45
45
  const mtime = statSync(filePath).mtimeMs;
46
46
  const cached = _parseCache.get(filePath);
47
47
  if (cached && cached.mtime === mtime) return cached.ast;
48
48
 
49
49
  const source = readFileSync(filePath, "utf8");
50
50
  const filename = basename(filePath);
51
- const tokens = new Lexer(source, filename).tokenize();
52
- const ast = new Parser(tokens, filename, source).parse();
53
- ast._source = source;
54
- _parseCache.set(filePath, { mtime, ast });
55
- return ast;
56
- }
57
-
58
- function parseTemplFile(filePath) {
59
- const mtime = statSync(filePath).mtimeMs;
60
- const cached = _parseCache.get(filePath);
61
- if (cached && cached.mtime === mtime) return cached.ast;
62
-
63
- const source = readFileSync(filePath, "utf8");
64
- const filename = basename(filePath);
65
- const tokens = new TemplLexer(source, filename).tokenize();
66
- const ast = new TemplParser(tokens, filename, source).parse();
51
+ const isTempl = filePath.endsWith(".templ");
52
+ const tokens = isTempl
53
+ ? new TemplLexer(source, filename).tokenize()
54
+ : new Lexer(source, filename).tokenize();
55
+ const ast = isTempl
56
+ ? new TemplParser(tokens, filename, source).parse()
57
+ : new Parser(tokens, filename, source).parse();
67
58
  ast._source = source;
68
59
  _parseCache.set(filePath, { mtime, ast });
69
60
  return ast;
@@ -76,53 +67,27 @@ function gwFilesIn(dir) {
76
67
  .map((f) => join(dir, f));
77
68
  }
78
69
 
79
- // ── Main entry points ─────────────────────────────────────────
80
-
81
- export function compileDir(dir, options = {}) {
82
- const files = gwFilesIn(dir);
83
- if (files.length === 0) throw new Error(`No .go files found in ${dir}`);
84
- return compileFiles(files, { ...options, fromDir: dir });
85
- }
86
-
87
- export function compileFiles(files, options = {}) {
88
- const fromDir = options.fromDir ?? dirname(resolve(files[0]));
89
-
90
- // ── 1. Parse ─────────────────────────────────────────────────
91
- const parseErrors = [];
92
- const programs = [];
93
- for (const f of files) {
94
- try {
95
- programs.push(f.endsWith(".templ") ? parseTemplFile(f) : parseFile(f));
96
- } catch (e) {
97
- parseErrors.push(e.message);
98
- }
99
- }
100
- if (parseErrors.length > 0) {
101
- throw new Error(parseErrors.join("\n"));
102
- }
103
-
104
- // Validate consistent package name
105
- const pkgNames = [...new Set(programs.map((p) => p.pkg.name))];
106
- if (pkgNames.length > 1)
107
- throw new Error(
108
- `Mixed package names in ${fromDir}: ${pkgNames.join(", ")}`,
109
- );
110
- const pkgName = pkgNames[0];
111
-
112
- // ── 2. Resolve imports ────────────────────────────────────────
113
- const checker = new TypeChecker();
114
- const jsImports = new Map(); // npm imports → exported names (for ESM emit)
115
- const bundledPackages = new Set(); // package names whose code is inlined
116
- const preambles = []; // JS code from compiled sub-packages
117
-
70
+ // ── Import resolution ─────────────────────────────────────────
71
+ //
72
+ // Shared by compileFiles (multi-file) and the single-file path in index.js.
73
+ // Mutates checker, jsImports, bundledPackages, and preambles in place.
74
+
75
+ export function resolveImports(
76
+ programs,
77
+ fromFile,
78
+ checker,
79
+ jsImports,
80
+ bundledPackages,
81
+ preambles,
82
+ ) {
83
+ const fromDir = dirname(resolve(fromFile));
118
84
  const allImports = programs.flatMap((p) => p.imports);
119
- const dummyFromFile = join(fromDir, "_dummy.go");
120
85
 
121
86
  // js: prefix — local .d.ts files
122
87
  for (const imp of allImports) {
123
88
  for (const { path, alias } of imp.imports) {
124
89
  if (!path.startsWith("js:")) continue;
125
- if (alias === "_") continue; // side-effect import — no type definitions
90
+ if (alias === "_") continue;
126
91
  const dtsPath = join(fromDir, path.slice(3));
127
92
  try {
128
93
  const { types, values } = parseDts(readFileSync(dtsPath, "utf8"));
@@ -133,12 +98,12 @@ export function compileFiles(files, options = {}) {
133
98
  }
134
99
  }
135
100
 
136
- // npm packages — exclude side-effect imports (alias "_") from type resolution
101
+ // npm packages — exclude side-effect imports from type resolution
137
102
  const allImportsNoSideEffect = allImports.map((imp) => ({
138
103
  ...imp,
139
104
  imports: imp.imports.filter(({ alias }) => alias !== "_"),
140
105
  }));
141
- const resolved = resolveAll(allImportsNoSideEffect, dummyFromFile, parseDts);
106
+ const resolved = resolveAll(allImportsNoSideEffect, fromFile, parseDts);
142
107
  for (const [path, info] of resolved) {
143
108
  if (!info) continue;
144
109
  checker.addDefinitions(info.types, info.values);
@@ -152,29 +117,20 @@ export function compileFiles(files, options = {}) {
152
117
  for (const { path, alias, _line } of imp.imports) {
153
118
  if (!isLocalPath(path) || seenLocalPaths.has(path)) continue;
154
119
  seenLocalPaths.add(path);
155
-
156
- const depDir = resolveGwDir(path, dummyFromFile);
120
+ const depDir = resolveGwDir(path, fromFile);
157
121
  if (!depDir) {
158
122
  console.error(
159
123
  `gofront: warning: cannot find local package '${path}' relative to ${fromDir}`,
160
124
  );
161
125
  continue;
162
126
  }
163
-
164
- // Recursively compile dependency
165
127
  const dep = compileDir(depDir);
166
128
  preambles.push(dep.js);
167
- if (alias === "_") continue; // side-effect import — bundle code but don't expose namespace
168
-
169
- // Dot import: flatten all exported symbols into global scope
129
+ if (alias === "_") continue;
170
130
  if (alias === ".") {
171
131
  checker.addDefinitions(dep.exportedTypes, dep.exportedSymbols);
172
- // All dep symbols must be treated as bundled so codegen
173
- // can emit them without a qualifier. We DON'T add a
174
- // namespace, so SelectorExpr never appears for these.
175
132
  continue;
176
133
  }
177
-
178
134
  const nameUsed = alias ?? dep.pkgName;
179
135
  bundledPackages.add(nameUsed);
180
136
  checker.addPackageNamespace(
@@ -186,6 +142,56 @@ export function compileFiles(files, options = {}) {
186
142
  }
187
143
  }
188
144
  }
145
+ }
146
+
147
+ // ── Main entry points ─────────────────────────────────────────
148
+
149
+ export function compileDir(dir, options = {}) {
150
+ const files = gwFilesIn(dir);
151
+ if (files.length === 0) throw new Error(`No .go files found in ${dir}`);
152
+ return compileFiles(files, { ...options, fromDir: dir });
153
+ }
154
+
155
+ export function compileFiles(files, options = {}) {
156
+ const fromDir = options.fromDir ?? dirname(resolve(files[0]));
157
+
158
+ // ── 1. Parse ─────────────────────────────────────────────────
159
+ const parseErrors = [];
160
+ const programs = [];
161
+ for (const f of files) {
162
+ try {
163
+ programs.push(parseGoFrontFile(f));
164
+ } catch (e) {
165
+ parseErrors.push(e.message);
166
+ }
167
+ }
168
+ if (parseErrors.length > 0) {
169
+ throw new Error(parseErrors.join("\n"));
170
+ }
171
+
172
+ // Validate consistent package name
173
+ const pkgNames = [...new Set(programs.map((p) => p.pkg.name))];
174
+ if (pkgNames.length > 1)
175
+ throw new Error(
176
+ `Mixed package names in ${fromDir}: ${pkgNames.join(", ")}`,
177
+ );
178
+ const pkgName = pkgNames[0];
179
+
180
+ // ── 2. Resolve imports ────────────────────────────────────────
181
+ const checker = new TypeChecker();
182
+ const jsImports = new Map(); // npm imports → exported names (for ESM emit)
183
+ const bundledPackages = new Set(); // package names whose code is inlined
184
+ const preambles = []; // JS code from compiled sub-packages
185
+
186
+ const dummyFromFile = join(fromDir, "_dummy.go");
187
+ resolveImports(
188
+ programs,
189
+ dummyFromFile,
190
+ checker,
191
+ jsImports,
192
+ bundledPackages,
193
+ preambles,
194
+ );
189
195
 
190
196
  // ── 3. Type-check ─────────────────────────────────────────────
191
197
  const errors = checker.checkAll(programs);