@xnoxs/flux-lang 4.0.2 → 4.0.4
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 +4997 -8155
- package/dist/flux.cjs.js +3767 -5619
- package/dist/flux.esm.js +3767 -5619
- package/dist/flux.min.js +95 -361
- package/index.js +9 -10
- package/package.json +4 -5
- package/src/self/bundler.flux +4 -5
- package/src/self/bundler.js +20 -16
- package/src/self/cli.js +116 -43
- package/src/self/codegen.flux +10 -1
- package/src/self/codegen.js +1 -799
- package/src/self/config.js +33 -19
- package/src/self/pkg.js +33 -111
package/index.js
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Flux Lang — Public API
|
|
5
5
|
*
|
|
6
|
-
* const { transpile } = require('flux-lang');
|
|
7
|
-
* import { transpile, format, buildStdlib } from 'flux-lang';
|
|
6
|
+
* const { transpile } = require('@xnoxs/flux-lang');
|
|
7
|
+
* import { transpile, format, buildStdlib } from '@xnoxs/flux-lang';
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const { transpile } = require('./src/transpiler');
|
|
11
|
-
const { format } = require('./src/formatter');
|
|
12
|
-
const { buildStdlib, detectUsedSymbols, STDLIB_SYMBOLS } = require('./src/stdlib');
|
|
13
|
-
const { Lexer } = require('./src/lexer');
|
|
14
|
-
const { Parser } = require('./src/parser');
|
|
15
|
-
const { bundle } = require('./src/bundler');
|
|
16
|
-
const { loadConfig, mergeConfig
|
|
10
|
+
const { transpile } = require('./src/self/transpiler');
|
|
11
|
+
const { format } = require('./src/self/formatter');
|
|
12
|
+
const { buildStdlib, detectUsedSymbols, STDLIB_SYMBOLS } = require('./src/self/stdlib');
|
|
13
|
+
const { Lexer } = require('./src/self/lexer');
|
|
14
|
+
const { Parser } = require('./src/self/parser');
|
|
15
|
+
const { bundle } = require('./src/self/bundler');
|
|
16
|
+
const { loadConfig, mergeConfig } = require('./src/self/config');
|
|
17
17
|
|
|
18
18
|
module.exports = {
|
|
19
19
|
transpile,
|
|
@@ -26,5 +26,4 @@ module.exports = {
|
|
|
26
26
|
bundle,
|
|
27
27
|
loadConfig,
|
|
28
28
|
mergeConfig,
|
|
29
|
-
defineConfig,
|
|
30
29
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnoxs/flux-lang",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"description": "Flux — A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.",
|
|
5
5
|
"main": "dist/flux.cjs.js",
|
|
6
6
|
"module": "dist/flux.esm.js",
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
"flux": "./bin/flux.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "node
|
|
13
|
-
"
|
|
14
|
-
"test": "node src/cli.js test tests/",
|
|
12
|
+
"build": "node -e \"console.log(\"build skipped\")\"",
|
|
13
|
+
"test": "node src/self/cli.js test tests/",
|
|
15
14
|
"test:file": "node src/cli.js test",
|
|
16
15
|
"check": "node src/cli.js check tests/01_basics.test.flux",
|
|
17
16
|
"bench": "node benchmarks/bench.js",
|
|
@@ -84,4 +83,4 @@
|
|
|
84
83
|
"devDependencies": {
|
|
85
84
|
"esbuild": "^0.28.1"
|
|
86
85
|
}
|
|
87
|
-
}
|
|
86
|
+
}
|
package/src/self/bundler.flux
CHANGED
|
@@ -124,11 +124,10 @@ export class Bundler:
|
|
|
124
124
|
|
|
125
125
|
for imp in mod.imports:
|
|
126
126
|
val srcId = toModuleId(imp.absPath)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
lines.push(' var ' + name + ' = ' + srcId + '._exports ? ' + srcId + '._exports.' + name + ' : ' + srcId + '.' + name + ';')
|
|
127
|
+
for name in imp.names:
|
|
128
|
+
val localName = name.alias ?? name.name
|
|
129
|
+
val importedName = name.name
|
|
130
|
+
lines.push(' var ' + localName + ' = ' + srcId + '._exports ? ' + srcId + '._exports.' + importedName + ' : ' + srcId + '.' + importedName + ';')
|
|
132
131
|
|
|
133
132
|
if mod.imports.length > 0: lines.push('')
|
|
134
133
|
|
package/src/self/bundler.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* compiled from src/self/bundler.flux by Flux Lang */
|
|
2
|
+
'use strict';
|
|
1
3
|
// ── Flux stdlib ──
|
|
2
4
|
|
|
3
5
|
function findIndex(arr, fn) { return arr.findIndex(fn); }
|
|
@@ -29,17 +31,25 @@ function toModuleId(absPath) {
|
|
|
29
31
|
}
|
|
30
32
|
function extractModuleInfo(ast, fromFile) {
|
|
31
33
|
const imports = [];
|
|
34
|
+
const npmImports = [];
|
|
32
35
|
const exports = [];
|
|
33
36
|
const body = [];
|
|
34
37
|
const dir = Path.dirname(fromFile);
|
|
35
38
|
for (const node of ast.body) {
|
|
36
39
|
if ((node.type == "ImportDecl")) {
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
+
const src = node.source;
|
|
41
|
+
if ((src.startsWith("./") || src.startsWith("../"))) {
|
|
42
|
+
let resolved = src;
|
|
43
|
+
if (!resolved.endsWith(".flux")) {
|
|
44
|
+
resolved = (resolved + ".flux");
|
|
45
|
+
}
|
|
46
|
+
const absPath = Path.resolve(dir, resolved);
|
|
47
|
+
imports.push({ names: node.names, source: node.source, absPath });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
npmImports.push({ names: node.names, source: src });
|
|
51
|
+
body.push(node);
|
|
40
52
|
}
|
|
41
|
-
const absPath = Path.resolve(dir, src);
|
|
42
|
-
imports.push({ names: node.names, source: node.source, absPath });
|
|
43
53
|
}
|
|
44
54
|
else if ((node.type == "ExportDecl")) {
|
|
45
55
|
const inner = node.decl;
|
|
@@ -58,7 +68,7 @@ function extractModuleInfo(ast, fromFile) {
|
|
|
58
68
|
body.push(node);
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
|
-
return { cleanAst: { type: "Program", body }, imports, exports };
|
|
71
|
+
return { cleanAst: { type: "Program", body }, imports, npmImports, exports };
|
|
62
72
|
}
|
|
63
73
|
function codegenModule(ast) {
|
|
64
74
|
const cg = makeCodeGen({ indent: " " });
|
|
@@ -122,9 +132,6 @@ class Bundler {
|
|
|
122
132
|
lines.push("\"use strict\";");
|
|
123
133
|
lines.push("");
|
|
124
134
|
}
|
|
125
|
-
lines.push("// flux_modules resolver");
|
|
126
|
-
lines.push(`(function()${var _p=require('path'),_fs=require('fs'),_M=require('module'),_d=_p.join(process.cwd(),'flux_modules','node_modules');if(_fs.existsSync(_d)){var _o=_M._resolveFilename.bind(_M);_M._resolveFilename=function(r,p,m,op){if(!r.startsWith('.')&&!r.startsWith('/')&&!r.startsWith('node:')){var _fp=_p.join(_d,r.split('/')[0]);if(_fs.existsSync(_fp)){try{return _o(_p.join(_d,r),p,m,op);}catch(_e){}}}return _o(r,p,m,op);};}})();`);
|
|
127
|
-
lines.push("");
|
|
128
135
|
lines.push("(function() {");
|
|
129
136
|
lines.push("");
|
|
130
137
|
for (const absPath of this.order) {
|
|
@@ -142,13 +149,10 @@ class Bundler {
|
|
|
142
149
|
}
|
|
143
150
|
for (const imp of mod.imports) {
|
|
144
151
|
const srcId = toModuleId(imp.absPath);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
for (const name of imp.names) {
|
|
150
|
-
lines.push(((((((((((((" var " + name) + " = ") + srcId) + "._exports ? ") + srcId) + "._exports.") + name) + " : ") + srcId) + ".") + name) + ";"));
|
|
151
|
-
}
|
|
152
|
+
for (const name of imp.names) {
|
|
153
|
+
const localName = (name.alias ?? name.name);
|
|
154
|
+
const importedName = name.name;
|
|
155
|
+
lines.push(((((((((((((" var " + localName) + " = ") + srcId) + "._exports ? ") + srcId) + "._exports.") + importedName) + " : ") + srcId) + ".") + importedName) + ";"));
|
|
152
156
|
}
|
|
153
157
|
}
|
|
154
158
|
if ((mod.imports.length > 0)) {
|
package/src/self/cli.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
/* compiled from src/self/cli.flux by Flux Lang */
|
|
2
|
+
'use strict';
|
|
1
3
|
// ── Flux stdlib ──
|
|
2
4
|
|
|
3
|
-
function
|
|
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); }
|
|
5
|
+
function find(arr, fn) { return arr.find(fn); }
|
|
8
6
|
|
|
9
7
|
function some(arr, fn) { return arr.some(fn); }
|
|
10
8
|
|
|
@@ -14,10 +12,6 @@ function clamp(val, min, max) {
|
|
|
14
12
|
return Math.min(Math.max(val, min), max);
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
function sum(arr) {
|
|
18
|
-
return arr.reduce(function(a, b) { return a + b; }, 0);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
function max(arr) {
|
|
22
16
|
if (arguments.length > 1) return Math.max.apply(null, arguments);
|
|
23
17
|
return Math.max.apply(null, arr);
|
|
@@ -96,7 +90,7 @@ function showHelp() {
|
|
|
96
90
|
console.log(bold("USAGE:"));
|
|
97
91
|
console.log(" flux <command> [options]\n");
|
|
98
92
|
console.log(bold("COMPILER:"));
|
|
99
|
-
const compilerCmds = [["compile <file.flux>", "Compile .flux → .js"], ["bundle <entry.flux>", "Bundle
|
|
93
|
+
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"]];
|
|
100
94
|
for (const __item__ of compilerCmds) {
|
|
101
95
|
const [cmd, desc] = __item__;
|
|
102
96
|
console.log((((" " + green(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
|
|
@@ -110,7 +104,7 @@ function showHelp() {
|
|
|
110
104
|
}
|
|
111
105
|
console.log();
|
|
112
106
|
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
|
|
107
|
+
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"]];
|
|
114
108
|
for (const __item__ of pkgCmds) {
|
|
115
109
|
const [cmd, desc] = __item__;
|
|
116
110
|
console.log((((" " + cyan(("flux " + cmd).padEnd(36))) + " ") + gray(desc)));
|
|
@@ -131,11 +125,12 @@ function showHelp() {
|
|
|
131
125
|
console.log(((" " + yellow("--typecheck ")) + " Enable type checking"));
|
|
132
126
|
console.log(((" " + yellow("--stdout ")) + " Print to terminal"));
|
|
133
127
|
console.log(((" " + yellow("--no-color ")) + " Disable colors"));
|
|
128
|
+
console.log(((" " + yellow("--template ")) + " Init template: script, server, webapp"));
|
|
134
129
|
console.log();
|
|
135
130
|
}
|
|
136
131
|
function parseArgs(argv) {
|
|
137
132
|
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" };
|
|
133
|
+
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
134
|
const positional = [];
|
|
140
135
|
let i = 0;
|
|
141
136
|
while ((i < args.length)) {
|
|
@@ -175,6 +170,10 @@ function parseArgs(argv) {
|
|
|
175
170
|
i = (i + 1);
|
|
176
171
|
opts.jsxTarget = args[i];
|
|
177
172
|
}
|
|
173
|
+
else if (((a == "--template") || (a == "-t"))) {
|
|
174
|
+
i = (i + 1);
|
|
175
|
+
opts.template = args[i];
|
|
176
|
+
}
|
|
178
177
|
else if (!a.startsWith("--")) {
|
|
179
178
|
positional.push(a);
|
|
180
179
|
}
|
|
@@ -243,6 +242,21 @@ function printErrors(errors, source, filePath) {
|
|
|
243
242
|
}
|
|
244
243
|
console.error();
|
|
245
244
|
}
|
|
245
|
+
function findFluxImports(source) {
|
|
246
|
+
const found = [];
|
|
247
|
+
const lines = source.split("\n");
|
|
248
|
+
for (const line of lines) {
|
|
249
|
+
const trimmed = line.trim();
|
|
250
|
+
const m = trimmed.match(/^import\s+.+\s+from\s+["'](\.[^"']+)["']/);
|
|
251
|
+
if (m) {
|
|
252
|
+
const src = m[1];
|
|
253
|
+
if (((!src.endsWith(".js") && !src.endsWith(".json")) && !src.endsWith(".node"))) {
|
|
254
|
+
found.push(src);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return found;
|
|
259
|
+
}
|
|
246
260
|
function cmdCompile(filePath, opts) {
|
|
247
261
|
const { source, abs } = readFluxFile(filePath);
|
|
248
262
|
const cfg = loadConfig(Path.dirname(abs));
|
|
@@ -276,6 +290,21 @@ function cmdCompile(filePath, opts) {
|
|
|
276
290
|
}
|
|
277
291
|
function cmdRun(filePath, opts) {
|
|
278
292
|
const { source, abs } = readFluxFile(filePath);
|
|
293
|
+
const fluxImports = findFluxImports(source);
|
|
294
|
+
if ((fluxImports.length > 0)) {
|
|
295
|
+
const example = fluxImports[0];
|
|
296
|
+
const entryRel = Path.relative(process.cwd(), abs);
|
|
297
|
+
const outFile = (Path.basename(abs, ".flux") + ".js");
|
|
298
|
+
console.error(red("\n✗ Cannot run a multi-file Flux project with `flux run`.\n"));
|
|
299
|
+
console.error((" Found: " + yellow((("import ... from \"" + example) + "\""))));
|
|
300
|
+
console.error();
|
|
301
|
+
console.error(((" Use " + cyan("flux bundle")) + " to compile all files into one, then run it:\n"));
|
|
302
|
+
console.error((" " + yellow(((("flux bundle " + entryRel) + " -o dist/") + outFile))));
|
|
303
|
+
console.error((" " + yellow(("node dist/" + outFile))));
|
|
304
|
+
console.error();
|
|
305
|
+
console.error(((" Or use " + cyan("flux run")) + " only for single-file scripts (no inter-file .flux imports).\n"));
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
279
308
|
const result = transpile(source, { jsx: (opts.jsx ?? false), jsxTarget: (opts.jsxTarget ?? "browser"), mangle: false });
|
|
280
309
|
if (!result.success) {
|
|
281
310
|
console.error(red("\n✗ Compile error"));
|
|
@@ -443,6 +472,10 @@ function cmdBundle(entryPath, opts) {
|
|
|
443
472
|
console.log(result.code);
|
|
444
473
|
return;
|
|
445
474
|
}
|
|
475
|
+
const outDir = Path.dirname(Path.resolve(outFile));
|
|
476
|
+
if (!Fs.existsSync(outDir)) {
|
|
477
|
+
Fs.mkdirSync(outDir, { recursive: true });
|
|
478
|
+
}
|
|
446
479
|
Fs.writeFileSync(outFile, result.code, "utf8");
|
|
447
480
|
const kb = (result.code.length / 1024).toFixed(1);
|
|
448
481
|
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 +606,7 @@ function cmdRepl(opts) {
|
|
|
573
606
|
}
|
|
574
607
|
function cmdInit(name, opts) {
|
|
575
608
|
const projectName = (name ?? "my-flux-app");
|
|
609
|
+
const template = (opts.template ?? "script");
|
|
576
610
|
const dir = Path.resolve(projectName);
|
|
577
611
|
if (Fs.existsSync(dir)) {
|
|
578
612
|
console.error(red(("✗ Directory already exists: " + projectName)));
|
|
@@ -581,38 +615,77 @@ function cmdInit(name, opts) {
|
|
|
581
615
|
Fs.mkdirSync(dir, { recursive: true });
|
|
582
616
|
Fs.mkdirSync(Path.join(dir, "src"), { recursive: true });
|
|
583
617
|
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(("
|
|
618
|
+
const gitignore = "node_modules/\ndist/\n*.js.map\n.DS_Store\n";
|
|
619
|
+
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\")";
|
|
620
|
+
if ((template == "script")) {
|
|
621
|
+
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()";
|
|
622
|
+
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: { } };
|
|
623
|
+
Fs.writeFileSync(Path.join(dir, "src", "main.flux"), mainFlux, "utf8");
|
|
624
|
+
Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
|
|
625
|
+
Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
|
|
626
|
+
Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
|
|
627
|
+
console.log();
|
|
628
|
+
console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("script")));
|
|
629
|
+
console.log();
|
|
630
|
+
console.log(bold(" Next steps:"));
|
|
631
|
+
console.log((" " + yellow(("cd " + projectName))));
|
|
632
|
+
console.log((" " + yellow("npm install")));
|
|
633
|
+
console.log((" " + yellow("npm run dev")));
|
|
634
|
+
console.log();
|
|
635
|
+
console.log(gray(" Add packages:"));
|
|
636
|
+
console.log((" " + yellow("flux add <package>")));
|
|
637
|
+
console.log();
|
|
638
|
+
return;
|
|
603
639
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
640
|
+
if ((template == "server")) {
|
|
641
|
+
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}\"))";
|
|
642
|
+
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: { } };
|
|
643
|
+
Fs.writeFileSync(Path.join(dir, "src", "server.flux"), serverFlux, "utf8");
|
|
644
|
+
Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
|
|
645
|
+
Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
|
|
646
|
+
Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
|
|
647
|
+
console.log();
|
|
648
|
+
console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("server")));
|
|
649
|
+
console.log();
|
|
650
|
+
console.log(bold(" Next steps:"));
|
|
651
|
+
console.log((" " + yellow(("cd " + projectName))));
|
|
652
|
+
console.log((" " + yellow("npm install")));
|
|
653
|
+
console.log((" " + yellow("npm run dev")));
|
|
654
|
+
console.log();
|
|
655
|
+
console.log(gray(" Build for production:"));
|
|
656
|
+
console.log((" " + yellow("npm run build && npm start")));
|
|
657
|
+
console.log();
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if ((template == "webapp")) {
|
|
661
|
+
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)";
|
|
662
|
+
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 )";
|
|
663
|
+
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}\"))";
|
|
664
|
+
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: { } };
|
|
665
|
+
Fs.writeFileSync(Path.join(dir, "src", "utils.flux"), utilsFlux, "utf8");
|
|
666
|
+
Fs.writeFileSync(Path.join(dir, "src", "routes.flux"), routesFlux, "utf8");
|
|
667
|
+
Fs.writeFileSync(Path.join(dir, "src", "server.flux"), serverFlux, "utf8");
|
|
668
|
+
Fs.writeFileSync(Path.join(dir, "tests", "main.test.flux"), testFlux, "utf8");
|
|
669
|
+
Fs.writeFileSync(Path.join(dir, "package.json"), (JSON.stringify(pkgJson, null, 2) + "\n"), "utf8");
|
|
670
|
+
Fs.writeFileSync(Path.join(dir, ".gitignore"), gitignore, "utf8");
|
|
671
|
+
console.log();
|
|
672
|
+
console.log((((green("✓ Project ") + bold((projectName + "/"))) + green(" created with template ")) + cyan("webapp")));
|
|
673
|
+
console.log();
|
|
674
|
+
console.log(bold(" Next steps:"));
|
|
675
|
+
console.log((" " + yellow(("cd " + projectName))));
|
|
676
|
+
console.log(((" " + yellow("npm install")) + gray(" # install dependencies")));
|
|
677
|
+
console.log(((" " + yellow("npm run build")) + gray(" # compile all .flux → dist/server.js")));
|
|
678
|
+
console.log(((" " + yellow("npm start")) + gray(" # run the server")));
|
|
679
|
+
console.log();
|
|
680
|
+
console.log((yellow(" Note: ") + gray("This project uses multiple .flux files.")));
|
|
681
|
+
console.log(((gray(" Always build with ") + yellow("npm run build")) + gray(" (flux bundle) before running.")));
|
|
682
|
+
console.log(((gray(" Do NOT use ") + red("flux run")) + gray(" on files that import other .flux files.")));
|
|
683
|
+
console.log();
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
console.error(red(("✗ Unknown template: " + template)));
|
|
687
|
+
console.error(gray(" Available templates: script, server, webapp"));
|
|
688
|
+
process.exit(1);
|
|
616
689
|
}
|
|
617
690
|
function cmdSelfHosted(sub, opts) {
|
|
618
691
|
const SELF = Path.join(__dirname, ".");
|
package/src/self/codegen.flux
CHANGED
|
@@ -54,6 +54,7 @@ export class CodeGenerator:
|
|
|
54
54
|
smBuilder: any
|
|
55
55
|
_needsFmt: bool
|
|
56
56
|
_loopDepth: int
|
|
57
|
+
_fnDepth: int
|
|
57
58
|
_version: string
|
|
58
59
|
|
|
59
60
|
fn i():
|
|
@@ -166,7 +167,9 @@ export class CodeGenerator:
|
|
|
166
167
|
else:
|
|
167
168
|
self.emit(prefix + asyncKw + 'function ' + name + '(' + params + ') {')
|
|
168
169
|
self.indIn()
|
|
170
|
+
self._fnDepth = self._fnDepth + 1
|
|
169
171
|
for s in node.body: self.genStmt(s)
|
|
172
|
+
self._fnDepth = self._fnDepth - 1
|
|
170
173
|
self.indOut()
|
|
171
174
|
self.emit('}')
|
|
172
175
|
|
|
@@ -200,7 +203,9 @@ export class CodeGenerator:
|
|
|
200
203
|
else:
|
|
201
204
|
self.emit(staticKw + asyncKw + m.name + '(' + params + ') {')
|
|
202
205
|
self.indIn()
|
|
206
|
+
self._fnDepth = self._fnDepth + 1
|
|
203
207
|
for s in m.body: self.genStmt(s)
|
|
208
|
+
self._fnDepth = self._fnDepth - 1
|
|
204
209
|
self.indOut()
|
|
205
210
|
self.emit('}')
|
|
206
211
|
self.blank()
|
|
@@ -304,8 +309,10 @@ export class CodeGenerator:
|
|
|
304
309
|
val exprSrc = self.genExpr(arm.body[0].expr)
|
|
305
310
|
if self._loopDepth > 0:
|
|
306
311
|
self.emit(exprSrc + ';')
|
|
307
|
-
else:
|
|
312
|
+
else if self._fnDepth > 0:
|
|
308
313
|
self.emit('return ' + exprSrc + ';')
|
|
314
|
+
else:
|
|
315
|
+
self.emit(exprSrc + ';')
|
|
309
316
|
else:
|
|
310
317
|
for s in arm.body: self.genStmt(s)
|
|
311
318
|
self.indOut()
|
|
@@ -492,11 +499,13 @@ export class CodeGenerator:
|
|
|
492
499
|
val savedLevel = self.level
|
|
493
500
|
val savedLoop = self._loopDepth
|
|
494
501
|
self._loopDepth = 0
|
|
502
|
+
self._fnDepth = self._fnDepth + 1
|
|
495
503
|
self.emit('(() => {')
|
|
496
504
|
self.indIn()
|
|
497
505
|
self.genMatch(node)
|
|
498
506
|
self.indOut()
|
|
499
507
|
self.emit('})()')
|
|
508
|
+
self._fnDepth = self._fnDepth - 1
|
|
500
509
|
self._loopDepth = savedLoop
|
|
501
510
|
val block = self.lines.splice(saved).map(l -> l.trimStart()).join(' ')
|
|
502
511
|
self.level = savedLevel
|