@xnoxs/flux-lang 3.2.1 → 3.3.0

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/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 };