add --minify and --obfuscate flags to elc JS pipeline

Adds two post-processing flags that produce production-ready browser JS in a
single elc invocation, replacing extract-js.py in the web product pipeline:

  elc --target=js --bundle --minify source.el > output.min.js
  elc --target=js --bundle --obfuscate source.el > output.obf.js

--minify shells out to terser (passes=2, no drop_console, drop_debugger).
--obfuscate shells out to javascript-obfuscator with the same options as the
old extract-js.py script. --obfuscate implies --minify.

Tool discovery: checks ./node_modules/.bin/, ../node_modules/.bin/ (monorepo),
then falls back to npx. Both flags require --target=js; passing either without
it exits 1 with a clear error.

Both tools receive a reserved-names list of globals referenced from HTML
onclick= attributes (neuronDemoToggle, signInWith, NEURON_CFG, etc.) so they
are not mangled.

Implementation adds stdout_to_file(path)/stdout_restore() builtins to the C
runtime so codegen's println-streamed output can be captured to a temp file
before being piped through the external tools. Temp files use
/tmp/elc-<pid>-<timestamp>.js naming and are cleaned up on success and failure.

Rebuilds dist/platform/elc and dist/platform/elc.c. Self-hosting verified.
This commit is contained in:
Will Anderson
2026-05-04 10:54:34 -05:00
parent 21694b79d2
commit 7b60d94b8a
7 changed files with 1147 additions and 183 deletions
+30
View File
@@ -155,6 +155,36 @@ el_val_t readline(void) {
return el_wrap_str(el_strdup(buf));
}
/* ── stdout redirect helpers ─────────────────────────────────────────────── *
* Used by elc post-processing (--minify, --obfuscate): capture codegen *
* output into a temp file, then pass it to the external tool. */
static int _stdout_saved_fd = -1;
/* stdout_to_file(path) — redirect stdout to <path>. Returns 1 on success. */
el_val_t stdout_to_file(el_val_t pathv) {
const char* path = EL_CSTR(pathv);
if (!path || !*path) return (el_val_t)(int64_t)0;
fflush(stdout);
_stdout_saved_fd = dup(STDOUT_FILENO);
if (_stdout_saved_fd < 0) return (el_val_t)(int64_t)0;
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) { close(_stdout_saved_fd); _stdout_saved_fd = -1; return (el_val_t)(int64_t)0; }
dup2(fd, STDOUT_FILENO);
close(fd);
return (el_val_t)(int64_t)1;
}
/* stdout_restore() — restore stdout from the saved fd. Returns 1 on success. */
el_val_t stdout_restore(void) {
if (_stdout_saved_fd < 0) return (el_val_t)(int64_t)0;
fflush(stdout);
dup2(_stdout_saved_fd, STDOUT_FILENO);
close(_stdout_saved_fd);
_stdout_saved_fd = -1;
return (el_val_t)(int64_t)1;
}
/* ── String builtins ─────────────────────────────────────────────────────── */
el_val_t el_str_concat(el_val_t av, el_val_t bv) {