@xnoxs/flux-lang 3.4.1 → 3.4.3

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/self/cli.js CHANGED
@@ -1 +1,836 @@
1
- function map(arr, fn) { return arr.map(fn); } function filter(arr, fn) { return arr.filter(fn); } function reduce(arr, fn, init) { return arguments.length >= 3 ? arr.reduce(fn, init) : arr.reduce(fn); } function some(arr, fn) { return arr.some(fn); } function join(arr, sep) { return arr.join(sep != null ? sep : ','); } function clamp(val, min, max) { return Math.min(Math.max(val, min), max); } function sum(arr) { return arr.reduce(function(a, b) { return a + b; }, 0); } function max(arr) { if (arguments.length > 1) return Math.max.apply(null, arguments); return Math.max.apply(null, arr); } function padStart(s, len, char) { return String(s).padStart(len, char || ' '); } function padEnd(s, len, char) { return String(s).padEnd(len, char || ' '); } function trim(s) { return String(s).trim(); } function trimEnd(s) { return String(s).trimEnd(); } function startsWith(s, prefix) { return String(s).startsWith(prefix); } function endsWith(s, suffix) { return String(s).endsWith(suffix); } function repeat(s, n) { return String(s).repeat(n); } "use strict"; const Fs = require("fs"); const Path = require("path"); const Os = require("os"); const { transpile, FLUX_VERSION, FLUX_STAGE } = require("./transpiler"); const { bundle } = require("./bundler"); const { format } = require("./formatter"); const { lint } = require("./linter"); const { runTests } = require("./test-runner"); const { loadConfig } = require("./config"); const { cmdAdd, cmdRemove, cmdInstall, cmdList, cmdSearch, cmdInfo, cmdPublish } = require("./pkg"); const _a = (FLUX_VERSION ?? "3.0.0"); const _b = (FLUX_STAGE ?? "self-hosted"); const _c = { reset: "\\x1b[0m", bold: "\\x1b[1m", dim: "\\x1b[2m", red: "\\x1b[31m", green: "\\x1b[32m", yellow: "\\x1b[33m", blue: "\\x1b[34m", cyan: "\\x1b[36m", white: "\\x1b[37m", gray: "\\x1b[90m", magenta: "\\x1b[35m" }; const _d = (process.env.NO_COLOR || !process.stdout.isTTY); function _e(_ai, _aj) { return (_d ? _aj : ((_ai + _aj) + _c.reset)); } function _f(_aj) { return _e(_c.bold, _aj); } function _g(_aj) { return _e(_c.gray, _aj); } function _h(_aj) { return _e(_c.green, _aj); } function _i(_aj) { return _e(_c.red, _aj); } function _j(_aj) { return _e(_c.cyan, _aj); } function _k(_aj) { return _e(_c.yellow, _aj); } function _l(_aj) { return _e(_c.blue, _aj); } function _m() { if (_d) { console.log((((("Flux Lang " + _a) + " [") + _b) + "]")); return; } console.log(_j(_f("███████╗██╗ ██╗ ██╗██╗ ██╗\n██╔════╝██║ ██║ ██║╚██╗██╔╝\n█████╗ ██║ ██║ ██║ ╚███╔╝\n██╔══╝ ██║ ██║ ██║ ██╔██╗\n██║ ███████╗╚██████╔╝██╔╝ ██╗\n╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝"))); console.log(_g(((((" Flux Lang v" + _a) + " [") + _b) + "] → JavaScript\n"))); } function _n() { _m(); console.log(_f("USAGE:")); console.log(" flux <command> [options]\n"); console.log(_f("COMPILER:")); const compilerCmds = [["compile <file.flux>", "Compile .flux → .js"], ["bundle <entry.flux>", "Bundle multiple files into one .js"], ["run <file.flux>", "Compile and run immediately"], ["watch <file.flux>", "Watch for changes, auto-compile"], ["check <file.flux>", "Type-check and static analysis"]]; for (const _ak of compilerCmds) { const [cmd, desc] = _ak; console.log((((" " + _h(("flux " + cmd).padEnd(36))) + " ") + _g(desc))); } console.log(); console.log(_f("TOOLING:")); const toolCmds = [["lint <file.flux>", "Full lint: types + style + immutability"], ["fmt <file.flux>", "Format source code in-place"], ["test [dir]", "Run *.test.flux files"], ["repl", "Interactive REPL mode"], ["tokens <file.flux>", "Show lexer token list"], ["ast <file.flux>", "Show Abstract Syntax Tree (JSON)"]]; for (const _ak of toolCmds) { const [cmd, desc] = _ak; console.log((((" " + _h(("flux " + cmd).padEnd(36))) + " ") + _g(desc))); } console.log(); console.log(_f("PACKAGE MANAGER:")); const pkgCmds = [["init [name]", "Scaffold a new Flux project"], ["add <pkg[@version]>", "Add a dependency"], ["remove <pkg>", "Remove a dependency"], ["install", "Install all dependencies"], ["list", "List installed packages"], ["search <query>", "Search the package registry"], ["info <pkg>", "Show package details"], ["publish", "Publish package to registry"]]; for (const _ak of pkgCmds) { const [cmd, desc] = _ak; console.log((((" " + _j(("flux " + cmd).padEnd(36))) + " ") + _g(desc))); } console.log(); console.log(_f("SELF-HOSTED:")); const selfCmds = [["self-hosted", "Show self-hosted compiler status"], ["self-hosted build", "Bootstrap: compile compiler with itself"], ["self-hosted verify", "Verify self-hosted output matches stage-0"]]; for (const _ak of selfCmds) { const [cmd, desc] = _ak; console.log((((" " + _k(("flux " + cmd).padEnd(36))) + " ") + _g(desc))); } console.log(); console.log(_f("OPTIONS:")); console.log(((" " + _k("--out, -o <file> ")) + " Output file")); console.log(((" " + _k("--sourcemap, -m ")) + " Generate .js.map")); console.log(((" " + _k("--watch, -w ")) + " Watch mode")); console.log(((" " + _k("--mangle ")) + " Minify identifiers")); console.log(((" " + _k("--typecheck ")) + " Enable type checking")); console.log(((" " + _k("--stdout ")) + " Print to terminal")); console.log(((" " + _k("--no-color ")) + " Disable colors")); console.log(); } function _o(_al) { const args = _al.slice(2); const opts = { out: null, sourcemap: false, mangle: false, typecheck: false, strict: false, stdout: false, watch: false, dev: false, verbose: false, jsx: false, jsxTarget: "browser" }; const positional = []; let i = 0; while ((i < args.length)) { const a = args[i]; if (((a == "--out") || (a == "-o"))) { i = (i + 1); opts.out = args[i]; } else if (((a == "--sourcemap") || (a == "-m"))) { opts.sourcemap = true; } else if ((a == "--mangle")) { opts.mangle = true; } else if (((a == "--typecheck") || (a == "-t"))) { opts.typecheck = true; } else if ((a == "--strict")) { opts.strict = true; } else if ((a == "--stdout")) { opts.stdout = true; } else if (((a == "--watch") || (a == "-w"))) { opts.watch = true; } else if ((a == "--dev")) { opts.dev = true; } else if (((a == "--verbose") || (a == "-v"))) { opts.verbose = true; } else if ((a == "--jsx")) { opts.jsx = true; } else if ((a == "--jsx-target")) { i = (i + 1); opts.jsxTarget = args[i]; } else if (!a.startsWith("--")) { positional.push(a); } i = (i + 1); } return { positional, opts }; } function _p(_am) { const abs = Path.resolve(_am); if (!Fs.existsSync(abs)) { console.error(_i(("✗ File not found: " + abs))); process.exit(1); } if (!_am.endsWith(".flux")) { console.warn(_k(("⚠ Not a .flux file: " + _am))); } return { source: Fs.readFileSync(abs, "utf8"), abs }; } function _q(_an, _ao) { if (_ao) { return Path.resolve(_ao); } const base = Path.basename(_an, ".flux"); return Path.join(Path.dirname(Path.resolve(_an)), (base + ".js")); } const _r = { ParseError: "Syntax error", LexerError: "Syntax error", CheckError: "Static error", TypeCheckError: "Type error", TypeError: "Type error" }; function _s(_ap, _aq, _am) { const lines = _aq.split("\n"); for (const _ar of _ap) { const kind = (_r[_ar.name] ?? "Error"); const stage = (_ar.stage ? _g(((" [" + _ar.stage) + "]")) : ""); console.error(); console.error((_i(_f(kind)) + stage)); if (_ar.line) { const fileLabel = (_am ? _j(Path.relative(process.cwd(), _am)) : ""); const locLabel = _k(((_ar.line + ":") + (_ar.col ?? 1))); if (fileLabel) { console.error((((" " + fileLabel) + ":") + locLabel)); } else { console.error((" Line " + locLabel)); } } console.error((" " + _ar.message)); if ((_ar.line && (_ar.line <= lines.length))) { const errLineIdx = (_ar.line - 1); const col = Math.max(0, ((_ar.col ?? 1) - 1)); const tokLen = Math.max(1, (_ar.len ?? 1)); if (((errLineIdx > 0) && (lines[(errLineIdx - 1)].trim() != ""))) { const prev = String((_ar.line - 1)).padStart(4); console.error(_g((((" " + prev) + " │ ") + lines[(errLineIdx - 1)]))); } const lineNum = String(_ar.line).padStart(4); console.error((_g(((" " + lineNum) + " │ ")) + lines[errLineIdx])); const squiggle = ("^" + "~".repeat(Math.max(0, (tokLen - 1)))); const pointer = (" ".repeat(col) + _i(squiggle)); console.error((_g(" │ ") + pointer)); if ((((errLineIdx + 1) < lines.length) && (lines[(errLineIdx + 1)].trim() != ""))) { const next = String((_ar.line + 1)).padStart(4); console.error(_g((((" " + next) + " │ ") + lines[(errLineIdx + 1)]))); } } if (_ar.hint) { console.error((_j(" Hint: ") + _g(_ar.hint))); } } console.error(); } function _t(_am, _as) { const { source: _aq, abs } = _p(_am); const cfg = loadConfig(Path.dirname(abs)); const outPath = _q(_am, _as.out); const mapPath = (outPath + ".map"); const t0 = Date.now(); const result = transpile(_aq, { sourcemap: (_as.sourcemap ?? cfg.sourcemap), mangle: (_as.mangle ?? cfg.mangle), typecheck: (_as.typecheck ?? cfg.typecheck), jsx: (_as.jsx ?? cfg.jsx), jsxTarget: (_as.jsxTarget ?? cfg.jsxTarget), sourceFile: Path.relative(Path.dirname(outPath), abs), outputFile: Path.basename(outPath) }); if (!result.success) { console.error(_i((("\n✗ Compile failed — " + result.errors.length) + " error(s)"))); _s(result.errors, _aq, abs); process.exit(1); } const elapsed = (Date.now() - t0); if (_as.stdout) { console.log(result.output); return; } Fs.writeFileSync(outPath, result.output, "utf8"); let extra = ""; if ((_as.sourcemap && result.sourceMap)) { Fs.writeFileSync(mapPath, result.sourceMap, "utf8"); extra = _g((" + " + Path.relative(process.cwd(), mapPath))); } const rel = Path.relative(process.cwd(), abs); const relO = Path.relative(process.cwd(), outPath); console.log((((((_h("✓ ") + _g((("(" + elapsed) + "ms) "))) + _l(rel)) + _g(" → ")) + _j(relO)) + extra)); if ((result.typeErrors && (result.typeErrors.length > 0))) { console.warn(_k((("\n⚠ " + result.typeErrors.length) + " type warning(s)"))); _s(result.typeErrors, _aq, abs); } } function _u(_am, _as) { const { source: _aq, abs } = _p(_am); const result = transpile(_aq, { jsx: (_as.jsx ?? false), jsxTarget: (_as.jsxTarget ?? "browser"), mangle: false }); if (!result.success) { console.error(_i("\n✗ Compile error")); _s(result.errors, _aq, abs); process.exit(1); } const tmpPath = Path.join(Os.tmpdir(), (("_flux_run_" + Date.now()) + ".js")); Fs.writeFileSync(tmpPath, result.output, "utf8"); console.log(_g((("▶ Running " + Path.basename(abs)) + " ...\n"))); try { require(tmpPath); } catch (_at) { console.error(_i(("\n[Runtime Error] " + _at.message))); process.exitCode = 1; } finally { try { Fs.unlinkSync(tmpPath); } catch (_au) { null; } } } function _v(_av) { const _aq = Fs.readFileSync(_av, "utf8"); const baseName = Path.basename(_av); const result = transpile(_aq, { check: true, typecheck: true }); if (!result.success) { const n = result.errors.length; const kind = (result.errors.some((_at) => (_at.name == "CheckError")) ? "static" : "syntax"); console.error(_i((((((("\n✗ " + baseName) + ": ") + n) + " ") + kind) + " error(s)"))); _s(result.errors, _aq, _av); return { ok: false, typeErrors: 0, warnings: 0 }; } let allOk = true; const typeErrors = (result.typeErrors ?? []); if ((typeErrors.length > 0)) { console.error(_i((((("\n✗ " + baseName) + ": ") + typeErrors.length) + " type error(s)"))); _s(typeErrors, _aq, _av); allOk = false; } const warnings = (result.typeWarnings ?? []); for (const _aw of warnings) { const fileRef = (_j(baseName) + (_aw.line ? _k((":" + _aw.line)) : "")); console.warn(((_k(" ⚠ ") + fileRef) + _g((" " + _aw.message)))); if (_aw.hint) { console.warn((_j(" Hint: ") + _g(_aw.hint))); } } const fnRe = /^(?:async )?function \w/gm; const clsRe = /^class \w/gm; const fns = (result.output.match(fnRe) ?? []).length; const cls = (result.output.match(clsRe) ?? []).length; if (allOk) { console.log((((_h("✓ ") + _j(baseName)) + _g(" — no errors")) + _g(((((((" Functions: " + fns) + " | Classes: ") + cls) + " | JS output: ") + result.output.split("\n").length) + " lines")))); } return { ok: allOk, typeErrors: typeErrors.length, warnings: warnings.length }; } function _w(_ax, _as) { if ((_ax.length == 0)) { console.error(_i("✗ No files specified")); process.exit(1); } let totalErrors = 0; let totalWarnings = 0; for (const _am of _ax) { const _av = Path.resolve(_am); if (!Fs.existsSync(_av)) { console.error(_i(("✗ File not found: " + _am))); totalErrors = (totalErrors + 1); continue; } const r = _v(_av); if (!r.ok) { totalErrors = (totalErrors + 1); } totalWarnings = (totalWarnings + r.warnings); } console.log(); if ((totalErrors > 0)) { console.error(_i((("✗ " + totalErrors) + " file(s) with errors"))); process.exit(1); } else { console.log((_h("✓ All files OK") + ((totalWarnings > 0) ? _k(((" (" + totalWarnings) + " warning(s))")) : ""))); } } function _x(_ax, _as) { let changed = 0; for (const _am of _ax) { const _av = Path.resolve(_am); if (!Fs.existsSync(_av)) { console.error(_i(("✗ Not found: " + _am))); continue; } const _aq = Fs.readFileSync(_av, "utf8"); const formatted = format(_aq); if ((formatted != _aq)) { if (!_as.stdout) { Fs.writeFileSync(_av, formatted, "utf8"); console.log(((_h("✓ ") + _g("Formatted ")) + _j(Path.relative(process.cwd(), _av)))); changed = (changed + 1); } else { console.log(formatted); } } else { console.log(((_g("○ ") + _g("No changes: ")) + Path.relative(process.cwd(), _av))); } } if ((!_as.stdout && (changed > 0))) { console.log(); console.log(_h((("✓ " + changed) + " file(s) formatted"))); } } function _y(_ax, _as) { let hasErrors = false; for (const _am of _ax) { const _av = Path.resolve(_am); if (!Fs.existsSync(_av)) { console.error(_i(("✗ Not found: " + _am))); continue; } const _aq = Fs.readFileSync(_av, "utf8"); const result = lint(_aq); const name = Path.relative(process.cwd(), _av); if (((result.errors.length == 0) && (result.warnings.length == 0))) { console.log(((_h("✓ ") + _j(name)) + _g(" — clean"))); } else { for (const _at of result.errors) { console.error((((_i(" error ") + _j(((name + ":") + (_at.line ?? "?")))) + " ") + _at.message)); hasErrors = true; } for (const _aw of result.warnings) { console.warn((((_k(" warn ") + _j(((name + ":") + (_aw.line ?? "?")))) + " ") + _aw.message)); } } } if (hasErrors) { process.exit(1); } } function _z(_ay, _as) { const _av = Path.resolve(_ay); if (!Fs.existsSync(_av)) { console.error(_i(("✗ File not found: " + _ay))); process.exit(1); } const outFile = (_as.out ?? Path.join(Path.dirname(_av), (Path.basename(_av, ".flux") + ".bundle.js"))); const t0 = Date.now(); const result = bundle(_av); if (!result.success) { console.error(_i("\n✗ Bundle failed:\n")); for (const _at of result.errors) { console.error(_i((" " + _at.message))); } process.exit(1); } const elapsed = (Date.now() - t0); if (_as.stdout) { console.log(result.code); return; } Fs.writeFileSync(outFile, result.code, "utf8"); const kb = (result.code.length / 1024).toFixed(1); console.log((((((_h("✓ Bundle done") + _g(((" (" + elapsed) + "ms) "))) + Path.basename(_av)) + _g(((" + " + (result.modules - 1)) + " module(s) → "))) + _j(Path.relative(process.cwd(), outFile))) + _g(((" [" + kb) + " KB]")))); } function _aa(_am, _as) { const _av = Path.resolve(_am); if (!Fs.existsSync(_av)) { console.error(_i(("✗ File not found: " + _am))); process.exit(1); } console.log(((_j("◉ Watching ") + Path.relative(process.cwd(), _av)) + _g(" (Ctrl+C to stop)\n"))); _t(_am, _as); let timer = null; function onDebounce() { console.log((_g((("\n" + new Date().toLocaleTimeString()) + " — ")) + _l("change detected"))); _t(_am, _as); } function onChange() { if (timer) { clearTimeout(timer); } timer = setTimeout(onDebounce, 80); } Fs.watch(_av, onChange); } function _ab(_am, _as) { const { source: _aq } = _p(_am); const { Lexer } = require(Path.join(__dirname, "lexer.js")); const lexer = new Lexer(_aq); const tokens = lexer.tokenize(); console.log(_g((("Tokens (" + tokens.length) + "):\n"))); for (const _az of tokens) { const loc = (_az.line ? _g((((" " + _az.line) + ":") + (_az.col ?? 1))) : ""); const val_ = ((_az.value != null) ? _j((" " + JSON.stringify(_az.value))) : ""); console.log((((" " + _k(_az.type.padEnd(16))) + val_) + loc)); } } function _ac(_am, _as) { const { source: _aq } = _p(_am); const result = transpile(_aq, { }); if (!result.success) { _s(result.errors, _aq, _am); process.exit(1); } console.log(JSON.stringify(result.ast, null, 2)); } function _ad(_as) { const readline = require("readline"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true, prompt: (_j("flux") + _g("> ")) }); console.log(); console.log((_f("Flux Lang Interactive REPL") + _g(((((" v" + _a) + " [") + _b) + "]")))); console.log(_g("Type Flux code to compile and run it. Ctrl+C or .exit to quit.\n")); rl.prompt(); let multiLine = ""; let inBlock = false; function onLine(_ba) { const line = _ba; if (((line.trim() == ".exit") || (line.trim() == ".quit"))) { console.log(_g("\nBye!")); process.exit(0); } if ((line.trim() == ".help")) { console.log(_g(" .exit — quit")); console.log(_g(" .clear — clear screen")); console.log(_g(" .help — this message")); rl.prompt(); return; } if ((line.trim() == ".clear")) { console.clear(); rl.prompt(); return; } const needsContinue = (line.trimEnd().endsWith(":") || inBlock); if ((needsContinue && (line.trim() != ""))) { multiLine = ((multiLine + line) + "\n"); inBlock = true; process.stdout.write(_g("... ")); return; } const src = (inBlock ? multiLine : line); multiLine = ""; inBlock = false; if (!src.trim()) { rl.prompt(); return; } try { const result = transpile(src, { check: false }); if (result.success) { const tmpPath = Path.join(Os.tmpdir(), (("_flux_repl_" + Date.now()) + ".js")); Fs.writeFileSync(tmpPath, result.output, "utf8"); try { const out = require(tmpPath); if ((out != undefined)) { console.log((_h("← ") + JSON.stringify(out))); } } catch (_at) { console.error((_i("Runtime: ") + _at.message)); } finally { try { Fs.unlinkSync(tmpPath); } catch (_au) { null; } } } else { for (const _at of result.errors) { console.error((_i("✗ ") + _at.message)); } } } catch (_bb) { console.error((_i("Error: ") + _bb.message)); } rl.prompt(); } function onClose() { console.log(_g("\nBye!")); process.exit(0); } rl.on("line", onLine); rl.on("close", onClose); } function _ae(_bc, _as) { const projectName = (_bc ?? "my-flux-app"); const dir = Path.resolve(projectName); if (Fs.existsSync(dir)) { console.error(_i(("✗ Directory already exists: " + projectName))); process.exit(1); } Fs.mkdirSync(dir, { recursive: true }); Fs.mkdirSync(Path.join(dir, "src"), { recursive: true }); Fs.mkdirSync(Path.join(dir, "tests"), { recursive: true }); const mainFlux = "// {projectName} — built with Flux Lang v{VERSION}\n// Run: flux run src/main.flux\n\n// ── Algebraic Data Types + Pattern Matching ───────────────────\ntype Shape = Circle(radius) | Rect(width, height) | Triangle(base, height)\n\nfn area(shape):\n match shape:\n when Circle(r): return Math.PI * r * r\n when Rect(w, h): return w * h\n when Triangle(b, h): return 0.5 * b * h\n\nfn describe(shape):\n match shape:\n when Circle(r): return \"Circle(r={r:.2f})\"\n when Rect(w, h): return \"Rect({w:.1f}x{h:.1f})\"\n when Triangle(b, h): return \"Triangle(b={b:.1f}, h={h:.1f})\"\n\n// ── Result type ───────────────────────────────────────────────\ntype Result = Ok(value) | Err(message)\n\nfn safeDivide(a, b):\n if b == 0: return Err(\"division by zero\")\n return Ok(a / b)\n\n// ── Utility functions ─────────────────────────────────────────\nfn greet(name): return \"Hello from Flux, {name}!\"\n\nfn formatList(items):\n if items.length == 0: return \"(empty)\"\n return \"[\" + items.join(\", \") + \"]\"\n\nfn clamp(value, lo, hi):\n if value < lo: return lo\n if value > hi: return hi\n return value\n\n// ── Pipe operator + stdlib ────────────────────────────────────\nval numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\nval processed = numbers\n |> filter(n -> n > 3)\n |> map(n -> n * n)\n |> sort\n\n// ── Main ──────────────────────────────────────────────────────\nprint(greet(\"{projectName}\"))\nprint(\"Squares > 3: {formatList(processed)}\")\nprint(\"clamp(12, 0, 10) = {clamp(12, 0, 10)}\")\n\nval shapes = [Circle(5.0), Rect(8.0, 3.0), Triangle(6.0, 4.0)]\nfor shape in shapes:\n val a = area(shape)\n print(\"{describe(shape)} area={a:.2f}\")\n\nmatch safeDivide(10.0, 3.0):\n when Ok(v): print(\"10 / 3 = {v:.4f}\")\n when Err(e): print(\"Error: {e}\")"; const utilsFlux = "// Utility functions for {projectName}\n// Use with: flux bundle src/main.flux (bundles imports automatically)\n\nexport fn greet(name):\n return \"Hello from Flux, {name}!\"\n\nexport fn formatList(items):\n if items.length == 0: return \"(empty)\"\n return \"[\" + items.join(\", \") + \"]\"\n\nexport fn clamp(value, lo, hi):\n if value < lo: return lo\n if value > hi: return hi\n return value\n\nexport fn sum(nums):\n return nums.reduce((acc, x) -> acc + x, 0)\n\nexport fn average(nums):\n if nums.length == 0: return 0\n return sum(nums) / nums.length"; const testFlux = "// Tests for {projectName}\n// Run: flux test tests/\n\n// ── Functions under test ──────────────────────────────────────\nfn add(a, b):\n return a + b\n\nfn mul(a, b):\n return a * b\n\nfn clamp(value, lo, hi):\n if value < lo: return lo\n if value > hi: return hi\n return value\n\nfn average(nums):\n if nums.length == 0: return 0\n return nums.reduce((s, x) -> s + x, 0) / nums.length\n\ntype Result = Ok(value) | Err(message)\n\nfn safeDivide(a, b):\n if b == 0: return Err(\"div by zero\")\n return Ok(a / b)\n\n// ── Test functions ────────────────────────────────────────────\nfn test_add():\n assert(add(1, 2) == 3, \"1+2=3\")\n assert(add(0, 0) == 0, \"0+0=0\")\n assert(add(-1, 1) == 0, \"-1+1=0\")\n\nfn test_mul():\n assert(mul(3, 4) == 12, \"3*4=12\")\n assert(mul(0, 5) == 0, \"0*5=0\")\n\nfn test_clamp():\n assert(clamp(5, 0, 10) == 5, \"within range\")\n assert(clamp(-1, 0, 10) == 0, \"below min\")\n assert(clamp(15, 0, 10) == 10, \"above max\")\n\nfn test_average():\n assert(average([2, 4, 6]) == 4, \"average of 2,4,6\")\n assert(average([]) == 0, \"empty list\")\n\nfn test_safe_divide():\n val ok = safeDivide(10, 2)\n match ok:\n when Ok(v): assert(v == 5, \"10/2=5\")\n when Err(e): assert(false, \"unexpected error\")\n val err = safeDivide(10, 0)\n match err:\n when Ok(v): assert(false, \"expected error\")\n when Err(e): assert(e == \"div by zero\", \"error message\")\n\nfn test_pipe_operator():\n val result = [1, 2, 3, 4, 5] |> filter(n -> n > 2) |> map(n -> n * 2)\n assert(result.length == 3, \"filter length\")\n assert(result[0] == 6, \"first element\")\n assert(result[2] == 10, \"last element\")"; const fluxJson = { name: projectName, version: "1.0.0", description: (("A Flux Lang v" + _a) + " project"), author: "", license: "MIT", entry: "src/main.flux", outDir: "dist", sourcemap: false, typecheck: true, scripts: { start: "flux run src/main.flux", build: "flux bundle src/main.flux -o dist/bundle.js", dev: "flux watch src/main.flux", check: "flux check src/main.flux", test: "flux test tests/", fmt: "flux fmt src/", lint: "flux lint src/" }, dependencies: { }, devDependencies: { "@xnoxs/flux-lang": ("^" + _a) } }; const gitignore = "node_modules/\ndist/\nflux_modules/\n*.js.map\n.DS_Store\n"; const readme = "# {projectName}\n\nA project built with [Flux Lang](https://flux-lang.dev) v{VERSION} — the self-hosted compiler.\n\n## Quick Start\n\n```bash\nflux run src/main.flux\n```\n\n## Project Structure\n\n```\n{projectName}/\n├── src/\n│ ├── main.flux # Entry point\n│ └── utils.flux # Utility functions\n├── tests/\n│ └── main.test.flux # Test suite\n├── flux.json # Project config\n└── README.md\n```\n\n## Commands\n\n| Command | Description |\n|---|---|\n| `flux run src/main.flux` | Run the project |\n| `flux bundle src/main.flux -o dist/bundle.js` | Bundle to single file |\n| `flux watch src/main.flux` | Watch mode |\n| `flux check src/main.flux` | Type check + static analysis |\n| `flux test tests/` | Run all tests |\n| `flux fmt src/` | Format source code |\n| `flux lint src/` | Lint for issues |\n| `flux add <package>` | Add a dependency |"; const allFiles = [(projectName + "/src/main.flux"), (projectName + "/src/utils.flux"), (projectName + "/tests/main.test.flux"), (projectName + "/flux.json"), (projectName + "/.gitignore"), (projectName + "/README.md")]; Fs.writeFileSync(Path.join(dir, "src", "main.flux"), mainFlux, "utf8"); Fs.writeFileSync(Path.join(dir, "src", "utils.flux"), utilsFlux, "utf8"); Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8"); Fs.writeFileSync(Path.join(dir, "flux.json"), (JSON.stringify(fluxJson, null, 2) + "\n"), "utf8"); Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8"); Fs.writeFileSync(Path.join(dir, "README.md"), readme, "utf8"); console.log(); console.log((_h("✓ Created: ") + _f((projectName + "/")))); console.log(); console.log(_g(" Files:")); for (const _bd of allFiles) { console.log((" " + _j(_bd))); } console.log(); console.log(_f(" Next steps:")); console.log((" " + _k(("cd " + projectName)))); console.log((" " + _k("flux run src/main.flux"))); console.log(); console.log(_g(" Or run the test suite:")); console.log((" " + _k("flux test tests/"))); console.log(); } function _af(_be, _as) { const SELF = Path.join(__dirname, "."); const coreModules = ["css-preprocessor", "checker", "type-checker", "jsx", "lexer", "parser", "codegen", "transpiler"]; const extModules = ["formatter", "sourcemap", "stdlib", "mangler", "linter", "bundler", "test-runner"]; const newModules = ["config", "pkg", "cli"]; const allModules = [...coreModules, ...extModules, ...newModules]; if ((_be == "build")) { const { execSync } = require("child_process"); const BIN = Path.join(__dirname, "../../bin/flux.js"); console.log(); console.log(_f("⚡ Flux Bootstrap — Stage 0")); console.log(_g(" Compiling self-hosted sources with stage-0 compiler...\n")); let ok = 0; let failed = 0; for (const _bc of allModules) { const src = Path.join(SELF, (_bc + ".flux")); const out = Path.join(SELF, (_bc + ".js")); if (!Fs.existsSync(src)) { console.log(_g(((" ○ " + _bc) + ".flux (skipped — not found)"))); continue; } try { const cmd = "node \"${BIN}\" compile \"${src}\" -o \"${out}\" --no-mangle"; execSync(cmd, { cwd: Path.join(__dirname, "../.."), stdio: "pipe" }); console.log((((_h(" ✓ ") + _bc) + ".flux → ") + _g((_bc + ".js")))); ok = (ok + 1); } catch (_at) { console.error((((_i(" ✗ ") + _bc) + ".flux — ") + _at.message.split("\n")[0])); failed = (failed + 1); } } console.log(); if ((failed == 0)) { console.log((_h("✓ Bootstrap complete! ") + _g((ok + " modules compiled")))); console.log(); console.log(" Activate self-hosted mode:"); console.log((" " + _k("FLUX_SELF_HOSTED=1 flux <command>"))); } else { console.log(_i((((("✗ " + failed) + " module(s) failed, ") + ok) + " succeeded"))); } console.log(); return; } if ((_be == "verify")) { console.log(_j("\n Verifying self-hosted compiler output...\n")); try { const selfMod = require(Path.join(SELF, "transpiler.js")); const stage0Mod = require(Path.join(__dirname, "../transpiler.js")); const testSrc = "fn greet(name): return \"Hello, {name}!\"\nval msg = greet(\"Flux\")"; const r0 = stage0Mod.transpile(testSrc, { }); const r1 = selfMod.transpile(testSrc, { }); function norm(_aj) { return _aj.replace(/\/\/.*/g, "").replace(/\s+/g, " ").trim(); } if ((norm(r0.output) == norm(r1.output))) { console.log(_h("✓ Self-hosted output matches stage-0!")); console.log(_h("✓ Flux is fully self-hosting.")); } else { console.log(_k("⚠ Outputs differ (minor differences are OK)")); console.log((_g(" Stage-0: ") + r0.output.split("\n")[0])); console.log((_g(" Self-hosted:") + r1.output.split("\n")[0])); } } catch (_at) { console.error(_i(("✗ Verify failed: " + _at.message))); } console.log(); return; } console.log(); console.log(_f(" Flux Self-Hosted Compiler Status\n")); const selfActive = (process.env.FLUX_SELF_HOSTED == "1"); if (selfActive) { console.log(((" Mode: " + _h("● ACTIVE")) + _g(" (using self-hosted compiler)"))); } else { console.log((((" Mode: " + _g("○ INACTIVE")) + " ") + _g("(using stage-0 compiler)"))); console.log((" Toggle: " + _k("FLUX_SELF_HOSTED=1 flux <command>"))); console.log((" Build: " + _k("flux self-hosted build"))); } console.log(); console.log(_f(((" Core Pipeline (" + coreModules.length) + " modules)"))); for (const _bc of coreModules) { const jsPath = Path.join(SELF, (_bc + ".js")); const flxPath = Path.join(SELF, (_bc + ".flux")); const hasJs = Fs.existsSync(jsPath); const hasFlx = Fs.existsSync(flxPath); const kb = (hasJs ? (Fs.statSync(jsPath).size / 1024).toFixed(1) : "?"); const sym = (hasJs ? _h("✓") : _i("✗")); const note = (!hasFlx ? _g(" (no .flux source)") : ""); console.log((((((" " + sym) + " ") + (_bc + ".flux").padEnd(26)) + _g((kb + " KB"))) + note)); } console.log(); console.log(_f(((" Extended Toolchain (" + extModules.length) + " modules)"))); for (const _bc of extModules) { const jsPath = Path.join(SELF, (_bc + ".js")); const flxPath = Path.join(SELF, (_bc + ".flux")); const hasJs = Fs.existsSync(jsPath); const hasFlx = Fs.existsSync(flxPath); const kb = (hasJs ? (Fs.statSync(jsPath).size / 1024).toFixed(1) : "?"); const sym = (hasJs ? _h("✓") : _i("✗")); const note = (!hasFlx ? _g(" (no .flux source)") : ""); console.log((((((" " + sym) + " ") + (_bc + ".flux").padEnd(26)) + _g((kb + " KB"))) + note)); } console.log(); console.log(_f(((" Ecosystem (" + newModules.length) + " modules)"))); for (const _bc of newModules) { const jsPath = Path.join(SELF, (_bc + ".js")); const flxPath = Path.join(SELF, (_bc + ".flux")); const hasJs = Fs.existsSync(jsPath); const hasFlx = Fs.existsSync(flxPath); const kb = (hasJs ? (Fs.statSync(jsPath).size / 1024).toFixed(1) : "?"); const sym = (hasJs ? _h("✓") : _k("○")); console.log(((((" " + sym) + " ") + (_bc + ".flux").padEnd(26)) + (hasJs ? _g((kb + " KB")) : _k("not built")))); } console.log(); } function _ag(_as) { if (_d) { console.log(("flux-lang v" + _a)); return; } console.log((_j(_f("⚡ Flux Lang")) + _g(((((" v" + _a) + " [") + _b) + "]")))); } function _ah() { const { positional, opts: _as } = _o(process.argv); const cmd = (positional[0] ?? "help"); if (cmd === "compile") { return _t(positional[1], _as); } else if (cmd === "run") { return _u(positional[1], _as); } else if (cmd === "check") { return _w(positional.slice(1), _as); } else if (cmd === "fmt") { return _x(positional.slice(1), _as); } else if (cmd === "format") { return _x(positional.slice(1), _as); } else if (cmd === "lint") { return _y(positional.slice(1), _as); } else if (cmd === "bundle") { return _z(positional[1], _as); } else if (cmd === "watch") { return _aa(positional[1], _as); } else if (cmd === "tokens") { return _ab(positional[1], _as); } else if (cmd === "ast") { return _ac(positional[1], _as); } else if (cmd === "repl") { return _ad(_as); } else if (cmd === "init") { return _ae(positional[1], _as); } else if (cmd === "add") { return cmdAdd(positional.slice(1), _as); } else if (cmd === "remove") { return cmdRemove(positional.slice(1), _as); } else if (cmd === "rm") { return cmdRemove(positional.slice(1), _as); } else if (cmd === "install") { return cmdInstall(_as); } else if (cmd === "i") { return cmdInstall(_as); } else if (cmd === "list") { return cmdList(_as); } else if (cmd === "ls") { return cmdList(_as); } else if (cmd === "search") { return cmdSearch(positional[1], _as); } else if (cmd === "info") { return cmdInfo(positional[1], _as); } else if (cmd === "publish") { return cmdPublish(_as); } else if (cmd === "self-hosted") { return _af(positional[1], _as); } else if (cmd === "version") { return _ag(_as); } else if (cmd === "-v") { return _ag(_as); } else if (cmd === "--version") { return _ag(_as); } else if (cmd === "help") { return _n(); } else if (cmd === "--help") { return _n(); } else if (cmd === "-h") { return _n(); } else { console.error(_i(("✗ Unknown command: " + cmd))); console.error(_g(" Run: flux help")); process.exit(1); } } _ah();
1
+ // ── Flux stdlib ──
2
+
3
+ function map(arr, fn) { return arr.map(fn); }
4
+
5
+ function filter(arr, fn) { return arr.filter(fn); }
6
+
7
+ function reduce(arr, fn, init) { return arguments.length >= 3 ? arr.reduce(fn, init) : arr.reduce(fn); }
8
+
9
+ function some(arr, fn) { return arr.some(fn); }
10
+
11
+ function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
12
+
13
+ function clamp(val, min, max) {
14
+ return Math.min(Math.max(val, min), max);
15
+ }
16
+
17
+ function sum(arr) {
18
+ return arr.reduce(function(a, b) { return a + b; }, 0);
19
+ }
20
+
21
+ function max(arr) {
22
+ if (arguments.length > 1) return Math.max.apply(null, arguments);
23
+ return Math.max.apply(null, arr);
24
+ }
25
+
26
+ function padStart(s, len, char) {
27
+ return String(s).padStart(len, char || ' ');
28
+ }
29
+
30
+ function padEnd(s, len, char) {
31
+ return String(s).padEnd(len, char || ' ');
32
+ }
33
+
34
+ function trim(s) { return String(s).trim(); }
35
+
36
+ function trimEnd(s) { return String(s).trimEnd(); }
37
+
38
+ function startsWith(s, prefix) { return String(s).startsWith(prefix); }
39
+
40
+ function endsWith(s, suffix) { return String(s).endsWith(suffix); }
41
+
42
+ function repeat(s, n) { return String(s).repeat(n); }
43
+ // ── end stdlib ──
44
+
45
+ // Generated by Flux Transpiler v3.2.0
46
+ "use strict";
47
+
48
+ const Fs = require("fs");
49
+ const Path = require("path");
50
+ const Os = require("os");
51
+ const { transpile, FLUX_VERSION, FLUX_STAGE } = require("./transpiler");
52
+ const { bundle } = require("./bundler");
53
+ const { format } = require("./formatter");
54
+ const { lint } = require("./linter");
55
+ const { runTests } = require("./test-runner");
56
+ const { loadConfig } = require("./config");
57
+ const { cmdAdd, cmdRemove, cmdInstall, cmdList, cmdSearch, cmdInfo, cmdPublish } = require("./pkg");
58
+ const VERSION = (FLUX_VERSION ?? "3.0.0");
59
+ const STAGE = (FLUX_STAGE ?? "self-hosted");
60
+ const C = { reset: "\\x1b[0m", bold: "\\x1b[1m", dim: "\\x1b[2m", red: "\\x1b[31m", green: "\\x1b[32m", yellow: "\\x1b[33m", blue: "\\x1b[34m", cyan: "\\x1b[36m", white: "\\x1b[37m", gray: "\\x1b[90m", magenta: "\\x1b[35m" };
61
+ const noColor = (process.env.NO_COLOR || !process.stdout.isTTY);
62
+ function clr(c, s) {
63
+ return (noColor ? s : ((c + s) + C.reset));
64
+ }
65
+ function bold(s) {
66
+ return clr(C.bold, s);
67
+ }
68
+ function gray(s) {
69
+ return clr(C.gray, s);
70
+ }
71
+ function green(s) {
72
+ return clr(C.green, s);
73
+ }
74
+ function red(s) {
75
+ return clr(C.red, s);
76
+ }
77
+ function cyan(s) {
78
+ return clr(C.cyan, s);
79
+ }
80
+ function yellow(s) {
81
+ return clr(C.yellow, s);
82
+ }
83
+ function blue(s) {
84
+ return clr(C.blue, s);
85
+ }
86
+ function showBanner() {
87
+ if (noColor) {
88
+ console.log((((("Flux Lang " + VERSION) + " [") + STAGE) + "]"));
89
+ return;
90
+ }
91
+ console.log(cyan(bold("███████╗██╗ ██╗ ██╗██╗ ██╗\n██╔════╝██║ ██║ ██║╚██╗██╔╝\n█████╗ ██║ ██║ ██║ ╚███╔╝\n██╔══╝ ██║ ██║ ██║ ██╔██╗\n██║ ███████╗╚██████╔╝██╔╝ ██╗\n╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝")));
92
+ console.log(gray(((((" Flux Lang v" + VERSION) + " [") + STAGE) + "] → JavaScript\n")));
93
+ }
94
+ function showHelp() {
95
+ showBanner();
96
+ console.log(bold("USAGE:"));
97
+ console.log(" flux <command> [options]\n");
98
+ console.log(bold("COMPILER:"));
99
+ const compilerCmds = [["compile <file.flux>", "Compile .flux → .js"], ["bundle <entry.flux>", "Bundle multiple files into one .js"], ["run <file.flux>", "Compile and run immediately"], ["watch <file.flux>", "Watch for changes, auto-compile"], ["check <file.flux>", "Type-check and static analysis"]];
100
+ for (const __item__ of compilerCmds) {
101
+ const [cmd, desc] = __item__;
102
+ console.log((((" " + green(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
103
+ }
104
+ console.log();
105
+ console.log(bold("TOOLING:"));
106
+ const toolCmds = [["lint <file.flux>", "Full lint: types + style + immutability"], ["fmt <file.flux>", "Format source code in-place"], ["test [dir]", "Run *.test.flux files"], ["repl", "Interactive REPL mode"], ["tokens <file.flux>", "Show lexer token list"], ["ast <file.flux>", "Show Abstract Syntax Tree (JSON)"]];
107
+ for (const __item__ of toolCmds) {
108
+ const [cmd, desc] = __item__;
109
+ console.log((((" " + green(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
110
+ }
111
+ console.log();
112
+ console.log(bold("PACKAGE MANAGER:"));
113
+ const pkgCmds = [["init [name]", "Scaffold a new Flux project"], ["add <pkg[@version]>", "Add a dependency"], ["remove <pkg>", "Remove a dependency"], ["install", "Install all dependencies"], ["list", "List installed packages"], ["search <query>", "Search the package registry"], ["info <pkg>", "Show package details"], ["publish", "Publish package to registry"]];
114
+ for (const __item__ of pkgCmds) {
115
+ const [cmd, desc] = __item__;
116
+ console.log((((" " + cyan(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
117
+ }
118
+ console.log();
119
+ console.log(bold("SELF-HOSTED:"));
120
+ const selfCmds = [["self-hosted", "Show self-hosted compiler status"], ["self-hosted build", "Bootstrap: compile compiler with itself"], ["self-hosted verify", "Verify self-hosted output matches stage-0"]];
121
+ for (const __item__ of selfCmds) {
122
+ const [cmd, desc] = __item__;
123
+ console.log((((" " + yellow(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
124
+ }
125
+ console.log();
126
+ console.log(bold("OPTIONS:"));
127
+ console.log(((" " + yellow("--out, -o <file> ")) + " Output file"));
128
+ console.log(((" " + yellow("--sourcemap, -m ")) + " Generate .js.map"));
129
+ console.log(((" " + yellow("--watch, -w ")) + " Watch mode"));
130
+ console.log(((" " + yellow("--mangle ")) + " Minify identifiers"));
131
+ console.log(((" " + yellow("--typecheck ")) + " Enable type checking"));
132
+ console.log(((" " + yellow("--stdout ")) + " Print to terminal"));
133
+ console.log(((" " + yellow("--no-color ")) + " Disable colors"));
134
+ console.log();
135
+ }
136
+ function parseArgs(argv) {
137
+ const args = argv.slice(2);
138
+ const opts = { out: null, sourcemap: false, mangle: false, typecheck: false, strict: false, stdout: false, watch: false, dev: false, verbose: false, jsx: false, jsxTarget: "browser" };
139
+ const positional = [];
140
+ let i = 0;
141
+ while ((i < args.length)) {
142
+ const a = args[i];
143
+ if (((a == "--out") || (a == "-o"))) {
144
+ i = (i + 1);
145
+ opts.out = args[i];
146
+ }
147
+ else if (((a == "--sourcemap") || (a == "-m"))) {
148
+ opts.sourcemap = true;
149
+ }
150
+ else if ((a == "--mangle")) {
151
+ opts.mangle = true;
152
+ }
153
+ else if (((a == "--typecheck") || (a == "-t"))) {
154
+ opts.typecheck = true;
155
+ }
156
+ else if ((a == "--strict")) {
157
+ opts.strict = true;
158
+ }
159
+ else if ((a == "--stdout")) {
160
+ opts.stdout = true;
161
+ }
162
+ else if (((a == "--watch") || (a == "-w"))) {
163
+ opts.watch = true;
164
+ }
165
+ else if ((a == "--dev")) {
166
+ opts.dev = true;
167
+ }
168
+ else if (((a == "--verbose") || (a == "-v"))) {
169
+ opts.verbose = true;
170
+ }
171
+ else if ((a == "--jsx")) {
172
+ opts.jsx = true;
173
+ }
174
+ else if ((a == "--jsx-target")) {
175
+ i = (i + 1);
176
+ opts.jsxTarget = args[i];
177
+ }
178
+ else if (!a.startsWith("--")) {
179
+ positional.push(a);
180
+ }
181
+ i = (i + 1);
182
+ }
183
+ return { positional, opts };
184
+ }
185
+ function readFluxFile(filePath) {
186
+ const abs = Path.resolve(filePath);
187
+ if (!Fs.existsSync(abs)) {
188
+ console.error(red(("✗ File not found: " + abs)));
189
+ process.exit(1);
190
+ }
191
+ if (!filePath.endsWith(".flux")) {
192
+ console.warn(yellow(("⚠ Not a .flux file: " + filePath)));
193
+ }
194
+ return { source: Fs.readFileSync(abs, "utf8"), abs };
195
+ }
196
+ function deriveOutPath(inputPath, outFlag) {
197
+ if (outFlag) {
198
+ return Path.resolve(outFlag);
199
+ }
200
+ const base = Path.basename(inputPath, ".flux");
201
+ return Path.join(Path.dirname(Path.resolve(inputPath)), (base + ".js"));
202
+ }
203
+ const ERROR_KIND = { ParseError: "Syntax error", LexerError: "Syntax error", CheckError: "Static error", TypeCheckError: "Type error", TypeError: "Type error" };
204
+ function printErrors(errors, source, filePath) {
205
+ const lines = source.split("\n");
206
+ for (const err of errors) {
207
+ const kind = (ERROR_KIND[err.name] ?? "Error");
208
+ const stage = (err.stage ? gray(((" [" + err.stage) + "]")) : "");
209
+ console.error();
210
+ console.error((red(bold(kind)) + stage));
211
+ if (err.line) {
212
+ const fileLabel = (filePath ? cyan(Path.relative(process.cwd(), filePath)) : "");
213
+ const locLabel = yellow(((err.line + ":") + (err.col ?? 1)));
214
+ if (fileLabel) {
215
+ console.error((((" " + fileLabel) + ":") + locLabel));
216
+ }
217
+ else {
218
+ console.error((" Line " + locLabel));
219
+ }
220
+ }
221
+ console.error((" " + err.message));
222
+ if ((err.line && (err.line <= lines.length))) {
223
+ const errLineIdx = (err.line - 1);
224
+ const col = Math.max(0, ((err.col ?? 1) - 1));
225
+ const tokLen = Math.max(1, (err.len ?? 1));
226
+ if (((errLineIdx > 0) && (lines[(errLineIdx - 1)].trim() != ""))) {
227
+ const prev = String((err.line - 1)).padStart(4);
228
+ console.error(gray((((" " + prev) + " │ ") + lines[(errLineIdx - 1)])));
229
+ }
230
+ const lineNum = String(err.line).padStart(4);
231
+ console.error((gray(((" " + lineNum) + " │ ")) + lines[errLineIdx]));
232
+ const squiggle = ("^" + "~".repeat(Math.max(0, (tokLen - 1))));
233
+ const pointer = (" ".repeat(col) + red(squiggle));
234
+ console.error((gray(" │ ") + pointer));
235
+ if ((((errLineIdx + 1) < lines.length) && (lines[(errLineIdx + 1)].trim() != ""))) {
236
+ const next = String((err.line + 1)).padStart(4);
237
+ console.error(gray((((" " + next) + " │ ") + lines[(errLineIdx + 1)])));
238
+ }
239
+ }
240
+ if (err.hint) {
241
+ console.error((cyan(" Hint: ") + gray(err.hint)));
242
+ }
243
+ }
244
+ console.error();
245
+ }
246
+ function cmdCompile(filePath, opts) {
247
+ const { source, abs } = readFluxFile(filePath);
248
+ const cfg = loadConfig(Path.dirname(abs));
249
+ const outPath = deriveOutPath(filePath, opts.out);
250
+ const mapPath = (outPath + ".map");
251
+ const t0 = Date.now();
252
+ const result = transpile(source, { sourcemap: (opts.sourcemap ?? cfg.sourcemap), mangle: (opts.mangle ?? cfg.mangle), typecheck: (opts.typecheck ?? cfg.typecheck), jsx: (opts.jsx ?? cfg.jsx), jsxTarget: (opts.jsxTarget ?? cfg.jsxTarget), sourceFile: Path.relative(Path.dirname(outPath), abs), outputFile: Path.basename(outPath) });
253
+ if (!result.success) {
254
+ console.error(red((("\n✗ Compile failed — " + result.errors.length) + " error(s)")));
255
+ printErrors(result.errors, source, abs);
256
+ process.exit(1);
257
+ }
258
+ const elapsed = (Date.now() - t0);
259
+ if (opts.stdout) {
260
+ console.log(result.output);
261
+ return;
262
+ }
263
+ Fs.writeFileSync(outPath, result.output, "utf8");
264
+ let extra = "";
265
+ if ((opts.sourcemap && result.sourceMap)) {
266
+ Fs.writeFileSync(mapPath, result.sourceMap, "utf8");
267
+ extra = gray((" + " + Path.relative(process.cwd(), mapPath)));
268
+ }
269
+ const rel = Path.relative(process.cwd(), abs);
270
+ const relO = Path.relative(process.cwd(), outPath);
271
+ console.log((((((green("✓ ") + gray((("(" + elapsed) + "ms) "))) + blue(rel)) + gray(" → ")) + cyan(relO)) + extra));
272
+ if ((result.typeErrors && (result.typeErrors.length > 0))) {
273
+ console.warn(yellow((("\n⚠ " + result.typeErrors.length) + " type warning(s)")));
274
+ printErrors(result.typeErrors, source, abs);
275
+ }
276
+ }
277
+ function cmdRun(filePath, opts) {
278
+ const { source, abs } = readFluxFile(filePath);
279
+ const result = transpile(source, { jsx: (opts.jsx ?? false), jsxTarget: (opts.jsxTarget ?? "browser"), mangle: false });
280
+ if (!result.success) {
281
+ console.error(red("\n✗ Compile error"));
282
+ printErrors(result.errors, source, abs);
283
+ process.exit(1);
284
+ }
285
+ const tmpPath = Path.join(Os.tmpdir(), (("_flux_run_" + Date.now()) + ".js"));
286
+ Fs.writeFileSync(tmpPath, result.output, "utf8");
287
+ console.log(gray((("▶ Running " + Path.basename(abs)) + " ...\n")));
288
+ try {
289
+ require(tmpPath);
290
+ }
291
+ catch (e) {
292
+ console.error(red(("\n[Runtime Error] " + e.message)));
293
+ process.exitCode = 1;
294
+ }
295
+ finally {
296
+ try {
297
+ Fs.unlinkSync(tmpPath);
298
+ }
299
+ catch (e2) {
300
+ null;
301
+ }
302
+ }
303
+ }
304
+ function runCheck(abs) {
305
+ const source = Fs.readFileSync(abs, "utf8");
306
+ const baseName = Path.basename(abs);
307
+ const result = transpile(source, { check: true, typecheck: true });
308
+ if (!result.success) {
309
+ const n = result.errors.length;
310
+ const kind = (result.errors.some((e) => (e.name == "CheckError")) ? "static" : "syntax");
311
+ console.error(red((((((("\n✗ " + baseName) + ": ") + n) + " ") + kind) + " error(s)")));
312
+ printErrors(result.errors, source, abs);
313
+ return { ok: false, typeErrors: 0, warnings: 0 };
314
+ }
315
+ let allOk = true;
316
+ const typeErrors = (result.typeErrors ?? []);
317
+ if ((typeErrors.length > 0)) {
318
+ console.error(red((((("\n✗ " + baseName) + ": ") + typeErrors.length) + " type error(s)")));
319
+ printErrors(typeErrors, source, abs);
320
+ allOk = false;
321
+ }
322
+ const warnings = (result.typeWarnings ?? []);
323
+ for (const w of warnings) {
324
+ const fileRef = (cyan(baseName) + (w.line ? yellow((":" + w.line)) : ""));
325
+ console.warn(((yellow(" ⚠ ") + fileRef) + gray((" " + w.message))));
326
+ if (w.hint) {
327
+ console.warn((cyan(" Hint: ") + gray(w.hint)));
328
+ }
329
+ }
330
+ const fnRe = /^(?:async )?function \w/gm;
331
+ const clsRe = /^class \w/gm;
332
+ const fns = (result.output.match(fnRe) ?? []).length;
333
+ const cls = (result.output.match(clsRe) ?? []).length;
334
+ if (allOk) {
335
+ console.log((((green("✓ ") + cyan(baseName)) + gray(" — no errors")) + gray(((((((" Functions: " + fns) + " | Classes: ") + cls) + " | JS output: ") + result.output.split("\n").length) + " lines"))));
336
+ }
337
+ return { ok: allOk, typeErrors: typeErrors.length, warnings: warnings.length };
338
+ }
339
+ function cmdCheck(filePaths, opts) {
340
+ if ((filePaths.length == 0)) {
341
+ console.error(red("✗ No files specified"));
342
+ process.exit(1);
343
+ }
344
+ let totalErrors = 0;
345
+ let totalWarnings = 0;
346
+ for (const filePath of filePaths) {
347
+ const abs = Path.resolve(filePath);
348
+ if (!Fs.existsSync(abs)) {
349
+ console.error(red(("✗ File not found: " + filePath)));
350
+ totalErrors = (totalErrors + 1);
351
+ continue;
352
+ }
353
+ const r = runCheck(abs);
354
+ if (!r.ok) {
355
+ totalErrors = (totalErrors + 1);
356
+ }
357
+ totalWarnings = (totalWarnings + r.warnings);
358
+ }
359
+ console.log();
360
+ if ((totalErrors > 0)) {
361
+ console.error(red((("✗ " + totalErrors) + " file(s) with errors")));
362
+ process.exit(1);
363
+ }
364
+ else {
365
+ console.log((green("✓ All files OK") + ((totalWarnings > 0) ? yellow(((" (" + totalWarnings) + " warning(s))")) : "")));
366
+ }
367
+ }
368
+ function cmdFmt(filePaths, opts) {
369
+ let changed = 0;
370
+ for (const filePath of filePaths) {
371
+ const abs = Path.resolve(filePath);
372
+ if (!Fs.existsSync(abs)) {
373
+ console.error(red(("✗ Not found: " + filePath)));
374
+ continue;
375
+ }
376
+ const source = Fs.readFileSync(abs, "utf8");
377
+ const formatted = format(source);
378
+ if ((formatted != source)) {
379
+ if (!opts.stdout) {
380
+ Fs.writeFileSync(abs, formatted, "utf8");
381
+ console.log(((green("✓ ") + gray("Formatted ")) + cyan(Path.relative(process.cwd(), abs))));
382
+ changed = (changed + 1);
383
+ }
384
+ else {
385
+ console.log(formatted);
386
+ }
387
+ }
388
+ else {
389
+ console.log(((gray("○ ") + gray("No changes: ")) + Path.relative(process.cwd(), abs)));
390
+ }
391
+ }
392
+ if ((!opts.stdout && (changed > 0))) {
393
+ console.log();
394
+ console.log(green((("✓ " + changed) + " file(s) formatted")));
395
+ }
396
+ }
397
+ function cmdLint(filePaths, opts) {
398
+ let hasErrors = false;
399
+ for (const filePath of filePaths) {
400
+ const abs = Path.resolve(filePath);
401
+ if (!Fs.existsSync(abs)) {
402
+ console.error(red(("✗ Not found: " + filePath)));
403
+ continue;
404
+ }
405
+ const source = Fs.readFileSync(abs, "utf8");
406
+ const result = lint(source);
407
+ const name = Path.relative(process.cwd(), abs);
408
+ if (((result.errors.length == 0) && (result.warnings.length == 0))) {
409
+ console.log(((green("✓ ") + cyan(name)) + gray(" — clean")));
410
+ }
411
+ else {
412
+ for (const e of result.errors) {
413
+ console.error((((red(" error ") + cyan(((name + ":") + (e.line ?? "?")))) + " ") + e.message));
414
+ hasErrors = true;
415
+ }
416
+ for (const w of result.warnings) {
417
+ console.warn((((yellow(" warn ") + cyan(((name + ":") + (w.line ?? "?")))) + " ") + w.message));
418
+ }
419
+ }
420
+ }
421
+ if (hasErrors) {
422
+ process.exit(1);
423
+ }
424
+ }
425
+ function cmdBundle(entryPath, opts) {
426
+ const abs = Path.resolve(entryPath);
427
+ if (!Fs.existsSync(abs)) {
428
+ console.error(red(("✗ File not found: " + entryPath)));
429
+ process.exit(1);
430
+ }
431
+ const outFile = (opts.out ?? Path.join(Path.dirname(abs), (Path.basename(abs, ".flux") + ".bundle.js")));
432
+ const t0 = Date.now();
433
+ const result = bundle(abs);
434
+ if (!result.success) {
435
+ console.error(red("\n✗ Bundle failed:\n"));
436
+ for (const e of result.errors) {
437
+ console.error(red((" " + e.message)));
438
+ }
439
+ process.exit(1);
440
+ }
441
+ const elapsed = (Date.now() - t0);
442
+ if (opts.stdout) {
443
+ console.log(result.code);
444
+ return;
445
+ }
446
+ Fs.writeFileSync(outFile, result.code, "utf8");
447
+ const kb = (result.code.length / 1024).toFixed(1);
448
+ console.log((((((green("✓ Bundle done") + gray(((" (" + elapsed) + "ms) "))) + Path.basename(abs)) + gray(((" + " + (result.modules - 1)) + " module(s) → "))) + cyan(Path.relative(process.cwd(), outFile))) + gray(((" [" + kb) + " KB]"))));
449
+ }
450
+ function cmdWatch(filePath, opts) {
451
+ const abs = Path.resolve(filePath);
452
+ if (!Fs.existsSync(abs)) {
453
+ console.error(red(("✗ File not found: " + filePath)));
454
+ process.exit(1);
455
+ }
456
+ console.log(((cyan("◉ Watching ") + Path.relative(process.cwd(), abs)) + gray(" (Ctrl+C to stop)\n")));
457
+ cmdCompile(filePath, opts);
458
+ let timer = null;
459
+ function onDebounce() {
460
+ console.log((gray((("\n" + new Date().toLocaleTimeString()) + " — ")) + blue("change detected")));
461
+ cmdCompile(filePath, opts);
462
+ }
463
+ function onChange() {
464
+ if (timer) {
465
+ clearTimeout(timer);
466
+ }
467
+ timer = setTimeout(onDebounce, 80);
468
+ }
469
+ Fs.watch(abs, onChange);
470
+ }
471
+ function cmdTokens(filePath, opts) {
472
+ const { source } = readFluxFile(filePath);
473
+ const { Lexer } = require(Path.join(__dirname, "lexer.js"));
474
+ const lexer = new Lexer(source);
475
+ const tokens = lexer.tokenize();
476
+ console.log(gray((("Tokens (" + tokens.length) + "):\n")));
477
+ for (const tok of tokens) {
478
+ const loc = (tok.line ? gray((((" " + tok.line) + ":") + (tok.col ?? 1))) : "");
479
+ const val_ = ((tok.value != null) ? cyan((" " + JSON.stringify(tok.value))) : "");
480
+ console.log((((" " + yellow(tok.type.padEnd(16))) + val_) + loc));
481
+ }
482
+ }
483
+ function cmdAst(filePath, opts) {
484
+ const { source } = readFluxFile(filePath);
485
+ const result = transpile(source, { });
486
+ if (!result.success) {
487
+ printErrors(result.errors, source, filePath);
488
+ process.exit(1);
489
+ }
490
+ console.log(JSON.stringify(result.ast, null, 2));
491
+ }
492
+ function cmdRepl(opts) {
493
+ const readline = require("readline");
494
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true, prompt: (cyan("flux") + gray("> ")) });
495
+ console.log();
496
+ console.log((bold("Flux Lang Interactive REPL") + gray(((((" v" + VERSION) + " [") + STAGE) + "]"))));
497
+ console.log(gray("Type Flux code to compile and run it. Ctrl+C or .exit to quit.\n"));
498
+ rl.prompt();
499
+ let multiLine = "";
500
+ let inBlock = false;
501
+ function onLine(rawLine) {
502
+ const line = rawLine;
503
+ if (((line.trim() == ".exit") || (line.trim() == ".quit"))) {
504
+ console.log(gray("\nBye!"));
505
+ process.exit(0);
506
+ }
507
+ if ((line.trim() == ".help")) {
508
+ console.log(gray(" .exit — quit"));
509
+ console.log(gray(" .clear — clear screen"));
510
+ console.log(gray(" .help — this message"));
511
+ rl.prompt();
512
+ return;
513
+ }
514
+ if ((line.trim() == ".clear")) {
515
+ console.clear();
516
+ rl.prompt();
517
+ return;
518
+ }
519
+ const needsContinue = (line.trimEnd().endsWith(":") || inBlock);
520
+ if ((needsContinue && (line.trim() != ""))) {
521
+ multiLine = ((multiLine + line) + "\n");
522
+ inBlock = true;
523
+ process.stdout.write(gray("... "));
524
+ return;
525
+ }
526
+ const src = (inBlock ? multiLine : line);
527
+ multiLine = "";
528
+ inBlock = false;
529
+ if (!src.trim()) {
530
+ rl.prompt();
531
+ return;
532
+ }
533
+ try {
534
+ const result = transpile(src, { check: false });
535
+ if (result.success) {
536
+ const tmpPath = Path.join(Os.tmpdir(), (("_flux_repl_" + Date.now()) + ".js"));
537
+ Fs.writeFileSync(tmpPath, result.output, "utf8");
538
+ try {
539
+ const out = require(tmpPath);
540
+ if ((out != undefined)) {
541
+ console.log((green("← ") + JSON.stringify(out)));
542
+ }
543
+ }
544
+ catch (e) {
545
+ console.error((red("Runtime: ") + e.message));
546
+ }
547
+ finally {
548
+ try {
549
+ Fs.unlinkSync(tmpPath);
550
+ }
551
+ catch (e2) {
552
+ null;
553
+ }
554
+ }
555
+ }
556
+ else {
557
+ for (const e of result.errors) {
558
+ console.error((red("✗ ") + e.message));
559
+ }
560
+ }
561
+ }
562
+ catch (e3) {
563
+ console.error((red("Error: ") + e3.message));
564
+ }
565
+ rl.prompt();
566
+ }
567
+ function onClose() {
568
+ console.log(gray("\nBye!"));
569
+ process.exit(0);
570
+ }
571
+ rl.on("line", onLine);
572
+ rl.on("close", onClose);
573
+ }
574
+ function cmdInit(name, opts) {
575
+ const projectName = (name ?? "my-flux-app");
576
+ const dir = Path.resolve(projectName);
577
+ if (Fs.existsSync(dir)) {
578
+ console.error(red(("✗ Directory already exists: " + projectName)));
579
+ process.exit(1);
580
+ }
581
+ Fs.mkdirSync(dir, { recursive: true });
582
+ Fs.mkdirSync(Path.join(dir, "src"), { recursive: true });
583
+ Fs.mkdirSync(Path.join(dir, "tests"), { recursive: true });
584
+ const mainFlux = "// {projectName} — built with Flux Lang v{VERSION}\n// Run: flux run src/main.flux\n\n// ── Algebraic Data Types + Pattern Matching ───────────────────\ntype Shape = Circle(radius) | Rect(width, height) | Triangle(base, height)\n\nfn area(shape):\n match shape:\n when Circle(r): return Math.PI * r * r\n when Rect(w, h): return w * h\n when Triangle(b, h): return 0.5 * b * h\n\nfn describe(shape):\n match shape:\n when Circle(r): return \"Circle(r={r:.2f})\"\n when Rect(w, h): return \"Rect({w:.1f}x{h:.1f})\"\n when Triangle(b, h): return \"Triangle(b={b:.1f}, h={h:.1f})\"\n\n// ── Result type ───────────────────────────────────────────────\ntype Result = Ok(value) | Err(message)\n\nfn safeDivide(a, b):\n if b == 0: return Err(\"division by zero\")\n return Ok(a / b)\n\n// ── Utility functions ─────────────────────────────────────────\nfn greet(name): return \"Hello from Flux, {name}!\"\n\nfn formatList(items):\n if items.length == 0: return \"(empty)\"\n return \"[\" + items.join(\", \") + \"]\"\n\nfn clamp(value, lo, hi):\n if value < lo: return lo\n if value > hi: return hi\n return value\n\n// ── Pipe operator + stdlib ────────────────────────────────────\nval numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\nval processed = numbers\n |> filter(n -> n > 3)\n |> map(n -> n * n)\n |> sort\n\n// ── Main ──────────────────────────────────────────────────────\nprint(greet(\"{projectName}\"))\nprint(\"Squares > 3: {formatList(processed)}\")\nprint(\"clamp(12, 0, 10) = {clamp(12, 0, 10)}\")\n\nval shapes = [Circle(5.0), Rect(8.0, 3.0), Triangle(6.0, 4.0)]\nfor shape in shapes:\n val a = area(shape)\n print(\"{describe(shape)} area={a:.2f}\")\n\nmatch safeDivide(10.0, 3.0):\n when Ok(v): print(\"10 / 3 = {v:.4f}\")\n when Err(e): print(\"Error: {e}\")";
585
+ const utilsFlux = "// Utility functions for {projectName}\n// Use with: flux bundle src/main.flux (bundles imports automatically)\n\nexport fn greet(name):\n return \"Hello from Flux, {name}!\"\n\nexport fn formatList(items):\n if items.length == 0: return \"(empty)\"\n return \"[\" + items.join(\", \") + \"]\"\n\nexport fn clamp(value, lo, hi):\n if value < lo: return lo\n if value > hi: return hi\n return value\n\nexport fn sum(nums):\n return nums.reduce((acc, x) -> acc + x, 0)\n\nexport fn average(nums):\n if nums.length == 0: return 0\n return sum(nums) / nums.length";
586
+ const testFlux = "// Tests for {projectName}\n// Run: flux test tests/\n\n// ── Functions under test ──────────────────────────────────────\nfn add(a, b):\n return a + b\n\nfn mul(a, b):\n return a * b\n\nfn clamp(value, lo, hi):\n if value < lo: return lo\n if value > hi: return hi\n return value\n\nfn average(nums):\n if nums.length == 0: return 0\n return nums.reduce((s, x) -> s + x, 0) / nums.length\n\ntype Result = Ok(value) | Err(message)\n\nfn safeDivide(a, b):\n if b == 0: return Err(\"div by zero\")\n return Ok(a / b)\n\n// ── Test functions ────────────────────────────────────────────\nfn test_add():\n assert(add(1, 2) == 3, \"1+2=3\")\n assert(add(0, 0) == 0, \"0+0=0\")\n assert(add(-1, 1) == 0, \"-1+1=0\")\n\nfn test_mul():\n assert(mul(3, 4) == 12, \"3*4=12\")\n assert(mul(0, 5) == 0, \"0*5=0\")\n\nfn test_clamp():\n assert(clamp(5, 0, 10) == 5, \"within range\")\n assert(clamp(-1, 0, 10) == 0, \"below min\")\n assert(clamp(15, 0, 10) == 10, \"above max\")\n\nfn test_average():\n assert(average([2, 4, 6]) == 4, \"average of 2,4,6\")\n assert(average([]) == 0, \"empty list\")\n\nfn test_safe_divide():\n val ok = safeDivide(10, 2)\n match ok:\n when Ok(v): assert(v == 5, \"10/2=5\")\n when Err(e): assert(false, \"unexpected error\")\n val err = safeDivide(10, 0)\n match err:\n when Ok(v): assert(false, \"expected error\")\n when Err(e): assert(e == \"div by zero\", \"error message\")\n\nfn test_pipe_operator():\n val result = [1, 2, 3, 4, 5] |> filter(n -> n > 2) |> map(n -> n * 2)\n assert(result.length == 3, \"filter length\")\n assert(result[0] == 6, \"first element\")\n assert(result[2] == 10, \"last element\")";
587
+ const fluxJson = { name: projectName, version: "1.0.0", description: (("A Flux Lang v" + VERSION) + " project"), author: "", license: "MIT", entry: "src/main.flux", outDir: "dist", sourcemap: false, typecheck: true, scripts: { start: "flux run src/main.flux", build: "flux bundle src/main.flux -o dist/bundle.js", dev: "flux watch src/main.flux", check: "flux check src/main.flux", test: "flux test tests/", fmt: "flux fmt src/", lint: "flux lint src/" }, dependencies: { }, devDependencies: { "@xnoxs/flux-lang": ("^" + VERSION) } };
588
+ const gitignore = "node_modules/\ndist/\nflux_modules/\n*.js.map\n.DS_Store\n";
589
+ const readme = "# {projectName}\n\nA project built with [Flux Lang](https://flux-lang.dev) v{VERSION} — the self-hosted compiler.\n\n## Quick Start\n\n```bash\nflux run src/main.flux\n```\n\n## Project Structure\n\n```\n{projectName}/\n├── src/\n│ ├── main.flux # Entry point\n│ └── utils.flux # Utility functions\n├── tests/\n│ └── main.test.flux # Test suite\n├── flux.json # Project config\n└── README.md\n```\n\n## Commands\n\n| Command | Description |\n|---|---|\n| `flux run src/main.flux` | Run the project |\n| `flux bundle src/main.flux -o dist/bundle.js` | Bundle to single file |\n| `flux watch src/main.flux` | Watch mode |\n| `flux check src/main.flux` | Type check + static analysis |\n| `flux test tests/` | Run all tests |\n| `flux fmt src/` | Format source code |\n| `flux lint src/` | Lint for issues |\n| `flux add <package>` | Add a dependency |";
590
+ const allFiles = [(projectName + "/src/main.flux"), (projectName + "/src/utils.flux"), (projectName + "/tests/main.test.flux"), (projectName + "/flux.json"), (projectName + "/.gitignore"), (projectName + "/README.md")];
591
+ Fs.writeFileSync(Path.join(dir, "src", "main.flux"), mainFlux, "utf8");
592
+ Fs.writeFileSync(Path.join(dir, "src", "utils.flux"), utilsFlux, "utf8");
593
+ Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
594
+ Fs.writeFileSync(Path.join(dir, "flux.json"), (JSON.stringify(fluxJson, null, 2) + "\n"), "utf8");
595
+ Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
596
+ Fs.writeFileSync(Path.join(dir, "README.md"), readme, "utf8");
597
+ console.log();
598
+ console.log((green("✓ Created: ") + bold((projectName + "/"))));
599
+ console.log();
600
+ console.log(gray(" Files:"));
601
+ for (const f of allFiles) {
602
+ console.log((" " + cyan(f)));
603
+ }
604
+ console.log();
605
+ console.log(bold(" Next steps:"));
606
+ console.log((" " + yellow(("cd " + projectName))));
607
+ console.log((" " + yellow("flux run src/main.flux")));
608
+ console.log();
609
+ console.log(gray(" Or run the test suite:"));
610
+ console.log((" " + yellow("flux test tests/")));
611
+ console.log();
612
+ }
613
+ function cmdSelfHosted(sub, opts) {
614
+ const SELF = Path.join(__dirname, ".");
615
+ const coreModules = ["css-preprocessor", "checker", "type-checker", "jsx", "lexer", "parser", "codegen", "transpiler"];
616
+ const extModules = ["formatter", "sourcemap", "stdlib", "mangler", "linter", "bundler", "test-runner"];
617
+ const newModules = ["config", "pkg", "cli"];
618
+ const allModules = [...coreModules, ...extModules, ...newModules];
619
+ if ((sub == "build")) {
620
+ const { execSync } = require("child_process");
621
+ const BIN = Path.join(__dirname, "../../bin/flux.js");
622
+ console.log();
623
+ console.log(bold("⚡ Flux Bootstrap — Stage 0"));
624
+ console.log(gray(" Compiling self-hosted sources with stage-0 compiler...\n"));
625
+ let ok = 0;
626
+ let failed = 0;
627
+ for (const name of allModules) {
628
+ const src = Path.join(SELF, (name + ".flux"));
629
+ const out = Path.join(SELF, (name + ".js"));
630
+ if (!Fs.existsSync(src)) {
631
+ console.log(gray(((" ○ " + name) + ".flux (skipped — not found)")));
632
+ continue;
633
+ }
634
+ try {
635
+ const cmd = "node \"${BIN}\" compile \"${src}\" -o \"${out}\" --no-mangle";
636
+ execSync(cmd, { cwd: Path.join(__dirname, "../.."), stdio: "pipe" });
637
+ console.log((((green(" ✓ ") + name) + ".flux → ") + gray((name + ".js"))));
638
+ ok = (ok + 1);
639
+ }
640
+ catch (e) {
641
+ console.error((((red(" ✗ ") + name) + ".flux — ") + e.message.split("\n")[0]));
642
+ failed = (failed + 1);
643
+ }
644
+ }
645
+ console.log();
646
+ if ((failed == 0)) {
647
+ console.log((green("✓ Bootstrap complete! ") + gray((ok + " modules compiled"))));
648
+ console.log();
649
+ console.log(" Activate self-hosted mode:");
650
+ console.log((" " + yellow("FLUX_SELF_HOSTED=1 flux <command>")));
651
+ }
652
+ else {
653
+ console.log(red((((("✗ " + failed) + " module(s) failed, ") + ok) + " succeeded")));
654
+ }
655
+ console.log();
656
+ return;
657
+ }
658
+ if ((sub == "verify")) {
659
+ console.log(cyan("\n Verifying self-hosted compiler output...\n"));
660
+ try {
661
+ const selfMod = require(Path.join(SELF, "transpiler.js"));
662
+ const stage0Mod = require(Path.join(__dirname, "../transpiler.js"));
663
+ const testSrc = "fn greet(name): return \"Hello, {name}!\"\nval msg = greet(\"Flux\")";
664
+ const r0 = stage0Mod.transpile(testSrc, { });
665
+ const r1 = selfMod.transpile(testSrc, { });
666
+ function norm(s) {
667
+ return s.replace(/\/\/.*/g, "").replace(/\s+/g, " ").trim();
668
+ }
669
+ if ((norm(r0.output) == norm(r1.output))) {
670
+ console.log(green("✓ Self-hosted output matches stage-0!"));
671
+ console.log(green("✓ Flux is fully self-hosting."));
672
+ }
673
+ else {
674
+ console.log(yellow("⚠ Outputs differ (minor differences are OK)"));
675
+ console.log((gray(" Stage-0: ") + r0.output.split("\n")[0]));
676
+ console.log((gray(" Self-hosted:") + r1.output.split("\n")[0]));
677
+ }
678
+ }
679
+ catch (e) {
680
+ console.error(red(("✗ Verify failed: " + e.message)));
681
+ }
682
+ console.log();
683
+ return;
684
+ }
685
+ console.log();
686
+ console.log(bold(" Flux Self-Hosted Compiler Status\n"));
687
+ const selfActive = (process.env.FLUX_SELF_HOSTED == "1");
688
+ if (selfActive) {
689
+ console.log(((" Mode: " + green("● ACTIVE")) + gray(" (using self-hosted compiler)")));
690
+ }
691
+ else {
692
+ console.log((((" Mode: " + gray("○ INACTIVE")) + " ") + gray("(using stage-0 compiler)")));
693
+ console.log((" Toggle: " + yellow("FLUX_SELF_HOSTED=1 flux <command>")));
694
+ console.log((" Build: " + yellow("flux self-hosted build")));
695
+ }
696
+ console.log();
697
+ console.log(bold(((" Core Pipeline (" + coreModules.length) + " modules)")));
698
+ for (const name of coreModules) {
699
+ const jsPath = Path.join(SELF, (name + ".js"));
700
+ const flxPath = Path.join(SELF, (name + ".flux"));
701
+ const hasJs = Fs.existsSync(jsPath);
702
+ const hasFlx = Fs.existsSync(flxPath);
703
+ const kb = (hasJs ? (Fs.statSync(jsPath).size / 1024).toFixed(1) : "?");
704
+ const sym = (hasJs ? green("✓") : red("✗"));
705
+ const note = (!hasFlx ? gray(" (no .flux source)") : "");
706
+ console.log((((((" " + sym) + " ") + (name + ".flux").padEnd(26)) + gray((kb + " KB"))) + note));
707
+ }
708
+ console.log();
709
+ console.log(bold(((" Extended Toolchain (" + extModules.length) + " modules)")));
710
+ for (const name of extModules) {
711
+ const jsPath = Path.join(SELF, (name + ".js"));
712
+ const flxPath = Path.join(SELF, (name + ".flux"));
713
+ const hasJs = Fs.existsSync(jsPath);
714
+ const hasFlx = Fs.existsSync(flxPath);
715
+ const kb = (hasJs ? (Fs.statSync(jsPath).size / 1024).toFixed(1) : "?");
716
+ const sym = (hasJs ? green("✓") : red("✗"));
717
+ const note = (!hasFlx ? gray(" (no .flux source)") : "");
718
+ console.log((((((" " + sym) + " ") + (name + ".flux").padEnd(26)) + gray((kb + " KB"))) + note));
719
+ }
720
+ console.log();
721
+ console.log(bold(((" Ecosystem (" + newModules.length) + " modules)")));
722
+ for (const name of newModules) {
723
+ const jsPath = Path.join(SELF, (name + ".js"));
724
+ const flxPath = Path.join(SELF, (name + ".flux"));
725
+ const hasJs = Fs.existsSync(jsPath);
726
+ const hasFlx = Fs.existsSync(flxPath);
727
+ const kb = (hasJs ? (Fs.statSync(jsPath).size / 1024).toFixed(1) : "?");
728
+ const sym = (hasJs ? green("✓") : yellow("○"));
729
+ console.log(((((" " + sym) + " ") + (name + ".flux").padEnd(26)) + (hasJs ? gray((kb + " KB")) : yellow("not built"))));
730
+ }
731
+ console.log();
732
+ }
733
+ function cmdVersion(opts) {
734
+ if (noColor) {
735
+ console.log(("flux-lang v" + VERSION));
736
+ return;
737
+ }
738
+ console.log((cyan(bold("⚡ Flux Lang")) + gray(((((" v" + VERSION) + " [") + STAGE) + "]"))));
739
+ }
740
+ function main() {
741
+ const { positional, opts } = parseArgs(process.argv);
742
+ const cmd = (positional[0] ?? "help");
743
+ if (cmd === "compile") {
744
+ return cmdCompile(positional[1], opts);
745
+ }
746
+ else if (cmd === "run") {
747
+ return cmdRun(positional[1], opts);
748
+ }
749
+ else if (cmd === "check") {
750
+ return cmdCheck(positional.slice(1), opts);
751
+ }
752
+ else if (cmd === "fmt") {
753
+ return cmdFmt(positional.slice(1), opts);
754
+ }
755
+ else if (cmd === "format") {
756
+ return cmdFmt(positional.slice(1), opts);
757
+ }
758
+ else if (cmd === "lint") {
759
+ return cmdLint(positional.slice(1), opts);
760
+ }
761
+ else if (cmd === "bundle") {
762
+ return cmdBundle(positional[1], opts);
763
+ }
764
+ else if (cmd === "watch") {
765
+ return cmdWatch(positional[1], opts);
766
+ }
767
+ else if (cmd === "tokens") {
768
+ return cmdTokens(positional[1], opts);
769
+ }
770
+ else if (cmd === "ast") {
771
+ return cmdAst(positional[1], opts);
772
+ }
773
+ else if (cmd === "repl") {
774
+ return cmdRepl(opts);
775
+ }
776
+ else if (cmd === "init") {
777
+ return cmdInit(positional[1], opts);
778
+ }
779
+ else if (cmd === "add") {
780
+ return cmdAdd(positional.slice(1), opts);
781
+ }
782
+ else if (cmd === "remove") {
783
+ return cmdRemove(positional.slice(1), opts);
784
+ }
785
+ else if (cmd === "rm") {
786
+ return cmdRemove(positional.slice(1), opts);
787
+ }
788
+ else if (cmd === "install") {
789
+ return cmdInstall(opts);
790
+ }
791
+ else if (cmd === "i") {
792
+ return cmdInstall(opts);
793
+ }
794
+ else if (cmd === "list") {
795
+ return cmdList(opts);
796
+ }
797
+ else if (cmd === "ls") {
798
+ return cmdList(opts);
799
+ }
800
+ else if (cmd === "search") {
801
+ return cmdSearch(positional[1], opts);
802
+ }
803
+ else if (cmd === "info") {
804
+ return cmdInfo(positional[1], opts);
805
+ }
806
+ else if (cmd === "publish") {
807
+ return cmdPublish(opts);
808
+ }
809
+ else if (cmd === "self-hosted") {
810
+ return cmdSelfHosted(positional[1], opts);
811
+ }
812
+ else if (cmd === "version") {
813
+ return cmdVersion(opts);
814
+ }
815
+ else if (cmd === "-v") {
816
+ return cmdVersion(opts);
817
+ }
818
+ else if (cmd === "--version") {
819
+ return cmdVersion(opts);
820
+ }
821
+ else if (cmd === "help") {
822
+ return showHelp();
823
+ }
824
+ else if (cmd === "--help") {
825
+ return showHelp();
826
+ }
827
+ else if (cmd === "-h") {
828
+ return showHelp();
829
+ }
830
+ else {
831
+ console.error(red(("✗ Unknown command: " + cmd)));
832
+ console.error(gray(" Run: flux help"));
833
+ process.exit(1);
834
+ }
835
+ }
836
+ main();