MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add URL support to module resolution and loading

+673 -91
+4
examples/demo/remote.js
··· 1 + import { hello } from 'https://r.justjs.dev/hello@0.0.1'; 2 + 3 + console.log(`hello is ${typeof hello}`); 4 + void hello();
+11
include/esm/remote.h
··· 1 + #ifndef ESM_REMOTE_H 2 + #define ESM_REMOTE_H 3 + 4 + #include <stdbool.h> 5 + #include <stddef.h> 6 + 7 + bool esm_is_url(const char *spec); 8 + char *esm_fetch_url(const char *url, size_t *out_len, char **out_error); 9 + char *esm_resolve_url(const char *specifier, const char *base_url); 10 + 11 + #endif
+2 -1
meson.build
··· 22 22 'src/repl.c', 23 23 'src/runtime.c', 24 24 'src/snapshot.c', 25 + 'src/esm/remote.c', 25 26 ) + files(module_files) 26 27 27 28 tls_lib = get_option('tls_library') ··· 162 163 timestamp = timestamp_opt 163 164 endif 164 165 165 - version_conf.set('ANT_VERSION', '0.3.5.' + timestamp + '-g' + git_hash) 166 + version_conf.set('ANT_VERSION', '0.3.6.' + timestamp + '-g' + git_hash) 166 167 version_conf.set('ANT_BUILD_TIMESTAMP', timestamp) 167 168 168 169 cmd_cc = meson.get_compiler('c')
+236 -62
src/ant.c
··· 11 11 #include "runtime.h" 12 12 #include "internal.h" 13 13 14 + #include <uv.h> 14 15 #include <oxc.h> 15 16 #include <assert.h> 16 17 #include <math.h> ··· 20 21 #include <stdlib.h> 21 22 #include <string.h> 22 23 #include <time.h> 24 + #include <utarray.h> 25 + #include <uthash.h> 26 + #include <float.h> 27 + #include <tlsuv/tlsuv.h> 28 + #include <tlsuv/http.h> 29 + 23 30 #ifdef _WIN32 24 31 #include <sys/stat.h> 25 32 #else 26 33 #include <sys/time.h> 27 34 #include <sys/stat.h> 28 35 #endif 29 - #include <utarray.h> 30 - #include <uthash.h> 31 - #include <float.h> 32 - #include <uv.h> 33 36 34 37 #define MCO_USE_VMEM_ALLOCATOR 35 38 #define MCO_ZERO_MEMORY ··· 45 48 #include "modules/child_process.h" 46 49 #include "modules/readline.h" 47 50 #include "modules/json.h" 51 + #include "esm/remote.h" 48 52 49 53 #define CORO_MALLOC(size) calloc(1, size) 50 54 #define CORO_FREE(ptr) free(ptr) ··· 180 184 bool is_json; 181 185 bool is_text; 182 186 bool is_image; 187 + bool is_url; 188 + char *url_content; 189 + size_t url_content_len; 183 190 struct esm_module *next; 184 191 UT_hash_handle hh; 185 192 } esm_module_t; ··· 18994 19001 } 18995 19002 18996 19003 static esm_module_t *esm_create_module(const char *path, const char *resolved_path) { 18997 - char *canonical_path = esm_canonicalize_path(resolved_path); 19004 + bool is_url = esm_is_url(resolved_path); 19005 + char *canonical_path = is_url ? strdup(resolved_path) : esm_canonicalize_path(resolved_path); 18998 19006 if (!canonical_path) return NULL; 18999 19007 19000 19008 esm_module_t *existing_mod = NULL; ··· 19019 19027 mod->is_json = esm_is_json(resolved_path); 19020 19028 mod->is_text = esm_is_text(resolved_path); 19021 19029 mod->is_image = esm_is_image(resolved_path); 19030 + mod->is_url = is_url; 19031 + mod->url_content = NULL; 19032 + mod->url_content_len = 0; 19022 19033 mod->next = NULL; 19023 19034 19024 19035 HASH_ADD_STR(global_module_cache.modules, resolved_path, mod); ··· 19033 19044 HASH_DEL(global_module_cache.modules, current); 19034 19045 if (current->path) free(current->path); 19035 19046 if (current->resolved_path) free(current->resolved_path); 19047 + if (current->url_content) free(current->url_content); 19036 19048 free(current); 19037 19049 } 19038 19050 global_module_cache.count = 0; ··· 19169 19181 return img_val; 19170 19182 } 19171 19183 19172 - FILE *fp = fopen(mod->resolved_path, "rb"); 19173 - if (!fp) { 19174 - mod->is_loading = false; 19175 - return js_mkerr(js, "Cannot open module: %s", mod->resolved_path); 19176 - } 19184 + char *content = NULL; 19185 + size_t size = 0; 19177 19186 19178 - fseek(fp, 0, SEEK_END); 19179 - long size = ftell(fp); 19180 - fseek(fp, 0, SEEK_SET); 19181 - 19182 - char *content = (char *)malloc(size + 1); 19183 - if (!content) { 19187 + if (mod->is_url) { 19188 + if (mod->url_content) { 19189 + content = strdup(mod->url_content); 19190 + size = mod->url_content_len; 19191 + } else { 19192 + char *error = NULL; 19193 + content = esm_fetch_url(mod->resolved_path, &size, &error); 19194 + if (!content) { 19195 + mod->is_loading = false; 19196 + jsval_t err = js_mkerr(js, "Cannot fetch module %s: %s", mod->resolved_path, error ? error : "unknown error"); 19197 + if (error) free(error); 19198 + return err; 19199 + } 19200 + mod->url_content = strdup(content); 19201 + mod->url_content_len = size; 19202 + } 19203 + } else { 19204 + FILE *fp = fopen(mod->resolved_path, "rb"); 19205 + if (!fp) { 19206 + mod->is_loading = false; 19207 + return js_mkerr(js, "Cannot open module: %s", mod->resolved_path); 19208 + } 19209 + 19210 + fseek(fp, 0, SEEK_END); 19211 + long fsize = ftell(fp); 19212 + fseek(fp, 0, SEEK_SET); 19213 + size = (size_t)fsize; 19214 + 19215 + content = (char *)malloc(size + 1); 19216 + if (!content) { 19217 + fclose(fp); 19218 + mod->is_loading = false; 19219 + return js_mkerr(js, "OOM loading module"); 19220 + } 19221 + 19222 + fread(content, 1, size, fp); 19184 19223 fclose(fp); 19185 - mod->is_loading = false; 19186 - return js_mkerr(js, "OOM loading module"); 19187 19224 } 19188 - 19189 - fread(content, 1, size, fp); 19190 - fclose(fp); 19191 19225 content[size] = '\0'; 19192 19226 19193 19227 char *js_code = content; ··· 19243 19277 19244 19278 jsoff_t spec_len; 19245 19279 jsoff_t spec_off = vstr(js, args[0], &spec_len); 19246 - const char *specifier = (char *)&js->mem[spec_off]; 19280 + char *specifier = strndup((char *)&js->mem[spec_off], spec_len); 19247 19281 19248 19282 ant_library_t *lib = find_library(specifier, spec_len); 19249 19283 if (lib) { 19284 + free(specifier); 19250 19285 jsval_t lib_obj = lib->init_fn(js); 19251 19286 if (is_err(lib_obj)) return builtin_Promise_reject(js, &lib_obj, 1); 19252 19287 jsval_t promise_args[] = { lib_obj }; ··· 19254 19289 } 19255 19290 19256 19291 const char *base_path = js->filename ? js->filename : "."; 19257 - char *resolved_path = esm_resolve_path(specifier, base_path); 19292 + char *resolved_path = NULL; 19293 + 19294 + if (esm_is_url(specifier) || esm_is_url(base_path)) { 19295 + resolved_path = esm_resolve_url(specifier, base_path); 19296 + } else resolved_path = esm_resolve_path(specifier, base_path); 19297 + 19258 19298 if (!resolved_path) { 19259 - return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 19299 + jsval_t err = js_mkerr(js, "Cannot resolve module: %s", specifier); 19300 + free(specifier); 19301 + return err; 19260 19302 } 19261 19303 19262 19304 esm_module_t *mod = esm_find_module(resolved_path); ··· 19264 19306 mod = esm_create_module(specifier, resolved_path); 19265 19307 if (!mod) { 19266 19308 free(resolved_path); 19267 - return js_mkerr(js, "Cannot create module"); 19309 + free(specifier); return js_mkerr(js, "Cannot create module"); 19268 19310 } 19269 19311 } 19270 19312 19271 19313 jsval_t ns = esm_load_module(js, mod); 19272 19314 free(resolved_path); 19315 + free(specifier); 19273 19316 19274 19317 if (is_err(ns)) return builtin_Promise_reject(js, &ns, 1); 19275 19318 ··· 19284 19327 19285 19328 jsoff_t spec_len; 19286 19329 jsoff_t spec_off = vstr(js, args[0], &spec_len); 19287 - const char *specifier = (char *)&js->mem[spec_off]; 19330 + char *specifier = strndup((char *)&js->mem[spec_off], spec_len); 19288 19331 19289 19332 const char *base_path = js->filename ? js->filename : "."; 19290 - char *resolved_path = esm_resolve_path(specifier, base_path); 19333 + char *resolved_path = NULL; 19334 + 19335 + if (esm_is_url(specifier) || esm_is_url(base_path)) { 19336 + resolved_path = esm_resolve_url(specifier, base_path); 19337 + } else resolved_path = esm_resolve_path(specifier, base_path); 19338 + 19291 19339 if (!resolved_path) { 19292 - return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 19340 + jsval_t err = js_mkerr(js, "Cannot resolve module: %s", specifier); 19341 + free(specifier); return err; 19342 + } 19343 + free(specifier); 19344 + 19345 + if (esm_is_url(resolved_path)) { 19346 + jsval_t result = js_mkstr(js, resolved_path, strlen(resolved_path)); 19347 + free(resolved_path); return result; 19293 19348 } 19294 19349 19295 19350 size_t url_len = strlen(resolved_path) + 8; ··· 19314 19369 jsval_t import_meta = mkobj(js, 0); 19315 19370 if (is_err(import_meta)) return; 19316 19371 19317 - size_t url_len = strlen(filename) + 8; 19318 - char *url = malloc(url_len); 19319 - if (url) { 19320 - snprintf(url, url_len, "file://%s", filename); 19321 - jsval_t url_val = js_mkstr(js, url, strlen(url)); 19372 + bool is_url = esm_is_url(filename); 19373 + 19374 + if (is_url) { 19375 + jsval_t url_val = js_mkstr(js, filename, strlen(filename)); 19322 19376 if (!is_err(url_val)) setprop(js, import_meta, js_mkstr(js, "url", 3), url_val); 19323 - free(url); 19377 + } else { 19378 + size_t url_len = strlen(filename) + 8; 19379 + char *url = malloc(url_len); 19380 + if (url) { 19381 + snprintf(url, url_len, "file://%s", filename); 19382 + jsval_t url_val = js_mkstr(js, url, strlen(url)); 19383 + if (!is_err(url_val)) setprop(js, import_meta, js_mkstr(js, "url", 3), url_val); free(url); 19384 + } 19324 19385 } 19325 19386 19326 19387 jsval_t filename_val = js_mkstr(js, filename, strlen(filename)); 19327 19388 if (!is_err(filename_val)) setprop(js, import_meta, js_mkstr(js, "filename", 8), filename_val); 19328 19389 19329 - char *filename_copy = strdup(filename); 19330 - if (filename_copy) { 19331 - char *dir = dirname(filename_copy); 19332 - if (dir) { 19333 - jsval_t dirname_val = js_mkstr(js, dir, strlen(dir)); 19334 - if (!is_err(dirname_val)) setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); 19390 + if (is_url) { 19391 + char *filename_copy = strdup(filename); 19392 + if (filename_copy) { 19393 + char *last_slash = strrchr(filename_copy, '/'); 19394 + char *scheme_end = strstr(filename_copy, "://"); 19395 + if (last_slash && scheme_end && last_slash > scheme_end + 2) { 19396 + *last_slash = '\0'; 19397 + jsval_t dirname_val = js_mkstr(js, filename_copy, strlen(filename_copy)); 19398 + if (!is_err(dirname_val)) setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); 19399 + } 19400 + free(filename_copy); 19401 + } 19402 + } else { 19403 + char *filename_copy = strdup(filename); 19404 + if (filename_copy) { 19405 + char *dir = dirname(filename_copy); 19406 + if (dir) { 19407 + jsval_t dirname_val = js_mkstr(js, dir, strlen(dir)); 19408 + if (!is_err(dirname_val)) setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); 19409 + } 19410 + free(filename_copy); 19335 19411 } 19336 - free(filename_copy); 19337 19412 } 19338 19413 19339 19414 setprop(js, import_meta, js_mkstr(js, "main", 4), js_mktrue()); ··· 19384 19459 19385 19460 jsoff_t spec_len; 19386 19461 jsoff_t spec_off = vstr(js, spec, &spec_len); 19387 - const char *specifier = (char *)&js->mem[spec_off]; 19388 - 19462 + char *spec_str = strndup((char *)&js->mem[spec_off], spec_len); 19389 19463 jsval_t ns = js_mkundef(); 19390 - ant_library_t *lib = find_library(specifier, spec_len); 19464 + ant_library_t *lib = find_library(spec_str, spec_len); 19391 19465 if (lib) { 19392 19466 ns = lib->init_fn(js); 19393 19467 } else { 19394 19468 const char *base_path = js->filename ? js->filename : "."; 19395 - char *resolved_path = esm_resolve_path(specifier, base_path); 19469 + char *resolved_path = NULL; 19470 + 19471 + if (esm_is_url(spec_str) || esm_is_url(base_path)) { 19472 + resolved_path = esm_resolve_url(spec_str, base_path); 19473 + } else resolved_path = esm_resolve_path(spec_str, base_path); 19474 + 19396 19475 if (!resolved_path) { 19397 - return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 19476 + free(spec_str); 19477 + return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, spec_str); 19398 19478 } 19399 19479 19400 19480 esm_module_t *mod = esm_find_module(resolved_path); 19401 19481 if (!mod) { 19402 - mod = esm_create_module(specifier, resolved_path); 19482 + mod = esm_create_module(spec_str, resolved_path); 19403 19483 if (!mod) { 19404 19484 free(resolved_path); 19405 - return js_mkerr(js, "Cannot create module"); 19485 + free(spec_str); return js_mkerr(js, "Cannot create module"); 19406 19486 } 19407 19487 } 19408 19488 ··· 19410 19490 JS_SAVE_STATE(js, saved); 19411 19491 19412 19492 ns = esm_load_module(js, mod); 19413 - 19414 19493 JS_RESTORE_STATE(js, saved); 19415 19494 19416 19495 free(resolved_path); 19417 19496 } 19497 + free(spec_str); 19418 19498 19419 19499 js->consumed = 1; next(js); js->consumed = 0; 19420 19500 if (is_err(ns)) return ns; ··· 19493 19573 19494 19574 jsoff_t spec_len; 19495 19575 jsoff_t spec_off = vstr(js, spec, &spec_len); 19496 - const char *specifier = (char *)&js->mem[spec_off]; 19576 + char *spec_str = strndup((char *)&js->mem[spec_off], spec_len); 19497 19577 19498 19578 jsval_t ns = js_mkundef(); 19499 - ant_library_t *lib = find_library(specifier, spec_len); 19579 + ant_library_t *lib = find_library(spec_str, spec_len); 19500 19580 if (lib) { 19501 19581 ns = lib->init_fn(js); 19502 19582 } else { 19503 19583 const char *base_path = js->filename ? js->filename : "."; 19504 - char *resolved_path = esm_resolve_path(specifier, base_path); 19584 + char *resolved_path = NULL; 19585 + 19586 + if (esm_is_url(spec_str) || esm_is_url(base_path)) { 19587 + resolved_path = esm_resolve_url(spec_str, base_path); 19588 + } else resolved_path = esm_resolve_path(spec_str, base_path); 19589 + 19505 19590 if (!resolved_path) { 19506 - return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 19591 + free(spec_str); 19592 + return js_mkerr(js, "Cannot resolve module: %s", spec_str); 19507 19593 } 19508 19594 19509 19595 esm_module_t *mod = esm_find_module(resolved_path); 19510 19596 if (!mod) { 19511 - mod = esm_create_module(specifier, resolved_path); 19597 + mod = esm_create_module(spec_str, resolved_path); 19512 19598 if (!mod) { 19513 19599 free(resolved_path); 19514 - return js_mkerr(js, "Cannot create module"); 19600 + free(spec_str); return js_mkerr(js, "Cannot create module"); 19515 19601 } 19516 19602 } 19517 19603 ··· 19519 19605 JS_SAVE_STATE(js, saved); 19520 19606 19521 19607 ns = esm_load_module(js, mod); 19522 - 19523 19608 JS_RESTORE_STATE(js, saved); 19524 19609 19525 19610 free(resolved_path); 19526 19611 } 19612 + free(spec_str); 19527 19613 19528 19614 js->consumed = 1; next(js); js->consumed = 0; 19529 19615 if (is_err(ns)) return ns; ··· 19599 19685 jsoff_t spec_len; 19600 19686 19601 19687 jsoff_t spec_off = vstr(js, spec, &spec_len); 19602 - const char *specifier = (char *)&js->mem[spec_off]; 19688 + char *spec_str = strndup((char *)&js->mem[spec_off], spec_len); 19603 19689 19604 19690 jsval_t ns = js_mkundef(); 19605 - ant_library_t *lib = find_library(specifier, spec_len); 19691 + ant_library_t *lib = find_library(spec_str, spec_len); 19606 19692 if (lib) { 19607 19693 ns = lib->init_fn(js); 19608 19694 } else { 19609 19695 const char *base_path = js->filename ? js->filename : "."; 19610 - char *resolved_path = esm_resolve_path(specifier, base_path); 19611 - if (!resolved_path) return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 19696 + char *resolved_path = NULL; 19697 + 19698 + if (esm_is_url(spec_str) || esm_is_url(base_path)) { 19699 + resolved_path = esm_resolve_url(spec_str, base_path); 19700 + } else resolved_path = esm_resolve_path(spec_str, base_path); 19701 + 19702 + if (!resolved_path) { 19703 + free(spec_str); 19704 + return js_mkerr(js, "Cannot resolve module: %s", spec_str); 19705 + } 19612 19706 19613 19707 esm_module_t *mod = esm_find_module(resolved_path); 19614 19708 if (!mod) { 19615 - mod = esm_create_module(specifier, resolved_path); 19709 + mod = esm_create_module(spec_str, resolved_path); 19616 19710 if (!mod) { 19617 19711 free(resolved_path); 19618 - return js_mkerr(js, "Cannot create module"); 19712 + free(spec_str); return js_mkerr(js, "Cannot create module"); 19619 19713 } 19620 19714 } 19621 19715 ··· 19633 19727 19634 19728 free(resolved_path); 19635 19729 } 19730 + free(spec_str); 19636 19731 19637 19732 js->consumed = 1; 19638 19733 next(js); ··· 19728 19823 } 19729 19824 19730 19825 return cls; 19826 + } 19827 + 19828 + if (next(js) == TOK_MUL) { 19829 + js->consumed = 1; 19830 + 19831 + const char *alias_name = NULL; 19832 + size_t alias_len = 0; 19833 + 19834 + if (next(js) == TOK_AS) { 19835 + js->consumed = 1; 19836 + EXPECT(TOK_IDENTIFIER, ); 19837 + alias_name = &js->code[js->toff]; 19838 + alias_len = js->tlen; 19839 + js->consumed = 1; 19840 + } 19841 + 19842 + EXPECT(TOK_FROM, ); 19843 + EXPECT(TOK_STRING, ); 19844 + 19845 + jsval_t spec = js_str_literal(js); 19846 + jsoff_t spec_len; 19847 + jsoff_t spec_off = vstr(js, spec, &spec_len); 19848 + char *spec_str = strndup((char *)&js->mem[spec_off], spec_len); 19849 + 19850 + jsval_t ns = js_mkundef(); 19851 + ant_library_t *lib = find_library(spec_str, spec_len); 19852 + if (lib) { 19853 + ns = lib->init_fn(js); 19854 + } else { 19855 + const char *base_path = js->filename ? js->filename : "."; 19856 + char *resolved_path = NULL; 19857 + 19858 + if (esm_is_url(spec_str) || esm_is_url(base_path)) { 19859 + resolved_path = esm_resolve_url(spec_str, base_path); 19860 + } else resolved_path = esm_resolve_path(spec_str, base_path); 19861 + 19862 + if (!resolved_path) { 19863 + free(spec_str); 19864 + return js_mkerr(js, "Cannot resolve module: %s", spec_str); 19865 + } 19866 + 19867 + esm_module_t *mod = esm_find_module(resolved_path); 19868 + if (!mod) { 19869 + mod = esm_create_module(spec_str, resolved_path); 19870 + if (!mod) { 19871 + free(resolved_path); 19872 + free(spec_str); 19873 + return js_mkerr(js, "Cannot create module"); 19874 + } 19875 + } 19876 + 19877 + js_parse_state_t saved; 19878 + JS_SAVE_STATE(js, saved); 19879 + 19880 + ns = esm_load_module(js, mod); 19881 + JS_RESTORE_STATE(js, saved); 19882 + 19883 + free(resolved_path); 19884 + } 19885 + free(spec_str); 19886 + 19887 + js->consumed = 1; next(js); js->consumed = 0; 19888 + if (is_err(ns)) return ns; 19889 + 19890 + if (alias_name) { 19891 + setprop(js, js->module_ns, js_mkstr(js, alias_name, alias_len), ns); 19892 + } else if (vtype(ns) == T_OBJ) { 19893 + js_prop_iter_t iter = js_prop_iter_begin(js, ns); 19894 + const char *key; 19895 + size_t key_len; 19896 + jsval_t value; 19897 + 19898 + while (js_prop_iter_next(&iter, &key, &key_len, &value)) { 19899 + setprop(js, js->module_ns, js_mkstr(js, key, key_len), resolveprop(js, value)); 19900 + } 19901 + js_prop_iter_end(&iter); 19902 + } 19903 + 19904 + return js_mkundef(); 19731 19905 } 19732 19906 19733 19907 if (next(js) == TOK_LBRACE) {
+372
src/esm/remote.c
··· 1 + #include "utils.h" 2 + #include "esm/remote.h" 3 + 4 + #include <stdio.h> 5 + #include <stdlib.h> 6 + #include <string.h> 7 + #include <stdint.h> 8 + #include <uv.h> 9 + #include <tlsuv/tlsuv.h> 10 + #include <tlsuv/http.h> 11 + 12 + #ifdef _WIN32 13 + #include <direct.h> 14 + #include <windows.h> 15 + #define mkdir_p(path) _mkdir(path) 16 + #define PATH_SEP '\\' 17 + #else 18 + #include <sys/stat.h> 19 + #include <sys/types.h> 20 + #define mkdir_p(path) mkdir(path, 0755) 21 + #define PATH_SEP '/' 22 + #endif 23 + 24 + typedef struct { 25 + char *data; 26 + size_t size; 27 + size_t capacity; 28 + int status_code; 29 + int completed; 30 + int failed; 31 + char *error_msg; 32 + tlsuv_http_t http_client; 33 + tlsuv_http_req_t *http_req; 34 + } esm_url_fetch_t; 35 + 36 + bool esm_is_url(const char *spec) { 37 + return (strncmp(spec, "http://", 7) == 0 || strncmp(spec, "https://", 8) == 0); 38 + } 39 + 40 + static int is_path_sep(char c) { 41 + return c == '/' || c == '\\'; 42 + } 43 + 44 + static void esm_url_fetch_close_cb(tlsuv_http_t *client) { 45 + esm_url_fetch_t *ctx = (esm_url_fetch_t *)client->data; 46 + ctx->completed = 1; 47 + } 48 + 49 + static const char *esm_get_home_dir(void) { 50 + #ifdef _WIN32 51 + const char *home = getenv("USERPROFILE"); 52 + if (home) return home; 53 + 54 + const char *drive = getenv("HOMEDRIVE"); 55 + const char *path = getenv("HOMEPATH"); 56 + if (drive && path) { 57 + static char win_home[MAX_PATH]; 58 + snprintf(win_home, sizeof(win_home), "%s%s", drive, path); 59 + return win_home; 60 + } 61 + #else 62 + const char *home = getenv("HOME"); 63 + if (home) return home; 64 + #endif 65 + return NULL; 66 + } 67 + 68 + static void esm_url_fetch_body_cb(tlsuv_http_req_t *http_req, char *body, ssize_t len) { 69 + esm_url_fetch_t *ctx = (esm_url_fetch_t *)http_req->data; 70 + 71 + if (len == UV_EOF) { 72 + tlsuv_http_close(&ctx->http_client, esm_url_fetch_close_cb); 73 + return; 74 + } 75 + 76 + if (len < 0) { 77 + ctx->failed = 1; 78 + ctx->error_msg = strdup(uv_strerror((int)len)); 79 + tlsuv_http_close(&ctx->http_client, esm_url_fetch_close_cb); 80 + return; 81 + } 82 + 83 + if (ctx->size + (size_t)len > ctx->capacity) { 84 + size_t new_cap = ctx->capacity * 2; 85 + while (new_cap < ctx->size + (size_t)len) new_cap *= 2; 86 + char *new_data = realloc(ctx->data, new_cap); 87 + if (!new_data) { 88 + ctx->failed = 1; 89 + ctx->error_msg = strdup("Out of memory"); 90 + tlsuv_http_close(&ctx->http_client, esm_url_fetch_close_cb); 91 + return; 92 + } 93 + ctx->data = new_data; 94 + ctx->capacity = new_cap; 95 + } 96 + 97 + memcpy(ctx->data + ctx->size, body, (size_t)len); 98 + ctx->size += (size_t)len; 99 + } 100 + 101 + static void esm_url_fetch_resp_cb(tlsuv_http_resp_t *resp, void *data) { 102 + (void)data; 103 + esm_url_fetch_t *ctx = (esm_url_fetch_t *)resp->req->data; 104 + 105 + if (resp->code < 0) { 106 + ctx->failed = 1; 107 + char err_buf[256]; 108 + snprintf(err_buf, sizeof(err_buf), "%s (code: %d)", uv_strerror(resp->code), resp->code); 109 + ctx->error_msg = strdup(err_buf); 110 + tlsuv_http_close(&ctx->http_client, esm_url_fetch_close_cb); 111 + return; 112 + } 113 + 114 + ctx->status_code = resp->code; 115 + resp->body_cb = esm_url_fetch_body_cb; 116 + } 117 + 118 + static char *esm_get_cache_path(const char *url) { 119 + const char *home = esm_get_home_dir(); 120 + if (!home) home = "."; 121 + 122 + uint64_t hash = hash_key(url, strlen(url)); 123 + 124 + size_t len = strlen(home) + 48; 125 + char *cache_path = malloc(len); 126 + if (!cache_path) return NULL; 127 + 128 + #ifdef _WIN32 129 + snprintf(cache_path, len, "%s\\.ant\\esm\\%016llx", home, (unsigned long long)hash); 130 + #else 131 + snprintf(cache_path, len, "%s/.ant/esm/%016llx", home, (unsigned long long)hash); 132 + #endif 133 + return cache_path; 134 + } 135 + 136 + static char *esm_read_cache(const char *cache_path, const char *url, size_t *out_len) { 137 + FILE *fp = fopen(cache_path, "rb"); 138 + if (!fp) return NULL; 139 + 140 + fseek(fp, 0, SEEK_END); 141 + long size = ftell(fp); 142 + fseek(fp, 0, SEEK_SET); 143 + 144 + char *content = malloc(size + 1); 145 + if (!content) { 146 + fclose(fp); 147 + return NULL; 148 + } 149 + 150 + fread(content, 1, size, fp); 151 + fclose(fp); 152 + content[size] = '\0'; 153 + 154 + char *newline = strchr(content, '\n'); 155 + if (!newline) { 156 + free(content); 157 + return NULL; 158 + } 159 + 160 + size_t url_len = newline - content; 161 + if (strncmp(content, url, url_len) != 0 || strlen(url) != url_len) { 162 + free(content); 163 + return NULL; 164 + } 165 + 166 + char *body = newline + 1; 167 + size_t body_len = size - (body - content); 168 + 169 + char *result = malloc(body_len + 1); 170 + if (!result) { 171 + free(content); 172 + return NULL; 173 + } 174 + 175 + memcpy(result, body, body_len); 176 + result[body_len] = '\0'; 177 + free(content); 178 + 179 + if (out_len) *out_len = body_len; 180 + return result; 181 + } 182 + 183 + static void esm_mkdir_recursive(char *path) { 184 + for (char *p = path + 1; *p; p++) { 185 + #ifdef _WIN32 186 + if (p == path + 2 && path[1] == ':') continue; 187 + #endif 188 + if (is_path_sep(*p)) { 189 + *p = '\0'; 190 + mkdir_p(path); 191 + *p = PATH_SEP; 192 + } 193 + } 194 + mkdir_p(path); 195 + } 196 + 197 + static void esm_write_cache(const char *cache_path, const char *url, const char *content, size_t len) { 198 + char *dir = strdup(cache_path); 199 + if (!dir) return; 200 + 201 + for (char *p = dir + strlen(dir) - 1; p > dir; p--) { 202 + if (is_path_sep(*p)) { *p = '\0'; break; } 203 + } 204 + 205 + esm_mkdir_recursive(dir); 206 + free(dir); 207 + 208 + FILE *fp = fopen(cache_path, "wb"); 209 + if (!fp) return; 210 + 211 + fprintf(fp, "%s\n", url); 212 + fwrite(content, 1, len, fp); 213 + fclose(fp); 214 + } 215 + 216 + char *esm_fetch_url(const char *url, size_t *out_len, char **out_error) { 217 + char *cache_path = esm_get_cache_path(url); 218 + if (cache_path) { 219 + char *cached = esm_read_cache(cache_path, url, out_len); 220 + if (cached) { free(cache_path); return cached; } 221 + } 222 + 223 + uv_loop_t *loop = uv_default_loop(); 224 + esm_url_fetch_t ctx = {0}; 225 + 226 + ctx.capacity = 16384; 227 + ctx.data = malloc(ctx.capacity); 228 + if (!ctx.data) { 229 + if (out_error) *out_error = strdup("Out of memory"); 230 + return NULL; 231 + } 232 + 233 + const char *scheme_end = strstr(url, "://"); 234 + if (!scheme_end) { 235 + free(ctx.data); 236 + if (out_error) *out_error = strdup("Invalid URL: no scheme"); 237 + return NULL; 238 + } 239 + 240 + const char *host_start = scheme_end + 3; 241 + const char *path_start = strchr(host_start, '/'); 242 + const char *at_in_host = NULL; 243 + 244 + for (const char *p = host_start; p < (path_start ? path_start : host_start + strlen(host_start)); p++) { 245 + if (*p == '@') at_in_host = p; 246 + } 247 + if (at_in_host) host_start = at_in_host + 1; 248 + 249 + size_t scheme_len = scheme_end - url; 250 + size_t host_len = path_start ? (size_t)(path_start - host_start) : strlen(host_start); 251 + const char *path = path_start ? path_start : "/"; 252 + 253 + char *host_url = calloc(1, scheme_len + 3 + host_len + 1); 254 + snprintf(host_url, scheme_len + 3 + host_len + 1, "%.*s://%.*s", (int)scheme_len, url, (int)host_len, host_start); 255 + int rc = tlsuv_http_init(loop, &ctx.http_client, host_url); 256 + free(host_url); 257 + 258 + if (rc != 0) { 259 + free(ctx.data); 260 + if (out_error) *out_error = strdup("Failed to initialize HTTP client"); 261 + return NULL; 262 + } 263 + 264 + ctx.http_client.data = &ctx; 265 + ctx.http_req = tlsuv_http_req(&ctx.http_client, "GET", path, esm_url_fetch_resp_cb, &ctx); 266 + 267 + if (!ctx.http_req) { 268 + free(ctx.data); 269 + tlsuv_http_close(&ctx.http_client, NULL); 270 + if (out_error) *out_error = strdup("Failed to create HTTP request"); 271 + return NULL; 272 + } 273 + 274 + ctx.http_req->data = &ctx; 275 + while (!ctx.completed) uv_run(loop, UV_RUN_ONCE); 276 + 277 + if (ctx.failed || ctx.status_code < 200 || ctx.status_code >= 400) { 278 + if (out_error) { 279 + if (ctx.error_msg) { 280 + *out_error = ctx.error_msg; 281 + ctx.error_msg = NULL; 282 + } else { 283 + char err_buf[64]; 284 + snprintf(err_buf, sizeof(err_buf), "HTTP error: %d", ctx.status_code); 285 + *out_error = strdup(err_buf); 286 + } 287 + } 288 + free(ctx.data); 289 + if (ctx.error_msg) free(ctx.error_msg); 290 + if (cache_path) free(cache_path); 291 + return NULL; 292 + } 293 + 294 + ctx.data[ctx.size] = '\0'; 295 + if (out_len) *out_len = ctx.size; 296 + if (ctx.error_msg) free(ctx.error_msg); 297 + 298 + if (cache_path) { 299 + esm_write_cache(cache_path, url, ctx.data, ctx.size); 300 + free(cache_path); 301 + } 302 + 303 + return ctx.data; 304 + } 305 + 306 + char *esm_resolve_url(const char *specifier, const char *base_url) { 307 + if (esm_is_url(specifier)) { 308 + return strdup(specifier); 309 + } 310 + 311 + if (specifier[0] == '/') { 312 + const char *scheme_end = strstr(base_url, "://"); 313 + if (!scheme_end) return NULL; 314 + 315 + const char *host_start = scheme_end + 3; 316 + const char *path_start = strchr(host_start, '/'); 317 + 318 + const char *at_in_host = NULL; 319 + for (const char *p = host_start; p < (path_start ? path_start : host_start + strlen(host_start)); p++) { 320 + if (*p == '@') at_in_host = p; 321 + } 322 + if (at_in_host) host_start = at_in_host + 1; 323 + 324 + size_t scheme_len = scheme_end - base_url; 325 + size_t host_len = path_start ? (size_t)(path_start - host_start) : strlen(host_start); 326 + 327 + size_t len = scheme_len + 3 + host_len + strlen(specifier) + 1; 328 + char *result = malloc(len); 329 + if (!result) return NULL; 330 + 331 + snprintf( 332 + result, len, "%.*s://%.*s%s", 333 + (int)scheme_len, base_url, 334 + (int)host_len, host_start, 335 + specifier 336 + ); 337 + 338 + return result; 339 + } 340 + 341 + if (specifier[0] == '.' && (specifier[1] == '/' || (specifier[1] == '.' && specifier[2] == '/'))) { 342 + char *base_copy = strdup(base_url); 343 + if (!base_copy) return NULL; 344 + 345 + char *last_slash = strrchr(base_copy, '/'); 346 + char *scheme_end = strstr(base_copy, "://"); 347 + if (scheme_end && last_slash > scheme_end + 2) { 348 + *last_slash = '\0'; 349 + } 350 + 351 + const char *spec = specifier; 352 + while (strncmp(spec, "../", 3) == 0) { 353 + spec += 3; 354 + char *prev_slash = strrchr(base_copy, '/'); 355 + if (prev_slash && prev_slash > scheme_end + 2) { 356 + *prev_slash = '\0'; 357 + } 358 + } 359 + 360 + if (strncmp(spec, "./", 2) == 0) spec += 2; 361 + size_t len = strlen(base_copy) + strlen(spec) + 2; 362 + char *result = malloc(len); 363 + if (!result) { free(base_copy); return NULL; } 364 + 365 + snprintf(result, len, "%s/%s", base_copy, spec); 366 + free(base_copy); 367 + 368 + return result; 369 + } 370 + 371 + return strdup(specifier); 372 + }
+48 -28
src/main.c
··· 12 12 #include "utils.h" 13 13 #include "runtime.h" 14 14 #include "snapshot.h" 15 + #include "esm/remote.h" 15 16 16 17 #include "modules/builtin.h" 17 18 #include "modules/buffer.h" ··· 72 73 js_run_event_loop(js); 73 74 } 74 75 75 - static int execute_module(struct js *js, const char *filename) { 76 - char *filename_copy = strdup(filename); 77 - char *dir = dirname(filename_copy); 78 - 79 - js_set(js, js_glob(js), "__dirname", js_mkstr(js, dir, strlen(dir))); 80 - js_set(js, js_glob(js), "__filename", js_mkstr(js, filename, strlen(filename))); 76 + static char *read_file(const char *filename, size_t *len) { 77 + FILE *fp = fopen(filename, "rb"); 78 + if (!fp) return NULL; 81 79 82 - free(filename_copy); 83 - 84 - FILE *fp = fopen(filename, "rb"); 85 - if (fp == NULL) { 86 - fprintf(stderr, "Error: Could not open file '%s'\n", filename); 87 - return EXIT_FAILURE; 88 - } 89 - 90 80 fseek(fp, 0, SEEK_END); 91 - long file_size = ftell(fp); 81 + long size = ftell(fp); 92 82 fseek(fp, 0, SEEK_SET); 93 - 94 - char *buffer = malloc(file_size + 1); 95 - if (buffer == NULL) { 96 - fprintf(stderr, "Error: Memory allocation failed\n"); 83 + 84 + char *buffer = malloc(size + 1); 85 + if (!buffer) { 97 86 fclose(fp); 98 - return EXIT_FAILURE; 87 + return NULL; 99 88 } 100 - 101 - size_t len = fread(buffer, 1, file_size, fp); 89 + 90 + *len = fread(buffer, 1, size, fp); 102 91 fclose(fp); 103 - buffer[len] = '\0'; 92 + buffer[*len] = '\0'; 104 93 105 - char abs_path[4096]; 106 - const char *use_path = NULL; 107 - if (realpath(filename, abs_path) != NULL) { 108 - use_path = abs_path; 109 - } else use_path = filename; 94 + return buffer; 95 + } 96 + 97 + static int execute_module(struct js *js, const char *filename) { 98 + char *buffer = NULL; 99 + size_t len = 0; 100 + const char *use_path = filename; 101 + 102 + if (esm_is_url(filename)) { 103 + char *error = NULL; 104 + buffer = esm_fetch_url(filename, &len, &error); 105 + 106 + if (!buffer) { 107 + fprintf(stderr, "Error: Could not fetch '%s': %s\n", filename, error ? error : "unknown error"); 108 + free(error); 109 + return EXIT_FAILURE; 110 + } 111 + 112 + js_set(js, js_glob(js), "__dirname", js_mkundef()); 113 + } else { 114 + buffer = read_file(filename, &len); 115 + if (!buffer) { 116 + fprintf(stderr, "Error: Could not open file '%s'\n", filename); 117 + return EXIT_FAILURE; 118 + } 119 + 120 + char *file_path = strdup(filename); 121 + char *dir = dirname(file_path); 122 + js_set(js, js_glob(js), "__dirname", js_mkstr(js, dir, strlen(dir))); 123 + free(file_path); 124 + 125 + char abs_path[4096]; 126 + if (realpath(filename, abs_path)) use_path = abs_path; 127 + } 110 128 111 129 char *js_code = buffer; 112 130 size_t js_len = len; ··· 122 140 } 123 141 124 142 js_set_filename(js, use_path); 143 + js_set(js, js_glob(js), "__filename", js_mkstr(js, filename, strlen(filename))); 144 + 125 145 js_setup_import_meta(js, use_path); 126 146 js_mkscope(js); 127 147