@xnoxs/flux-lang 3.2.0 → 3.2.2
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 +20 -0
- package/README.md +915 -597
- package/bin/flux.js +1 -1395
- package/dist/flux-cli.js +9564 -0
- package/dist/flux.cjs.js +1 -1
- package/dist/flux.esm.js +1 -1
- package/dist/flux.min.js +1 -1
- package/package.json +24 -16
- package/scripts/build.js +28 -29
- package/src/bundler.js +0 -216
- package/src/checker.js +0 -322
- package/src/codegen.js +0 -832
- package/src/css-preprocessor.js +0 -399
- package/src/jsx.js +0 -480
- package/src/lexer.js +0 -518
- package/src/linter.js +0 -784
- package/src/mangler.js +0 -280
- package/src/parser.js +0 -1708
- package/src/sourcemap.js +0 -82
- package/src/test-runner.js +0 -239
- package/src/transpiler.js +0 -172
- package/src/type-checker.js +0 -1206
package/dist/flux.cjs.js
CHANGED
package/dist/flux.esm.js
CHANGED
package/dist/flux.min.js
CHANGED
package/package.json
CHANGED
|
@@ -1,34 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnoxs/flux-lang",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "Flux — A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "dist/flux.cjs.js",
|
|
6
|
+
"module": "dist/flux.esm.js",
|
|
6
7
|
"types": "index.d.ts",
|
|
7
8
|
"bin": {
|
|
8
9
|
"flux": "./bin/flux.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
|
-
"build":
|
|
12
|
-
"prepublishOnly":
|
|
13
|
-
"test":
|
|
14
|
-
"test:file":
|
|
15
|
-
"check":
|
|
16
|
-
"bench":
|
|
17
|
-
"start":
|
|
18
|
-
"repl":
|
|
19
|
-
"version":
|
|
12
|
+
"build": "node scripts/build.js",
|
|
13
|
+
"prepublishOnly": "npm test && npm run build",
|
|
14
|
+
"test": "node src/cli.js test tests/",
|
|
15
|
+
"test:file": "node src/cli.js test",
|
|
16
|
+
"check": "node src/cli.js check tests/01_basics.test.flux",
|
|
17
|
+
"bench": "node benchmarks/bench.js",
|
|
18
|
+
"start": "node src/cli.js run app/server.flux",
|
|
19
|
+
"repl": "node src/cli.js repl",
|
|
20
|
+
"version": "node src/cli.js version"
|
|
20
21
|
},
|
|
21
22
|
"exports": {
|
|
22
|
-
".":
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/flux.esm.js",
|
|
25
|
+
"require": "./dist/flux.cjs.js",
|
|
26
|
+
"default": "./dist/flux.cjs.js"
|
|
27
|
+
},
|
|
25
28
|
"./dist/cjs": "./dist/flux.cjs.js",
|
|
26
|
-
"./dist/esm": "./dist/flux.esm.js"
|
|
29
|
+
"./dist/esm": "./dist/flux.esm.js",
|
|
30
|
+
"./dist/min": "./dist/flux.min.js",
|
|
31
|
+
"./stdlib": "./src/stdlib.js",
|
|
32
|
+
"./formatter":"./src/formatter.js"
|
|
27
33
|
},
|
|
28
34
|
"files": [
|
|
29
35
|
"bin/",
|
|
30
|
-
"src/",
|
|
31
36
|
"dist/",
|
|
37
|
+
"src/stdlib.js",
|
|
38
|
+
"src/formatter.js",
|
|
39
|
+
"src/self/",
|
|
32
40
|
"scripts/build.js",
|
|
33
41
|
"index.js",
|
|
34
42
|
"index.d.ts",
|
package/scripts/build.js
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* Flux Lang — esbuild bundle script
|
|
6
6
|
*
|
|
7
7
|
* Produces:
|
|
8
|
-
* dist/flux.cjs.js —
|
|
9
|
-
* dist/flux.esm.js — ESM bundle
|
|
10
|
-
* dist/flux.min.js —
|
|
8
|
+
* dist/flux.cjs.js — Library CJS bundle (Node, require('@xnoxs/flux-lang'))
|
|
9
|
+
* dist/flux.esm.js — Library ESM bundle (import from '@xnoxs/flux-lang')
|
|
10
|
+
* dist/flux.min.js — Library minified (browser / CDN)
|
|
11
|
+
* dist/flux-cli.js — CLI bundle (used by bin/flux.js shim)
|
|
11
12
|
*
|
|
12
13
|
* Usage:
|
|
13
14
|
* node scripts/build.js
|
|
@@ -28,12 +29,21 @@ const banner = `/*!
|
|
|
28
29
|
const outDir = path.resolve(__dirname, '../dist');
|
|
29
30
|
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
+
const sharedLib = {
|
|
32
33
|
entryPoints: ['index.js'],
|
|
33
34
|
bundle: true,
|
|
34
35
|
platform: 'node',
|
|
35
36
|
target: ['node16'],
|
|
36
|
-
external: ['esbuild'],
|
|
37
|
+
external: ['esbuild'],
|
|
38
|
+
banner: { js: banner },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const sharedCli = {
|
|
42
|
+
entryPoints: ['src/cli.js'],
|
|
43
|
+
bundle: true,
|
|
44
|
+
platform: 'node',
|
|
45
|
+
target: ['node16'],
|
|
46
|
+
external: ['esbuild'],
|
|
37
47
|
banner: { js: banner },
|
|
38
48
|
};
|
|
39
49
|
|
|
@@ -41,32 +51,21 @@ async function build() {
|
|
|
41
51
|
const start = Date.now();
|
|
42
52
|
|
|
43
53
|
await Promise.all([
|
|
44
|
-
|
|
45
|
-
esbuild.build({
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
outfile: 'dist/flux.cjs.js',
|
|
49
|
-
}),
|
|
50
|
-
|
|
51
|
-
// ESM bundle (for modern bundlers / type:"module" projects)
|
|
52
|
-
esbuild.build({
|
|
53
|
-
...shared,
|
|
54
|
-
format: 'esm',
|
|
55
|
-
outfile: 'dist/flux.esm.js',
|
|
56
|
-
}),
|
|
57
|
-
|
|
58
|
-
// Minified CJS (CDN / browser via require shim)
|
|
59
|
-
esbuild.build({
|
|
60
|
-
...shared,
|
|
61
|
-
format: 'cjs',
|
|
62
|
-
minify: true,
|
|
63
|
-
outfile: 'dist/flux.min.js',
|
|
64
|
-
}),
|
|
54
|
+
esbuild.build({ ...sharedLib, format: 'cjs', outfile: 'dist/flux.cjs.js' }),
|
|
55
|
+
esbuild.build({ ...sharedLib, format: 'esm', outfile: 'dist/flux.esm.js' }),
|
|
56
|
+
esbuild.build({ ...sharedLib, format: 'cjs', minify: true, outfile: 'dist/flux.min.js' }),
|
|
57
|
+
esbuild.build({ ...sharedCli, format: 'cjs', outfile: 'dist/flux-cli.js' }),
|
|
65
58
|
]);
|
|
66
59
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
const files = [
|
|
61
|
+
'dist/flux.cjs.js',
|
|
62
|
+
'dist/flux.esm.js',
|
|
63
|
+
'dist/flux.min.js',
|
|
64
|
+
'dist/flux-cli.js',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const sizes = files.map(f => {
|
|
68
|
+
const kb = (fs.statSync(f).size / 1024).toFixed(1);
|
|
70
69
|
return ` ${f.padEnd(22)} ${kb} kB`;
|
|
71
70
|
});
|
|
72
71
|
|
package/src/bundler.js
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { Lexer } = require('./lexer');
|
|
6
|
-
const { Parser } = require('./parser');
|
|
7
|
-
const { CodeGenerator } = require('./codegen');
|
|
8
|
-
|
|
9
|
-
class BundleError extends Error {
|
|
10
|
-
constructor(msg, file) {
|
|
11
|
-
super(file ? `[${path.basename(file)}] ${msg}` : msg);
|
|
12
|
-
this.name = 'BundleError';
|
|
13
|
-
this.file = file;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// ── AST transformation helpers ────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
// Hapus node ImportDeclaration dan ExportDeclaration dari body AST
|
|
20
|
-
// Kembalikan: { cleanAst, imports, exports }
|
|
21
|
-
function extractModuleInfo(ast, fromFile) {
|
|
22
|
-
const imports = []; // { names: [], source, absPath }
|
|
23
|
-
const exports = []; // nama yang diekspor
|
|
24
|
-
const body = [];
|
|
25
|
-
const dir = path.dirname(fromFile);
|
|
26
|
-
|
|
27
|
-
for (const node of ast.body) {
|
|
28
|
-
if (node.type === 'ImportDecl') {
|
|
29
|
-
let src = node.source;
|
|
30
|
-
if (!src.endsWith('.flux')) src += '.flux';
|
|
31
|
-
const absPath = path.resolve(dir, src);
|
|
32
|
-
imports.push({ names: node.names, source: node.source, absPath });
|
|
33
|
-
|
|
34
|
-
} else if (node.type === 'ExportDecl') {
|
|
35
|
-
const inner = node.decl;
|
|
36
|
-
if (inner.type === 'FnDecl') exports.push(inner.name);
|
|
37
|
-
if (inner.type === 'ClassDecl') exports.push(inner.name);
|
|
38
|
-
if (inner.type === 'VarDecl') exports.push(inner.name);
|
|
39
|
-
body.push(inner);
|
|
40
|
-
|
|
41
|
-
} else {
|
|
42
|
-
body.push(node);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return { cleanAst: { type: 'Program', body }, imports, exports };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Hasilkan kode JS dari clean AST
|
|
50
|
-
function codegenModule(ast) {
|
|
51
|
-
const { code } = new CodeGenerator({ indent: ' ' }).generate(ast);
|
|
52
|
-
// Hapus header boilerplate dari tiap modul (dua baris pertama)
|
|
53
|
-
const lines = code.split('\n');
|
|
54
|
-
// Hapus "// Dihasilkan oleh Flux Transpiler", '"use strict";', dan baris kosong pertama
|
|
55
|
-
const start = lines.findIndex(l => l.trim() !== '' && !l.includes('Dihasilkan') && !l.includes('"use strict"'));
|
|
56
|
-
return lines.slice(start).join('\n');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Konversi nama modul (path) ke identifier JS yang aman
|
|
60
|
-
function toModuleId(absPath) {
|
|
61
|
-
const base = path.basename(absPath, '.flux');
|
|
62
|
-
return '_flux_' + base.replace(/[^a-zA-Z0-9]/g, '_');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ── Bundler class ─────────────────────────────────────────────────────────────
|
|
66
|
-
|
|
67
|
-
class Bundler {
|
|
68
|
-
constructor(entryFile, options = {}) {
|
|
69
|
-
this.entryFile = path.resolve(entryFile);
|
|
70
|
-
this.options = options;
|
|
71
|
-
this.modules = new Map(); // absPath → { ast, imports, exports, source }
|
|
72
|
-
this.order = []; // topological order (dependencies first)
|
|
73
|
-
this.visited = new Set();
|
|
74
|
-
this.inStack = new Set(); // cycle detection
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
bundle() {
|
|
78
|
-
this.collect(this.entryFile);
|
|
79
|
-
return this.link();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ── Fase 1: Kumpulkan semua modul secara rekursif ─────────────────
|
|
83
|
-
collect(absPath) {
|
|
84
|
-
if (this.visited.has(absPath)) return;
|
|
85
|
-
if (this.inStack.has(absPath)) {
|
|
86
|
-
throw new BundleError(`Dependensi sirkular terdeteksi: ${absPath}`, absPath);
|
|
87
|
-
}
|
|
88
|
-
if (!fs.existsSync(absPath)) {
|
|
89
|
-
throw new BundleError(`File tidak ditemukan: ${absPath}`, absPath);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
this.inStack.add(absPath);
|
|
93
|
-
|
|
94
|
-
const source = fs.readFileSync(absPath, 'utf8');
|
|
95
|
-
|
|
96
|
-
// Lex + Parse
|
|
97
|
-
let ast;
|
|
98
|
-
try {
|
|
99
|
-
const tokens = new Lexer(source).tokenize();
|
|
100
|
-
ast = new Parser(tokens).parse();
|
|
101
|
-
} catch (e) {
|
|
102
|
-
throw new BundleError(`Error saat parsing: ${e.message}`, absPath);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const { cleanAst, imports, exports } = extractModuleInfo(ast, absPath);
|
|
106
|
-
this.modules.set(absPath, { cleanAst, imports, exports, source, absPath });
|
|
107
|
-
|
|
108
|
-
// Proses dependensi dulu (DFS)
|
|
109
|
-
for (const imp of imports) {
|
|
110
|
-
this.collect(imp.absPath);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.inStack.delete(absPath);
|
|
114
|
-
this.visited.add(absPath);
|
|
115
|
-
this.order.push(absPath); // entry ditambahkan terakhir
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ── Fase 2: Link semua modul menjadi satu file JS ─────────────────
|
|
119
|
-
link() {
|
|
120
|
-
const lines = [];
|
|
121
|
-
const banner = this.options.banner !== false;
|
|
122
|
-
|
|
123
|
-
if (banner) {
|
|
124
|
-
lines.push('// Dihasilkan oleh Flux Bundler');
|
|
125
|
-
lines.push(`// Entry: ${path.basename(this.entryFile)}`);
|
|
126
|
-
lines.push(`// Modul: ${this.order.length}`);
|
|
127
|
-
lines.push('"use strict";');
|
|
128
|
-
lines.push('');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
lines.push('(function() {');
|
|
132
|
-
lines.push('');
|
|
133
|
-
|
|
134
|
-
for (const absPath of this.order) {
|
|
135
|
-
const { cleanAst, imports, exports } = this.modules.get(absPath);
|
|
136
|
-
const isEntry = absPath === this.entryFile;
|
|
137
|
-
const modId = toModuleId(absPath);
|
|
138
|
-
const relName = path.relative(process.cwd(), absPath);
|
|
139
|
-
|
|
140
|
-
lines.push(` // ${'─'.repeat(60)}`);
|
|
141
|
-
lines.push(` // Modul: ${relName}`);
|
|
142
|
-
lines.push(` // ${'─'.repeat(60)}`);
|
|
143
|
-
|
|
144
|
-
if (!isEntry) {
|
|
145
|
-
// Bungkus modul non-entry dalam object exports
|
|
146
|
-
lines.push(` var ${modId} = (function() {`);
|
|
147
|
-
lines.push(` var _exports = {};`);
|
|
148
|
-
lines.push('');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Inject import bindings dari modul lain
|
|
152
|
-
for (const imp of imports) {
|
|
153
|
-
const srcId = toModuleId(imp.absPath);
|
|
154
|
-
if (imp.names.length === 1) {
|
|
155
|
-
// import defaultExport from "./mod"
|
|
156
|
-
lines.push(` var ${imp.names[0]} = ${srcId};`);
|
|
157
|
-
} else {
|
|
158
|
-
// import { a, b } from "./mod"
|
|
159
|
-
for (const name of imp.names) {
|
|
160
|
-
lines.push(` var ${name} = ${srcId}._exports ? ${srcId}._exports.${name} : ${srcId}.${name};`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (imports.length > 0) lines.push('');
|
|
166
|
-
|
|
167
|
-
// Kode modul yang sudah dibersihkan
|
|
168
|
-
const moduleCode = codegenModule(cleanAst);
|
|
169
|
-
lines.push(moduleCode);
|
|
170
|
-
|
|
171
|
-
if (!isEntry) {
|
|
172
|
-
// Registrasikan semua export ke object _exports
|
|
173
|
-
if (exports.length > 0) {
|
|
174
|
-
lines.push('');
|
|
175
|
-
for (const expName of exports) {
|
|
176
|
-
lines.push(` _exports.${expName} = ${expName};`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
lines.push('');
|
|
180
|
-
lines.push(` return _exports;`);
|
|
181
|
-
lines.push(` })()`);
|
|
182
|
-
lines.push(` ${modId}._exports = ${modId};`);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
lines.push('');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
lines.push('})();');
|
|
189
|
-
|
|
190
|
-
return { code: lines.join('\n'), modules: this.order.length };
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Bundle beberapa file Flux menjadi satu JS.
|
|
196
|
-
* @param {string} entryFile - path ke file .flux utama
|
|
197
|
-
* @param {object} options
|
|
198
|
-
* @returns {{ success, code, modules, errors }}
|
|
199
|
-
*/
|
|
200
|
-
function bundle(entryFile, options = {}) {
|
|
201
|
-
const result = { success: false, code: '', modules: 0, errors: [] };
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
const bundler = new Bundler(entryFile, options);
|
|
205
|
-
const { code, modules } = bundler.bundle();
|
|
206
|
-
result.code = code;
|
|
207
|
-
result.modules = modules;
|
|
208
|
-
result.success = true;
|
|
209
|
-
} catch (e) {
|
|
210
|
-
result.errors.push({ message: e.message, name: e.name, file: e.file || null });
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return result;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
module.exports = { bundle, Bundler, BundleError };
|