@xnoxs/flux-lang 3.5.3 → 4.0.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/CHANGELOG.md +44 -0
- package/README.md +3 -1
- package/dist/flux-cli.js +171 -11
- package/dist/flux.cjs.js +64 -1
- package/dist/flux.esm.js +64 -1
- package/dist/flux.min.js +20 -20
- package/package.json +1 -1
- package/scripts/build.js +0 -0
- package/src/config.js +0 -0
- package/src/self/bundler.flux +0 -0
- package/src/self/checker.flux +0 -0
- package/src/self/cli.flux +0 -0
- package/src/self/codegen.flux +5 -2
- package/src/self/codegen.js +8 -3
- package/src/self/config.flux +0 -0
- package/src/self/css-preprocessor.flux +0 -0
- package/src/self/formatter.flux +0 -0
- package/src/self/jsx.flux +0 -0
- package/src/self/lexer.flux +2 -2
- package/src/self/lexer.js +2 -2
- package/src/self/linter.flux +0 -0
- package/src/self/mangler.flux +0 -0
- package/src/self/parser.flux +41 -0
- package/src/self/parser.js +49 -0
- package/src/self/pkg.flux +76 -0
- package/src/self/pkg.js +82 -0
- package/src/self/sourcemap.flux +0 -0
- package/src/self/stdlib.flux +0 -0
- package/src/self/test-runner.flux +0 -0
- package/src/self/transpiler.flux +14 -4
- package/src/self/transpiler.js +13 -2
- package/src/self/type-checker.flux +12 -0
- package/src/self/type-checker.js +17 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnoxs/flux-lang",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Flux — A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.",
|
|
5
5
|
"main": "dist/flux.cjs.js",
|
|
6
6
|
"module": "dist/flux.esm.js",
|
package/scripts/build.js
CHANGED
|
File without changes
|
package/src/config.js
CHANGED
|
File without changes
|
package/src/self/bundler.flux
CHANGED
|
File without changes
|
package/src/self/checker.flux
CHANGED
|
File without changes
|
package/src/self/cli.flux
CHANGED
|
File without changes
|
package/src/self/codegen.flux
CHANGED
|
@@ -54,6 +54,7 @@ export class CodeGenerator:
|
|
|
54
54
|
smBuilder: any
|
|
55
55
|
_needsFmt: bool
|
|
56
56
|
_loopDepth: int
|
|
57
|
+
_version: string
|
|
57
58
|
|
|
58
59
|
fn i():
|
|
59
60
|
var s = ''
|
|
@@ -83,7 +84,7 @@ export class CodeGenerator:
|
|
|
83
84
|
self.lines = []
|
|
84
85
|
self.level = 0
|
|
85
86
|
self._needsFmt = false
|
|
86
|
-
self.emit('// Generated by Flux Transpiler
|
|
87
|
+
self.emit('// Generated by Flux Transpiler v' + (self._version ?? "3.5.3") + ' (self-hosted)')
|
|
87
88
|
self.emit('"use strict";')
|
|
88
89
|
self.blank()
|
|
89
90
|
for node in ast.body: self.genStmt(node)
|
|
@@ -111,6 +112,7 @@ export class CodeGenerator:
|
|
|
111
112
|
else if node.type == 'TypeDecl': self.genTypeDecl(node)
|
|
112
113
|
else if node.type == 'InterfaceDecl': self.genInterfaceDecl(node)
|
|
113
114
|
else if node.type == 'EnumDecl': self.genEnumDecl(node)
|
|
115
|
+
else if node.type == 'DeclareDecl': return
|
|
114
116
|
else if node.type == 'ExprStmt': self.emit(self.genExpr(node.expr) + ';')
|
|
115
117
|
else:
|
|
116
118
|
throw new Error("Unknown statement: {node.type}")
|
|
@@ -552,4 +554,5 @@ export class CodeGenerator:
|
|
|
552
554
|
export fn makeCodeGen(opts):
|
|
553
555
|
val ind = opts?.indent ?? ' '
|
|
554
556
|
val smb = opts?.smBuilder ?? null
|
|
555
|
-
|
|
557
|
+
val ver = opts?.version ?? "3.5.3"
|
|
558
|
+
return new CodeGenerator(ind, 0, [], {}, smb, false, 0, ver)
|
package/src/self/codegen.js
CHANGED
|
@@ -52,7 +52,7 @@ function getAllFields(name, reg, visited) {
|
|
|
52
52
|
return [...parent, ...reg[name].fields];
|
|
53
53
|
}
|
|
54
54
|
class CodeGenerator {
|
|
55
|
-
constructor(ind, level, lines, clsReg, smBuilder, _needsFmt, _loopDepth) {
|
|
55
|
+
constructor(ind, level, lines, clsReg, smBuilder, _needsFmt, _loopDepth, _version) {
|
|
56
56
|
this.ind = ind;
|
|
57
57
|
this.level = level;
|
|
58
58
|
this.lines = lines;
|
|
@@ -60,6 +60,7 @@ class CodeGenerator {
|
|
|
60
60
|
this.smBuilder = smBuilder;
|
|
61
61
|
this._needsFmt = _needsFmt;
|
|
62
62
|
this._loopDepth = _loopDepth;
|
|
63
|
+
this._version = _version;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
i() {
|
|
@@ -99,7 +100,7 @@ class CodeGenerator {
|
|
|
99
100
|
this.lines = [];
|
|
100
101
|
this.level = 0;
|
|
101
102
|
this._needsFmt = false;
|
|
102
|
-
this.emit("// Generated by Flux Transpiler
|
|
103
|
+
this.emit((("// Generated by Flux Transpiler v" + (this._version ?? "3.5.3")) + " (self-hosted)"));
|
|
103
104
|
this.emit("\"use strict\";");
|
|
104
105
|
this.blank();
|
|
105
106
|
for (const node of ast.body) {
|
|
@@ -169,6 +170,9 @@ class CodeGenerator {
|
|
|
169
170
|
else if ((node.type == "EnumDecl")) {
|
|
170
171
|
this.genEnumDecl(node);
|
|
171
172
|
}
|
|
173
|
+
else if ((node.type == "DeclareDecl")) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
172
176
|
else if ((node.type == "ExprStmt")) {
|
|
173
177
|
this.emit((this.genExpr(node.expr) + ";"));
|
|
174
178
|
}
|
|
@@ -789,6 +793,7 @@ module.exports.CodeGenerator = CodeGenerator;
|
|
|
789
793
|
function makeCodeGen(opts) {
|
|
790
794
|
const ind = (opts?.indent ?? " ");
|
|
791
795
|
const smb = (opts?.smBuilder ?? null);
|
|
792
|
-
|
|
796
|
+
const ver = (opts?.version ?? "3.5.3");
|
|
797
|
+
return new CodeGenerator(ind, 0, [], { }, smb, false, 0, ver);
|
|
793
798
|
}
|
|
794
799
|
module.exports.makeCodeGen = makeCodeGen;
|
package/src/self/config.flux
CHANGED
|
File without changes
|
|
File without changes
|
package/src/self/formatter.flux
CHANGED
|
File without changes
|
package/src/self/jsx.flux
CHANGED
|
File without changes
|
package/src/self/lexer.flux
CHANGED
|
@@ -43,7 +43,7 @@ export val T = {
|
|
|
43
43
|
export val TokenType = T
|
|
44
44
|
|
|
45
45
|
val KEYWORDS = {
|
|
46
|
-
"var": 'VAR', "val": 'VAL', "fn": 'FN', "return": 'RETURN',
|
|
46
|
+
"var": 'VAR', "val": 'VAL', "fn": 'FN', "return": 'RETURN', "declare": 'DECLARE',
|
|
47
47
|
"if": 'IF', "else": 'ELSE', "for": 'FOR', "in": 'IN',
|
|
48
48
|
"while": 'WHILE', "break": 'BREAK', "continue": 'CONTINUE', "do": 'DO',
|
|
49
49
|
"class": 'CLASS', "extends": 'EXTENDS', "self": 'SELF', "new": 'NEW',
|
|
@@ -345,7 +345,7 @@ export class Lexer:
|
|
|
345
345
|
if word == '_' and not /[a-zA-Z0-9_]/.test(self.ch()):
|
|
346
346
|
self.tok(T.WILDCARD, '_', l, c)
|
|
347
347
|
continue
|
|
348
|
-
val kw = KEYWORDS[word]
|
|
348
|
+
val kw = Object.prototype.hasOwnProperty.call(KEYWORDS, word) ? KEYWORDS[word] : undefined
|
|
349
349
|
if kw == '__TRUE__': self.tok(T.BOOL, true, l, c)
|
|
350
350
|
else if kw == '__FALSE__': self.tok(T.BOOL, false, l, c)
|
|
351
351
|
else if kw == '__NULL__': self.tok(T.NULL, null, l, c)
|
package/src/self/lexer.js
CHANGED
|
@@ -18,7 +18,7 @@ const T = { NUMBER: "NUMBER", STRING: "STRING", BOOL: "BOOL", NULL: "NULL", IDEN
|
|
|
18
18
|
module.exports.T = T;
|
|
19
19
|
const TokenType = T;
|
|
20
20
|
module.exports.TokenType = TokenType;
|
|
21
|
-
const KEYWORDS = { var: "VAR", val: "VAL", fn: "FN", return: "RETURN", if: "IF", else: "ELSE", for: "FOR", in: "IN", while: "WHILE", break: "BREAK", continue: "CONTINUE", do: "DO", class: "CLASS", extends: "EXTENDS", self: "SELF", new: "NEW", interface: "INTERFACE", implements: "IMPLEMENTS", private: "PRIVATE", public: "PUBLIC", protected: "PROTECTED", readonly: "READONLY", static: "STATIC", abstract: "ABSTRACT", override: "OVERRIDE", match: "MATCH", when: "WHEN", import: "IMPORT", export: "EXPORT", from: "FROM", as: "AS", default: "DEFAULT", and: "AND", or: "OR", not: "NOT", async: "ASYNC", await: "AWAIT", try: "TRY", catch: "CATCH", finally: "FINALLY", throw: "THROW", typeof: "TYPEOF", instanceof: "INSTANCEOF", type: "TYPE", enum: "ENUM", satisfies: "SATISFIES", is: "IS", const: "CONST", true: "__TRUE__", false: "__FALSE__", null: "__NULL__" };
|
|
21
|
+
const KEYWORDS = { var: "VAR", val: "VAL", fn: "FN", return: "RETURN", declare: "DECLARE", if: "IF", else: "ELSE", for: "FOR", in: "IN", while: "WHILE", break: "BREAK", continue: "CONTINUE", do: "DO", class: "CLASS", extends: "EXTENDS", self: "SELF", new: "NEW", interface: "INTERFACE", implements: "IMPLEMENTS", private: "PRIVATE", public: "PUBLIC", protected: "PROTECTED", readonly: "READONLY", static: "STATIC", abstract: "ABSTRACT", override: "OVERRIDE", match: "MATCH", when: "WHEN", import: "IMPORT", export: "EXPORT", from: "FROM", as: "AS", default: "DEFAULT", and: "AND", or: "OR", not: "NOT", async: "ASYNC", await: "AWAIT", try: "TRY", catch: "CATCH", finally: "FINALLY", throw: "THROW", typeof: "TYPEOF", instanceof: "INSTANCEOF", type: "TYPE", enum: "ENUM", satisfies: "SATISFIES", is: "IS", const: "CONST", true: "__TRUE__", false: "__FALSE__", null: "__NULL__" };
|
|
22
22
|
class Lexer {
|
|
23
23
|
constructor(src, pos, line, col, tokens, indentStack, nestDepth) {
|
|
24
24
|
this.src = src;
|
|
@@ -454,7 +454,7 @@ class Lexer {
|
|
|
454
454
|
this.tok(T.WILDCARD, "_", l, c);
|
|
455
455
|
continue;
|
|
456
456
|
}
|
|
457
|
-
const kw = KEYWORDS[word];
|
|
457
|
+
const kw = (Object.prototype.hasOwnProperty.call(KEYWORDS, word) ? KEYWORDS[word] : undefined);
|
|
458
458
|
if ((kw == "__TRUE__")) {
|
|
459
459
|
this.tok(T.BOOL, true, l, c);
|
|
460
460
|
}
|
package/src/self/linter.flux
CHANGED
|
File without changes
|
package/src/self/mangler.flux
CHANGED
|
File without changes
|
package/src/self/parser.flux
CHANGED
|
@@ -85,6 +85,7 @@ export class Parser:
|
|
|
85
85
|
if tok.type == T.FN: return self.parseFnDecl(false)
|
|
86
86
|
if tok.type == T.ASYNC: return self.parseAsyncFn()
|
|
87
87
|
if tok.type == T.CLASS: return self.parseClassDecl()
|
|
88
|
+
if tok.type == T.DECLARE: return self.parseDeclareDecl()
|
|
88
89
|
if tok.type == T.IF: return self.parseIf()
|
|
89
90
|
if tok.type == T.FOR: return self.parseFor()
|
|
90
91
|
if tok.type == T.WHILE: return self.parseWhile()
|
|
@@ -668,6 +669,46 @@ export class Parser:
|
|
|
668
669
|
if not self.check(T.FN): self.err('Expected fn after async')
|
|
669
670
|
return self.parseFnDecl(true)
|
|
670
671
|
|
|
672
|
+
fn parseDeclareDecl():
|
|
673
|
+
val loc = self.eat(T.DECLARE)
|
|
674
|
+
val tok = self.peek()
|
|
675
|
+
|
|
676
|
+
if tok.type == T.FN or tok.type == T.ASYNC:
|
|
677
|
+
val isAsync = tok.type == T.ASYNC
|
|
678
|
+
self.skip()
|
|
679
|
+
if isAsync: self.eat(T.FN)
|
|
680
|
+
val name = self.check(T.IDENT) ? self.skip().value : null
|
|
681
|
+
val params = self.parseParamList()
|
|
682
|
+
var retType = null
|
|
683
|
+
if self.check(T.ARROW):
|
|
684
|
+
self.pos = self.pos + 1
|
|
685
|
+
retType = self.parseTypeAnn()
|
|
686
|
+
else if self.check(T.COLON):
|
|
687
|
+
self.pos = self.pos + 1
|
|
688
|
+
retType = self.parseTypeAnn()
|
|
689
|
+
self.skipNewlines()
|
|
690
|
+
val decl = { type: 'FnDecl', name, params, retType, body: [], inline: false, async: isAsync, loc: tok }
|
|
691
|
+
return { type: 'DeclareDecl', decl, loc }
|
|
692
|
+
|
|
693
|
+
if tok.type == T.VAL or tok.type == T.VAR:
|
|
694
|
+
val kind = tok.type == T.VAR ? 'var' : 'val'
|
|
695
|
+
self.skip()
|
|
696
|
+
val name = self.eat(T.IDENT).value
|
|
697
|
+
var typeAnn = null
|
|
698
|
+
if self.maybe(T.COLON): typeAnn = self.parseTypeAnn()
|
|
699
|
+
self.skipNewlines()
|
|
700
|
+
val decl = { type: 'VarDecl', kind, name, typeAnn, init: null, loc: tok }
|
|
701
|
+
return { type: 'DeclareDecl', decl, loc }
|
|
702
|
+
|
|
703
|
+
if tok.type == T.CLASS:
|
|
704
|
+
self.skip()
|
|
705
|
+
val name = self.eat(T.IDENT).value
|
|
706
|
+
self.skipNewlines()
|
|
707
|
+
val decl = { type: 'ClassDecl', name, fields: [], methods: [], loc: tok }
|
|
708
|
+
return { type: 'DeclareDecl', decl, loc }
|
|
709
|
+
|
|
710
|
+
self.err('Expected fn, async fn, val, var, or class after declare')
|
|
711
|
+
|
|
671
712
|
fn parseParamList():
|
|
672
713
|
self.eat(T.LPAREN)
|
|
673
714
|
val params = []
|
package/src/self/parser.js
CHANGED
|
@@ -121,6 +121,9 @@ class Parser {
|
|
|
121
121
|
if ((tok.type == T.CLASS)) {
|
|
122
122
|
return this.parseClassDecl();
|
|
123
123
|
}
|
|
124
|
+
if ((tok.type == T.DECLARE)) {
|
|
125
|
+
return this.parseDeclareDecl();
|
|
126
|
+
}
|
|
124
127
|
if ((tok.type == T.IF)) {
|
|
125
128
|
return this.parseIf();
|
|
126
129
|
}
|
|
@@ -921,6 +924,52 @@ class Parser {
|
|
|
921
924
|
return this.parseFnDecl(true);
|
|
922
925
|
}
|
|
923
926
|
|
|
927
|
+
parseDeclareDecl() {
|
|
928
|
+
const loc = this.eat(T.DECLARE);
|
|
929
|
+
const tok = this.peek();
|
|
930
|
+
if (((tok.type == T.FN) || (tok.type == T.ASYNC))) {
|
|
931
|
+
const isAsync = (tok.type == T.ASYNC);
|
|
932
|
+
this.skip();
|
|
933
|
+
if (isAsync) {
|
|
934
|
+
this.eat(T.FN);
|
|
935
|
+
}
|
|
936
|
+
const name = (this.check(T.IDENT) ? this.skip().value : null);
|
|
937
|
+
const params = this.parseParamList();
|
|
938
|
+
let retType = null;
|
|
939
|
+
if (this.check(T.ARROW)) {
|
|
940
|
+
this.pos = (this.pos + 1);
|
|
941
|
+
retType = this.parseTypeAnn();
|
|
942
|
+
}
|
|
943
|
+
else if (this.check(T.COLON)) {
|
|
944
|
+
this.pos = (this.pos + 1);
|
|
945
|
+
retType = this.parseTypeAnn();
|
|
946
|
+
}
|
|
947
|
+
this.skipNewlines();
|
|
948
|
+
const decl = { type: "FnDecl", name, params, retType, body: [], inline: false, async: isAsync, loc: tok };
|
|
949
|
+
return { type: "DeclareDecl", decl, loc };
|
|
950
|
+
}
|
|
951
|
+
if (((tok.type == T.VAL) || (tok.type == T.VAR))) {
|
|
952
|
+
const kind = ((tok.type == T.VAR) ? "var" : "val");
|
|
953
|
+
this.skip();
|
|
954
|
+
const name = this.eat(T.IDENT).value;
|
|
955
|
+
let typeAnn = null;
|
|
956
|
+
if (this.maybe(T.COLON)) {
|
|
957
|
+
typeAnn = this.parseTypeAnn();
|
|
958
|
+
}
|
|
959
|
+
this.skipNewlines();
|
|
960
|
+
const decl = { type: "VarDecl", kind, name, typeAnn, init: null, loc: tok };
|
|
961
|
+
return { type: "DeclareDecl", decl, loc };
|
|
962
|
+
}
|
|
963
|
+
if ((tok.type == T.CLASS)) {
|
|
964
|
+
this.skip();
|
|
965
|
+
const name = this.eat(T.IDENT).value;
|
|
966
|
+
this.skipNewlines();
|
|
967
|
+
const decl = { type: "ClassDecl", name, fields: [], methods: [], loc: tok };
|
|
968
|
+
return { type: "DeclareDecl", decl, loc };
|
|
969
|
+
}
|
|
970
|
+
this.err("Expected fn, async fn, val, var, or class after declare");
|
|
971
|
+
}
|
|
972
|
+
|
|
924
973
|
parseParamList() {
|
|
925
974
|
this.eat(T.LPAREN);
|
|
926
975
|
const params = [];
|
package/src/self/pkg.flux
CHANGED
|
@@ -323,6 +323,82 @@ export async fn cmdInfo(name, opts):
|
|
|
323
323
|
catch(e):
|
|
324
324
|
err("Could not fetch info for " + name + ": " + e.message)
|
|
325
325
|
|
|
326
|
+
// ── flux upgrade [--check] ────────────────────────────────────
|
|
327
|
+
export async fn cmdUpgrade(opts):
|
|
328
|
+
val isCheck = opts?.check ?? false
|
|
329
|
+
val cwd = process.cwd()
|
|
330
|
+
val pkg = readPackage(cwd)
|
|
331
|
+
|
|
332
|
+
if not pkg:
|
|
333
|
+
err("No flux.json found. Run: flux init")
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
val deps = pkg.dependencies ?? {}
|
|
337
|
+
val devDeps = pkg.devDependencies ?? {}
|
|
338
|
+
val all = { ...deps, ...devDeps }
|
|
339
|
+
val names = Object.keys(all)
|
|
340
|
+
|
|
341
|
+
if names.length == 0:
|
|
342
|
+
info("No dependencies to upgrade")
|
|
343
|
+
console.log()
|
|
344
|
+
return
|
|
345
|
+
|
|
346
|
+
if isCheck:
|
|
347
|
+
console.log(clr(C.cyan, "\n Checking " + names.length + " package(s) for updates...\n"))
|
|
348
|
+
else:
|
|
349
|
+
console.log(clr(C.cyan, "\n Upgrading " + names.length + " package(s) to latest...\n"))
|
|
350
|
+
|
|
351
|
+
var outdated = 0
|
|
352
|
+
var updated = 0
|
|
353
|
+
|
|
354
|
+
for name in names:
|
|
355
|
+
val current = all[name]
|
|
356
|
+
val spinner = startSpinner("Checking " + clr(C.bold, name) + " ...")
|
|
357
|
+
try:
|
|
358
|
+
val info_ = await fetchJson(REGISTRY_URL + "/" + name)
|
|
359
|
+
val latest = info_["dist-tags"]?.latest ?? null
|
|
360
|
+
stopSpinner(spinner)
|
|
361
|
+
|
|
362
|
+
if not latest:
|
|
363
|
+
console.log(" " + clr(C.gray, "? ") + clr(C.bold, name) + clr(C.gray, " — could not resolve latest"))
|
|
364
|
+
continue
|
|
365
|
+
|
|
366
|
+
val currentClean = current.replace("^", "").replace("~", "")
|
|
367
|
+
if currentClean == latest:
|
|
368
|
+
console.log(" " + clr(C.green, "✓ ") + clr(C.bold, name) + clr(C.gray, " " + current + " (up to date)"))
|
|
369
|
+
else:
|
|
370
|
+
outdated = outdated + 1
|
|
371
|
+
if isCheck:
|
|
372
|
+
console.log(" " + clr(C.yellow, "↑ ") + clr(C.bold, name) + clr(C.gray, " " + current + " → ") + clr(C.green, "^" + latest))
|
|
373
|
+
else:
|
|
374
|
+
val isDev = devDeps[name] != null
|
|
375
|
+
val depKey = isDev ? "devDependencies" : "dependencies"
|
|
376
|
+
pkg[depKey][name] = "^" + latest
|
|
377
|
+
updated = updated + 1
|
|
378
|
+
console.log(" " + clr(C.green, "✓ ") + clr(C.bold, name) + clr(C.gray, " " + current + " → ") + clr(C.green, "^" + latest))
|
|
379
|
+
|
|
380
|
+
catch(e):
|
|
381
|
+
stopSpinner(spinner)
|
|
382
|
+
console.log(" " + clr(C.red, "✗ ") + clr(C.bold, name) + clr(C.gray, " — " + e.message))
|
|
383
|
+
|
|
384
|
+
console.log()
|
|
385
|
+
|
|
386
|
+
if isCheck:
|
|
387
|
+
if outdated == 0:
|
|
388
|
+
ok("All packages are up to date")
|
|
389
|
+
else:
|
|
390
|
+
console.log(clr(C.yellow, " " + outdated + " package(s) can be upgraded"))
|
|
391
|
+
console.log(clr(C.gray, " Run ") + clr(C.yellow, "flux upgrade") + clr(C.gray, " to apply updates"))
|
|
392
|
+
else:
|
|
393
|
+
if updated == 0:
|
|
394
|
+
ok("All packages are already up to date")
|
|
395
|
+
else:
|
|
396
|
+
saveFluxJson(pkg, cwd)
|
|
397
|
+
ok(updated + " package(s) updated in flux.json")
|
|
398
|
+
console.log(clr(C.gray, " Run ") + clr(C.yellow, "flux install") + clr(C.gray, " to install updated versions"))
|
|
399
|
+
|
|
400
|
+
console.log()
|
|
401
|
+
|
|
326
402
|
// ── flux publish ──────────────────────────────────────────────
|
|
327
403
|
export fn cmdPublish(opts):
|
|
328
404
|
val cwd = process.cwd()
|
package/src/self/pkg.js
CHANGED
|
@@ -310,6 +310,88 @@ async function cmdInfo(name, opts) {
|
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
module.exports.cmdInfo = cmdInfo;
|
|
313
|
+
async function cmdUpgrade(opts) {
|
|
314
|
+
const isCheck = (opts?.check ?? false);
|
|
315
|
+
const cwd = process.cwd();
|
|
316
|
+
const pkg = readPackage(cwd);
|
|
317
|
+
if (!pkg) {
|
|
318
|
+
err("No flux.json found. Run: flux init");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const deps = (pkg.dependencies ?? { });
|
|
322
|
+
const devDeps = (pkg.devDependencies ?? { });
|
|
323
|
+
const all = { ...deps, ...devDeps };
|
|
324
|
+
const names = Object.keys(all);
|
|
325
|
+
if ((names.length == 0)) {
|
|
326
|
+
info("No dependencies to upgrade");
|
|
327
|
+
console.log();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (isCheck) {
|
|
331
|
+
console.log(clr(C.cyan, (("\n Checking " + names.length) + " package(s) for updates...\n")));
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
console.log(clr(C.cyan, (("\n Upgrading " + names.length) + " package(s) to latest...\n")));
|
|
335
|
+
}
|
|
336
|
+
let outdated = 0;
|
|
337
|
+
let updated = 0;
|
|
338
|
+
for (const name of names) {
|
|
339
|
+
const current = all[name];
|
|
340
|
+
const spinner = startSpinner((("Checking " + clr(C.bold, name)) + " ..."));
|
|
341
|
+
try {
|
|
342
|
+
const info_ = await fetchJson(((REGISTRY_URL + "/") + name));
|
|
343
|
+
const latest = (info_["dist-tags"]?.latest ?? null);
|
|
344
|
+
stopSpinner(spinner);
|
|
345
|
+
if (!latest) {
|
|
346
|
+
console.log((((" " + clr(C.gray, "? ")) + clr(C.bold, name)) + clr(C.gray, " — could not resolve latest")));
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const currentClean = current.replace("^", "").replace("~", "");
|
|
350
|
+
if ((currentClean == latest)) {
|
|
351
|
+
console.log((((" " + clr(C.green, "✓ ")) + clr(C.bold, name)) + clr(C.gray, ((" " + current) + " (up to date)"))));
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
outdated = (outdated + 1);
|
|
355
|
+
if (isCheck) {
|
|
356
|
+
console.log(((((" " + clr(C.yellow, "↑ ")) + clr(C.bold, name)) + clr(C.gray, ((" " + current) + " → "))) + clr(C.green, ("^" + latest))));
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
const isDev = (devDeps[name] != null);
|
|
360
|
+
const depKey = (isDev ? "devDependencies" : "dependencies");
|
|
361
|
+
pkg[depKey][name] = ("^" + latest);
|
|
362
|
+
updated = (updated + 1);
|
|
363
|
+
console.log(((((" " + clr(C.green, "✓ ")) + clr(C.bold, name)) + clr(C.gray, ((" " + current) + " → "))) + clr(C.green, ("^" + latest))));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
stopSpinner(spinner);
|
|
369
|
+
console.log((((" " + clr(C.red, "✗ ")) + clr(C.bold, name)) + clr(C.gray, (" — " + e.message))));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
console.log();
|
|
373
|
+
if (isCheck) {
|
|
374
|
+
if ((outdated == 0)) {
|
|
375
|
+
ok("All packages are up to date");
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
console.log(clr(C.yellow, ((" " + outdated) + " package(s) can be upgraded")));
|
|
379
|
+
console.log(((clr(C.gray, " Run ") + clr(C.yellow, "flux upgrade")) + clr(C.gray, " to apply updates")));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
if ((updated == 0)) {
|
|
384
|
+
ok("All packages are already up to date");
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
saveFluxJson(pkg, cwd);
|
|
388
|
+
ok((updated + " package(s) updated in flux.json"));
|
|
389
|
+
console.log(((clr(C.gray, " Run ") + clr(C.yellow, "flux install")) + clr(C.gray, " to install updated versions")));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
console.log();
|
|
393
|
+
}
|
|
394
|
+
module.exports.cmdUpgrade = cmdUpgrade;
|
|
313
395
|
function cmdPublish(opts) {
|
|
314
396
|
const cwd = process.cwd();
|
|
315
397
|
const pkg = readPackage(cwd);
|
package/src/self/sourcemap.flux
CHANGED
|
File without changes
|
package/src/self/stdlib.flux
CHANGED
|
File without changes
|
|
File without changes
|
package/src/self/transpiler.flux
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// Flux Self-Hosted Transpiler — v3.
|
|
2
|
+
// Flux Self-Hosted Transpiler — v3.5 (100% Self-Hosting)
|
|
3
3
|
// src/self/transpiler.flux — written in Flux, compiled by stage-0
|
|
4
4
|
//
|
|
5
5
|
// Full pipeline:
|
|
@@ -15,8 +15,18 @@ import { transformJsx, FLUX_H_BROWSER, FLUX_H_SERVER, FLUX_CSS_BROWSER, FLUX_CSS
|
|
|
15
15
|
import { Checker } from './checker'
|
|
16
16
|
import { FluxTypeChecker } from './type-checker'
|
|
17
17
|
|
|
18
|
-
// ── Version info
|
|
19
|
-
|
|
18
|
+
// ── Version info — always in sync with package.json ──────────
|
|
19
|
+
import Fs from "fs"
|
|
20
|
+
import Path from "path"
|
|
21
|
+
|
|
22
|
+
fn _readFluxVersion():
|
|
23
|
+
try:
|
|
24
|
+
val pkgPath = Path.resolve(__dirname, "../../package.json")
|
|
25
|
+
return JSON.parse(Fs.readFileSync(pkgPath, "utf8")).version
|
|
26
|
+
catch(e):
|
|
27
|
+
return "4.0.1"
|
|
28
|
+
|
|
29
|
+
export val FLUX_VERSION = _readFluxVersion()
|
|
20
30
|
export val FLUX_STAGE = "self-hosted"
|
|
21
31
|
|
|
22
32
|
// ── Public API ────────────────────────────────────────────────
|
|
@@ -89,7 +99,7 @@ export fn transpile(source, options):
|
|
|
89
99
|
// ── Stage 5: Code generation ────────────────────────────────
|
|
90
100
|
result.stage = "codegen"
|
|
91
101
|
val indent = opts.mangle ? "" : " "
|
|
92
|
-
val cg = makeCodeGen({ indent })
|
|
102
|
+
val cg = makeCodeGen({ indent, version: FLUX_VERSION })
|
|
93
103
|
val genResult = cg.generate(ast)
|
|
94
104
|
var code = genResult.code
|
|
95
105
|
|
package/src/self/transpiler.js
CHANGED
|
@@ -8,7 +8,18 @@ const { transformCss } = require("./css-preprocessor");
|
|
|
8
8
|
const { transformJsx, FLUX_H_BROWSER, FLUX_H_SERVER, FLUX_CSS_BROWSER, FLUX_CSS_SERVER } = require("./jsx");
|
|
9
9
|
const { Checker } = require("./checker");
|
|
10
10
|
const { FluxTypeChecker } = require("./type-checker");
|
|
11
|
-
const
|
|
11
|
+
const Fs = require("fs");
|
|
12
|
+
const Path = require("path");
|
|
13
|
+
function _readFluxVersion() {
|
|
14
|
+
try {
|
|
15
|
+
const pkgPath = Path.resolve(__dirname, "../../package.json");
|
|
16
|
+
return JSON.parse(Fs.readFileSync(pkgPath, "utf8")).version;
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
return "4.0.1";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const FLUX_VERSION = _readFluxVersion();
|
|
12
23
|
module.exports.FLUX_VERSION = FLUX_VERSION;
|
|
13
24
|
const FLUX_STAGE = "self-hosted";
|
|
14
25
|
module.exports.FLUX_STAGE = FLUX_STAGE;
|
|
@@ -61,7 +72,7 @@ function transpile(source, options) {
|
|
|
61
72
|
}
|
|
62
73
|
result.stage = "codegen";
|
|
63
74
|
const indent = (opts.mangle ? "" : " ");
|
|
64
|
-
const cg = makeCodeGen({ indent });
|
|
75
|
+
const cg = makeCodeGen({ indent, version: FLUX_VERSION });
|
|
65
76
|
const genResult = cg.generate(ast);
|
|
66
77
|
let code = genResult.code;
|
|
67
78
|
if (opts._jsxHelpers) {
|
|
@@ -734,6 +734,18 @@ export class FluxTypeChecker:
|
|
|
734
734
|
clsEnv.set("self", T_OBJECT(shape))
|
|
735
735
|
for m in node.methods: self._checkStmt(m, clsEnv)
|
|
736
736
|
|
|
737
|
+
else if node.type == "DeclareDecl":
|
|
738
|
+
val d = node.decl
|
|
739
|
+
if d.type == "FnDecl":
|
|
740
|
+
val retType = d.retType ? parseAnnotation(d.retType) : null
|
|
741
|
+
val paramTypes = d.params.map(p -> p.typeAnn ? parseAnnotation(p.typeAnn) : T_UNKNOWN)
|
|
742
|
+
if d.name: env.set(d.name, T_FN(paramTypes, retType ?? T_UNKNOWN))
|
|
743
|
+
else if d.type == "VarDecl":
|
|
744
|
+
val t = d.typeAnn ? parseAnnotation(d.typeAnn) : T_UNKNOWN
|
|
745
|
+
env.set(d.name, t)
|
|
746
|
+
else if d.type == "ClassDecl":
|
|
747
|
+
env.set(d.name, T_NAMED(d.name))
|
|
748
|
+
|
|
737
749
|
else if node.type == "TypeDecl":
|
|
738
750
|
for v in node.variants:
|
|
739
751
|
if v.fields.length == 0:
|
package/src/self/type-checker.js
CHANGED
|
@@ -995,6 +995,23 @@ class FluxTypeChecker {
|
|
|
995
995
|
this._checkStmt(m, clsEnv);
|
|
996
996
|
}
|
|
997
997
|
}
|
|
998
|
+
else if ((node.type == "DeclareDecl")) {
|
|
999
|
+
const d = node.decl;
|
|
1000
|
+
if ((d.type == "FnDecl")) {
|
|
1001
|
+
const retType = (d.retType ? parseAnnotation(d.retType) : null);
|
|
1002
|
+
const paramTypes = d.params.map((p) => (p.typeAnn ? parseAnnotation(p.typeAnn) : T_UNKNOWN));
|
|
1003
|
+
if (d.name) {
|
|
1004
|
+
env.set(d.name, T_FN(paramTypes, (retType ?? T_UNKNOWN)));
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
else if ((d.type == "VarDecl")) {
|
|
1008
|
+
const t = (d.typeAnn ? parseAnnotation(d.typeAnn) : T_UNKNOWN);
|
|
1009
|
+
env.set(d.name, t);
|
|
1010
|
+
}
|
|
1011
|
+
else if ((d.type == "ClassDecl")) {
|
|
1012
|
+
env.set(d.name, T_NAMED(d.name));
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
998
1015
|
else if ((node.type == "TypeDecl")) {
|
|
999
1016
|
for (const v of node.variants) {
|
|
1000
1017
|
if ((v.fields.length == 0)) {
|