@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/CHANGELOG.md +7 -0
- package/dist/flux-cli.js +8093 -4979
- package/dist/flux.cjs.js +3 -2
- package/dist/flux.esm.js +12 -11
- package/dist/flux.min.js +2 -2
- package/index.js +10 -9
- package/package.json +5 -4
- package/src/self/bundler.flux +16 -21
- package/src/self/bundler.js +1 -185
- package/src/self/cli.flux +220 -274
- package/src/self/cli.js +58 -124
- package/src/self/config.flux +28 -36
- package/src/self/config.js +31 -34
- package/src/self/pkg.flux +136 -39
- package/src/self/pkg.js +124 -37
- package/src/self/transpiler.flux +1 -1
- package/src/self/transpiler.js +1 -3
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
|
|
3
|
+
function map(arr, fn) { return arr.map(fn); }
|
|
6
4
|
|
|
7
|
-
function
|
|
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
|
|
96
|
-
|
|
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
|
-
|
|
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]
|
|
104
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
|
609
|
-
const
|
|
610
|
-
if ((
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
console.log(("
|
|
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.
|
|
677
|
-
console.
|
|
678
|
-
|
|
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
|
}
|
package/src/self/config.flux
CHANGED
|
@@ -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
|
|
6
|
-
// Config file
|
|
7
|
-
//
|
|
8
|
-
//
|
|
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.
|
|
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
|
|
39
|
+
// ── Load flux.json ────────────────────────────────────────────
|
|
41
40
|
export fn loadConfig(cwd_):
|
|
42
41
|
val cwd = cwd_ ?? process.cwd()
|
|
43
42
|
|
|
44
|
-
//
|
|
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
|
-
//
|
|
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
|
|
74
|
+
// ── Write flux.json ───────────────────────────────────────────
|
|
79
75
|
export fn writeConfig(config, cwd_):
|
|
80
|
-
val cwd
|
|
81
|
-
val
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
96
|
+
// ── Read flux.json package info ───────────────────────────────
|
|
105
97
|
export fn readPackage(cwd_):
|
|
106
|
-
val 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(
|
|
102
|
+
if Fs.existsSync(fluxJson):
|
|
111
103
|
try:
|
|
112
|
-
return JSON.parse(Fs.readFileSync(
|
|
104
|
+
return JSON.parse(Fs.readFileSync(fluxJson, "utf8"))
|
|
113
105
|
catch(e): return null
|
|
114
106
|
|
|
115
|
-
if Fs.existsSync(
|
|
107
|
+
if Fs.existsSync(pkgJson):
|
|
116
108
|
try:
|
|
117
|
-
return JSON.parse(Fs.readFileSync(
|
|
109
|
+
return JSON.parse(Fs.readFileSync(pkgJson, "utf8"))
|
|
118
110
|
catch(e2): return null
|
|
119
111
|
|
|
120
112
|
return null
|
package/src/self/config.js
CHANGED
|
@@ -1,27 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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.
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
80
|
+
const pkgJson = Path.join(cwd, "package.json");
|
|
81
|
+
if (Fs.existsSync(fluxJson)) {
|
|
85
82
|
try {
|
|
86
|
-
return JSON.parse(Fs.readFileSync(
|
|
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(
|
|
89
|
+
if (Fs.existsSync(pkgJson)) {
|
|
93
90
|
try {
|
|
94
|
-
return JSON.parse(Fs.readFileSync(
|
|
91
|
+
return JSON.parse(Fs.readFileSync(pkgJson, "utf8"));
|
|
95
92
|
}
|
|
96
93
|
catch (e2) {
|
|
97
94
|
return null;
|