@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/CHANGELOG.md +13 -0
- package/bin/flux.js +1 -1395
- package/dist/flux-cli.js +9704 -0
- package/dist/flux.cjs.js +63 -2
- package/dist/flux.esm.js +63 -2
- package/dist/flux.min.js +18 -16
- package/index.js +4 -0
- package/package.json +25 -15
- package/scripts/build.js +28 -29
- package/src/config.js +99 -0
- 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/sourcemap.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Source Map v3 Generator
|
|
5
|
-
* Maps generated JS line/col back to original Flux source line/col.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
9
|
-
|
|
10
|
-
function encodeVLQ(value) {
|
|
11
|
-
let vlq = value < 0 ? ((-value) << 1) | 1 : (value << 1);
|
|
12
|
-
let result = '';
|
|
13
|
-
do {
|
|
14
|
-
let digit = vlq & 0x1F;
|
|
15
|
-
vlq >>>= 5;
|
|
16
|
-
if (vlq > 0) digit |= 0x20;
|
|
17
|
-
result += BASE64[digit];
|
|
18
|
-
} while (vlq > 0);
|
|
19
|
-
return result;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
class SourceMapBuilder {
|
|
23
|
-
constructor(sourceFile, sourceContent) {
|
|
24
|
-
this.sourceFile = sourceFile;
|
|
25
|
-
this.sourceContent = sourceContent;
|
|
26
|
-
// mappings[genLine] = [{ genCol, srcLine, srcCol }, ...]
|
|
27
|
-
// All indices are 0-based.
|
|
28
|
-
this.mappings = [];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Record one mapping.
|
|
33
|
-
* @param {number} genLine - 0-based generated (JS) line
|
|
34
|
-
* @param {number} genCol - 0-based generated column
|
|
35
|
-
* @param {number} srcLine - 0-based source (.flux) line
|
|
36
|
-
* @param {number} srcCol - 0-based source column
|
|
37
|
-
*/
|
|
38
|
-
addMapping(genLine, genCol, srcLine, srcCol) {
|
|
39
|
-
while (this.mappings.length <= genLine) this.mappings.push([]);
|
|
40
|
-
this.mappings[genLine].push({ genCol, srcLine, srcCol });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// VLQ-encode all mappings into source map v3 "mappings" string
|
|
44
|
-
encodeMappings() {
|
|
45
|
-
let prevSrcLine = 0;
|
|
46
|
-
let prevSrcCol = 0;
|
|
47
|
-
|
|
48
|
-
return this.mappings.map(lineSegs => {
|
|
49
|
-
let prevGenCol = 0;
|
|
50
|
-
return lineSegs.map(seg => {
|
|
51
|
-
const encoded = [
|
|
52
|
-
encodeVLQ(seg.genCol - prevGenCol),
|
|
53
|
-
encodeVLQ(0), // source file index (always 0)
|
|
54
|
-
encodeVLQ(seg.srcLine - prevSrcLine),
|
|
55
|
-
encodeVLQ(seg.srcCol - prevSrcCol),
|
|
56
|
-
].join('');
|
|
57
|
-
prevGenCol = seg.genCol;
|
|
58
|
-
prevSrcLine = seg.srcLine;
|
|
59
|
-
prevSrcCol = seg.srcCol;
|
|
60
|
-
return encoded;
|
|
61
|
-
}).join(',');
|
|
62
|
-
}).join(';');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
toJSON(outputFile = '') {
|
|
66
|
-
return {
|
|
67
|
-
version: 3,
|
|
68
|
-
file: outputFile,
|
|
69
|
-
sourceRoot: '',
|
|
70
|
-
sources: [this.sourceFile],
|
|
71
|
-
sourcesContent: [this.sourceContent],
|
|
72
|
-
names: [],
|
|
73
|
-
mappings: this.encodeMappings(),
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
toString(outputFile = '') {
|
|
78
|
-
return JSON.stringify(this.toJSON(outputFile));
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
module.exports = { SourceMapBuilder, encodeVLQ };
|
package/src/test-runner.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
|
|
7
|
-
// ── Test Runner ───────────────────────────────────────────────────────────────
|
|
8
|
-
// Discovers and runs Flux test files (*.test.flux)
|
|
9
|
-
// Test functions must be named test_* (e.g. fn test_arithmetic():)
|
|
10
|
-
// Globals injected: assert(cond, msg), assertEqual(a, b), assertThrows(fn, msg)
|
|
11
|
-
|
|
12
|
-
const C = {
|
|
13
|
-
reset: '\x1b[0m',
|
|
14
|
-
bold: '\x1b[1m',
|
|
15
|
-
red: '\x1b[31m',
|
|
16
|
-
green: '\x1b[32m',
|
|
17
|
-
yellow: '\x1b[33m',
|
|
18
|
-
cyan: '\x1b[36m',
|
|
19
|
-
gray: '\x1b[90m',
|
|
20
|
-
dim: '\x1b[2m',
|
|
21
|
-
};
|
|
22
|
-
const clr = (c, s) => (process.env.NO_COLOR ? s : `${c}${s}${C.reset}`);
|
|
23
|
-
|
|
24
|
-
// ── Assertion helpers injected into test execution ────────────────────────────
|
|
25
|
-
const TEST_HELPERS = `
|
|
26
|
-
"use strict";
|
|
27
|
-
|
|
28
|
-
const __results = [];
|
|
29
|
-
let __current = null;
|
|
30
|
-
|
|
31
|
-
function assert(condition, message) {
|
|
32
|
-
if (!condition) {
|
|
33
|
-
throw new Error(message || 'Assertion failed: expected truthy');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function assertEqual(actual, expected, message) {
|
|
38
|
-
const ok = JSON.stringify(actual) === JSON.stringify(expected);
|
|
39
|
-
if (!ok) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
message ||
|
|
42
|
-
'Expected ' + JSON.stringify(expected) + ' but got ' + JSON.stringify(actual)
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function assertNotEqual(actual, expected, message) {
|
|
48
|
-
const ok = JSON.stringify(actual) !== JSON.stringify(expected);
|
|
49
|
-
if (!ok) {
|
|
50
|
-
throw new Error(message || 'Expected values to be different but both were ' + JSON.stringify(actual));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function assertThrows(fn, message) {
|
|
55
|
-
try { fn(); }
|
|
56
|
-
catch (_) { return; }
|
|
57
|
-
throw new Error(message || 'Expected function to throw, but it did not');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function assertClose(actual, expected, delta, message) {
|
|
61
|
-
delta = delta || 1e-9;
|
|
62
|
-
if (Math.abs(actual - expected) > delta) {
|
|
63
|
-
throw new Error(message || ('Expected ' + actual + ' to be close to ' + expected + ' (delta=' + delta + ')'));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Register test results
|
|
68
|
-
function __runTest(name, fn) {
|
|
69
|
-
try {
|
|
70
|
-
fn();
|
|
71
|
-
__results.push({ name: name, ok: true });
|
|
72
|
-
} catch(e) {
|
|
73
|
-
__results.push({ name: name, ok: false, error: e.message });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Auto-detect and run test functions after module evaluation
|
|
78
|
-
function __runAll(mod) {
|
|
79
|
-
const names = Object.getOwnPropertyNames(mod).filter(k => k.startsWith('test_') && typeof mod[k] === 'function');
|
|
80
|
-
for (const n of names) __runTest(n.replace(/^test_/, '').replace(/_/g, ' '), mod[n]);
|
|
81
|
-
return __results;
|
|
82
|
-
}
|
|
83
|
-
`;
|
|
84
|
-
|
|
85
|
-
function discoverTestFiles(target) {
|
|
86
|
-
const abs = path.resolve(target);
|
|
87
|
-
|
|
88
|
-
if (!fs.existsSync(abs)) {
|
|
89
|
-
throw new Error(`Not found: ${abs}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const stat = fs.statSync(abs);
|
|
93
|
-
if (stat.isDirectory()) {
|
|
94
|
-
return fs.readdirSync(abs)
|
|
95
|
-
.filter(f => f.endsWith('.test.flux'))
|
|
96
|
-
.map(f => path.join(abs, f))
|
|
97
|
-
.sort();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (abs.endsWith('.flux')) return [abs];
|
|
101
|
-
throw new Error(`Expected a .flux file or directory, got: ${abs}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function runTestFile(filePath, transpile) {
|
|
105
|
-
const source = fs.readFileSync(filePath, 'utf8');
|
|
106
|
-
const result = transpile(source);
|
|
107
|
-
|
|
108
|
-
if (!result.success) {
|
|
109
|
-
return {
|
|
110
|
-
file: path.basename(filePath),
|
|
111
|
-
errors: result.errors,
|
|
112
|
-
tests: [],
|
|
113
|
-
compile: false,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Wrap compiled JS: inject helpers + collect results
|
|
118
|
-
const wrapped = [
|
|
119
|
-
TEST_HELPERS,
|
|
120
|
-
result.output,
|
|
121
|
-
// Expose all top-level functions for test discovery
|
|
122
|
-
'module.exports = typeof module !== "undefined" ? (function() {',
|
|
123
|
-
' const m = {};',
|
|
124
|
-
' // collect test_ functions defined at top level',
|
|
125
|
-
' const keys = Object.keys(global).filter(k => k.startsWith("test_") && typeof global[k] === "function");',
|
|
126
|
-
' keys.forEach(k => m[k] = global[k]);',
|
|
127
|
-
' return m;',
|
|
128
|
-
'})() : {};',
|
|
129
|
-
].join('\n');
|
|
130
|
-
|
|
131
|
-
// Better approach: scan compiled JS for function declarations named test_*
|
|
132
|
-
const fnNames = [];
|
|
133
|
-
const fnRe = /^function (test_[a-zA-Z0-9_]+)\s*\(/mg;
|
|
134
|
-
let match;
|
|
135
|
-
while ((match = fnRe.exec(result.output)) !== null) {
|
|
136
|
-
fnNames.push(match[1]);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Build runner that calls each test_ function
|
|
140
|
-
const runner = [
|
|
141
|
-
TEST_HELPERS,
|
|
142
|
-
result.output,
|
|
143
|
-
'',
|
|
144
|
-
'// ── auto-generated test runner ──',
|
|
145
|
-
...fnNames.map(n => `__runTest(${JSON.stringify(n.replace(/^test_/, '').replace(/_/g, ' '))}, ${n});`),
|
|
146
|
-
'module.exports = { __results };',
|
|
147
|
-
].join('\n');
|
|
148
|
-
|
|
149
|
-
const tmpPath = path.join(os.tmpdir(), `_flux_test_${Date.now()}.js`);
|
|
150
|
-
try {
|
|
151
|
-
fs.writeFileSync(tmpPath, runner, 'utf8');
|
|
152
|
-
const mod = require(tmpPath);
|
|
153
|
-
const tests = (mod && mod.__results) ? mod.__results : [];
|
|
154
|
-
return {
|
|
155
|
-
file: path.basename(filePath),
|
|
156
|
-
tests,
|
|
157
|
-
compile: true,
|
|
158
|
-
errors: [],
|
|
159
|
-
};
|
|
160
|
-
} catch (e) {
|
|
161
|
-
return {
|
|
162
|
-
file: path.basename(filePath),
|
|
163
|
-
tests: [],
|
|
164
|
-
compile: false,
|
|
165
|
-
errors: [{ message: `Runtime error: ${e.message}` }],
|
|
166
|
-
};
|
|
167
|
-
} finally {
|
|
168
|
-
try { fs.unlinkSync(tmpPath); } catch (_) {}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function runTests(target, transpile) {
|
|
173
|
-
let files;
|
|
174
|
-
try {
|
|
175
|
-
files = discoverTestFiles(target);
|
|
176
|
-
} catch (e) {
|
|
177
|
-
console.error(clr(C.red, `✗ ${e.message}`));
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (files.length === 0) {
|
|
182
|
-
console.log(clr(C.yellow, `No *.test.flux files found in: ${target}`));
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let totalPass = 0;
|
|
187
|
-
let totalFail = 0;
|
|
188
|
-
let totalFiles = 0;
|
|
189
|
-
const t0 = Date.now();
|
|
190
|
-
|
|
191
|
-
for (const file of files) {
|
|
192
|
-
totalFiles++;
|
|
193
|
-
console.log(clr(C.cyan, `\n◈ ${path.basename(file)}`));
|
|
194
|
-
|
|
195
|
-
const result = runTestFile(file, transpile);
|
|
196
|
-
|
|
197
|
-
if (!result.compile) {
|
|
198
|
-
console.log(clr(C.red, ` ✗ Compile error:`));
|
|
199
|
-
for (const e of result.errors) {
|
|
200
|
-
console.log(clr(C.red, ` ${e.message}`));
|
|
201
|
-
}
|
|
202
|
-
totalFail++;
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (result.tests.length === 0) {
|
|
207
|
-
console.log(clr(C.yellow, ` (no test_ functions found)`));
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
for (const t of result.tests) {
|
|
212
|
-
if (t.ok) {
|
|
213
|
-
totalPass++;
|
|
214
|
-
console.log(clr(C.green, ` ✓`) + clr(C.gray, ` ${t.name}`));
|
|
215
|
-
} else {
|
|
216
|
-
totalFail++;
|
|
217
|
-
console.log(clr(C.red, ` ✗ ${t.name}`));
|
|
218
|
-
console.log(clr(C.dim, ` ${t.error}`));
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const elapsed = Date.now() - t0;
|
|
224
|
-
const total = totalPass + totalFail;
|
|
225
|
-
|
|
226
|
-
console.log();
|
|
227
|
-
console.log(clr(C.bold, '─'.repeat(50)));
|
|
228
|
-
console.log(
|
|
229
|
-
clr(C.bold, `Results: `) +
|
|
230
|
-
clr(C.green, `${totalPass} passed`) + clr(C.gray, ', ') +
|
|
231
|
-
(totalFail > 0 ? clr(C.red, `${totalFail} failed`) : clr(C.gray, `${totalFail} failed`)) +
|
|
232
|
-
clr(C.gray, ` (${total} total) in ${elapsed}ms`)
|
|
233
|
-
);
|
|
234
|
-
console.log();
|
|
235
|
-
|
|
236
|
-
if (totalFail > 0) process.exitCode = 1;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
module.exports = { runTests, runTestFile, discoverTestFiles };
|
package/src/transpiler.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { Lexer } = require('./lexer');
|
|
4
|
-
const { Parser } = require('./parser');
|
|
5
|
-
const { CodeGenerator } = require('./codegen');
|
|
6
|
-
const { SourceMapBuilder } = require('./sourcemap');
|
|
7
|
-
const { transformJsx } = require('./jsx');
|
|
8
|
-
const { Mangler } = require('./mangler');
|
|
9
|
-
const { transformCss } = require('./css-preprocessor');
|
|
10
|
-
const { Checker } = require('./checker');
|
|
11
|
-
const { FluxTypeChecker } = require('./type-checker');
|
|
12
|
-
const { buildStdlib, detectUsedSymbols } = require('./stdlib');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Transpile Flux source code to JavaScript.
|
|
16
|
-
*
|
|
17
|
-
* @param {string} source - Flux source code
|
|
18
|
-
* @param {object} [options]
|
|
19
|
-
* @param {boolean} options.sourcemap - generate source map
|
|
20
|
-
* @param {string} options.sourceFile - source .flux filename (for source map)
|
|
21
|
-
* @param {string} options.outputFile - output .js filename (for source map)
|
|
22
|
-
* @param {boolean} options.mangle - obfuscate variable/function names
|
|
23
|
-
* @param {boolean} options.jsx - enable JSX (default: true)
|
|
24
|
-
* @param {string} options.jsxTarget - 'browser' | 'server' (default: 'browser')
|
|
25
|
-
* @param {boolean} options.typecheck - run type checker (default: true)
|
|
26
|
-
* @param {boolean} options.check - run val-immutability checker
|
|
27
|
-
* @param {boolean} options.stdlib - inject stdlib helpers (default: true)
|
|
28
|
-
* @returns {{ success, output, sourceMap, ast, tokens, errors, typeErrors, typeWarnings, stage, nameMap }}
|
|
29
|
-
*/
|
|
30
|
-
function transpile(source, options = {}) {
|
|
31
|
-
const result = {
|
|
32
|
-
success: false,
|
|
33
|
-
output: '',
|
|
34
|
-
sourceMap: null,
|
|
35
|
-
ast: null,
|
|
36
|
-
tokens: null,
|
|
37
|
-
errors: [],
|
|
38
|
-
typeErrors: [],
|
|
39
|
-
typeWarnings: [],
|
|
40
|
-
stage: null,
|
|
41
|
-
nameMap: null,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
// ── Stage 0a: CSS Block Preprocessing ────────────────────────────────────
|
|
46
|
-
result.stage = 'css';
|
|
47
|
-
const cssProcessed = transformCss(source);
|
|
48
|
-
|
|
49
|
-
// ── Stage 0b: JSX Preprocessing ──────────────────────────────────────────
|
|
50
|
-
result.stage = 'jsx';
|
|
51
|
-
const jsxTarget = options.jsxTarget || 'browser';
|
|
52
|
-
const enableJsx = options.jsx !== false;
|
|
53
|
-
let processedSource = cssProcessed;
|
|
54
|
-
let jsxRuntime = '';
|
|
55
|
-
|
|
56
|
-
if (enableJsx) {
|
|
57
|
-
const jsxResult = transformJsx(cssProcessed, { target: jsxTarget });
|
|
58
|
-
processedSource = jsxResult.source;
|
|
59
|
-
jsxRuntime = jsxResult.hasJsx ? jsxResult.runtimeHelpers : '';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ── Stage 1: Lexer ────────────────────────────────────────────────────────
|
|
63
|
-
result.stage = 'lexer';
|
|
64
|
-
const tokens = new Lexer(processedSource).tokenize();
|
|
65
|
-
result.tokens = tokens;
|
|
66
|
-
|
|
67
|
-
// ── Stage 2: Parser ───────────────────────────────────────────────────────
|
|
68
|
-
result.stage = 'parser';
|
|
69
|
-
const ast = new Parser(tokens).parse();
|
|
70
|
-
result.ast = ast;
|
|
71
|
-
|
|
72
|
-
// ── Stage 3 (optional): Val-immutability Checker ──────────────────────────
|
|
73
|
-
if (options.check) {
|
|
74
|
-
result.stage = 'checker';
|
|
75
|
-
const checker = new Checker();
|
|
76
|
-
const analysis = checker.check(ast);
|
|
77
|
-
if (analysis.errors.length > 0) {
|
|
78
|
-
result.errors = analysis.errors;
|
|
79
|
-
result.stage = null;
|
|
80
|
-
return result;
|
|
81
|
-
}
|
|
82
|
-
result.typeWarnings = [...(result.typeWarnings || []), ...analysis.warnings];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ── Stage 3b (optional): Type Checker ────────────────────────────────────
|
|
86
|
-
if (options.typecheck !== false) {
|
|
87
|
-
result.stage = 'typecheck';
|
|
88
|
-
const tc = new FluxTypeChecker();
|
|
89
|
-
const tcResult = tc.check(ast);
|
|
90
|
-
result.typeErrors = tcResult.errors;
|
|
91
|
-
result.typeWarnings = [...(result.typeWarnings || []), ...tcResult.warnings];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ── Stage 4 (optional): Name Mangling ────────────────────────────────────
|
|
95
|
-
let finalAst = ast;
|
|
96
|
-
if (options.mangle) {
|
|
97
|
-
result.stage = 'mangler';
|
|
98
|
-
const mangler = new Mangler();
|
|
99
|
-
finalAst = mangler.mangle(ast);
|
|
100
|
-
result.nameMap = mangler.getMap();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ── Stage 5: Code Generator ───────────────────────────────────────────────
|
|
104
|
-
result.stage = 'codegen';
|
|
105
|
-
|
|
106
|
-
const genOptions = { indent: options.mangle ? '' : ' ' };
|
|
107
|
-
|
|
108
|
-
if (options.sourcemap) {
|
|
109
|
-
genOptions.smBuilder = new SourceMapBuilder(
|
|
110
|
-
options.sourceFile || 'source.flux',
|
|
111
|
-
source
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const { code, smBuilder } = new CodeGenerator(genOptions).generate(finalAst);
|
|
116
|
-
|
|
117
|
-
// ── Stage 6: Stdlib Injection ────────────────────────────────────────────
|
|
118
|
-
// Detect which stdlib helpers are needed and prepend them.
|
|
119
|
-
const injectStdlib = options.stdlib !== false;
|
|
120
|
-
let stdlibPreamble = '';
|
|
121
|
-
if (injectStdlib) {
|
|
122
|
-
const usedSymbols = detectUsedSymbols(code);
|
|
123
|
-
if (usedSymbols.length > 0) {
|
|
124
|
-
stdlibPreamble = buildStdlib(usedSymbols) + '\n';
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ── Assemble final output ────────────────────────────────────────────────
|
|
129
|
-
const runtimePrefix = jsxRuntime ? `// Flux JSX Runtime\n"use strict";\n${jsxRuntime}\n` : '';
|
|
130
|
-
const cleanCode = jsxRuntime ? code.replace(/^\/\/ Generated.*\n"use strict";\n/, '') : code;
|
|
131
|
-
|
|
132
|
-
let finalCode;
|
|
133
|
-
if (smBuilder) {
|
|
134
|
-
const outFile = options.outputFile || 'output.js';
|
|
135
|
-
const mapFile = outFile + '.map';
|
|
136
|
-
result.sourceMap = smBuilder.toString(outFile);
|
|
137
|
-
finalCode = runtimePrefix + stdlibPreamble + cleanCode + `\n//# sourceMappingURL=${mapFile}\n`;
|
|
138
|
-
} else {
|
|
139
|
-
finalCode = runtimePrefix + stdlibPreamble + cleanCode;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (options.mangle) {
|
|
143
|
-
const allLines = finalCode.split('\n').map(l => l.trim()).filter(l => l.length > 0);
|
|
144
|
-
const headerLines = [];
|
|
145
|
-
const codeLines = [];
|
|
146
|
-
for (const l of allLines) {
|
|
147
|
-
if (l.startsWith('//') && codeLines.length === 0) headerLines.push(l);
|
|
148
|
-
else codeLines.push(l);
|
|
149
|
-
}
|
|
150
|
-
finalCode = [...headerLines, codeLines.join(' ')].join('\n');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
result.output = finalCode;
|
|
154
|
-
result.success = true;
|
|
155
|
-
result.stage = null;
|
|
156
|
-
|
|
157
|
-
} catch (e) {
|
|
158
|
-
result.errors.push({
|
|
159
|
-
message: e.message,
|
|
160
|
-
name: e.name,
|
|
161
|
-
stage: result.stage,
|
|
162
|
-
line: e.tok ? e.tok.line : (e.line || null),
|
|
163
|
-
col: e.tok ? e.tok.col : (e.col || null),
|
|
164
|
-
len: e.tok && e.tok.value ? String(e.tok.value).length : 1,
|
|
165
|
-
hint: e.hint || null,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
module.exports = { transpile };
|