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.
- package/README.md +6 -6
- package/package.json +7 -4
- package/src/codegen/expressions.js +520 -1717
- package/src/codegen/index.js +576 -0
- package/src/codegen/runtime.js +151 -0
- package/src/codegen/statements.js +305 -296
- package/src/codegen/stdlib/builder.js +36 -0
- package/src/codegen/stdlib/bytes.js +62 -0
- package/src/codegen/stdlib/errors.js +17 -0
- package/src/codegen/stdlib/fmt.js +101 -0
- package/src/codegen/stdlib/gom.js +152 -0
- package/src/codegen/stdlib/html.js +17 -0
- package/src/codegen/stdlib/index.js +95 -0
- package/src/codegen/stdlib/io.js +33 -0
- package/src/codegen/stdlib/maps.js +42 -0
- package/src/codegen/stdlib/math.js +53 -0
- package/src/codegen/stdlib/os.js +10 -0
- package/src/codegen/stdlib/path.js +31 -0
- package/src/codegen/stdlib/rand.js +24 -0
- package/src/codegen/stdlib/regexp.js +51 -0
- package/src/codegen/stdlib/slices.js +48 -0
- package/src/codegen/stdlib/sort.js +28 -0
- package/src/codegen/stdlib/strconv.js +30 -0
- package/src/codegen/stdlib/strings.js +83 -0
- package/src/codegen/stdlib/time.js +54 -0
- package/src/codegen/stdlib/unicode.js +22 -0
- package/src/codegen/stdlib/utf8.js +23 -0
- package/src/codegen/templ.js +281 -0
- package/src/compiler.js +80 -74
- package/src/dts-parser.js +355 -371
- package/src/index.js +13 -49
- package/src/lexer.js +186 -272
- package/src/minifier.js +305 -318
- package/src/parser/expressions.js +51 -34
- package/src/{parser.js → parser/index.js} +4 -4
- package/src/parser/statements.js +24 -34
- package/src/parser/types.js +31 -29
- package/src/templ-parser.js +2 -2
- package/src/tokens.js +109 -0
- package/src/typechecker/assignability.js +258 -0
- package/src/typechecker/expressions.js +559 -549
- package/src/typechecker/index.js +555 -0
- package/src/typechecker/resolve.js +421 -0
- package/src/typechecker/statements.js +267 -280
- package/src/typechecker/stdlib/core.js +592 -0
- package/src/typechecker/stdlib/extended.js +332 -0
- package/src/typechecker/stdlib.js +9 -1060
- package/src/typechecker/termination.js +83 -0
- package/src/codegen.js +0 -882
- 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
|
|
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
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
// ──
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
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;
|
|
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
|
|
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,
|
|
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;
|
|
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);
|