@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/dist/flux.cjs.js
CHANGED
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.5",
|
|
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
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// src/self/bundler.flux — written in Flux, compiled by stage-0
|
|
4
4
|
//
|
|
5
5
|
// Bundles multiple Flux modules into a single JS file.
|
|
6
|
+
// Resolves all inter-file .flux imports recursively.
|
|
7
|
+
// npm packages (non-relative imports) remain as require() calls.
|
|
6
8
|
// ============================================================
|
|
7
9
|
|
|
8
10
|
import Fs from "fs"
|
|
@@ -16,17 +18,25 @@ fn toModuleId(absPath):
|
|
|
16
18
|
return '_flux_' + base.replace(/[^a-zA-Z0-9]/g, '_')
|
|
17
19
|
|
|
18
20
|
fn extractModuleInfo(ast, fromFile):
|
|
19
|
-
val imports
|
|
20
|
-
val
|
|
21
|
-
val
|
|
22
|
-
val
|
|
21
|
+
val imports = []
|
|
22
|
+
val npmImports = []
|
|
23
|
+
val exports = []
|
|
24
|
+
val body = []
|
|
25
|
+
val dir = Path.dirname(fromFile)
|
|
23
26
|
|
|
24
27
|
for node in ast.body:
|
|
25
28
|
if node.type == 'ImportDecl':
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
val src = node.source
|
|
30
|
+
// Relative import → resolve as .flux file
|
|
31
|
+
if src.startsWith('./') or src.startsWith('../'):
|
|
32
|
+
var resolved = src
|
|
33
|
+
if not resolved.endsWith('.flux'): resolved = resolved + '.flux'
|
|
34
|
+
val absPath = Path.resolve(dir, resolved)
|
|
35
|
+
imports.push({ names: node.names, source: node.source, absPath })
|
|
36
|
+
else:
|
|
37
|
+
// npm package import — keep as-is (require in output)
|
|
38
|
+
npmImports.push({ names: node.names, source: src })
|
|
39
|
+
body.push(node)
|
|
30
40
|
|
|
31
41
|
else if node.type == 'ExportDecl':
|
|
32
42
|
val inner = node.decl
|
|
@@ -38,7 +48,7 @@ fn extractModuleInfo(ast, fromFile):
|
|
|
38
48
|
else:
|
|
39
49
|
body.push(node)
|
|
40
50
|
|
|
41
|
-
return { cleanAst: { type: 'Program', body }, imports, exports }
|
|
51
|
+
return { cleanAst: { type: 'Program', body }, imports, npmImports, exports }
|
|
42
52
|
|
|
43
53
|
fn codegenModule(ast):
|
|
44
54
|
val cg = makeCodeGen({ indent: ' ' })
|
|
@@ -80,6 +90,7 @@ export class Bundler:
|
|
|
80
90
|
val info = extractModuleInfo(ast, absPath)
|
|
81
91
|
self.modules.set(absPath, { cleanAst: info.cleanAst, imports: info.imports, exports: info.exports, source, absPath })
|
|
82
92
|
|
|
93
|
+
// Recursively collect all .flux imports
|
|
83
94
|
for imp in info.imports:
|
|
84
95
|
self.collect(imp.absPath)
|
|
85
96
|
|
|
@@ -98,12 +109,6 @@ export class Bundler:
|
|
|
98
109
|
lines.push('"use strict";')
|
|
99
110
|
lines.push('')
|
|
100
111
|
|
|
101
|
-
// flux_modules resolver: patches Module._resolveFilename so require() looks in
|
|
102
|
-
// flux_modules/node_modules/ first (packages installed via `flux install`).
|
|
103
|
-
lines.push('// flux_modules resolver')
|
|
104
|
-
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);};}})();")
|
|
105
|
-
lines.push('')
|
|
106
|
-
|
|
107
112
|
lines.push('(function() {')
|
|
108
113
|
lines.push('')
|
|
109
114
|
|
|
@@ -125,7 +130,7 @@ export class Bundler:
|
|
|
125
130
|
for imp in mod.imports:
|
|
126
131
|
val srcId = toModuleId(imp.absPath)
|
|
127
132
|
for name in imp.names:
|
|
128
|
-
val localName
|
|
133
|
+
val localName = name.alias ?? name.name
|
|
129
134
|
val importedName = name.name
|
|
130
135
|
lines.push(' var ' + localName + ' = ' + srcId + '._exports ? ' + srcId + '._exports.' + importedName + ' : ' + srcId + '.' + importedName + ';')
|
|
131
136
|
|
package/src/self/bundler.js
CHANGED
|
@@ -1 +1,185 @@
|
|
|
1
|
-
|
|
1
|
+
/* compiled from src/self/bundler.flux */
|
|
2
|
+
'use strict';
|
|
3
|
+
// Generated by Flux Transpiler v3.5.3 (self-hosted)
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const Fs = require("fs");
|
|
7
|
+
const Path = require("path");
|
|
8
|
+
const { lexerize } = require("./lexer");
|
|
9
|
+
const { makeParser } = require("./parser");
|
|
10
|
+
const { makeCodeGen } = require("./codegen");
|
|
11
|
+
function toModuleId(absPath) {
|
|
12
|
+
const base = Path.basename(absPath, ".flux");
|
|
13
|
+
return ("_flux_" + base.replace(/[^a-zA-Z0-9]/g, "_"));
|
|
14
|
+
}
|
|
15
|
+
function extractModuleInfo(ast, fromFile) {
|
|
16
|
+
const imports = [];
|
|
17
|
+
const npmImports = [];
|
|
18
|
+
const exports = [];
|
|
19
|
+
const body = [];
|
|
20
|
+
const dir = Path.dirname(fromFile);
|
|
21
|
+
for (const node of ast.body) {
|
|
22
|
+
if ((node.type == "ImportDecl")) {
|
|
23
|
+
const src = node.source;
|
|
24
|
+
if ((src.startsWith("./") || src.startsWith("../"))) {
|
|
25
|
+
let resolved = src;
|
|
26
|
+
if (!resolved.endsWith(".flux")) {
|
|
27
|
+
resolved = (resolved + ".flux");
|
|
28
|
+
}
|
|
29
|
+
const absPath = Path.resolve(dir, resolved);
|
|
30
|
+
imports.push({ names: node.names, source: node.source, absPath });
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
npmImports.push({ names: node.names, source: src });
|
|
34
|
+
body.push(node);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if ((node.type == "ExportDecl")) {
|
|
38
|
+
const inner = node.decl;
|
|
39
|
+
if ((inner.type == "FnDecl")) {
|
|
40
|
+
exports.push(inner.name);
|
|
41
|
+
}
|
|
42
|
+
if ((inner.type == "ClassDecl")) {
|
|
43
|
+
exports.push(inner.name);
|
|
44
|
+
}
|
|
45
|
+
if ((inner.type == "VarDecl")) {
|
|
46
|
+
exports.push(inner.name);
|
|
47
|
+
}
|
|
48
|
+
body.push(inner);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
body.push(node);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { cleanAst: { type: "Program", body }, imports, npmImports, exports };
|
|
55
|
+
}
|
|
56
|
+
function codegenModule(ast) {
|
|
57
|
+
const cg = makeCodeGen({ indent: " " });
|
|
58
|
+
const result = cg.generate(ast);
|
|
59
|
+
const lines = result.code.split("\n");
|
|
60
|
+
const start = lines.findIndex((l) => (((l.trim() != "") && !l.includes("Generated by Flux")) && !l.includes("\"use strict\"")));
|
|
61
|
+
return lines.slice(start).join("\n");
|
|
62
|
+
}
|
|
63
|
+
class Bundler {
|
|
64
|
+
constructor(entryFile, options, modules, order, visited, inStack) {
|
|
65
|
+
this.entryFile = entryFile;
|
|
66
|
+
this.options = options;
|
|
67
|
+
this.modules = modules;
|
|
68
|
+
this.order = order;
|
|
69
|
+
this.visited = visited;
|
|
70
|
+
this.inStack = inStack;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
bundle() {
|
|
74
|
+
this.collect(this.entryFile);
|
|
75
|
+
return this.link();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
collect(absPath) {
|
|
79
|
+
if (this.visited.has(absPath)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (this.inStack.has(absPath)) {
|
|
83
|
+
throw new Error(((("[" + Path.basename(absPath)) + "] Circular dependency detected: ") + absPath));
|
|
84
|
+
}
|
|
85
|
+
if (!Fs.existsSync(absPath)) {
|
|
86
|
+
throw new Error(((("[" + Path.basename(absPath)) + "] File not found: ") + absPath));
|
|
87
|
+
}
|
|
88
|
+
this.inStack.add(absPath);
|
|
89
|
+
const source = Fs.readFileSync(absPath, "utf8");
|
|
90
|
+
let ast = null;
|
|
91
|
+
try {
|
|
92
|
+
const tokens = lexerize(source).tokenize();
|
|
93
|
+
ast = makeParser(tokens).parse();
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
throw new Error(((("[" + Path.basename(absPath)) + "] Parse error: ") + e.message));
|
|
97
|
+
}
|
|
98
|
+
const info = extractModuleInfo(ast, absPath);
|
|
99
|
+
this.modules.set(absPath, { cleanAst: info.cleanAst, imports: info.imports, exports: info.exports, source, absPath });
|
|
100
|
+
for (const imp of info.imports) {
|
|
101
|
+
this.collect(imp.absPath);
|
|
102
|
+
}
|
|
103
|
+
this.inStack.delete(absPath);
|
|
104
|
+
this.visited.add(absPath);
|
|
105
|
+
this.order.push(absPath);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
link() {
|
|
109
|
+
const lines = [];
|
|
110
|
+
const banner = (this.options.banner != false);
|
|
111
|
+
if (banner) {
|
|
112
|
+
lines.push("// Generated by Flux Bundler (self-hosted)");
|
|
113
|
+
lines.push(("// Entry: " + Path.basename(this.entryFile)));
|
|
114
|
+
lines.push(("// Modules: " + this.order.length));
|
|
115
|
+
lines.push("\"use strict\";");
|
|
116
|
+
lines.push("");
|
|
117
|
+
}
|
|
118
|
+
lines.push("(function() {");
|
|
119
|
+
lines.push("");
|
|
120
|
+
for (const absPath of this.order) {
|
|
121
|
+
const mod = this.modules.get(absPath);
|
|
122
|
+
const isEntry = (absPath == this.entryFile);
|
|
123
|
+
const modId = toModuleId(absPath);
|
|
124
|
+
const relName = Path.relative(process.cwd(), absPath);
|
|
125
|
+
lines.push((" // " + "─".repeat(60)));
|
|
126
|
+
lines.push((" // Module: " + relName));
|
|
127
|
+
lines.push((" // " + "─".repeat(60)));
|
|
128
|
+
if (!isEntry) {
|
|
129
|
+
lines.push(((" var " + modId) + " = (function() {"));
|
|
130
|
+
lines.push(" var _exports = {};");
|
|
131
|
+
lines.push("");
|
|
132
|
+
}
|
|
133
|
+
for (const imp of mod.imports) {
|
|
134
|
+
const srcId = toModuleId(imp.absPath);
|
|
135
|
+
for (const name of imp.names) {
|
|
136
|
+
const localName = (name.alias ?? name.name);
|
|
137
|
+
const importedName = name.name;
|
|
138
|
+
lines.push(((((((((((((" var " + localName) + " = ") + srcId) + "._exports ? ") + srcId) + "._exports.") + importedName) + " : ") + srcId) + ".") + importedName) + ";"));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if ((mod.imports.length > 0)) {
|
|
142
|
+
lines.push("");
|
|
143
|
+
}
|
|
144
|
+
const moduleCode = codegenModule(mod.cleanAst);
|
|
145
|
+
lines.push(moduleCode);
|
|
146
|
+
if (!isEntry) {
|
|
147
|
+
if ((mod.exports.length > 0)) {
|
|
148
|
+
lines.push("");
|
|
149
|
+
for (const expName of mod.exports) {
|
|
150
|
+
lines.push(((((" _exports." + expName) + " = ") + expName) + ";"));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push(" return _exports;");
|
|
155
|
+
lines.push(" })()");
|
|
156
|
+
lines.push(((((" " + modId) + "._exports = ") + modId) + ";"));
|
|
157
|
+
}
|
|
158
|
+
lines.push("");
|
|
159
|
+
}
|
|
160
|
+
lines.push("})();");
|
|
161
|
+
return { code: lines.join("\n"), modules: this.order.length };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
module.exports.Bundler = Bundler;
|
|
167
|
+
function makeBundler(entryFile, options) {
|
|
168
|
+
return new Bundler(Path.resolve(entryFile), (options ?? { }), new Map(), [], new Set(), new Set());
|
|
169
|
+
}
|
|
170
|
+
module.exports.makeBundler = makeBundler;
|
|
171
|
+
function bundle(entryFile, options) {
|
|
172
|
+
const result = { success: false, code: "", modules: 0, errors: [] };
|
|
173
|
+
try {
|
|
174
|
+
const b = makeBundler(entryFile, options);
|
|
175
|
+
const out = b.bundle();
|
|
176
|
+
result.code = out.code;
|
|
177
|
+
result.modules = out.modules;
|
|
178
|
+
result.success = true;
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
result.errors.push({ message: e.message, name: e.name, file: null });
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
module.exports.bundle = bundle;
|