Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504#include "utils.h"#include "messages.h"
#include <crprintf.h>#include <errno.h>#include <skim.h>#include <stdbool.h>#include <stdio.h>#include <sys/stat.h>#include <yyjson.h>
#ifdef _WIN32#include <direct.h>#include <windows.h>#define ANT_MKDIR(path) _mkdir(path)#else#include <limits.h>#include <unistd.h>#define ANT_MKDIR(path) mkdir(path, 0755)#endif
#ifdef __APPLE__#include <mach-o/dyld.h>#endif
const char *const module_resolve_extensions[] = { ".js", ".mjs", ".cjs", ".ts", ".mts", ".cts", ".json", ".node", NULL};
static _Thread_local skim_context_t ts_strip_context;static _Thread_local bool ts_strip_context_ready;
static int ensure_ts_strip_context(const char **error_detail) { if (ts_strip_context_ready) return 0;
if (skim_context_init(&ts_strip_context) != 0) { if (error_detail) *error_detail = "out of memory while initializing TypeScript stripper"; return SKIM_ERR_TRANSFORM_FAILED; }
ts_strip_context_ready = true; return 0;}
void ts_strip_thread_cleanup(void) { if (!ts_strip_context_ready) return; skim_context_deinit(&ts_strip_context); ts_strip_context_ready = false;}
static const char *ant_home_dir(void) {#ifdef _WIN32 const char *home = getenv("USERPROFILE"); if (home && home[0]) return home;#else const char *home = getenv("HOME"); if (home && home[0]) return home;#endif return NULL;}
static const char *ant_absolute_env(const char *name) {#ifdef _WIN32 return NULL;#else const char *value = getenv(name); if (!value || value[0] != '/') return NULL; return value;#endif}
bool ant_env_bool(const char *value, bool default_value) { if (!value || !*value) return default_value; if ( strcmp(value, "0") == 0 || strcmp(value, "false") == 0 || strcmp(value, "FALSE") == 0 || strcmp(value, "off") == 0 || strcmp(value, "OFF") == 0 || strcmp(value, "no") == 0 || strcmp(value, "NO") == 0 ) return false; return true;}
static int ant_join_app_path( char *out, size_t out_size, const char *base, const char *app, const char *suffix) { if (!out || out_size == 0 || !base || !base[0] || !app || !app[0]) return -1; while (suffix && (*suffix == '/' || *suffix == '\\')) suffix++;
int written = 0; if (suffix && suffix[0]) written = snprintf(out, out_size, "%s/%s/%s", base, app, suffix); else written = snprintf(out, out_size, "%s/%s", base, app); return (written < 0 || (size_t)written >= out_size) ? -1 : 0;}
static int ant_legacy_home_path(char *out, size_t out_size, const char *suffix) { const char *home = ant_home_dir(); if (!home) return -1; return ant_join_app_path(out, out_size, home, ".ant", suffix);}
static bool ant_legacy_home_exists(void) { char path[4096]; if (ant_legacy_home_path(path, sizeof(path), NULL) != 0) return false; struct stat st; return stat(path, &st) == 0;}
static int ant_xdg_path( char *out, size_t out_size, const char *env_name, const char *home_suffix, const char *suffix) {#ifdef _WIN32 const char *home = ant_home_dir(); if (!home) return -1; return ant_join_app_path(out, out_size, home, ".ant", suffix);#else if (ant_legacy_home_exists()) { return ant_legacy_home_path(out, out_size, suffix); }
const char *base = ant_absolute_env(env_name); if (base) return ant_join_app_path(out, out_size, base, "ant", suffix);
const char *home = ant_home_dir(); if (!home) return -1; char fallback[4096]; int written = snprintf(fallback, sizeof(fallback), "%s/%s", home, home_suffix); if (written < 0 || (size_t)written >= sizeof(fallback)) return -1; return ant_join_app_path(out, out_size, fallback, "ant", suffix);#endif}
#ifdef _WIN32static inline bool ant_is_path_sep(char c) { return c == '\\' || c == '/';}
static char *ant_skip_path_components(char *s, int n) { while (*s && n > 0) if (ant_is_path_sep(*s++)) n--; return s;}#endif
static bool ant_mkdir_one(const char *path) { if (ANT_MKDIR(path) == 0) return true; struct stat st; return stat(path, &st) == 0 && S_ISDIR(st.st_mode);}
int ant_mkdir_p(const char *path) { if (!path || !path[0]) return -1;
char tmp[4096]; size_t len = strlen(path); if (len >= sizeof(tmp)) return -1; memcpy(tmp, path, len + 1);
while ( len > 1 && (tmp[len - 1] == '/' || tmp[len - 1] == '\\') ) tmp[--len] = '\0';
char *start = tmp + 1;#ifdef _WIN32 char *root = tmp; bool unc = ant_is_path_sep(tmp[0]) && ant_is_path_sep(tmp[1]); if (unc && (tmp[2] == '?' || tmp[2] == '.') && ant_is_path_sep(tmp[3])) { root = tmp + 4; unc = _strnicmp(root, "UNC", 3) == 0 && ant_is_path_sep(root[3]); if (unc) root += 4; } else if (unc) root = tmp + 2;
if (unc) start = ant_skip_path_components(root, 2); else if (root[0] && root[1] == ':') start = ant_is_path_sep(root[2]) ? root + 3 : root + 2; else if (root != tmp) start = ant_skip_path_components(root, 1);#endif
for (char *p = start; *p; p++) { if (*p != '/' && *p != '\\') continue; char sep = *p; *p = '\0'; if (p > tmp && !ant_mkdir_one(tmp)) return -1; *p = sep; }
return ant_mkdir_one(tmp) ? 0 : -1;}
int ant_xdg_cache_path(char *out, size_t out_size, const char *suffix) { return ant_xdg_path(out, out_size, "XDG_CACHE_HOME", ".cache", suffix);}
int ant_xdg_data_path(char *out, size_t out_size, const char *suffix) { return ant_xdg_path(out, out_size, "XDG_DATA_HOME", ".local/share", suffix);}
int ant_xdg_state_path(char *out, size_t out_size, const char *suffix) { return ant_xdg_path(out, out_size, "XDG_STATE_HOME", ".local/state", suffix);}
int ant_user_bin_path(char *out, size_t out_size) { const char *home = ant_home_dir(); if (!home || !out || out_size == 0) return -1;#ifdef _WIN32 int written = snprintf(out, out_size, "%s/.ant/bin", home);#else if (ant_legacy_home_exists()) { return ant_legacy_home_path(out, out_size, "bin"); } int written = snprintf(out, out_size, "%s/.local/bin", home);#endif return (written < 0 || (size_t)written >= out_size) ? -1 : 0;}
int ant_get_exe_path(char *out, size_t out_len, int argc, char **argv) { if (!out || out_len == 0) return -1; out[0] = '\0';
#ifdef _WIN32 DWORD len = GetModuleFileNameA(NULL, out, (DWORD)out_len); if (len > 0 && len < out_len) return 0;#else#ifdef __APPLE__ char tmp[PATH_MAX]; uint32_t size = (uint32_t)sizeof(tmp); if (_NSGetExecutablePath(tmp, &size) == 0) { char resolved[PATH_MAX]; const char *src = realpath(tmp, resolved) ? resolved : tmp; if ((size_t)snprintf(out, out_len, "%s", src) < out_len) return 0; }#elif defined(__linux__) ssize_t len = readlink("/proc/self/exe", out, out_len); if (len > 0 && (size_t)len < out_len) { out[len] = '\0'; return 0; }#endif#endif
if (argc > 0 && argv && argv[0] && (size_t)snprintf(out, out_len, "%s", argv[0]) < out_len) return 0; return -1;}
int is_typescript_file(const char *filename) { if (filename == NULL) return 0; size_t len = strlen(filename); if (len < 3) return 0; const char *ext = filename + len; while (ext > filename && *(ext - 1) != '.' && *(ext - 1) != '/') ext--; if (ext == filename || *(ext - 1) != '.') return 0; ext--; return (strcmp(ext, ".ts") == 0 || strcmp(ext, ".mts") == 0 || strcmp(ext, ".cts") == 0);}
int transform_typescript( char **buffer, size_t len, const char *filename, ant_ts_source_mode_t source_mode, size_t *out_len, const char **error_detail) { if (out_len) *out_len = len; if (error_detail) *error_detail = NULL;
if (!buffer || !*buffer) { if (error_detail) *error_detail = "null input/output passed"; return SKIM_ERR_NULL_INPUT; } char *input = *buffer; char error_buf[256] = {0}; size_t stripped_len = 0;
int init_result = ensure_ts_strip_context(error_detail); if (init_result < 0) return init_result;
skim_context_reset(&ts_strip_context); skim_error_t strip_error = SKIM_ERR_TRANSFORM_FAILED; skim_source_mode_t skim_mode = SKIM_SOURCE_AUTO; switch (source_mode) { case ANT_TS_SOURCE_MODULE: skim_mode = SKIM_SOURCE_MODULE; break; case ANT_TS_SOURCE_SCRIPT: skim_mode = SKIM_SOURCE_SCRIPT; break; case ANT_TS_SOURCE_COMMONJS: skim_mode = SKIM_SOURCE_COMMONJS; break; case ANT_TS_SOURCE_AUTO: break; }
const char *stripped = skim_strip_typescript_borrowed( &ts_strip_context, input, len, filename, skim_mode, NULL, &stripped_len, &strip_error, error_buf, sizeof(error_buf) );
if (!stripped) { if (error_buf[0] != '\0') { size_t msg_len = strlen(error_buf); size_t copy_len = msg_len > len ? len : msg_len; memcpy(input, error_buf, copy_len); input[copy_len] = '\0'; } else input[0] = '\0'; if (error_detail) *error_detail = input[0] != '\0' ? input : "unknown strip error"; return (int)strip_error; }
char *next = realloc(input, stripped_len + 1); if (!next) { if (error_detail) *error_detail = "out of memory while resizing strip output buffer"; return SKIM_ERR_OUTPUT_TOO_LARGE; }
memcpy(next, stripped, stripped_len + 1); *buffer = next; if (out_len) *out_len = stripped_len;
return 0;}
int strip_typescript_inplace( char **buffer, size_t len, const char *filename, size_t *out_len, const char **error_detail) { if (out_len) *out_len = len; if (error_detail) *error_detail = NULL; if (!is_typescript_file(filename)) return 0; return transform_typescript( buffer, len, filename, ANT_TS_SOURCE_AUTO, out_len, error_detail );}
static bool is_entrypoint_script_extension(const char *ext) { return ext && strcmp(ext, ".json") != 0 && strcmp(ext, ".node") != 0;}
static bool has_js_extension(const char *filename) { const char *slash = strrchr(filename, '/'); const char *base = slash ? slash + 1 : filename; const char *dot = strrchr(base, '.'); if (!dot) return false; for (const char *const *ext = module_resolve_extensions; *ext; ext++) { if (!is_entrypoint_script_extension(*ext)) continue; if (strcmp(dot, *ext) == 0) return true; } return false;}
static char *resolve_existing_entrypoint_file(const char *filename, const struct stat *st) { if (!S_ISREG(st->st_mode)) return NULL;
const char *slash = strrchr(filename, '/'); const char *base = slash ? slash + 1 : filename; const char *dot = strrchr(base, '.'); if (dot && !has_js_extension(filename)) return NULL; return strdup(filename);}
static char *resolve_entrypoint_file_extensions(const char *filename) { struct stat st; if (has_js_extension(filename)) return NULL; size_t base_len = strlen(filename);
for (const char *const *ext = module_resolve_extensions; *ext; ext++) { if (!is_entrypoint_script_extension(*ext)) continue; size_t ext_len = strlen(*ext); char *test_path = try_oom(base_len + ext_len + 1); memcpy(test_path, filename, base_len); memcpy(test_path + base_len, *ext, ext_len + 1); if (stat(test_path, &st) == 0 && S_ISREG(st.st_mode)) { return test_path; } free(test_path); } return NULL;}
static char *resolve_entrypoint_file(const char *filename) { struct stat st; if (stat(filename, &st) == 0) return resolve_existing_entrypoint_file(filename, &st); return resolve_entrypoint_file_extensions(filename);}
static char *resolve_directory_index(const char *directory) { size_t len = strlen(directory); bool has_slash = len > 0 && (directory[len - 1] == '/' || directory[len - 1] == '\\'); char *index_path = try_oom(len + (has_slash ? 0 : 1) + sizeof("index"));
sprintf(index_path, "%s%sindex", directory, has_slash ? "" : "/"); char *resolved = resolve_entrypoint_file(index_path); free(index_path); return resolved;}
static char *resolve_directory_main(const char *directory) { size_t dir_len = strlen(directory); bool has_slash = dir_len > 0 && (directory[dir_len - 1] == '/' || directory[dir_len - 1] == '\\'); char *package_path = try_oom(dir_len + (has_slash ? 0 : 1) + sizeof("package.json")); sprintf(package_path, "%s%spackage.json", directory, has_slash ? "" : "/");
yyjson_doc *doc = yyjson_read_file(package_path, 0, NULL, NULL); free(package_path); if (!doc) return NULL;
yyjson_val *root = yyjson_doc_get_root(doc); yyjson_val *main = root && yyjson_is_obj(root) ? yyjson_obj_get(root, "main") : NULL; const char *main_str = main && yyjson_is_str(main) ? yyjson_get_str(main) : NULL; char *resolved = NULL;
if (main_str && main_str[0]) { size_t main_len = strlen(main_str); char *main_path = try_oom(dir_len + (has_slash ? 0 : 1) + main_len + 1); sprintf(main_path, "%s%s%s", directory, has_slash ? "" : "/", main_str);
resolved = resolve_entrypoint_file(main_path); if (!resolved) { struct stat st; if (stat(main_path, &st) == 0 && S_ISDIR(st.st_mode)) resolved = resolve_directory_index(main_path); }
free(main_path); }
yyjson_doc_free(doc); return resolved;}
char *resolve_js_file(const char *filename) { extern bool esm_is_url(const char *path); if (!filename) return NULL; if (esm_is_url(filename)) return strdup(filename);
struct stat st; if (stat(filename, &st) == 0) { if (S_ISDIR(st.st_mode)) { char *resolved = resolve_directory_main(filename); return resolved ? resolved : resolve_directory_index(filename); } return resolve_existing_entrypoint_file(filename, &st); }
return resolve_entrypoint_file_extensions(filename);}
char *resolve_typescript_source_fallback(const char *filename) { if (!filename) return NULL;
const char *mapped_ext = NULL; size_t trim_len = 0;
size_t len = strlen(filename); if (len > 3 && strcmp(filename + len - 3, ".js") == 0) { mapped_ext = ".ts"; trim_len = 3; } else if (len > 4 && strcmp(filename + len - 4, ".mjs") == 0) { mapped_ext = ".mts"; trim_len = 4; } else if (len > 4 && strcmp(filename + len - 4, ".cjs") == 0) { mapped_ext = ".cts"; trim_len = 4; } else return NULL;
size_t mapped_len = strlen(mapped_ext); char *mapped = try_oom(len - trim_len + mapped_len + 1); memcpy(mapped, filename, len - trim_len); memcpy(mapped + len - trim_len, mapped_ext, mapped_len + 1); return mapped;}
void *try_oom(size_t size) { void *p = malloc(size); if (!p) { crfprintf(stderr, msg.oom_fatal); exit(EXIT_FAILURE); } return p;}