@xnoxs/flux-lang 4.0.9 → 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 +19 -0
- package/dist/flux-cli.js +7959 -4853
- package/dist/flux.cjs.js +5507 -3647
- package/dist/flux.esm.js +5505 -3650
- package/dist/flux.min.js +356 -95
- package/dist/transpiler.js +4 -0
- package/package.json +1 -1
- package/src/config.js +101 -86
- package/src/formatter.js +105 -100
- package/src/self/bundler.js +1 -197
- package/src/self/checker.js +0 -2
- package/src/self/cli.js +0 -2
- package/src/self/codegen.js +1 -811
- package/src/self/config.js +0 -2
- package/src/self/css-preprocessor.js +1 -3
- package/src/self/formatter.js +0 -2
- package/src/self/jsx.js +2 -4
- package/src/self/lexer.js +6 -8
- package/src/self/linter.js +0 -2
- package/src/self/mangler.js +0 -2
- package/src/self/parser.js +0 -2
- package/src/self/pkg.js +0 -2
- package/src/self/sourcemap.js +0 -2
- package/src/self/stdlib.js +0 -2
- package/src/self/test-runner.js +0 -2
- package/src/self/transpiler.js +0 -2
- package/src/self/type-checker.js +0 -2
- package/src/stdlib.js +731 -218
- package/src/self/index.flux +0 -87
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnoxs/flux-lang",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.1",
|
|
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",
|
package/src/config.js
CHANGED
|
@@ -1,101 +1,116 @@
|
|
|
1
|
-
/* compiled from src/self/config.flux by Flux Lang */
|
|
2
1
|
'use strict';
|
|
3
|
-
// ── Flux stdlib ──
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Flux Lang — Project Config Loader
|
|
5
|
+
*
|
|
6
|
+
* Looks for flux.config.js (or .fluxrc.js) in the current working directory.
|
|
7
|
+
* Returns a plain object with config values; returns {} if no config found.
|
|
8
|
+
*
|
|
9
|
+
* Config schema:
|
|
10
|
+
* module.exports = {
|
|
11
|
+
* jsxTarget : 'browser' | 'server' | 'react', // default: 'browser'
|
|
12
|
+
* outDir : './dist', // output dir for compiled files
|
|
13
|
+
* sourcemap : false, // generate .js.map files
|
|
14
|
+
* mangle : true, // obfuscate variable/function names
|
|
15
|
+
* ignore : [], // glob patterns to skip in `flux test`
|
|
16
|
+
* entry : 'src/main.flux', // default bundle entry
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* CLI flags always take precedence over config values.
|
|
20
|
+
*/
|
|
6
21
|
|
|
7
|
-
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
8
24
|
|
|
9
|
-
|
|
25
|
+
const CONFIG_NAMES = [
|
|
26
|
+
'flux.config.js',
|
|
27
|
+
'flux.config.cjs',
|
|
28
|
+
'.fluxrc.js',
|
|
29
|
+
];
|
|
10
30
|
|
|
11
|
-
|
|
12
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Load project config from CWD (or a given directory).
|
|
33
|
+
* @param {string} [cwd] - directory to search (default: process.cwd())
|
|
34
|
+
* @returns {object} raw config object (or {} if none found)
|
|
35
|
+
*/
|
|
36
|
+
function loadConfig(cwd) {
|
|
37
|
+
const dir = cwd || process.cwd();
|
|
13
38
|
|
|
14
|
-
|
|
15
|
-
|
|
39
|
+
for (const name of CONFIG_NAMES) {
|
|
40
|
+
const configPath = path.join(dir, name);
|
|
41
|
+
if (!fs.existsSync(configPath)) continue;
|
|
42
|
+
|
|
43
|
+
// Bust require cache so tests can reload different configs
|
|
44
|
+
delete require.cache[require.resolve(configPath)];
|
|
16
45
|
|
|
17
|
-
const Fs = require("fs");
|
|
18
|
-
const Path = require("path");
|
|
19
|
-
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: { } } };
|
|
20
|
-
module.exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
21
|
-
function loadConfig(cwd_) {
|
|
22
|
-
const cwd = (cwd_ ?? process.cwd());
|
|
23
|
-
const jsonPath = Path.join(cwd, "flux.json");
|
|
24
|
-
if (Fs.existsSync(jsonPath)) {
|
|
25
|
-
try {
|
|
26
|
-
const raw = Fs.readFileSync(jsonPath, "utf8");
|
|
27
|
-
const parsed = JSON.parse(raw);
|
|
28
|
-
return mergeConfig(DEFAULT_CONFIG, parsed);
|
|
29
|
-
}
|
|
30
|
-
catch (e) {
|
|
31
|
-
throw new Error(("Invalid flux.json: " + e.message));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const jsPath = Path.join(cwd, "flux.config.js");
|
|
35
|
-
if (Fs.existsSync(jsPath)) {
|
|
36
46
|
try {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const val_ = overrides[key];
|
|
54
|
-
if (((val_ != null) && (val_ != undefined))) {
|
|
55
|
-
result[key] = val_;
|
|
47
|
+
const raw = require(configPath);
|
|
48
|
+
const cfg = raw && raw.default ? raw.default : raw;
|
|
49
|
+
|
|
50
|
+
if (typeof cfg !== 'object' || cfg === null) {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
`[flux] warning: ${name} must export a plain object — ignoring\n`
|
|
53
|
+
);
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return cfg;
|
|
58
|
+
} catch (e) {
|
|
59
|
+
process.stderr.write(
|
|
60
|
+
`[flux] error loading ${name}: ${e.message}\n`
|
|
61
|
+
);
|
|
62
|
+
return {};
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
|
-
|
|
65
|
+
|
|
66
|
+
return {};
|
|
59
67
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Merge file config with CLI opts (CLI flags win).
|
|
71
|
+
* @param {object} fileConfig - from loadConfig()
|
|
72
|
+
* @param {object} cliOpts - parsed CLI flags
|
|
73
|
+
* @returns {object} merged options safe to pass to transpile()
|
|
74
|
+
*/
|
|
75
|
+
function mergeConfig(fileConfig, cliOpts) {
|
|
76
|
+
const cfg = fileConfig || {};
|
|
77
|
+
const cli = cliOpts || {};
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
jsxTarget : cli.jsxTarget !== undefined ? cli.jsxTarget : (cfg.jsxTarget || 'browser'),
|
|
81
|
+
outDir : cli.outDir !== undefined ? cli.outDir : (cfg.outDir || null),
|
|
82
|
+
sourcemap : cli.sourcemap !== undefined ? cli.sourcemap : (cfg.sourcemap || false),
|
|
83
|
+
mangle : cli.mangle !== undefined ? cli.mangle : (cfg.mangle !== undefined ? cfg.mangle : true),
|
|
84
|
+
ignore : cli.ignore !== undefined ? cli.ignore : (cfg.ignore || []),
|
|
85
|
+
entry : cli.entry !== undefined ? cli.entry : (cfg.entry || null),
|
|
86
|
+
};
|
|
66
87
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return { valid: (errors.length == 0), errors };
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Identity helper for IDE type-checking / autocompletion.
|
|
91
|
+
* Usage in flux.config.js: const { defineConfig } = require('@xnoxs/flux-lang/config');
|
|
92
|
+
* @param {object} config
|
|
93
|
+
* @returns {object}
|
|
94
|
+
*/
|
|
95
|
+
function defineConfig(config) {
|
|
96
|
+
return config;
|
|
77
97
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
return JSON.parse(Fs.readFileSync(pkgJson, "utf8"));
|
|
94
|
-
}
|
|
95
|
-
catch (e2) {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Minimal glob-pattern matcher (also used by test-runner shim to avoid circular require).
|
|
101
|
+
*/
|
|
102
|
+
function matchIgnore(filePath, patterns) {
|
|
103
|
+
if (!patterns || patterns.length === 0) return false;
|
|
104
|
+
const normalize = p => p.replace(/\\/g, '/');
|
|
105
|
+
const fp = normalize(filePath);
|
|
106
|
+
for (const pattern of patterns) {
|
|
107
|
+
let re = normalize(pattern).replace(/\*\*/g, '\x00DS\x00');
|
|
108
|
+
re = re.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
109
|
+
re = re.replace(/\*/g, '[^/]*');
|
|
110
|
+
re = re.replace(/\x00DS\x00\//g, '(.+/)?').replace(/\x00DS\x00/g, '.*');
|
|
111
|
+
if (new RegExp('(^|/)' + re + '$').test(fp)) return true;
|
|
98
112
|
}
|
|
99
|
-
return
|
|
113
|
+
return false;
|
|
100
114
|
}
|
|
101
|
-
|
|
115
|
+
|
|
116
|
+
module.exports = { loadConfig, mergeConfig, defineConfig, matchIgnore };
|
package/src/formatter.js
CHANGED
|
@@ -1,135 +1,140 @@
|
|
|
1
|
-
/* compiled from src/self/formatter.flux by Flux Lang */
|
|
2
1
|
'use strict';
|
|
3
|
-
// ── Flux stdlib ──
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
// ── Flux Code Formatter ───────────────────────────────────────────────────────
|
|
4
|
+
// Normalizes Flux source code:
|
|
5
|
+
// • 4-space indentation
|
|
6
|
+
// • Strips trailing whitespace
|
|
7
|
+
// • Max 1 consecutive blank line
|
|
8
|
+
// • Blank line after top-level fn/class/type declarations
|
|
9
|
+
// • Spacing around binary operators
|
|
10
|
+
// • Consistent colon / arrow spacing
|
|
6
11
|
|
|
7
|
-
function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
|
|
8
|
-
|
|
9
|
-
function max(arr) {
|
|
10
|
-
if (arguments.length > 1) return Math.max.apply(null, arguments);
|
|
11
|
-
return Math.max.apply(null, arr);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function round(n, decimals) {
|
|
15
|
-
if (decimals == null) return Math.round(n);
|
|
16
|
-
var f = Math.pow(10, decimals);
|
|
17
|
-
return Math.round(n * f) / f;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function trimStart(s) { return String(s).trimStart(); }
|
|
21
|
-
|
|
22
|
-
function trimEnd(s) { return String(s).trimEnd(); }
|
|
23
|
-
|
|
24
|
-
function startsWith(s, prefix) { return String(s).startsWith(prefix); }
|
|
25
|
-
|
|
26
|
-
function repeat(s, n) { return String(s).repeat(n); }
|
|
27
|
-
// ── end stdlib ──
|
|
28
|
-
|
|
29
|
-
// Generated by Flux Transpiler v3.2.0
|
|
30
|
-
"use strict";
|
|
31
|
-
|
|
32
|
-
function normalizeOperators(line) {
|
|
33
|
-
if (((line.match(/:/g) ?? []).length > 2)) {
|
|
34
|
-
return line;
|
|
35
|
-
}
|
|
36
|
-
let result = line;
|
|
37
|
-
result = result.replace(/([^=!<>+\-*\/%])=(?!=|>)/g, "$1 = ");
|
|
38
|
-
result = result.replace(/\s{2,}=/g, " =");
|
|
39
|
-
result = result.replace(/([^\s]) +/g, "$1 ");
|
|
40
|
-
result = result.replace(/,(?!\s)/g, ", ");
|
|
41
|
-
result = result.replace(/\s*->\s*/g, " -> ");
|
|
42
|
-
result = result.replace(/\s*=>\s*/g, " => ");
|
|
43
|
-
result = result.trimEnd();
|
|
44
|
-
return result;
|
|
45
|
-
}
|
|
46
12
|
function format(source) {
|
|
47
|
-
const lines = source.replace(/\r\n/g,
|
|
13
|
+
const lines = source.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
14
|
+
|
|
15
|
+
// ── 1. Detect indentation unit from the first indented line ─────────────
|
|
48
16
|
let indentUnit = 4;
|
|
49
17
|
for (const line of lines) {
|
|
50
18
|
const m = line.match(/^( +)\S/);
|
|
51
19
|
if (m) {
|
|
52
20
|
const w = m[1].length;
|
|
53
|
-
if (
|
|
54
|
-
indentUnit = w;
|
|
55
|
-
}
|
|
21
|
+
if (w > 0 && w < indentUnit) indentUnit = w;
|
|
56
22
|
break;
|
|
57
23
|
}
|
|
58
24
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
25
|
+
if (indentUnit < 1) indentUnit = 4;
|
|
26
|
+
|
|
27
|
+
// ── 2. Strip trailing whitespace + normalize indent to 4 spaces ─────────
|
|
28
|
+
const normalized = lines.map(line => {
|
|
63
29
|
const stripped = line.trimEnd();
|
|
64
|
-
if (!stripped)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
const spaces = (m2 ? m2[1].length : 0);
|
|
69
|
-
const level = Math.round((spaces / indentUnit));
|
|
30
|
+
if (!stripped) return '';
|
|
31
|
+
const m = stripped.match(/^( *)/);
|
|
32
|
+
const spaces = m ? m[1].length : 0;
|
|
33
|
+
const level = Math.round(spaces / indentUnit);
|
|
70
34
|
const content = stripped.trimStart();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
35
|
+
|
|
36
|
+
// Normalize spacing around operators in the content (only when not a string literal line)
|
|
37
|
+
const formatted = content.startsWith('//') || content.startsWith('*')
|
|
38
|
+
? content
|
|
39
|
+
: normalizeOperators(content);
|
|
40
|
+
|
|
41
|
+
return ' '.repeat(level) + formatted;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ── 3. Collapse multiple blank lines → max 1 ────────────────────────────
|
|
78
45
|
const collapsed = [];
|
|
79
46
|
let blankRun = 0;
|
|
80
47
|
for (const line of normalized) {
|
|
81
|
-
if (
|
|
82
|
-
blankRun
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
48
|
+
if (line === '') {
|
|
49
|
+
blankRun++;
|
|
50
|
+
if (blankRun <= 1) collapsed.push('');
|
|
51
|
+
} else {
|
|
88
52
|
blankRun = 0;
|
|
89
53
|
collapsed.push(line);
|
|
90
54
|
}
|
|
91
55
|
}
|
|
56
|
+
|
|
57
|
+
// ── 4. Add blank line after top-level fn/class/type/async fn blocks ──────
|
|
92
58
|
const withSpacing = [];
|
|
93
|
-
let i = 0;
|
|
94
|
-
while ((i < collapsed.length)) {
|
|
59
|
+
for (let i = 0; i < collapsed.length; i++) {
|
|
95
60
|
withSpacing.push(collapsed[i]);
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
61
|
+
const cur = collapsed[i].trimStart();
|
|
62
|
+
const next = (collapsed[i + 1] || '').trimStart();
|
|
63
|
+
const indent = collapsed[i].match(/^( *)/)[1].length;
|
|
64
|
+
|
|
65
|
+
// After dedented fn/class/type at indent=0 or indent=4, add blank line
|
|
66
|
+
if (indent <= 4 && /^(fn |async fn |class |type )/.test(cur)) {
|
|
67
|
+
// No blank needed if next line is blank already
|
|
68
|
+
if (next !== '' && !/^(fn |async fn |class |type )/.test(next)) {
|
|
69
|
+
// skip — next content comes right after declaration line, part of fn
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// After closing a fn/class block at top level (no indent dedent signal,
|
|
74
|
+
// but we detect when a non-indented line follows an indented block)
|
|
75
|
+
if (i > 0) {
|
|
76
|
+
const prevIndent = collapsed[i - 1].match(/^( *)/)[1].length;
|
|
77
|
+
if (prevIndent >= 4 && indent === 0 && cur !== '') {
|
|
78
|
+
// We just came back to top-level — add blank if last added wasn't blank
|
|
79
|
+
if (withSpacing[withSpacing.length - 2] !== '') {
|
|
80
|
+
withSpacing.splice(withSpacing.length - 1, 0, '');
|
|
103
81
|
}
|
|
104
82
|
}
|
|
105
83
|
}
|
|
106
|
-
i = (i + 1);
|
|
107
84
|
}
|
|
85
|
+
|
|
86
|
+
// ── 5. Trim leading/trailing blank lines + ensure single trailing newline ─
|
|
108
87
|
let out = withSpacing;
|
|
109
|
-
while (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
88
|
+
while (out.length > 0 && out[0] === '') out.shift();
|
|
89
|
+
while (out.length > 0 && out[out.length - 1] === '') out.pop();
|
|
90
|
+
out.push('');
|
|
91
|
+
|
|
92
|
+
return out.join('\n');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Operator spacing normalizer ──────────────────────────────────────────────
|
|
96
|
+
// Ensures spaces around binary operators without touching strings/comments
|
|
97
|
+
function normalizeOperators(line) {
|
|
98
|
+
// Skip lines that look like CSS / strings (heuristic: too many : or { })
|
|
99
|
+
if ((line.match(/:/g) || []).length > 2) return line;
|
|
100
|
+
|
|
101
|
+
let result = line;
|
|
102
|
+
|
|
103
|
+
// Add space around = (but not ==, !=, <=, >=, ->, =>, +=, -=, *=, /=, %=)
|
|
104
|
+
result = result.replace(/([^=!<>+\-*/%])=(?!=|>)/g, '$1 = ');
|
|
105
|
+
result = result.replace(/\s{2,}=/g, ' =');
|
|
106
|
+
|
|
107
|
+
// Normalize multiple spaces inside (but not at start = indentation)
|
|
108
|
+
result = result.replace(/([^\s]) +/g, '$1 ');
|
|
109
|
+
|
|
110
|
+
// Ensure space after comma
|
|
111
|
+
result = result.replace(/,(?!\s)/g, ', ');
|
|
112
|
+
|
|
113
|
+
// Ensure space before/after -> and =>
|
|
114
|
+
result = result.replace(/\s*->\s*/g, ' -> ');
|
|
115
|
+
result = result.replace(/\s*=>\s*/g, ' => ');
|
|
116
|
+
|
|
117
|
+
// Trim end
|
|
118
|
+
result = result.trimEnd();
|
|
119
|
+
|
|
120
|
+
return result;
|
|
117
121
|
}
|
|
118
|
-
|
|
122
|
+
|
|
123
|
+
// ── Diff output ──────────────────────────────────────────────────────────────
|
|
119
124
|
function diff(original, formatted) {
|
|
120
|
-
const origLines = original.split(
|
|
121
|
-
const fmtLines
|
|
122
|
-
const changes
|
|
125
|
+
const origLines = original.split('\n');
|
|
126
|
+
const fmtLines = formatted.split('\n');
|
|
127
|
+
const changes = [];
|
|
128
|
+
|
|
123
129
|
const maxLen = Math.max(origLines.length, fmtLines.length);
|
|
124
|
-
let
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
changes.push({ line: (j + 1), original: o, formatted: f });
|
|
130
|
+
for (let i = 0; i < maxLen; i++) {
|
|
131
|
+
const o = origLines[i];
|
|
132
|
+
const f = fmtLines[i];
|
|
133
|
+
if (o !== f) {
|
|
134
|
+
changes.push({ line: i + 1, original: o, formatted: f });
|
|
130
135
|
}
|
|
131
|
-
j = (j + 1);
|
|
132
136
|
}
|
|
133
137
|
return changes;
|
|
134
138
|
}
|
|
135
|
-
|
|
139
|
+
|
|
140
|
+
module.exports = { format, diff };
|