@xnoxs/flux-lang 4.0.3 → 4.0.5
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/dist/flux-cli.js +4994 -8166
- package/dist/flux.cjs.js +1 -1
- package/index.js +9 -10
- package/package.json +4 -5
- package/src/self/bundler.flux +21 -16
- package/src/self/bundler.js +185 -1
- package/src/self/cli.flux +274 -220
- package/src/self/cli.js +153 -125
- package/src/self/config.flux +36 -28
- package/src/self/config.js +34 -31
- package/src/self/pkg.flux +39 -136
- package/src/self/pkg.js +37 -124
package/src/self/cli.js
CHANGED
|
@@ -1,48 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
1
|
+
/* compiled from src/self/cli.flux */
|
|
2
|
+
'use strict';
|
|
3
|
+
// Generated by Flux Transpiler v3.5.3 (self-hosted)
|
|
46
4
|
"use strict";
|
|
47
5
|
|
|
48
6
|
const Fs = require("fs");
|
|
@@ -57,7 +15,7 @@ const { loadConfig } = require("./config");
|
|
|
57
15
|
const { cmdAdd, cmdRemove, cmdInstall, cmdList, cmdSearch, cmdInfo, cmdPublish } = require("./pkg");
|
|
58
16
|
const VERSION = (FLUX_VERSION ?? "3.0.0");
|
|
59
17
|
const STAGE = (FLUX_STAGE ?? "self-hosted");
|
|
60
|
-
const C = { reset: "
|
|
18
|
+
const C = { reset: "\\u001b[0m", bold: "\\u001b[1m", dim: "\\u001b[2m", red: "\\u001b[31m", green: "\\u001b[32m", yellow: "\\u001b[33m", blue: "\\u001b[34m", cyan: "\\u001b[36m", white: "\\u001b[37m", gray: "\\u001b[90m", magenta: "\\u001b[35m" };
|
|
61
19
|
const noColor = (process.env.NO_COLOR || !process.stdout.isTTY);
|
|
62
20
|
function clr(c, s) {
|
|
63
21
|
return (noColor ? s : ((c + s) + C.reset));
|
|
@@ -96,32 +54,20 @@ function showHelp() {
|
|
|
96
54
|
console.log(bold("USAGE:"));
|
|
97
55
|
console.log(" flux <command> [options]\n");
|
|
98
56
|
console.log(bold("COMPILER:"));
|
|
99
|
-
const compilerCmds = [["compile <file.flux>", "Compile .flux → .js"], ["bundle <entry.flux>", "Bundle
|
|
100
|
-
|
|
101
|
-
const [cmd, desc] = __item__;
|
|
102
|
-
console.log((((" " + green(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
|
|
103
|
-
}
|
|
57
|
+
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"]];
|
|
58
|
+
compilerCmds.forEach((pair) => console.log((((" " + green(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
|
|
104
59
|
console.log();
|
|
105
60
|
console.log(bold("TOOLING:"));
|
|
106
61
|
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
|
-
|
|
108
|
-
const [cmd, desc] = __item__;
|
|
109
|
-
console.log((((" " + green(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
|
|
110
|
-
}
|
|
62
|
+
toolCmds.forEach((pair) => console.log((((" " + green(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
|
|
111
63
|
console.log();
|
|
112
64
|
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
|
|
114
|
-
|
|
115
|
-
const [cmd, desc] = __item__;
|
|
116
|
-
console.log((((" " + cyan(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
|
|
117
|
-
}
|
|
65
|
+
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"]];
|
|
66
|
+
pkgCmds.forEach((pair) => console.log((((" " + cyan(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
|
|
118
67
|
console.log();
|
|
119
68
|
console.log(bold("SELF-HOSTED:"));
|
|
120
69
|
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
|
-
|
|
122
|
-
const [cmd, desc] = __item__;
|
|
123
|
-
console.log((((" " + yellow(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
|
|
124
|
-
}
|
|
70
|
+
selfCmds.forEach((pair) => console.log((((" " + yellow(("flux " + pair[0]).padEnd(36))) + " ") + gray(pair[1]))));
|
|
125
71
|
console.log();
|
|
126
72
|
console.log(bold("OPTIONS:"));
|
|
127
73
|
console.log(((" " + yellow("--out, -o <file> ")) + " Output file"));
|
|
@@ -131,11 +77,12 @@ function showHelp() {
|
|
|
131
77
|
console.log(((" " + yellow("--typecheck ")) + " Enable type checking"));
|
|
132
78
|
console.log(((" " + yellow("--stdout ")) + " Print to terminal"));
|
|
133
79
|
console.log(((" " + yellow("--no-color ")) + " Disable colors"));
|
|
80
|
+
console.log(((" " + yellow("--template ")) + " Init template: script, server, webapp"));
|
|
134
81
|
console.log();
|
|
135
82
|
}
|
|
136
83
|
function parseArgs(argv) {
|
|
137
84
|
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" };
|
|
85
|
+
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 };
|
|
139
86
|
const positional = [];
|
|
140
87
|
let i = 0;
|
|
141
88
|
while ((i < args.length)) {
|
|
@@ -175,6 +122,10 @@ function parseArgs(argv) {
|
|
|
175
122
|
i = (i + 1);
|
|
176
123
|
opts.jsxTarget = args[i];
|
|
177
124
|
}
|
|
125
|
+
else if (((a == "--template") || (a == "-t"))) {
|
|
126
|
+
i = (i + 1);
|
|
127
|
+
opts.template = args[i];
|
|
128
|
+
}
|
|
178
129
|
else if (!a.startsWith("--")) {
|
|
179
130
|
positional.push(a);
|
|
180
131
|
}
|
|
@@ -243,6 +194,21 @@ function printErrors(errors, source, filePath) {
|
|
|
243
194
|
}
|
|
244
195
|
console.error();
|
|
245
196
|
}
|
|
197
|
+
function findFluxImports(source) {
|
|
198
|
+
const found = [];
|
|
199
|
+
const lines = source.split("\n");
|
|
200
|
+
for (const line of lines) {
|
|
201
|
+
const trimmed = line.trim();
|
|
202
|
+
const m = trimmed.match(/^import\s+.+\s+from\s+["'](\.[^"']+)["']/);
|
|
203
|
+
if (m) {
|
|
204
|
+
const src = m[1];
|
|
205
|
+
if (((!src.endsWith(".js") && !src.endsWith(".json")) && !src.endsWith(".node"))) {
|
|
206
|
+
found.push(src);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return found;
|
|
211
|
+
}
|
|
246
212
|
function cmdCompile(filePath, opts) {
|
|
247
213
|
const { source, abs } = readFluxFile(filePath);
|
|
248
214
|
const cfg = loadConfig(Path.dirname(abs));
|
|
@@ -276,6 +242,21 @@ function cmdCompile(filePath, opts) {
|
|
|
276
242
|
}
|
|
277
243
|
function cmdRun(filePath, opts) {
|
|
278
244
|
const { source, abs } = readFluxFile(filePath);
|
|
245
|
+
const fluxImports = findFluxImports(source);
|
|
246
|
+
if ((fluxImports.length > 0)) {
|
|
247
|
+
const example = fluxImports[0];
|
|
248
|
+
const entryRel = Path.relative(process.cwd(), abs);
|
|
249
|
+
const outFile = (Path.basename(abs, ".flux") + ".js");
|
|
250
|
+
console.error(red("\n✗ Cannot run a multi-file Flux project with `flux run`.\n"));
|
|
251
|
+
console.error((" Found: " + yellow((("import ... from \"" + example) + "\""))));
|
|
252
|
+
console.error();
|
|
253
|
+
console.error(((" Use " + cyan("flux bundle")) + " to compile all files into one, then run it:\n"));
|
|
254
|
+
console.error((" " + yellow(((("flux bundle " + entryRel) + " -o dist/") + outFile))));
|
|
255
|
+
console.error((" " + yellow(("node dist/" + outFile))));
|
|
256
|
+
console.error();
|
|
257
|
+
console.error(((" Or use " + cyan("flux run")) + " only for single-file scripts (no inter-file .flux imports).\n"));
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
279
260
|
const result = transpile(source, { jsx: (opts.jsx ?? false), jsxTarget: (opts.jsxTarget ?? "browser"), mangle: false });
|
|
280
261
|
if (!result.success) {
|
|
281
262
|
console.error(red("\n✗ Compile error"));
|
|
@@ -443,6 +424,10 @@ function cmdBundle(entryPath, opts) {
|
|
|
443
424
|
console.log(result.code);
|
|
444
425
|
return;
|
|
445
426
|
}
|
|
427
|
+
const outDir = Path.dirname(Path.resolve(outFile));
|
|
428
|
+
if (!Fs.existsSync(outDir)) {
|
|
429
|
+
Fs.mkdirSync(outDir, { recursive: true });
|
|
430
|
+
}
|
|
446
431
|
Fs.writeFileSync(outFile, result.code, "utf8");
|
|
447
432
|
const kb = (result.code.length / 1024).toFixed(1);
|
|
448
433
|
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]"))));
|
|
@@ -573,6 +558,7 @@ function cmdRepl(opts) {
|
|
|
573
558
|
}
|
|
574
559
|
function cmdInit(name, opts) {
|
|
575
560
|
const projectName = (name ?? "my-flux-app");
|
|
561
|
+
const template = (opts.template ?? "script");
|
|
576
562
|
const dir = Path.resolve(projectName);
|
|
577
563
|
if (Fs.existsSync(dir)) {
|
|
578
564
|
console.error(red(("✗ Directory already exists: " + projectName)));
|
|
@@ -581,38 +567,77 @@ function cmdInit(name, opts) {
|
|
|
581
567
|
Fs.mkdirSync(dir, { recursive: true });
|
|
582
568
|
Fs.mkdirSync(Path.join(dir, "src"), { recursive: true });
|
|
583
569
|
Fs.mkdirSync(Path.join(dir, "tests"), { recursive: true });
|
|
584
|
-
const
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
console.log(("
|
|
570
|
+
const gitignore = "node_modules/\ndist/\n*.js.map\n.DS_Store\n";
|
|
571
|
+
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\")";
|
|
572
|
+
if ((template == "script")) {
|
|
573
|
+
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()";
|
|
574
|
+
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: { } };
|
|
575
|
+
Fs.writeFileSync(Path.join(dir, "src", "main.flux"), mainFlux, "utf8");
|
|
576
|
+
Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
|
|
577
|
+
Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
|
|
578
|
+
Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
|
|
579
|
+
console.log();
|
|
580
|
+
console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("script")));
|
|
581
|
+
console.log();
|
|
582
|
+
console.log(bold(" Next steps:"));
|
|
583
|
+
console.log((" " + yellow(("cd " + projectName))));
|
|
584
|
+
console.log((" " + yellow("npm install")));
|
|
585
|
+
console.log((" " + yellow("npm run dev")));
|
|
586
|
+
console.log();
|
|
587
|
+
console.log(gray(" Add packages:"));
|
|
588
|
+
console.log((" " + yellow("flux add <package>")));
|
|
589
|
+
console.log();
|
|
590
|
+
return;
|
|
603
591
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
592
|
+
if ((template == "server")) {
|
|
593
|
+
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}\"))";
|
|
594
|
+
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: { } };
|
|
595
|
+
Fs.writeFileSync(Path.join(dir, "src", "server.flux"), serverFlux, "utf8");
|
|
596
|
+
Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
|
|
597
|
+
Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
|
|
598
|
+
Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
|
|
599
|
+
console.log();
|
|
600
|
+
console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("server")));
|
|
601
|
+
console.log();
|
|
602
|
+
console.log(bold(" Next steps:"));
|
|
603
|
+
console.log((" " + yellow(("cd " + projectName))));
|
|
604
|
+
console.log((" " + yellow("npm install")));
|
|
605
|
+
console.log((" " + yellow("npm run dev")));
|
|
606
|
+
console.log();
|
|
607
|
+
console.log(gray(" Build for production:"));
|
|
608
|
+
console.log((" " + yellow("npm run build && npm start")));
|
|
609
|
+
console.log();
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if ((template == "webapp")) {
|
|
613
|
+
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)";
|
|
614
|
+
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 )";
|
|
615
|
+
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}\"))";
|
|
616
|
+
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: { } };
|
|
617
|
+
Fs.writeFileSync(Path.join(dir, "src", "utils.flux"), utilsFlux, "utf8");
|
|
618
|
+
Fs.writeFileSync(Path.join(dir, "src", "routes.flux"), routesFlux, "utf8");
|
|
619
|
+
Fs.writeFileSync(Path.join(dir, "src", "server.flux"), serverFlux, "utf8");
|
|
620
|
+
Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
|
|
621
|
+
Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
|
|
622
|
+
Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
|
|
623
|
+
console.log();
|
|
624
|
+
console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("webapp")));
|
|
625
|
+
console.log();
|
|
626
|
+
console.log(bold(" Next steps:"));
|
|
627
|
+
console.log((" " + yellow(("cd " + projectName))));
|
|
628
|
+
console.log(((" " + yellow("npm install")) + gray(" # install dependencies")));
|
|
629
|
+
console.log(((" " + yellow("npm run build")) + gray(" # compile all .flux → dist/server.js")));
|
|
630
|
+
console.log(((" " + yellow("npm start")) + gray(" # run the server")));
|
|
631
|
+
console.log();
|
|
632
|
+
console.log((yellow(" Note: ") + gray("This project uses multiple .flux files.")));
|
|
633
|
+
console.log(((gray(" Always build with ") + yellow("npm run build")) + gray(" (flux bundle) before running.")));
|
|
634
|
+
console.log(((gray(" Do NOT use ") + red("flux run")) + gray(" on files that import other .flux files.")));
|
|
635
|
+
console.log();
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
console.error(red(("✗ Unknown template: " + template)));
|
|
639
|
+
console.error(gray(" Available templates: script, server, webapp"));
|
|
640
|
+
process.exit(1);
|
|
616
641
|
}
|
|
617
642
|
function cmdSelfHosted(sub, opts) {
|
|
618
643
|
const SELF = Path.join(__dirname, ".");
|
|
@@ -745,91 +770,94 @@ function main() {
|
|
|
745
770
|
const { positional, opts } = parseArgs(process.argv);
|
|
746
771
|
const cmd = (positional[0] ?? "help");
|
|
747
772
|
if (cmd === "compile") {
|
|
748
|
-
|
|
773
|
+
cmdCompile(positional[1], opts);
|
|
749
774
|
}
|
|
750
775
|
else if (cmd === "run") {
|
|
751
|
-
|
|
776
|
+
cmdRun(positional[1], opts);
|
|
752
777
|
}
|
|
753
778
|
else if (cmd === "check") {
|
|
754
|
-
|
|
779
|
+
cmdCheck(positional.slice(1), opts);
|
|
755
780
|
}
|
|
756
781
|
else if (cmd === "fmt") {
|
|
757
|
-
|
|
782
|
+
cmdFmt(positional.slice(1), opts);
|
|
758
783
|
}
|
|
759
784
|
else if (cmd === "format") {
|
|
760
|
-
|
|
785
|
+
cmdFmt(positional.slice(1), opts);
|
|
761
786
|
}
|
|
762
787
|
else if (cmd === "lint") {
|
|
763
|
-
|
|
788
|
+
cmdLint(positional.slice(1), opts);
|
|
764
789
|
}
|
|
765
790
|
else if (cmd === "bundle") {
|
|
766
|
-
|
|
791
|
+
cmdBundle(positional[1], opts);
|
|
767
792
|
}
|
|
768
793
|
else if (cmd === "watch") {
|
|
769
|
-
|
|
794
|
+
cmdWatch(positional[1], opts);
|
|
770
795
|
}
|
|
771
796
|
else if (cmd === "tokens") {
|
|
772
|
-
|
|
797
|
+
cmdTokens(positional[1], opts);
|
|
773
798
|
}
|
|
774
799
|
else if (cmd === "ast") {
|
|
775
|
-
|
|
800
|
+
cmdAst(positional[1], opts);
|
|
776
801
|
}
|
|
777
802
|
else if (cmd === "repl") {
|
|
778
|
-
|
|
803
|
+
cmdRepl(opts);
|
|
804
|
+
}
|
|
805
|
+
else if (cmd === "test") {
|
|
806
|
+
runTests(positional.slice(1), opts);
|
|
779
807
|
}
|
|
780
808
|
else if (cmd === "init") {
|
|
781
|
-
|
|
809
|
+
cmdInit(positional[1], opts);
|
|
782
810
|
}
|
|
783
811
|
else if (cmd === "add") {
|
|
784
|
-
|
|
812
|
+
cmdAdd(positional.slice(1), opts);
|
|
785
813
|
}
|
|
786
814
|
else if (cmd === "remove") {
|
|
787
|
-
|
|
815
|
+
cmdRemove(positional.slice(1), opts);
|
|
788
816
|
}
|
|
789
817
|
else if (cmd === "rm") {
|
|
790
|
-
|
|
818
|
+
cmdRemove(positional.slice(1), opts);
|
|
791
819
|
}
|
|
792
820
|
else if (cmd === "install") {
|
|
793
|
-
|
|
821
|
+
cmdInstall(opts);
|
|
794
822
|
}
|
|
795
823
|
else if (cmd === "i") {
|
|
796
|
-
|
|
824
|
+
cmdInstall(opts);
|
|
797
825
|
}
|
|
798
826
|
else if (cmd === "list") {
|
|
799
|
-
|
|
827
|
+
cmdList(opts);
|
|
800
828
|
}
|
|
801
829
|
else if (cmd === "ls") {
|
|
802
|
-
|
|
830
|
+
cmdList(opts);
|
|
803
831
|
}
|
|
804
832
|
else if (cmd === "search") {
|
|
805
|
-
|
|
833
|
+
cmdSearch(positional[1], opts);
|
|
806
834
|
}
|
|
807
835
|
else if (cmd === "info") {
|
|
808
|
-
|
|
836
|
+
cmdInfo(positional[1], opts);
|
|
809
837
|
}
|
|
810
838
|
else if (cmd === "publish") {
|
|
811
|
-
|
|
839
|
+
cmdPublish(opts);
|
|
812
840
|
}
|
|
813
841
|
else if (cmd === "self-hosted") {
|
|
814
|
-
|
|
842
|
+
cmdSelfHosted(positional[1], opts);
|
|
815
843
|
}
|
|
816
844
|
else if (cmd === "version") {
|
|
817
|
-
|
|
845
|
+
cmdVersion(opts);
|
|
818
846
|
}
|
|
819
847
|
else if (cmd === "-v") {
|
|
820
|
-
|
|
848
|
+
cmdVersion(opts);
|
|
821
849
|
}
|
|
822
850
|
else if (cmd === "--version") {
|
|
823
|
-
|
|
851
|
+
cmdVersion(opts);
|
|
824
852
|
}
|
|
825
853
|
else if (cmd === "help") {
|
|
826
|
-
|
|
854
|
+
showHelp();
|
|
827
855
|
}
|
|
828
856
|
else if (cmd === "--help") {
|
|
829
|
-
|
|
857
|
+
showHelp();
|
|
830
858
|
}
|
|
831
859
|
else if (cmd === "-h") {
|
|
832
|
-
|
|
860
|
+
showHelp();
|
|
833
861
|
}
|
|
834
862
|
else {
|
|
835
863
|
console.error(red(("✗ Unknown command: " + cmd)));
|
package/src/self/config.flux
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
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
|
-
//
|
|
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
|
|
9
10
|
// ============================================================
|
|
10
11
|
|
|
11
12
|
import Fs from "fs"
|
|
@@ -24,7 +25,7 @@ export val DEFAULT_CONFIG = {
|
|
|
24
25
|
watch: false,
|
|
25
26
|
ignore: [],
|
|
26
27
|
selfHosted: false,
|
|
27
|
-
registry: "https://registry.
|
|
28
|
+
registry: "https://registry.npmjs.org",
|
|
28
29
|
pkg: {
|
|
29
30
|
name: "",
|
|
30
31
|
version: "1.0.0",
|
|
@@ -36,11 +37,22 @@ export val DEFAULT_CONFIG = {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
// ── Load flux.json
|
|
40
|
+
// ── Load config (package.json "flux" key first, flux.json fallback) ──
|
|
40
41
|
export fn loadConfig(cwd_):
|
|
41
42
|
val cwd = cwd_ ?? process.cwd()
|
|
42
43
|
|
|
43
|
-
// Try
|
|
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)
|
|
44
56
|
val jsonPath = Path.join(cwd, "flux.json")
|
|
45
57
|
if Fs.existsSync(jsonPath):
|
|
46
58
|
try:
|
|
@@ -50,15 +62,7 @@ export fn loadConfig(cwd_):
|
|
|
50
62
|
catch(e):
|
|
51
63
|
throw new Error("Invalid flux.json: " + e.message)
|
|
52
64
|
|
|
53
|
-
//
|
|
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
|
-
|
|
65
|
+
// 3. Default config
|
|
62
66
|
return { ...DEFAULT_CONFIG }
|
|
63
67
|
|
|
64
68
|
// ── Deep merge config objects ─────────────────────────────────
|
|
@@ -71,12 +75,16 @@ export fn mergeConfig(base, overrides):
|
|
|
71
75
|
result[key] = val_
|
|
72
76
|
return result
|
|
73
77
|
|
|
74
|
-
// ── Write flux.json
|
|
78
|
+
// ── Write flux key into package.json ─────────────────────────
|
|
75
79
|
export fn writeConfig(config, cwd_):
|
|
76
|
-
val cwd
|
|
77
|
-
val
|
|
78
|
-
|
|
79
|
-
Fs.
|
|
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")
|
|
80
88
|
|
|
81
89
|
// ── Validate config ───────────────────────────────────────────
|
|
82
90
|
export fn validateConfig(config):
|
|
@@ -93,20 +101,20 @@ export fn validateConfig(config):
|
|
|
93
101
|
errors: errors,
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
// ── Read flux.json
|
|
104
|
+
// ── Read package info (package.json first, flux.json fallback) ────
|
|
97
105
|
export fn readPackage(cwd_):
|
|
98
|
-
val cwd
|
|
106
|
+
val cwd = cwd_ ?? process.cwd()
|
|
107
|
+
val pkgJson = Path.join(cwd, "package.json")
|
|
99
108
|
val fluxJson = Path.join(cwd, "flux.json")
|
|
100
|
-
val pkgJson = Path.join(cwd, "package.json")
|
|
101
109
|
|
|
102
|
-
if Fs.existsSync(
|
|
110
|
+
if Fs.existsSync(pkgJson):
|
|
103
111
|
try:
|
|
104
|
-
return JSON.parse(Fs.readFileSync(
|
|
112
|
+
return JSON.parse(Fs.readFileSync(pkgJson, "utf8"))
|
|
105
113
|
catch(e): return null
|
|
106
114
|
|
|
107
|
-
if Fs.existsSync(
|
|
115
|
+
if Fs.existsSync(fluxJson):
|
|
108
116
|
try:
|
|
109
|
-
return JSON.parse(Fs.readFileSync(
|
|
117
|
+
return JSON.parse(Fs.readFileSync(fluxJson, "utf8"))
|
|
110
118
|
catch(e2): return null
|
|
111
119
|
|
|
112
120
|
return null
|