@xnoxs/flux-lang 4.0.8 → 4.1.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/src/self/cli.js CHANGED
@@ -1,10 +1,10 @@
1
- /* compiled from src/self/cli.flux */
2
- 'use strict';
3
1
  // ── Flux stdlib ──
4
2
 
5
- function forEach(arr, fn) { arr.forEach(fn); return arr; }
3
+ function map(arr, fn) { return arr.map(fn); }
6
4
 
7
- function find(arr, fn) { return arr.find(fn); }
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
8
 
9
9
  function some(arr, fn) { return arr.some(fn); }
10
10
 
@@ -14,6 +14,10 @@ function clamp(val, min, max) {
14
14
  return Math.min(Math.max(val, min), max);
15
15
  }
16
16
 
17
+ function sum(arr) {
18
+ return arr.reduce(function(a, b) { return a + b; }, 0);
19
+ }
20
+
17
21
  function max(arr) {
18
22
  if (arguments.length > 1) return Math.max.apply(null, arguments);
19
23
  return Math.max.apply(null, arr);
@@ -92,20 +96,32 @@ function showHelp() {
92
96
  console.log(bold("USAGE:"));
93
97
  console.log(" flux <command> [options]\n");
94
98
  console.log(bold("COMPILER:"));
95
- const compilerCmds = [["compile <file.flux>", "Compile .flux → .js"], ["bundle <entry.flux>", "Bundle multi-file project into one .js"], ["run <file.flux>", "Compile and run a single-file script"], ["watch <file.flux>", "Watch for changes, auto-compile"], ["check <file.flux>", "Type-check and static analysis"]];
96
- compilerCmds.forEach((pair) => console.log((((" " + green(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
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
+ }
97
104
  console.log();
98
105
  console.log(bold("TOOLING:"));
99
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)"]];
100
- toolCmds.forEach((pair) => console.log((((" " + green(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
107
+ for (const __item__ of toolCmds) {
108
+ const [cmd, desc] = __item__;
109
+ console.log((((" " + green(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
110
+ }
101
111
  console.log();
102
112
  console.log(bold("PACKAGE MANAGER:"));
103
- const pkgCmds = [["init [name] [--template]", "Scaffold a new Flux project"], ["add <pkg[@version]>", "Add a dependency (npm install --save)"], ["remove <pkg>", "Remove a dependency (npm uninstall)"], ["install", "Install all dependencies (npm install)"], ["list", "List installed packages"], ["search <query>", "Search the npm registry"], ["info <pkg>", "Show package details"], ["publish", "Publish package to npm registry"]];
104
- pkgCmds.forEach((pair) => console.log((((" " + cyan(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
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
+ }
105
118
  console.log();
106
119
  console.log(bold("SELF-HOSTED:"));
107
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"]];
108
- selfCmds.forEach((pair) => console.log((((" " + yellow(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
121
+ for (const __item__ of selfCmds) {
122
+ const [cmd, desc] = __item__;
123
+ console.log((((" " + yellow(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
124
+ }
109
125
  console.log();
110
126
  console.log(bold("OPTIONS:"));
111
127
  console.log(((" " + yellow("--out, -o <file> ")) + " Output file"));
@@ -115,12 +131,11 @@ function showHelp() {
115
131
  console.log(((" " + yellow("--typecheck ")) + " Enable type checking"));
116
132
  console.log(((" " + yellow("--stdout ")) + " Print to terminal"));
117
133
  console.log(((" " + yellow("--no-color ")) + " Disable colors"));
118
- console.log(((" " + yellow("--template ")) + " Init template: script, server, webapp"));
119
134
  console.log();
120
135
  }
121
136
  function parseArgs(argv) {
122
137
  const args = argv.slice(2);
123
- const opts = { out: null, sourcemap: false, mangle: false, typecheck: false, strict: false, stdout: false, watch: false, dev: false, verbose: false, jsx: false, jsxTarget: "browser", template: null };
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" };
124
139
  const positional = [];
125
140
  let i = 0;
126
141
  while ((i < args.length)) {
@@ -160,10 +175,6 @@ function parseArgs(argv) {
160
175
  i = (i + 1);
161
176
  opts.jsxTarget = args[i];
162
177
  }
163
- else if (((a == "--template") || (a == "-t"))) {
164
- i = (i + 1);
165
- opts.template = args[i];
166
- }
167
178
  else if (!a.startsWith("--")) {
168
179
  positional.push(a);
169
180
  }
@@ -232,21 +243,6 @@ function printErrors(errors, source, filePath) {
232
243
  }
233
244
  console.error();
234
245
  }
235
- function findFluxImports(source) {
236
- const found = [];
237
- const lines = source.split("\n");
238
- for (const line of lines) {
239
- const trimmed = line.trim();
240
- const m = trimmed.match(/^import\s+.+\s+from\s+["'](\.[^"']+)["']/);
241
- if (m) {
242
- const src = m[1];
243
- if (((!src.endsWith(".js") && !src.endsWith(".json")) && !src.endsWith(".node"))) {
244
- found.push(src);
245
- }
246
- }
247
- }
248
- return found;
249
- }
250
246
  function cmdCompile(filePath, opts) {
251
247
  const { source, abs } = readFluxFile(filePath);
252
248
  const cfg = loadConfig(Path.dirname(abs));
@@ -280,21 +276,6 @@ function cmdCompile(filePath, opts) {
280
276
  }
281
277
  function cmdRun(filePath, opts) {
282
278
  const { source, abs } = readFluxFile(filePath);
283
- const fluxImports = findFluxImports(source);
284
- if ((fluxImports.length > 0)) {
285
- const example = fluxImports[0];
286
- const entryRel = Path.relative(process.cwd(), abs);
287
- const outFile = (Path.basename(abs, ".flux") + ".js");
288
- console.error(red("\n✗ Cannot run a multi-file Flux project with `flux run`.\n"));
289
- console.error((" Found: " + yellow((("import ... from \"" + example) + "\""))));
290
- console.error();
291
- console.error(((" Use " + cyan("flux bundle")) + " to compile all files into one, then run it:\n"));
292
- console.error((" " + yellow(((("flux bundle " + entryRel) + " -o dist/") + outFile))));
293
- console.error((" " + yellow(("node dist/" + outFile))));
294
- console.error();
295
- console.error(((" Or use " + cyan("flux run")) + " only for single-file scripts (no inter-file .flux imports).\n"));
296
- process.exit(1);
297
- }
298
279
  const result = transpile(source, { jsx: (opts.jsx ?? false), jsxTarget: (opts.jsxTarget ?? "browser"), mangle: false });
299
280
  if (!result.success) {
300
281
  console.error(red("\n✗ Compile error"));
@@ -462,10 +443,6 @@ function cmdBundle(entryPath, opts) {
462
443
  console.log(result.code);
463
444
  return;
464
445
  }
465
- const outDir = Path.dirname(Path.resolve(outFile));
466
- if (!Fs.existsSync(outDir)) {
467
- Fs.mkdirSync(outDir, { recursive: true });
468
- }
469
446
  Fs.writeFileSync(outFile, result.code, "utf8");
470
447
  const kb = (result.code.length / 1024).toFixed(1);
471
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]"))));
@@ -596,7 +573,6 @@ function cmdRepl(opts) {
596
573
  }
597
574
  function cmdInit(name, opts) {
598
575
  const projectName = (name ?? "my-flux-app");
599
- const template = (opts.template ?? "script");
600
576
  const dir = Path.resolve(projectName);
601
577
  if (Fs.existsSync(dir)) {
602
578
  console.error(red(("✗ Directory already exists: " + projectName)));
@@ -605,77 +581,38 @@ function cmdInit(name, opts) {
605
581
  Fs.mkdirSync(dir, { recursive: true });
606
582
  Fs.mkdirSync(Path.join(dir, "src"), { recursive: true });
607
583
  Fs.mkdirSync(Path.join(dir, "tests"), { recursive: true });
608
- const gitignore = "node_modules/\ndist/\n*.js.map\n.DS_Store\n";
609
- const testFlux = "// Tests for {projectName}\n// Run: flux test tests/\n\nfn add(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 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_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\")";
610
- if ((template == "script")) {
611
- const mainFlux = "// {projectName} a Flux script\n// Run: npm run dev (or: node_modules/.bin/flux run src/main.flux)\n\nimport Fs from \"fs\"\nimport Path from \"path\"\n\nfn main():\n print(\"Hello from Flux!\")\n print(\"Node version: {process.version}\")\n\nmain()";
612
- const pkgJson = { name: projectName, version: "1.0.0", description: "A Flux project", main: "dist/main.js", scripts: { dev: "flux run src/main.flux", build: "flux bundle src/main.flux -o dist/main.js", start: "node dist/main.js", test: "flux test tests/" }, flux: { entry: "src/main.flux", outDir: "dist", mangle: false, sourcemap: false }, dependencies: { "@xnoxs/flux-lang": ("^" + VERSION) }, devDependencies: { } };
613
- Fs.writeFileSync(Path.join(dir, "src", "main.flux"), mainFlux, "utf8");
614
- Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
615
- Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
616
- Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
617
- console.log();
618
- console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("script")));
619
- console.log();
620
- console.log(bold(" Next steps:"));
621
- console.log((" " + yellow(("cd " + projectName))));
622
- console.log((" " + yellow("npm install")));
623
- console.log((" " + yellow("npm run dev")));
624
- console.log();
625
- console.log(gray(" Add packages:"));
626
- console.log((" " + yellow("flux add <package>")));
627
- console.log();
628
- return;
629
- }
630
- if ((template == "server")) {
631
- const serverFlux = "// {projectName} — Flux HTTP server\n// Run: npm run dev\n// Build: npm run build && npm start\n\nimport Express from \"express\"\n\nval app = Express()\nval PORT = process.env.PORT or 3000\n\napp.use(Express.json())\n\napp.get(\"/\", (req, res) ->\n res.json({ status: \"ok\", message: \"Hello from Flux!\", port: PORT })\n)\n\napp.listen(PORT, -> print(\"Server running at http://localhost:{PORT}\"))";
632
- const pkgJson = { name: projectName, version: "1.0.0", description: "A Flux HTTP server", main: "dist/server.js", scripts: { dev: "flux run src/server.flux", build: "flux bundle src/server.flux -o dist/server.js", start: "node dist/server.js", test: "flux test tests/" }, flux: { entry: "src/server.flux", outDir: "dist", mangle: false, sourcemap: false }, dependencies: { "@xnoxs/flux-lang": ("^" + VERSION), express: "^4.18.0" }, devDependencies: { } };
633
- Fs.writeFileSync(Path.join(dir, "src", "server.flux"), serverFlux, "utf8");
634
- Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
635
- Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
636
- Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
637
- console.log();
638
- console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("server")));
639
- console.log();
640
- console.log(bold(" Next steps:"));
641
- console.log((" " + yellow(("cd " + projectName))));
642
- console.log((" " + yellow("npm install")));
643
- console.log((" " + yellow("npm run dev")));
644
- console.log();
645
- console.log(gray(" Build for production:"));
646
- console.log((" " + yellow("npm run build && npm start")));
647
- console.log();
648
- return;
649
- }
650
- if ((template == "webapp")) {
651
- const utilsFlux = "// Utility functions\nexport fn formatDate(date):\n val d = new Date(date)\n return d.toISOString().split(\"T\")[0]\n\nexport fn paginate(items, page, perPage):\n val start = (page - 1) * perPage\n return items.slice(start, start + perPage)";
652
- const routesFlux = "// Route handlers\nimport { formatDate, paginate } from \"./utils\" // inter-file .flux import\n\nval items = [\n { id: 1, name: \"Item A\", date: \"2024-01-01\" },\n { id: 2, name: \"Item B\", date: \"2024-02-01\" },\n { id: 3, name: \"Item C\", date: \"2024-03-01\" },\n]\n\nexport fn setupRoutes(app):\n app.get(\"/items\", (req, res) ->\n val page = parseInt(req.query.page or \"1\")\n val perPage = parseInt(req.query.per_page or \"10\")\n val data = paginate(items, page, perPage)\n res.json({ data, page })\n )\n\n app.get(\"/items/:id\", (req, res) ->\n val item = items.find(i -> i.id == parseInt(req.params.id))\n if item == null:\n res.status(404).json({ error: \"Not found\" })\n else:\n res.json({ data: item, date: formatDate(item.date) })\n )";
653
- const serverFlux = "// Entry point — MUST be compiled with: npm run build\n// Inter-file .flux imports only work via flux bundle, NOT flux run\nimport Express from \"express\"\nimport { setupRoutes } from \"./routes\" // inter-file .flux import\n\nval app = Express()\nval PORT = process.env.PORT or 3000\n\napp.use(Express.json())\nsetupRoutes(app)\n\napp.listen(PORT, -> print(\"{projectName} running at http://localhost:{PORT}\"))";
654
- const pkgJson = { name: projectName, version: "1.0.0", description: "A Flux web app", main: "dist/server.js", scripts: { build: "flux bundle src/server.flux -o dist/server.js", start: "node dist/server.js", dev: "flux bundle src/server.flux -o dist/server.js && node dist/server.js", watch: "flux watch src/server.flux", test: "flux test tests/" }, flux: { entry: "src/server.flux", outDir: "dist", mangle: false, sourcemap: true }, dependencies: { "@xnoxs/flux-lang": ("^" + VERSION), express: "^4.18.0" }, devDependencies: { } };
655
- Fs.writeFileSync(Path.join(dir, "src", "utils.flux"), utilsFlux, "utf8");
656
- Fs.writeFileSync(Path.join(dir, "src", "routes.flux"), routesFlux, "utf8");
657
- Fs.writeFileSync(Path.join(dir, "src", "server.flux"), serverFlux, "utf8");
658
- Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
659
- Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
660
- Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
661
- console.log();
662
- console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("webapp")));
663
- console.log();
664
- console.log(bold(" Next steps:"));
665
- console.log((" " + yellow(("cd " + projectName))));
666
- console.log(((" " + yellow("npm install")) + gray(" # install dependencies")));
667
- console.log(((" " + yellow("npm run build")) + gray(" # compile all .flux → dist/server.js")));
668
- console.log(((" " + yellow("npm start")) + gray(" # run the server")));
669
- console.log();
670
- console.log((yellow(" Note: ") + gray("This project uses multiple .flux files.")));
671
- console.log(((gray(" Always build with ") + yellow("npm run build")) + gray(" (flux bundle) before running.")));
672
- console.log(((gray(" Do NOT use ") + red("flux run")) + gray(" on files that import other .flux files.")));
673
- console.log();
674
- return;
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_modules/ # Packages installed by `flux install` (gitignored)\n├── flux.json # Project config & dependencies\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)));
675
603
  }
676
- console.error(red(("✗ Unknown template: " + template)));
677
- console.error(gray(" Available templates: script, server, webapp"));
678
- process.exit(1);
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(" Add packages (installed into flux_modules/):"));
610
+ console.log((" " + yellow("flux add <package>")));
611
+ console.log((" " + yellow("flux install")));
612
+ console.log();
613
+ console.log(gray(" Or run the test suite:"));
614
+ console.log((" " + yellow("flux test tests/")));
615
+ console.log();
679
616
  }
680
617
  function cmdSelfHosted(sub, opts) {
681
618
  const SELF = Path.join(__dirname, ".");
@@ -840,9 +777,6 @@ function main() {
840
777
  else if (cmd === "repl") {
841
778
  return cmdRepl(opts);
842
779
  }
843
- else if (cmd === "test") {
844
- return runTests((positional[1] ?? "tests/"), (src) => transpile(src, { }));
845
- }
846
780
  else if (cmd === "init") {
847
781
  return cmdInit(positional[1], opts);
848
782
  }
@@ -2,11 +2,10 @@
2
2
  // Flux Self-Hosted Config Reader
3
3
  // src/self/config.flux — written in Flux, compiled by stage-0
4
4
  //
5
- // Reads config from package.json ("flux" key) or flux.json fallback.
6
- // Config file resolution order:
7
- // 1. package.json → { "flux": { ... } } ← primary
8
- // 2. flux.json ← legacy fallback
9
- // 3. default config ← if none found
5
+ // Reads flux.json project config and merges with CLI flags.
6
+ // Config file locations (searched in order):
7
+ // ./flux.json
8
+ // ./flux.config.js
10
9
  // ============================================================
11
10
 
12
11
  import Fs from "fs"
@@ -25,7 +24,7 @@ export val DEFAULT_CONFIG = {
25
24
  watch: false,
26
25
  ignore: [],
27
26
  selfHosted: false,
28
- registry: "https://registry.npmjs.org",
27
+ registry: "https://registry.flux-lang.dev",
29
28
  pkg: {
30
29
  name: "",
31
30
  version: "1.0.0",
@@ -37,22 +36,11 @@ export val DEFAULT_CONFIG = {
37
36
  }
38
37
  }
39
38
 
40
- // ── Load config (package.json "flux" key first, flux.json fallback) ──
39
+ // ── Load flux.json ────────────────────────────────────────────
41
40
  export fn loadConfig(cwd_):
42
41
  val cwd = cwd_ ?? process.cwd()
43
42
 
44
- // 1. Try package.json → "flux" key (primary)
45
- val pkgJsonPath = Path.join(cwd, "package.json")
46
- if Fs.existsSync(pkgJsonPath):
47
- try:
48
- val raw = Fs.readFileSync(pkgJsonPath, "utf8")
49
- val parsed = JSON.parse(raw)
50
- if parsed.flux and typeof parsed.flux == "object":
51
- return mergeConfig(DEFAULT_CONFIG, parsed.flux)
52
- catch(e):
53
- throw new Error("Invalid package.json: " + e.message)
54
-
55
- // 2. Try flux.json (legacy fallback)
43
+ // Try flux.json first
56
44
  val jsonPath = Path.join(cwd, "flux.json")
57
45
  if Fs.existsSync(jsonPath):
58
46
  try:
@@ -62,7 +50,15 @@ export fn loadConfig(cwd_):
62
50
  catch(e):
63
51
  throw new Error("Invalid flux.json: " + e.message)
64
52
 
65
- // 3. Default config
53
+ // Try flux.config.js
54
+ val jsPath = Path.join(cwd, "flux.config.js")
55
+ if Fs.existsSync(jsPath):
56
+ try:
57
+ val loaded = require(jsPath)
58
+ return mergeConfig(DEFAULT_CONFIG, loaded)
59
+ catch(e2):
60
+ throw new Error("Invalid flux.config.js: " + e2.message)
61
+
66
62
  return { ...DEFAULT_CONFIG }
67
63
 
68
64
  // ── Deep merge config objects ─────────────────────────────────
@@ -75,16 +71,12 @@ export fn mergeConfig(base, overrides):
75
71
  result[key] = val_
76
72
  return result
77
73
 
78
- // ── Write flux key into package.json ─────────────────────────
74
+ // ── Write flux.json ───────────────────────────────────────────
79
75
  export fn writeConfig(config, cwd_):
80
- val cwd = cwd_ ?? process.cwd()
81
- val pkgPath = Path.join(cwd, "package.json")
82
- var pkg = {}
83
- if Fs.existsSync(pkgPath):
84
- try: pkg = JSON.parse(Fs.readFileSync(pkgPath, "utf8"))
85
- catch(e): pkg = {}
86
- pkg.flux = config
87
- Fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8")
76
+ val cwd = cwd_ ?? process.cwd()
77
+ val jsonPath = Path.join(cwd, "flux.json")
78
+ val content = JSON.stringify(config, null, 2) + "\n"
79
+ Fs.writeFileSync(jsonPath, content, "utf8")
88
80
 
89
81
  // ── Validate config ───────────────────────────────────────────
90
82
  export fn validateConfig(config):
@@ -101,20 +93,20 @@ export fn validateConfig(config):
101
93
  errors: errors,
102
94
  }
103
95
 
104
- // ── Read package info (package.json first, flux.json fallback) ────
96
+ // ── Read flux.json package info ───────────────────────────────
105
97
  export fn readPackage(cwd_):
106
- val cwd = cwd_ ?? process.cwd()
107
- val pkgJson = Path.join(cwd, "package.json")
98
+ val cwd = cwd_ ?? process.cwd()
108
99
  val fluxJson = Path.join(cwd, "flux.json")
100
+ val pkgJson = Path.join(cwd, "package.json")
109
101
 
110
- if Fs.existsSync(pkgJson):
102
+ if Fs.existsSync(fluxJson):
111
103
  try:
112
- return JSON.parse(Fs.readFileSync(pkgJson, "utf8"))
104
+ return JSON.parse(Fs.readFileSync(fluxJson, "utf8"))
113
105
  catch(e): return null
114
106
 
115
- if Fs.existsSync(fluxJson):
107
+ if Fs.existsSync(pkgJson):
116
108
  try:
117
- return JSON.parse(Fs.readFileSync(fluxJson, "utf8"))
109
+ return JSON.parse(Fs.readFileSync(pkgJson, "utf8"))
118
110
  catch(e2): return null
119
111
 
120
112
  return null
@@ -1,27 +1,23 @@
1
- /* compiled from src/self/config.flux */
2
- 'use strict';
3
- // Generated by Flux Transpiler v3.5.3 (self-hosted)
1
+ // ── Flux stdlib ──
2
+
3
+ function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
4
+
5
+ function includes(arr, val) { return arr.includes(val); }
6
+
7
+ function keys(obj) { return Object.keys(obj); }
8
+
9
+ function endsWith(s, suffix) { return String(s).endsWith(suffix); }
10
+ // ── end stdlib ──
11
+
12
+ // Generated by Flux Transpiler v3.2.0
4
13
  "use strict";
5
14
 
6
15
  const Fs = require("fs");
7
16
  const Path = require("path");
8
- const DEFAULT_CONFIG = { entry: "src/main.flux", outDir: "dist", sourcemap: false, mangle: false, jsx: false, jsxTarget: "browser", typecheck: true, strict: false, watch: false, ignore: [], selfHosted: false, registry: "https://registry.npmjs.org", pkg: { name: "", version: "1.0.0", description: "", author: "", license: "MIT", deps: { }, devDeps: { } } };
17
+ const DEFAULT_CONFIG = { entry: "src/main.flux", outDir: "dist", sourcemap: false, mangle: false, jsx: false, jsxTarget: "browser", typecheck: true, strict: false, watch: false, ignore: [], selfHosted: false, registry: "https://registry.flux-lang.dev", pkg: { name: "", version: "1.0.0", description: "", author: "", license: "MIT", deps: { }, devDeps: { } } };
9
18
  module.exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
10
19
  function loadConfig(cwd_) {
11
20
  const cwd = (cwd_ ?? process.cwd());
12
- const pkgJsonPath = Path.join(cwd, "package.json");
13
- if (Fs.existsSync(pkgJsonPath)) {
14
- try {
15
- const raw = Fs.readFileSync(pkgJsonPath, "utf8");
16
- const parsed = JSON.parse(raw);
17
- if ((parsed.flux && (typeof parsed.flux == "object"))) {
18
- return mergeConfig(DEFAULT_CONFIG, parsed.flux);
19
- }
20
- }
21
- catch (e) {
22
- throw new Error(("Invalid package.json: " + e.message));
23
- }
24
- }
25
21
  const jsonPath = Path.join(cwd, "flux.json");
26
22
  if (Fs.existsSync(jsonPath)) {
27
23
  try {
@@ -33,6 +29,16 @@ function loadConfig(cwd_) {
33
29
  throw new Error(("Invalid flux.json: " + e.message));
34
30
  }
35
31
  }
32
+ const jsPath = Path.join(cwd, "flux.config.js");
33
+ if (Fs.existsSync(jsPath)) {
34
+ try {
35
+ const loaded = require(jsPath);
36
+ return mergeConfig(DEFAULT_CONFIG, loaded);
37
+ }
38
+ catch (e2) {
39
+ throw new Error(("Invalid flux.config.js: " + e2.message));
40
+ }
41
+ }
36
42
  return { ...DEFAULT_CONFIG };
37
43
  }
38
44
  module.exports.loadConfig = loadConfig;
@@ -52,18 +58,9 @@ function mergeConfig(base, overrides) {
52
58
  module.exports.mergeConfig = mergeConfig;
53
59
  function writeConfig(config, cwd_) {
54
60
  const cwd = (cwd_ ?? process.cwd());
55
- const pkgPath = Path.join(cwd, "package.json");
56
- let pkg = { };
57
- if (Fs.existsSync(pkgPath)) {
58
- try {
59
- pkg = JSON.parse(Fs.readFileSync(pkgPath, "utf8"));
60
- }
61
- catch (e) {
62
- pkg = { };
63
- }
64
- }
65
- pkg.flux = config;
66
- Fs.writeFileSync(pkgPath, (JSON.stringify(pkg, null, 2) + "\n"), "utf8");
61
+ const jsonPath = Path.join(cwd, "flux.json");
62
+ const content = (JSON.stringify(config, null, 2) + "\n");
63
+ Fs.writeFileSync(jsonPath, content, "utf8");
67
64
  }
68
65
  module.exports.writeConfig = writeConfig;
69
66
  function validateConfig(config) {
@@ -79,19 +76,19 @@ function validateConfig(config) {
79
76
  module.exports.validateConfig = validateConfig;
80
77
  function readPackage(cwd_) {
81
78
  const cwd = (cwd_ ?? process.cwd());
82
- const pkgJson = Path.join(cwd, "package.json");
83
79
  const fluxJson = Path.join(cwd, "flux.json");
84
- if (Fs.existsSync(pkgJson)) {
80
+ const pkgJson = Path.join(cwd, "package.json");
81
+ if (Fs.existsSync(fluxJson)) {
85
82
  try {
86
- return JSON.parse(Fs.readFileSync(pkgJson, "utf8"));
83
+ return JSON.parse(Fs.readFileSync(fluxJson, "utf8"));
87
84
  }
88
85
  catch (e) {
89
86
  return null;
90
87
  }
91
88
  }
92
- if (Fs.existsSync(fluxJson)) {
89
+ if (Fs.existsSync(pkgJson)) {
93
90
  try {
94
- return JSON.parse(Fs.readFileSync(fluxJson, "utf8"));
91
+ return JSON.parse(Fs.readFileSync(pkgJson, "utf8"));
95
92
  }
96
93
  catch (e2) {
97
94
  return null;