@xnoxs/flux-lang 4.0.8 → 4.0.9
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 +0 -12
- package/dist/flux-cli.js +559 -551
- package/dist/flux.cjs.js +3689 -5548
- package/dist/flux.esm.js +3691 -5545
- package/dist/flux.min.js +95 -356
- package/index.js +10 -9
- package/package.json +5 -4
- package/src/config.js +86 -101
- package/src/formatter.js +100 -105
- package/src/self/bundler.flux +16 -21
- package/src/self/bundler.js +28 -16
- package/src/self/checker.js +2 -0
- package/src/self/cli.flux +220 -274
- package/src/self/cli.js +59 -123
- package/src/self/codegen.js +811 -1
- package/src/self/config.flux +28 -36
- package/src/self/config.js +32 -33
- package/src/self/css-preprocessor.js +3 -1
- package/src/self/formatter.js +2 -0
- package/src/self/index.flux +87 -0
- package/src/self/jsx.js +4 -2
- package/src/self/lexer.js +8 -6
- package/src/self/linter.js +2 -0
- package/src/self/mangler.js +2 -0
- package/src/self/parser.js +2 -0
- package/src/self/pkg.flux +136 -39
- package/src/self/pkg.js +125 -36
- package/src/self/sourcemap.js +2 -0
- package/src/self/stdlib.js +2 -0
- package/src/self/test-runner.js +2 -0
- package/src/self/transpiler.flux +1 -1
- package/src/self/transpiler.js +2 -2
- package/src/self/type-checker.js +2 -0
- package/src/stdlib.js +218 -731
- package/dist/transpiler.js +0 -4
package/index.js
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Flux Lang — Public API
|
|
5
5
|
*
|
|
6
|
-
* const { transpile } = require('
|
|
7
|
-
* import { transpile, format, buildStdlib } from '
|
|
6
|
+
* const { transpile } = require('flux-lang');
|
|
7
|
+
* import { transpile, format, buildStdlib } from 'flux-lang';
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const { transpile } = require('./src/
|
|
11
|
-
const { format } = require('./src/
|
|
12
|
-
const { buildStdlib, detectUsedSymbols, STDLIB_SYMBOLS } = require('./src/
|
|
13
|
-
const { Lexer } = require('./src/
|
|
14
|
-
const { Parser } = require('./src/
|
|
15
|
-
const { bundle } = require('./src/
|
|
16
|
-
const { loadConfig, mergeConfig }
|
|
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, defineConfig } = require('./src/config');
|
|
17
17
|
|
|
18
18
|
module.exports = {
|
|
19
19
|
transpile,
|
|
@@ -26,4 +26,5 @@ module.exports = {
|
|
|
26
26
|
bundle,
|
|
27
27
|
loadConfig,
|
|
28
28
|
mergeConfig,
|
|
29
|
+
defineConfig,
|
|
29
30
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnoxs/flux-lang",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.9",
|
|
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,8 +9,9 @@
|
|
|
9
9
|
"flux": "./bin/flux.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "node
|
|
13
|
-
"
|
|
12
|
+
"build": "node scripts/build.js",
|
|
13
|
+
"prepublishOnly": "npm test && npm run build",
|
|
14
|
+
"test": "node src/cli.js test tests/",
|
|
14
15
|
"test:file": "node src/cli.js test",
|
|
15
16
|
"check": "node src/cli.js check tests/01_basics.test.flux",
|
|
16
17
|
"bench": "node benchmarks/bench.js",
|
|
@@ -83,4 +84,4 @@
|
|
|
83
84
|
"devDependencies": {
|
|
84
85
|
"esbuild": "^0.28.1"
|
|
85
86
|
}
|
|
86
|
-
}
|
|
87
|
+
}
|
package/src/config.js
CHANGED
|
@@ -1,116 +1,101 @@
|
|
|
1
|
+
/* compiled from src/self/config.flux by Flux Lang */
|
|
1
2
|
'use strict';
|
|
3
|
+
// ── Flux stdlib ──
|
|
2
4
|
|
|
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
|
-
*/
|
|
5
|
+
function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
|
|
21
6
|
|
|
22
|
-
|
|
23
|
-
const path = require('path');
|
|
7
|
+
function includes(arr, val) { return arr.includes(val); }
|
|
24
8
|
|
|
25
|
-
|
|
26
|
-
'flux.config.js',
|
|
27
|
-
'flux.config.cjs',
|
|
28
|
-
'.fluxrc.js',
|
|
29
|
-
];
|
|
9
|
+
function keys(obj) { return Object.keys(obj); }
|
|
30
10
|
|
|
31
|
-
|
|
32
|
-
|
|
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();
|
|
11
|
+
function endsWith(s, suffix) { return String(s).endsWith(suffix); }
|
|
12
|
+
// ── end stdlib ──
|
|
38
13
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (!fs.existsSync(configPath)) continue;
|
|
42
|
-
|
|
43
|
-
// Bust require cache so tests can reload different configs
|
|
44
|
-
delete require.cache[require.resolve(configPath)];
|
|
14
|
+
// Generated by Flux Transpiler v3.2.0
|
|
15
|
+
"use strict";
|
|
45
16
|
|
|
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)) {
|
|
46
25
|
try {
|
|
47
|
-
const raw =
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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 {};
|
|
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));
|
|
63
32
|
}
|
|
64
33
|
}
|
|
65
|
-
|
|
66
|
-
|
|
34
|
+
const jsPath = Path.join(cwd, "flux.config.js");
|
|
35
|
+
if (Fs.existsSync(jsPath)) {
|
|
36
|
+
try {
|
|
37
|
+
const loaded = require(jsPath);
|
|
38
|
+
return mergeConfig(DEFAULT_CONFIG, loaded);
|
|
39
|
+
}
|
|
40
|
+
catch (e2) {
|
|
41
|
+
throw new Error(("Invalid flux.config.js: " + e2.message));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { ...DEFAULT_CONFIG };
|
|
67
45
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
};
|
|
46
|
+
module.exports.loadConfig = loadConfig;
|
|
47
|
+
function mergeConfig(base, overrides) {
|
|
48
|
+
if (!overrides) {
|
|
49
|
+
return { ...base };
|
|
50
|
+
}
|
|
51
|
+
const result = { ...base };
|
|
52
|
+
for (const key of Object.keys(overrides)) {
|
|
53
|
+
const val_ = overrides[key];
|
|
54
|
+
if (((val_ != null) && (val_ != undefined))) {
|
|
55
|
+
result[key] = val_;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
87
59
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*/
|
|
95
|
-
function defineConfig(config) {
|
|
96
|
-
return config;
|
|
60
|
+
module.exports.mergeConfig = mergeConfig;
|
|
61
|
+
function writeConfig(config, cwd_) {
|
|
62
|
+
const cwd = (cwd_ ?? process.cwd());
|
|
63
|
+
const jsonPath = Path.join(cwd, "flux.json");
|
|
64
|
+
const content = (JSON.stringify(config, null, 2) + "\n");
|
|
65
|
+
Fs.writeFileSync(jsonPath, content, "utf8");
|
|
97
66
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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;
|
|
67
|
+
module.exports.writeConfig = writeConfig;
|
|
68
|
+
function validateConfig(config) {
|
|
69
|
+
const errors = [];
|
|
70
|
+
if ((config.entry && !config.entry.endsWith(".flux"))) {
|
|
71
|
+
errors.push("entry must be a .flux file");
|
|
112
72
|
}
|
|
113
|
-
|
|
73
|
+
if ((config.jsxTarget && !["browser", "server", "react"].includes(config.jsxTarget))) {
|
|
74
|
+
errors.push("jsxTarget must be 'browser', 'server', or 'react'");
|
|
75
|
+
}
|
|
76
|
+
return { valid: (errors.length == 0), errors };
|
|
114
77
|
}
|
|
115
|
-
|
|
116
|
-
|
|
78
|
+
module.exports.validateConfig = validateConfig;
|
|
79
|
+
function readPackage(cwd_) {
|
|
80
|
+
const cwd = (cwd_ ?? process.cwd());
|
|
81
|
+
const fluxJson = Path.join(cwd, "flux.json");
|
|
82
|
+
const pkgJson = Path.join(cwd, "package.json");
|
|
83
|
+
if (Fs.existsSync(fluxJson)) {
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(Fs.readFileSync(fluxJson, "utf8"));
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (Fs.existsSync(pkgJson)) {
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(Fs.readFileSync(pkgJson, "utf8"));
|
|
94
|
+
}
|
|
95
|
+
catch (e2) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
module.exports.readPackage = readPackage;
|
package/src/formatter.js
CHANGED
|
@@ -1,140 +1,135 @@
|
|
|
1
|
+
/* compiled from src/self/formatter.flux by Flux Lang */
|
|
1
2
|
'use strict';
|
|
3
|
+
// ── Flux stdlib ──
|
|
2
4
|
|
|
3
|
-
|
|
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
|
|
5
|
+
function map(arr, fn) { return arr.map(fn); }
|
|
11
6
|
|
|
12
|
-
function
|
|
13
|
-
|
|
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(); }
|
|
14
23
|
|
|
15
|
-
|
|
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
|
+
function format(source) {
|
|
47
|
+
const lines = source.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
16
48
|
let indentUnit = 4;
|
|
17
49
|
for (const line of lines) {
|
|
18
50
|
const m = line.match(/^( +)\S/);
|
|
19
51
|
if (m) {
|
|
20
52
|
const w = m[1].length;
|
|
21
|
-
if (w > 0 && w < indentUnit)
|
|
53
|
+
if (((w > 0) && (w < indentUnit))) {
|
|
54
|
+
indentUnit = w;
|
|
55
|
+
}
|
|
22
56
|
break;
|
|
23
57
|
}
|
|
24
58
|
}
|
|
25
|
-
if (indentUnit < 1)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
59
|
+
if ((indentUnit < 1)) {
|
|
60
|
+
indentUnit = 4;
|
|
61
|
+
}
|
|
62
|
+
function normalizeLine(line) {
|
|
29
63
|
const stripped = line.trimEnd();
|
|
30
|
-
if (!stripped)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
64
|
+
if (!stripped) {
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
const m2 = stripped.match(/^( *)/);
|
|
68
|
+
const spaces = (m2 ? m2[1].length : 0);
|
|
69
|
+
const level = Math.round((spaces / indentUnit));
|
|
34
70
|
const content = stripped.trimStart();
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// ── 3. Collapse multiple blank lines → max 1 ────────────────────────────
|
|
71
|
+
let formatted = normalizeOperators(content);
|
|
72
|
+
if ((content.startsWith("//") || content.startsWith("*"))) {
|
|
73
|
+
formatted = content;
|
|
74
|
+
}
|
|
75
|
+
return (" ".repeat(level) + formatted);
|
|
76
|
+
}
|
|
77
|
+
const normalized = lines.map((line) => normalizeLine(line));
|
|
45
78
|
const collapsed = [];
|
|
46
79
|
let blankRun = 0;
|
|
47
80
|
for (const line of normalized) {
|
|
48
|
-
if (line
|
|
49
|
-
blankRun
|
|
50
|
-
if (blankRun <= 1)
|
|
51
|
-
|
|
81
|
+
if ((line == "")) {
|
|
82
|
+
blankRun = (blankRun + 1);
|
|
83
|
+
if ((blankRun <= 1)) {
|
|
84
|
+
collapsed.push("");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
52
88
|
blankRun = 0;
|
|
53
89
|
collapsed.push(line);
|
|
54
90
|
}
|
|
55
91
|
}
|
|
56
|
-
|
|
57
|
-
// ── 4. Add blank line after top-level fn/class/type/async fn blocks ──────
|
|
58
92
|
const withSpacing = [];
|
|
59
|
-
|
|
93
|
+
let i = 0;
|
|
94
|
+
while ((i < collapsed.length)) {
|
|
60
95
|
withSpacing.push(collapsed[i]);
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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, '');
|
|
96
|
+
const indent = (collapsed[i].match(/^( *)/) ?? ["", ""])[1].length;
|
|
97
|
+
if ((i > 0)) {
|
|
98
|
+
const prevIndent = (collapsed[(i - 1)].match(/^( *)/) ?? ["", ""])[1].length;
|
|
99
|
+
const cur = collapsed[i].trimStart();
|
|
100
|
+
if ((((prevIndent >= 4) && (indent == 0)) && (cur != ""))) {
|
|
101
|
+
if ((withSpacing[(withSpacing.length - 2)] != "")) {
|
|
102
|
+
withSpacing.splice((withSpacing.length - 1), 0, "");
|
|
81
103
|
}
|
|
82
104
|
}
|
|
83
105
|
}
|
|
106
|
+
i = (i + 1);
|
|
84
107
|
}
|
|
85
|
-
|
|
86
|
-
// ── 5. Trim leading/trailing blank lines + ensure single trailing newline ─
|
|
87
108
|
let out = withSpacing;
|
|
88
|
-
while (out.length > 0 && out[0]
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
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;
|
|
109
|
+
while (((out.length > 0) && (out[0] == ""))) {
|
|
110
|
+
out.shift();
|
|
111
|
+
}
|
|
112
|
+
while (((out.length > 0) && (out[(out.length - 1)] == ""))) {
|
|
113
|
+
out.pop();
|
|
114
|
+
}
|
|
115
|
+
out.push("");
|
|
116
|
+
return out.join("\n");
|
|
121
117
|
}
|
|
122
|
-
|
|
123
|
-
// ── Diff output ──────────────────────────────────────────────────────────────
|
|
118
|
+
module.exports.format = format;
|
|
124
119
|
function diff(original, formatted) {
|
|
125
|
-
const origLines = original.split(
|
|
126
|
-
const fmtLines
|
|
127
|
-
const changes
|
|
128
|
-
|
|
120
|
+
const origLines = original.split("\n");
|
|
121
|
+
const fmtLines = formatted.split("\n");
|
|
122
|
+
const changes = [];
|
|
129
123
|
const maxLen = Math.max(origLines.length, fmtLines.length);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
124
|
+
let j = 0;
|
|
125
|
+
while ((j < maxLen)) {
|
|
126
|
+
const o = origLines[j];
|
|
127
|
+
const f = fmtLines[j];
|
|
128
|
+
if ((o != f)) {
|
|
129
|
+
changes.push({ line: (j + 1), original: o, formatted: f });
|
|
135
130
|
}
|
|
131
|
+
j = (j + 1);
|
|
136
132
|
}
|
|
137
133
|
return changes;
|
|
138
134
|
}
|
|
139
|
-
|
|
140
|
-
module.exports = { format, diff };
|
|
135
|
+
module.exports.diff = diff;
|
package/src/self/bundler.flux
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
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.
|
|
8
6
|
// ============================================================
|
|
9
7
|
|
|
10
8
|
import Fs from "fs"
|
|
@@ -18,25 +16,17 @@ fn toModuleId(absPath):
|
|
|
18
16
|
return '_flux_' + base.replace(/[^a-zA-Z0-9]/g, '_')
|
|
19
17
|
|
|
20
18
|
fn extractModuleInfo(ast, fromFile):
|
|
21
|
-
val imports
|
|
22
|
-
val
|
|
23
|
-
val
|
|
24
|
-
val
|
|
25
|
-
val dir = Path.dirname(fromFile)
|
|
19
|
+
val imports = []
|
|
20
|
+
val exports = []
|
|
21
|
+
val body = []
|
|
22
|
+
val dir = Path.dirname(fromFile)
|
|
26
23
|
|
|
27
24
|
for node in ast.body:
|
|
28
25
|
if node.type == 'ImportDecl':
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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)
|
|
26
|
+
var src = node.source
|
|
27
|
+
if not src.endsWith('.flux'): src = src + '.flux'
|
|
28
|
+
val absPath = Path.resolve(dir, src)
|
|
29
|
+
imports.push({ names: node.names, source: node.source, absPath })
|
|
40
30
|
|
|
41
31
|
else if node.type == 'ExportDecl':
|
|
42
32
|
val inner = node.decl
|
|
@@ -48,7 +38,7 @@ fn extractModuleInfo(ast, fromFile):
|
|
|
48
38
|
else:
|
|
49
39
|
body.push(node)
|
|
50
40
|
|
|
51
|
-
return { cleanAst: { type: 'Program', body }, imports,
|
|
41
|
+
return { cleanAst: { type: 'Program', body }, imports, exports }
|
|
52
42
|
|
|
53
43
|
fn codegenModule(ast):
|
|
54
44
|
val cg = makeCodeGen({ indent: ' ' })
|
|
@@ -90,7 +80,6 @@ export class Bundler:
|
|
|
90
80
|
val info = extractModuleInfo(ast, absPath)
|
|
91
81
|
self.modules.set(absPath, { cleanAst: info.cleanAst, imports: info.imports, exports: info.exports, source, absPath })
|
|
92
82
|
|
|
93
|
-
// Recursively collect all .flux imports
|
|
94
83
|
for imp in info.imports:
|
|
95
84
|
self.collect(imp.absPath)
|
|
96
85
|
|
|
@@ -109,6 +98,12 @@ export class Bundler:
|
|
|
109
98
|
lines.push('"use strict";')
|
|
110
99
|
lines.push('')
|
|
111
100
|
|
|
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
|
+
|
|
112
107
|
lines.push('(function() {')
|
|
113
108
|
lines.push('')
|
|
114
109
|
|
|
@@ -130,7 +125,7 @@ export class Bundler:
|
|
|
130
125
|
for imp in mod.imports:
|
|
131
126
|
val srcId = toModuleId(imp.absPath)
|
|
132
127
|
for name in imp.names:
|
|
133
|
-
val localName
|
|
128
|
+
val localName = name.alias ?? name.name
|
|
134
129
|
val importedName = name.name
|
|
135
130
|
lines.push(' var ' + localName + ' = ' + srcId + '._exports ? ' + srcId + '._exports.' + importedName + ' : ' + srcId + '.' + importedName + ';')
|
|
136
131
|
|
package/src/self/bundler.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
/* compiled from src/self/bundler.flux */
|
|
1
|
+
/* compiled from src/self/bundler.flux by Flux Lang */
|
|
2
2
|
'use strict';
|
|
3
|
-
//
|
|
3
|
+
// ── Flux stdlib ──
|
|
4
|
+
|
|
5
|
+
function findIndex(arr, fn) { return arr.findIndex(fn); }
|
|
6
|
+
|
|
7
|
+
function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
|
|
8
|
+
|
|
9
|
+
function includes(arr, val) { return arr.includes(val); }
|
|
10
|
+
|
|
11
|
+
function trim(s) { return String(s).trim(); }
|
|
12
|
+
|
|
13
|
+
function startsWith(s, prefix) { return String(s).startsWith(prefix); }
|
|
14
|
+
|
|
15
|
+
function endsWith(s, suffix) { return String(s).endsWith(suffix); }
|
|
16
|
+
|
|
17
|
+
function repeat(s, n) { return String(s).repeat(n); }
|
|
18
|
+
// ── end stdlib ──
|
|
19
|
+
|
|
20
|
+
// Generated by Flux Transpiler v3.2.0
|
|
4
21
|
"use strict";
|
|
5
22
|
|
|
6
23
|
const Fs = require("fs");
|
|
@@ -14,25 +31,17 @@ function toModuleId(absPath) {
|
|
|
14
31
|
}
|
|
15
32
|
function extractModuleInfo(ast, fromFile) {
|
|
16
33
|
const imports = [];
|
|
17
|
-
const npmImports = [];
|
|
18
34
|
const exports = [];
|
|
19
35
|
const body = [];
|
|
20
36
|
const dir = Path.dirname(fromFile);
|
|
21
37
|
for (const node of ast.body) {
|
|
22
38
|
if ((node.type == "ImportDecl")) {
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
|
|
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);
|
|
39
|
+
let src = node.source;
|
|
40
|
+
if (!src.endsWith(".flux")) {
|
|
41
|
+
src = (src + ".flux");
|
|
35
42
|
}
|
|
43
|
+
const absPath = Path.resolve(dir, src);
|
|
44
|
+
imports.push({ names: node.names, source: node.source, absPath });
|
|
36
45
|
}
|
|
37
46
|
else if ((node.type == "ExportDecl")) {
|
|
38
47
|
const inner = node.decl;
|
|
@@ -51,7 +60,7 @@ function extractModuleInfo(ast, fromFile) {
|
|
|
51
60
|
body.push(node);
|
|
52
61
|
}
|
|
53
62
|
}
|
|
54
|
-
return { cleanAst: { type: "Program", body }, imports,
|
|
63
|
+
return { cleanAst: { type: "Program", body }, imports, exports };
|
|
55
64
|
}
|
|
56
65
|
function codegenModule(ast) {
|
|
57
66
|
const cg = makeCodeGen({ indent: " " });
|
|
@@ -115,6 +124,9 @@ class Bundler {
|
|
|
115
124
|
lines.push("\"use strict\";");
|
|
116
125
|
lines.push("");
|
|
117
126
|
}
|
|
127
|
+
lines.push("// flux_modules resolver");
|
|
128
|
+
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);};}})();");
|
|
129
|
+
lines.push("");
|
|
118
130
|
lines.push("(function() {");
|
|
119
131
|
lines.push("");
|
|
120
132
|
for (const absPath of this.order) {
|