@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/src/mangler.js
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// ── Safe names: never mangle these ────────────────────────────────────────────
|
|
4
|
-
const SAFE = new Set([
|
|
5
|
-
// JS globals
|
|
6
|
-
'undefined','null','true','false','NaN','Infinity','arguments','this','super',
|
|
7
|
-
'console','document','window','navigator','location','history','screen',
|
|
8
|
-
'Math','JSON','Array','Object','String','Number','Boolean','Symbol','BigInt',
|
|
9
|
-
'Promise','Proxy','Reflect','WeakMap','WeakSet','WeakRef',
|
|
10
|
-
'Map','Set','Date','RegExp','Error','TypeError','RangeError','SyntaxError',
|
|
11
|
-
'URIError','EvalError','ReferenceError',
|
|
12
|
-
'parseInt','parseFloat','isNaN','isFinite',
|
|
13
|
-
'encodeURIComponent','decodeURIComponent','encodeURI','decodeURI',
|
|
14
|
-
'atob','btoa','structuredClone','queueMicrotask',
|
|
15
|
-
'setTimeout','setInterval','clearTimeout','clearInterval',
|
|
16
|
-
'requestAnimationFrame','cancelAnimationFrame',
|
|
17
|
-
'fetch','XMLHttpRequest','WebSocket','EventSource','FormData','Headers',
|
|
18
|
-
'Request','Response','URL','URLSearchParams','Blob','File','FileReader',
|
|
19
|
-
'alert','confirm','prompt',
|
|
20
|
-
'Event','CustomEvent','Node','Element','HTMLElement','DocumentFragment',
|
|
21
|
-
'MutationObserver','IntersectionObserver','ResizeObserver',
|
|
22
|
-
// Node.js
|
|
23
|
-
'process','Buffer','require','module','exports','__dirname','__filename',
|
|
24
|
-
'global','globalThis','setImmediate','clearImmediate','queueMicrotask',
|
|
25
|
-
'TextEncoder','TextDecoder','AbortController','AbortSignal','performance',
|
|
26
|
-
// Common imports used in Flux apps
|
|
27
|
-
'Http','Fs','Path','Url','Crypto','Os','Net','Stream','Events','Util',
|
|
28
|
-
'http','fs','path','url','crypto','os','net','stream','events','util',
|
|
29
|
-
// Flux runtime helpers (must never be mangled)
|
|
30
|
-
'_fluxH','_fluxCSS',
|
|
31
|
-
// Common variable names that map to globals
|
|
32
|
-
'print',
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
|
-
// ── Name generator ─────────────────────────────────────────────────────────────
|
|
36
|
-
function makeName(n) {
|
|
37
|
-
// Produces: _a, _b, ..., _z, _aa, _ab, ...
|
|
38
|
-
const chars = 'abcdefghijklmnopqrstuvwxyz';
|
|
39
|
-
let s = '';
|
|
40
|
-
n++;
|
|
41
|
-
while (n > 0) {
|
|
42
|
-
s = chars[(n - 1) % 26] + s;
|
|
43
|
-
n = Math.floor((n - 1) / 26);
|
|
44
|
-
}
|
|
45
|
-
return '_' + s;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ── Mangler ────────────────────────────────────────────────────────────────────
|
|
49
|
-
class Mangler {
|
|
50
|
-
constructor() {
|
|
51
|
-
this.map = new Map(); // original name → mangled name
|
|
52
|
-
this.counter = 0;
|
|
53
|
-
this.exported = new Set(); // names that are exported (keep readable)
|
|
54
|
-
this.imported = new Set(); // names brought in via import (keep as-is)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ── Public API ───────────────────────────────────────────────
|
|
58
|
-
mangle(ast) {
|
|
59
|
-
this._collectExportedAndImported(ast);
|
|
60
|
-
this._collectDeclarations(ast);
|
|
61
|
-
return this._walkProgram(ast);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getMap() { return Object.fromEntries(this.map); }
|
|
65
|
-
|
|
66
|
-
// ── Step 1: collect exported + imported names ────────────────
|
|
67
|
-
_collectExportedAndImported(ast) {
|
|
68
|
-
for (const node of ast.body) {
|
|
69
|
-
if (node.type === 'ExportDecl') {
|
|
70
|
-
const d = node.decl;
|
|
71
|
-
if (d && d.name) this.exported.add(d.name);
|
|
72
|
-
if (d && d.type === 'DestructureDecl') {
|
|
73
|
-
(d.pattern || []).forEach(p => p && this.exported.add(p.alias || p.name || p.key));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (node.type === 'ImportDecl') {
|
|
77
|
-
if (node.defaultName) this.imported.add(node.defaultName);
|
|
78
|
-
(node.names || []).forEach(n => this.imported.add(n));
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ── Step 2: collect all top-level user-defined names ─────────
|
|
84
|
-
_collectDeclarations(ast) {
|
|
85
|
-
this._walkStmts(ast.body, { toplevel: true, collect: true });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ── Step 3: walk AST and rewrite names ───────────────────────
|
|
89
|
-
_walkProgram(ast) {
|
|
90
|
-
const body = this._walkStmts(ast.body, { toplevel: true });
|
|
91
|
-
return { ...ast, body };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
_walkStmts(stmts, ctx = {}) {
|
|
95
|
-
return stmts.map(s => this._walkStmt(s, ctx));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
_walkStmt(node, ctx = {}) {
|
|
99
|
-
if (!node) return node;
|
|
100
|
-
switch (node.type) {
|
|
101
|
-
case 'VarDecl': return this._walkVarDecl(node, ctx);
|
|
102
|
-
case 'DestructureDecl':return this._walkDestructure(node, ctx);
|
|
103
|
-
case 'FnDecl': return this._walkFnDecl(node, ctx);
|
|
104
|
-
case 'ClassDecl': return this._walkClassDecl(node, ctx);
|
|
105
|
-
case 'IfStmt': return { ...node,
|
|
106
|
-
cond: this._walkExpr(node.cond),
|
|
107
|
-
then: this._walkStmts(node.then),
|
|
108
|
-
elseifs: node.elseifs.map(e => ({ cond: this._walkExpr(e.cond), body: this._walkStmts(e.body) })),
|
|
109
|
-
else_: node.else_ ? this._walkStmts(node.else_) : null };
|
|
110
|
-
case 'ForInStmt': return { ...node,
|
|
111
|
-
var: this._mangleLocal(node.var),
|
|
112
|
-
iter: this._walkExpr(node.iter),
|
|
113
|
-
body: this._walkStmts(node.body) };
|
|
114
|
-
case 'WhileStmt': return { ...node, cond: this._walkExpr(node.cond), body: this._walkStmts(node.body) };
|
|
115
|
-
case 'DoWhileStmt': return { ...node, cond: this._walkExpr(node.cond), body: this._walkStmts(node.body) };
|
|
116
|
-
case 'MatchStmt': return { ...node,
|
|
117
|
-
subject: this._walkExpr(node.subject),
|
|
118
|
-
arms: node.arms.map(a => ({ ...a, body: this._walkStmts(a.body) })) };
|
|
119
|
-
case 'ReturnStmt': return { ...node, value: this._walkExpr(node.value) };
|
|
120
|
-
case 'ThrowStmt': return { ...node, value: this._walkExpr(node.value) };
|
|
121
|
-
case 'TryCatchStmt': return { ...node,
|
|
122
|
-
tryBody: this._walkStmts(node.tryBody),
|
|
123
|
-
catchParam: node.catchParam ? this._mangleLocal(node.catchParam) : null,
|
|
124
|
-
catchBody: node.catchBody ? this._walkStmts(node.catchBody) : null,
|
|
125
|
-
finallyBody: node.finallyBody ? this._walkStmts(node.finallyBody) : null };
|
|
126
|
-
case 'ExportDecl': {
|
|
127
|
-
const d = node.decl;
|
|
128
|
-
// Walk the inner decl but keep the name intact for the export statement
|
|
129
|
-
return { ...node, decl: this._walkStmt(d, ctx) };
|
|
130
|
-
}
|
|
131
|
-
case 'ImportDecl': return node; // pass through unchanged
|
|
132
|
-
case 'TypeDecl': return node; // ADT types — variant names must stay readable
|
|
133
|
-
case 'ExprStmt': return { ...node, expr: this._walkExpr(node.expr) };
|
|
134
|
-
case 'BreakStmt':
|
|
135
|
-
case 'ContinueStmt': return node;
|
|
136
|
-
default: return node;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
_walkVarDecl(node, ctx) {
|
|
141
|
-
// In collect mode, just register the name
|
|
142
|
-
if (ctx.collect) { this._registerName(node.name); return node; }
|
|
143
|
-
return {
|
|
144
|
-
...node,
|
|
145
|
-
name: this._mangleName(node.name),
|
|
146
|
-
init: node.init ? this._walkExpr(node.init) : null,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
_walkDestructure(node, ctx) {
|
|
151
|
-
if (ctx.collect) {
|
|
152
|
-
(node.pattern || []).forEach(p => {
|
|
153
|
-
if (p) this._registerName(p.alias || p.name || p.key);
|
|
154
|
-
});
|
|
155
|
-
return node;
|
|
156
|
-
}
|
|
157
|
-
if (node.patternType === 'array') {
|
|
158
|
-
// codegen uses p.name for array destructuring elements
|
|
159
|
-
const pattern = node.pattern.map(p => {
|
|
160
|
-
if (!p) return p;
|
|
161
|
-
if (p.rest) return { ...p, name: this._mangleName(p.name) };
|
|
162
|
-
return { ...p, name: this._mangleName(p.name), defaultVal: p.defaultVal ? this._walkExpr(p.defaultVal) : null };
|
|
163
|
-
});
|
|
164
|
-
return { ...node, pattern, init: this._walkExpr(node.init) };
|
|
165
|
-
} else {
|
|
166
|
-
// codegen uses p.alias (or p.key when alias === key) for object destructuring
|
|
167
|
-
const pattern = node.pattern.map(p => {
|
|
168
|
-
if (!p) return p;
|
|
169
|
-
if (p.rest) return { ...p, key: p.key, alias: this._mangleName(p.alias || p.key) };
|
|
170
|
-
return { ...p, alias: this._mangleName(p.alias || p.key), defaultVal: p.defaultVal ? this._walkExpr(p.defaultVal) : null };
|
|
171
|
-
});
|
|
172
|
-
return { ...node, pattern, init: this._walkExpr(node.init) };
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
_walkFnDecl(node, ctx) {
|
|
177
|
-
if (ctx.collect && node.name) { this._registerName(node.name); return node; }
|
|
178
|
-
const mangledName = node.name ? this._mangleName(node.name) : null;
|
|
179
|
-
const params = node.params.map(p => ({
|
|
180
|
-
...p,
|
|
181
|
-
name: this._mangleParam(p.name),
|
|
182
|
-
defaultVal: p.defaultVal ? this._walkExpr(p.defaultVal) : null,
|
|
183
|
-
}));
|
|
184
|
-
const body = node.inline ? this._walkExpr(node.body) : this._walkStmts(node.body);
|
|
185
|
-
return { ...node, name: mangledName, params, body };
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
_walkClassDecl(node, ctx) {
|
|
189
|
-
if (ctx.collect && node.name) { this._registerName(node.name); return node; }
|
|
190
|
-
const mangledName = this._mangleName(node.name);
|
|
191
|
-
const methods = node.methods.map(m => {
|
|
192
|
-
// Method names that match well-known lifecycle / JS built-ins — keep them
|
|
193
|
-
const params = m.params.map(p => ({ ...p, name: this._mangleParam(p.name), defaultVal: p.defaultVal ? this._walkExpr(p.defaultVal) : null }));
|
|
194
|
-
const body = m.inline ? this._walkExpr(m.body) : this._walkStmts(m.body);
|
|
195
|
-
return { ...m, params, body };
|
|
196
|
-
});
|
|
197
|
-
const superClass = node.superClass ? this._mangleName(node.superClass) : null;
|
|
198
|
-
return { ...node, name: mangledName, superClass, methods };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
_walkExpr(node) {
|
|
202
|
-
if (!node) return node;
|
|
203
|
-
switch (node.type) {
|
|
204
|
-
case 'Identifier': return { ...node, name: this._mangleName(node.name) };
|
|
205
|
-
case 'NumberLit':
|
|
206
|
-
case 'BoolLit':
|
|
207
|
-
case 'NullLit':
|
|
208
|
-
case 'StringLit':
|
|
209
|
-
case 'SelfExpr': return node;
|
|
210
|
-
case 'TemplateLit': return { ...node, parts: node.parts.map(p => {
|
|
211
|
-
if (p.type !== 'expr') return p;
|
|
212
|
-
// Replace every identifier in the raw expression string that has a mangled name.
|
|
213
|
-
// Uses word-boundary replacement so "name" inside "username" is not touched.
|
|
214
|
-
let expr = p.value;
|
|
215
|
-
for (const [orig, mangled] of this.map) {
|
|
216
|
-
expr = expr.replace(new RegExp(`(?<![\\w$])${orig}(?![\\w$])`, 'g'), mangled);
|
|
217
|
-
}
|
|
218
|
-
return { ...p, value: expr };
|
|
219
|
-
}) };
|
|
220
|
-
case 'BinaryExpr': return { ...node, left: this._walkExpr(node.left), right: this._walkExpr(node.right) };
|
|
221
|
-
case 'UnaryExpr': return { ...node, operand: this._walkExpr(node.operand) };
|
|
222
|
-
case 'UpdateExpr': return { ...node, operand: this._walkExpr(node.operand) };
|
|
223
|
-
case 'AwaitExpr': return { ...node, operand: this._walkExpr(node.operand) };
|
|
224
|
-
case 'TypeofExpr': return { ...node, operand: this._walkExpr(node.operand) };
|
|
225
|
-
case 'TernaryExpr': return { ...node, cond: this._walkExpr(node.cond), then: this._walkExpr(node.then), else_: this._walkExpr(node.else_) };
|
|
226
|
-
case 'AssignExpr': return { ...node, target: this._walkExpr(node.target), value: this._walkExpr(node.value) };
|
|
227
|
-
case 'SpreadExpr': return { ...node, expr: this._walkExpr(node.expr) };
|
|
228
|
-
case 'PipeExpr': return { ...node, left: this._walkExpr(node.left), right: this._walkExpr(node.right) };
|
|
229
|
-
case 'MemberExpr': return { ...node, obj: this._walkExpr(node.obj) /* prop is kept */ };
|
|
230
|
-
case 'OptMemberExpr': return { ...node, obj: this._walkExpr(node.obj) };
|
|
231
|
-
case 'IndexExpr': return { ...node, obj: this._walkExpr(node.obj), idx: this._walkExpr(node.idx) };
|
|
232
|
-
case 'OptIndexExpr': return { ...node, obj: this._walkExpr(node.obj), idx: this._walkExpr(node.idx) };
|
|
233
|
-
case 'CallExpr': return { ...node, callee: this._walkExpr(node.callee), args: node.args.map(a => this._walkExpr(a)) };
|
|
234
|
-
case 'OptCallExpr': return { ...node, callee: this._walkExpr(node.callee), args: node.args.map(a => this._walkExpr(a)) };
|
|
235
|
-
case 'NewExpr': return { ...node, callee: this._mangleName(node.callee), args: node.args.map(a => this._walkExpr(a)) };
|
|
236
|
-
case 'LambdaExpr': return { ...node,
|
|
237
|
-
params: node.params.map(p => ({ ...p, name: this._mangleParam(p.name) })),
|
|
238
|
-
body: this._walkExpr(node.body) };
|
|
239
|
-
case 'FnDecl': return this._walkFnDecl(node, {});
|
|
240
|
-
case 'ArrayExpr': return { ...node, items: node.items.map(i => this._walkExpr(i)) };
|
|
241
|
-
case 'ObjectExpr': return { ...node, pairs: node.pairs.map(p => ({
|
|
242
|
-
...p, value: this._walkExpr(p.value)
|
|
243
|
-
// keys are kept — they are property names, not variable references
|
|
244
|
-
})) };
|
|
245
|
-
case 'RangeExpr': return { ...node, start: this._walkExpr(node.start), end: this._walkExpr(node.end) };
|
|
246
|
-
default: return node;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// ── Name helpers ─────────────────────────────────────────────
|
|
251
|
-
_registerName(name) {
|
|
252
|
-
if (!name || SAFE.has(name) || this.exported.has(name) || this.imported.has(name)) return;
|
|
253
|
-
if (!this.map.has(name)) this.map.set(name, makeName(this.counter++));
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
_mangleName(name) {
|
|
257
|
-
if (!name) return name;
|
|
258
|
-
if (SAFE.has(name) || this.exported.has(name) || this.imported.has(name)) return name;
|
|
259
|
-
// If we have a registered mangled name, use it
|
|
260
|
-
if (this.map.has(name)) return this.map.get(name);
|
|
261
|
-
// Not in map — likely a global or unregistered reference; return as-is
|
|
262
|
-
return name;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// For local params and loop variables — always mangle even if not pre-registered
|
|
266
|
-
_mangleLocal(name) {
|
|
267
|
-
if (!name || SAFE.has(name)) return name;
|
|
268
|
-
if (!this.map.has(name)) this.map.set(name, makeName(this.counter++));
|
|
269
|
-
return this.map.get(name);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Same as _mangleLocal but for function params
|
|
273
|
-
_mangleParam(name) {
|
|
274
|
-
if (!name || SAFE.has(name)) return name;
|
|
275
|
-
if (!this.map.has(name)) this.map.set(name, makeName(this.counter++));
|
|
276
|
-
return this.map.get(name);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
module.exports = { Mangler, SAFE, makeName };
|