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.

at master 1528 lines 44 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include "esm/loader.h" 4#include "esm/commonjs.h" 5#include "esm/library.h" 6#include "esm/remote.h" 7#include "esm/builtin_bundle.h" 8 9#include "modules/json.h" 10#include "modules/napi.h" 11#include "modules/uri.h" 12 13#include "errors.h" 14#include "gc/modules.h" 15#include "internal.h" 16#include "reactor.h" 17#include "utils.h" 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22#include <sys/stat.h> 23#ifndef _WIN32 24#include <libgen.h> 25#include <unistd.h> 26#endif 27#include <uthash.h> 28#include <yyjson.h> 29 30typedef enum { 31 ESM_MODULE_KIND_CODE = 0, 32 ESM_MODULE_KIND_JSON, 33 ESM_MODULE_KIND_TEXT, 34 ESM_MODULE_KIND_IMAGE, 35 ESM_MODULE_KIND_NATIVE, 36 ESM_MODULE_KIND_URL, 37} esm_module_kind_t; 38 39typedef struct esm_module { 40 char *path; 41 char *cache_key; 42 char *resolved_path; 43 char *url_content; 44 45 size_t url_content_len; 46 const uint8_t *embedded_code; 47 size_t embedded_code_len; 48 49 ant_value_t namespace_obj; 50 ant_value_t default_export; 51 ant_value_t tla_promise; 52 UT_hash_handle hh; 53 esm_module_kind_t kind; 54 ant_module_format_t format; 55 56 bool is_loaded; 57 bool is_loading; 58 bool has_tla; 59} esm_module_t; 60 61typedef struct { 62 esm_module_t *modules; 63 int count; 64} esm_module_cache_t; 65 66typedef struct { 67 char *data; 68 size_t size; 69} esm_file_data_t; 70 71static esm_module_cache_t global_module_cache = {NULL, 0}; 72static int esm_dynamic_import_depth = 0; 73 74static char *esm_resolve_node_module(const char *specifier, const char *base_path); 75static char *esm_canonicalize_path(const char *path); 76 77static char *esm_file_url_to_path(ant_t *js, const char *specifier) { 78 if (!specifier || strncmp(specifier, "file:", 5) != 0) return NULL; 79 80 const char *p = specifier + 5; 81 if (strncmp(p, "///", 3) == 0) p += 2; 82 else if (strncmp(p, "//localhost/", 12) == 0) p += 11; 83 84 if (*p == '\0') return NULL; 85 86 ant_value_t encoded = js_mkstr(js, p, strlen(p)); 87 ant_value_t decoded = js_decodeURI(js, &encoded, 1); 88 89 size_t len = 0; 90 char *str = js_getstr(js, decoded, &len); 91 92 return str ? strndup(str, len) : NULL; 93} 94 95static char *esm_make_cache_key(const char *module_key) { 96 if (!module_key) return NULL; 97 if (esm_has_builtin_scheme(module_key)) return strdup(module_key); 98 if (esm_is_data_url(module_key)) return strdup(module_key); 99 if (esm_is_url(module_key)) return strdup(module_key); 100 return esm_canonicalize_path(module_key); 101} 102 103static char *esm_get_extension(const char *path) { 104 const char *dot = strrchr(path, '.'); 105 const char *slash = strrchr(path, '/'); 106 107 if (dot && (!slash || dot > slash)) { 108 return strdup(dot); 109 } 110 return strdup(".js"); 111} 112 113static bool esm_is_relative_specifier(const char *specifier) { 114 return 115 strcmp(specifier, ".") == 0 || 116 strcmp(specifier, "..") == 0 || 117 strncmp(specifier, "./", 2) == 0 || 118 strncmp(specifier, "../", 3) == 0; 119} 120 121static char *esm_try_resolve(const char *dir, const char *spec, const char *suffix) { 122 char path[PATH_MAX]; 123 snprintf(path, PATH_MAX, "%s/%s%s", dir, spec, suffix); 124 char *resolved = realpath(path, NULL); 125 if (resolved) { 126 struct stat st; 127 if (stat(resolved, &st) == 0 && S_ISREG(st.st_mode)) return resolved; 128 free(resolved); 129 } 130 return NULL; 131} 132 133static bool esm_has_extension(const char *spec) { 134 const char *slash = strrchr(spec, '/'); 135 const char *dot = strrchr(slash ? slash : spec, '.'); 136 137 if (!dot) return false; 138 for ( 139 const char *const *ext = module_resolve_extensions; 140 *ext; ext++ 141 ) if (strcmp(dot, *ext) == 0) return true; 142 143 return false; 144} 145 146static char *esm_try_resolve_with_exts(const char *dir, const char *spec, bool has_ext) { 147 const char *const *exts = module_resolve_extensions; 148 char *result = NULL; 149 150 if ((result = esm_try_resolve(dir, spec, ""))) return result; 151 if (has_ext) return NULL; 152 153 for (int i = 0; exts[i]; i++) { 154 if ((result = esm_try_resolve(dir, spec, exts[i]))) return result; 155 } 156 return NULL; 157} 158 159static char *esm_try_resolve_index_with_exts(const char *dir, const char *spec) { 160 char idx[PATH_MAX]; 161 snprintf(idx, sizeof(idx), "%s/index", spec); 162 return esm_try_resolve_with_exts(dir, idx, false); 163} 164 165static char *esm_try_resolve_relative_typescript_source_fallback( 166 const char *dir, 167 const char *spec, 168 const char *base_path 169) { 170 if (!is_typescript_file(base_path)) return NULL; 171 172 char *ts_spec = resolve_typescript_source_fallback(spec); 173 if (!ts_spec) return NULL; 174 175 char *resolved = esm_try_resolve(dir, ts_spec, ""); 176 free(ts_spec); 177 return resolved; 178} 179 180static ant_value_t esm_default_export_or_namespace(ant_t *js, ant_value_t ns) { 181 ant_value_t default_val = js_get_slot(ns, SLOT_DEFAULT); 182 return vtype(default_val) != T_UNDEF ? default_val : ns; 183} 184 185static ant_value_t esm_make_namespace_object(ant_t *js) { 186 ant_value_t ns = js_mkobj(js); 187 js_set_slot(ns, SLOT_BRAND, js_mknum(BRAND_MODULE_NAMESPACE)); 188 js_set_slot(ns, SLOT_MODULE_LOADING, js_true); 189 return ns; 190} 191 192static ant_value_t esm_complete_value_module(esm_module_t *mod, ant_value_t value) { 193 if (is_err(value)) { 194 mod->is_loading = false; 195 return value; 196 } 197 mod->namespace_obj = value; 198 mod->default_export = value; 199 mod->is_loaded = true; 200 mod->is_loading = false; 201 return value; 202} 203 204static ant_value_t esm_complete_namespace_module(ant_t *js, esm_module_t *mod, ant_value_t ns) { 205 mod->namespace_obj = ns; 206 mod->default_export = esm_default_export_or_namespace(js, ns); 207 mod->is_loaded = true; 208 mod->is_loading = false; 209 js_set_slot(ns, SLOT_MODULE_LOADING, js_mkundef()); 210 return ns; 211} 212 213static ant_value_t esm_prepare_eval_ctx( 214 ant_t *js, 215 const char *resolved_path, 216 ant_value_t ns, 217 ant_module_format_t format, 218 bool is_main, 219 const char *fallback_parent_path, 220 ant_module_t *out_ctx 221) { 222 ant_value_t module_ctx = js_create_module_context(js, resolved_path, is_main); 223 224 if (is_err(module_ctx)) return module_ctx; 225 if (is_object_type(ns)) js_set_slot_wb(js, ns, SLOT_MODULE_CTX, module_ctx); 226 227 *out_ctx = (ant_module_t){ 228 .module_ns = ns, 229 .module_ctx = module_ctx, 230 .prev_import_meta_prop = js_mkundef(), 231 .format = format, 232 .prev = NULL, 233 }; 234 235 return js_mkundef(); 236} 237 238static char *esm_try_resolve_from_extension_list( 239 const char *dir, 240 const char *spec, 241 const char *first_ext, 242 const char *skip_ext 243) { 244 char *result = NULL; 245 if (first_ext && first_ext[0]) { 246 if ((result = esm_try_resolve(dir, spec, first_ext))) return result; 247 } 248 249 const char *const *exts = module_resolve_extensions; 250 for (int i = 0; exts[i]; i++) { 251 if (skip_ext && strcmp(skip_ext, exts[i]) == 0) continue; 252 if ((result = esm_try_resolve(dir, spec, exts[i]))) return result; 253 } 254 return NULL; 255} 256 257static char *esm_resolve_absolute(const char *specifier) { 258 char *result = esm_try_resolve_with_exts("", specifier, esm_has_extension(specifier)); 259 if (result) return result; 260 return esm_try_resolve_index_with_exts("", specifier); 261} 262 263static char *esm_get_base_dir(const char *base_path) { 264 if (!base_path || !base_path[0]) { 265 char cwd[PATH_MAX]; 266 if (!getcwd(cwd, sizeof(cwd))) return NULL; 267 return strdup(cwd); 268 } 269 270 char candidate[PATH_MAX]; 271 if (base_path[0] == '/') { 272 snprintf(candidate, sizeof(candidate), "%s", base_path); 273 } else { 274 char cwd[PATH_MAX]; 275 if (!getcwd(cwd, sizeof(cwd))) return NULL; 276 snprintf(candidate, sizeof(candidate), "%s/%s", cwd, base_path); 277 } 278 279 char *resolved = realpath(candidate, NULL); 280 const char *resolved_or_candidate = resolved ? resolved : candidate; 281 282 struct stat st; 283 if (stat(resolved_or_candidate, &st) == 0 && S_ISDIR(st.st_mode)) { 284 char *out = realpath(resolved_or_candidate, NULL); 285 if (!out) out = strdup(resolved_or_candidate); 286 if (resolved) free(resolved); 287 return out; 288 } 289 290 char *tmp = strdup(resolved_or_candidate); 291 if (resolved) free(resolved); 292 if (!tmp) return NULL; 293 294 char *dir = dirname(tmp); 295 char *out = realpath(dir, NULL); 296 if (!out) out = strdup(dir); 297 298 free(tmp); 299 return out; 300} 301 302static bool esm_split_package_specifier( 303 const char *specifier, 304 char *package_name, 305 size_t package_name_size, 306 const char **subpath_out 307) { 308 if (!specifier || !specifier[0]) return false; 309 if (specifier[0] == '.' || specifier[0] == '/' || specifier[0] == '#') return false; 310 311 const char *slash = NULL; 312 if (specifier[0] == '@') { 313 const char *first = strchr(specifier, '/'); 314 if (!first || first == specifier + 1) return false; 315 slash = strchr(first + 1, '/'); 316 if (first[1] == '\0') return false; 317 if (!slash) { 318 if (strlen(specifier) >= package_name_size) return false; 319 strcpy(package_name, specifier); 320 *subpath_out = NULL; 321 return true; 322 } 323 } else slash = strchr(specifier, '/'); 324 325 size_t name_len = slash ? (size_t)(slash - specifier) : strlen(specifier); 326 if (name_len == 0 || name_len >= package_name_size) return false; 327 memcpy(package_name, specifier, name_len); 328 package_name[name_len] = '\0'; 329 330 if (slash && slash[1] != '\0') *subpath_out = slash + 1; 331 else *subpath_out = NULL; 332 333 return true; 334} 335 336static char *esm_find_node_module_dir(const char *start_dir, const char *package_name) { 337 if (!start_dir || !package_name) return NULL; 338 339 char current[PATH_MAX]; 340 snprintf(current, sizeof(current), "%s", start_dir); 341 342 while (true) { 343 char candidate[PATH_MAX]; 344 snprintf(candidate, sizeof(candidate), "%s/node_modules/%s", current, package_name); 345 346 struct stat st; 347 if (stat(candidate, &st) == 0 && S_ISDIR(st.st_mode)) { 348 char *resolved = realpath(candidate, NULL); 349 return resolved ? resolved : strdup(candidate); 350 } 351 352 if (strcmp(current, "/") == 0) break; 353 char *slash = strrchr(current, '/'); 354 355 if (!slash) break; 356 if (slash == current) current[1] = '\0'; 357 else *slash = '\0'; 358 } 359 360 return NULL; 361} 362 363static bool esm_matches_pattern_key(const char *key, const char *request, const char **capture, size_t *capture_len) { 364 const char *star = strchr(key, '*'); 365 if (!star) return false; 366 367 size_t key_len = strlen(key); 368 size_t req_len = strlen(request); 369 size_t prefix_len = (size_t)(star - key); 370 size_t suffix_len = key_len - prefix_len - 1; 371 372 if (req_len < prefix_len + suffix_len) return false; 373 if (strncmp(request, key, prefix_len) != 0) return false; 374 if (suffix_len > 0 && strcmp(request + req_len - suffix_len, star + 1) != 0) return false; 375 376 *capture = request + prefix_len; 377 *capture_len = req_len - prefix_len - suffix_len; 378 379 return true; 380} 381 382static char *esm_replace_star(const char *pattern, const char *capture, size_t capture_len) { 383 const char *star = strchr(pattern, '*'); 384 if (!star) return strdup(pattern); 385 386 size_t prefix_len = (size_t)(star - pattern); 387 size_t suffix_len = strlen(star + 1); 388 size_t out_len = prefix_len + capture_len + suffix_len; 389 char *out = (char *)malloc(out_len + 1); 390 if (!out) return NULL; 391 392 memcpy(out, pattern, prefix_len); 393 memcpy(out + prefix_len, capture, capture_len); 394 memcpy(out + prefix_len + capture_len, star + 1, suffix_len); 395 out[out_len] = '\0'; 396 397 return out; 398} 399 400static char *esm_resolve_exports_target( 401 yyjson_val *target, 402 const char *package_dir, 403 const char *capture, 404 size_t capture_len, 405 const char *base_path, 406 bool allow_bare_specifiers, 407 bool prefer_require 408) { 409 if (!target) return NULL; 410 411 if (yyjson_is_str(target)) { 412 const char *target_str = yyjson_get_str(target); 413 if (!target_str || !target_str[0]) return NULL; 414 415 if (target_str[0] == '.' && target_str[1] == '/') { 416 char *mapped = esm_replace_star(target_str + 2, capture, capture_len); 417 if (!mapped) return NULL; 418 419 char *resolved = esm_try_resolve_with_exts(package_dir, mapped, esm_has_extension(mapped)); 420 if (!resolved) resolved = esm_try_resolve_index_with_exts(package_dir, mapped); 421 free(mapped); 422 return resolved; 423 } 424 425 if (!allow_bare_specifiers) return NULL; 426 if (target_str[0] == '#') return NULL; 427 return esm_resolve_node_module(target_str, base_path); 428 } 429 430 if (yyjson_is_arr(target)) { 431 size_t idx, max; 432 yyjson_val *item; 433 yyjson_arr_foreach(target, idx, max, item) { 434 char *resolved = esm_resolve_exports_target( 435 item, package_dir, capture, 436 capture_len, base_path, allow_bare_specifiers, prefer_require 437 ); 438 if (resolved) return resolved; 439 } 440 return NULL; 441 } 442 443 if (yyjson_is_obj(target)) { 444 static const char *const import_conditions[] = {"import", "node", "default"}; 445 static const char *const require_conditions[] = {"require", "node", "default"}; 446 447 const char *const *conditions = prefer_require 448 ? require_conditions 449 : import_conditions; 450 451 size_t condition_count = prefer_require 452 ? sizeof(require_conditions) / sizeof(require_conditions[0]) 453 : sizeof(import_conditions) / sizeof(import_conditions[0]); 454 455 for (size_t i = 0; i < condition_count; i++) { 456 yyjson_val *cond_target = yyjson_obj_get(target, conditions[i]); 457 if (!cond_target) continue; 458 char *resolved = esm_resolve_exports_target( 459 cond_target, package_dir, capture, 460 capture_len, base_path, allow_bare_specifiers, prefer_require 461 ); 462 if (resolved) return resolved; 463 } 464 } 465 466 return NULL; 467} 468 469static char *esm_resolve_package_map( 470 yyjson_val *map_obj, 471 const char *request_key, 472 const char *package_dir, 473 const char *base_path, 474 bool allow_bare_specifiers, 475 bool prefer_require 476) { 477 if (!map_obj || !yyjson_is_obj(map_obj)) return NULL; 478 479 yyjson_val *exact = yyjson_obj_get(map_obj, request_key); 480 if (exact) return esm_resolve_exports_target( 481 exact, package_dir, "", 0, 482 base_path, allow_bare_specifiers, prefer_require 483 ); 484 485 const char *best_capture = NULL; 486 size_t best_capture_len = 0; 487 488 yyjson_val *best_target = NULL; 489 size_t best_prefix_len = 0; 490 491 size_t idx, max; 492 yyjson_val *k, *v; 493 494 yyjson_obj_foreach(map_obj, idx, max, k, v) { 495 if (!yyjson_is_str(k)) continue; 496 const char *key = yyjson_get_str(k); 497 if (!key) continue; 498 499 const char *capture = NULL; 500 size_t capture_len = 0; 501 if (!esm_matches_pattern_key(key, request_key, &capture, &capture_len)) continue; 502 503 const char *star = strchr(key, '*'); 504 size_t prefix_len = (size_t)(star - key); 505 if (best_target && prefix_len < best_prefix_len) continue; 506 507 best_target = v; 508 best_capture = capture; 509 best_capture_len = capture_len; 510 best_prefix_len = prefix_len; 511 } 512 513 if (!best_target) return NULL; 514 return esm_resolve_exports_target( 515 best_target, package_dir, best_capture, 516 best_capture_len, base_path, allow_bare_specifiers, prefer_require 517 ); 518} 519 520static char *esm_resolve_package_main_entry(yyjson_val *root, const char *package_dir) { 521 if (!root || !yyjson_is_obj(root)) return NULL; 522 523 yyjson_val *main = yyjson_obj_get(root, "main"); 524 if (!main || !yyjson_is_str(main)) return NULL; 525 526 const char *main_str = yyjson_get_str(main); 527 if (!main_str || !main_str[0]) return NULL; 528 529 char *resolved = esm_try_resolve_with_exts(package_dir, main_str, esm_has_extension(main_str)); 530 if (!resolved) resolved = esm_try_resolve_index_with_exts(package_dir, main_str); 531 return resolved; 532} 533 534static char *esm_resolve_package_entrypoint(const char *package_dir, const char *subpath, const char *base_path, bool prefer_require) { 535 char pkg_json_path[PATH_MAX]; 536 snprintf(pkg_json_path, sizeof(pkg_json_path), "%s/package.json", package_dir); 537 538 yyjson_doc *doc = yyjson_read_file(pkg_json_path, 0, NULL, NULL); 539 yyjson_val *root = doc ? yyjson_doc_get_root(doc) : NULL; 540 yyjson_val *exports = (root && yyjson_is_obj(root)) ? yyjson_obj_get(root, "exports") : NULL; 541 542 if (exports) { 543 char subpath_key[PATH_MAX]; 544 if (subpath && subpath[0]) snprintf(subpath_key, sizeof(subpath_key), "./%s", subpath); 545 else snprintf(subpath_key, sizeof(subpath_key), "."); 546 547 char *resolved = NULL; 548 if (yyjson_is_obj(exports)) { 549 bool has_subpath_keys = false; 550 size_t idx, max; 551 yyjson_val *k, *v; 552 yyjson_obj_foreach(exports, idx, max, k, v) { 553 if (!yyjson_is_str(k)) continue; 554 const char *key = yyjson_get_str(k); 555 if (key && key[0] == '.') { has_subpath_keys = true; break; } 556 } 557 if (has_subpath_keys) resolved = esm_resolve_package_map(exports, subpath_key, package_dir, base_path, false, prefer_require); 558 else if (!subpath || !subpath[0]) resolved = esm_resolve_exports_target(exports, package_dir, "", 0, base_path, false, prefer_require); 559 } else if (!subpath || !subpath[0]) resolved = esm_resolve_exports_target(exports, package_dir, "", 0, base_path, false, prefer_require); 560 561 if (doc) yyjson_doc_free(doc); 562 return resolved; 563 } 564 565 if (!subpath || !subpath[0]) { 566 char *resolved = esm_resolve_package_main_entry(root, package_dir); 567 if (resolved) { 568 if (doc) yyjson_doc_free(doc); 569 return resolved; 570 } 571 if (doc) yyjson_doc_free(doc); 572 return esm_try_resolve_index_with_exts(package_dir, "."); 573 } 574 575 char *resolved = esm_try_resolve_with_exts(package_dir, subpath, esm_has_extension(subpath)); 576 if (!resolved) resolved = esm_try_resolve_index_with_exts(package_dir, subpath); 577 if (doc) yyjson_doc_free(doc); 578 return resolved; 579} 580 581static char *esm_resolve_package_imports(const char *specifier, const char *base_path, bool prefer_require) { 582 char *start_dir = esm_get_base_dir(base_path); 583 if (!start_dir) return NULL; 584 585 char current[PATH_MAX]; 586 snprintf(current, sizeof(current), "%s", start_dir); 587 free(start_dir); 588 589 while (true) { 590 char pkg_json_path[PATH_MAX]; 591 snprintf(pkg_json_path, sizeof(pkg_json_path), "%s/package.json", current); 592 593 yyjson_doc *doc = yyjson_read_file(pkg_json_path, 0, NULL, NULL); 594 if (doc) { 595 yyjson_val *root = yyjson_doc_get_root(doc); 596 yyjson_val *imports = (root && yyjson_is_obj(root)) ? yyjson_obj_get(root, "imports") : NULL; 597 char *resolved = NULL; 598 if (imports && yyjson_is_obj(imports)) { 599 resolved = esm_resolve_package_map( 600 imports, specifier, current, 601 base_path, true, prefer_require 602 ); 603 } 604 yyjson_doc_free(doc); 605 return resolved; 606 } 607 608 if (strcmp(current, "/") == 0) break; 609 char *slash = strrchr(current, '/'); 610 if (!slash) break; 611 if (slash == current) current[1] = '\0'; 612 else *slash = '\0'; 613 } 614 615 return NULL; 616} 617 618static char *esm_resolve_node_module_cond(const char *specifier, const char *base_path, bool prefer_require) { 619 char package_name[PATH_MAX]; 620 const char *subpath = NULL; 621 if (!esm_split_package_specifier(specifier, package_name, sizeof(package_name), &subpath)) { 622 return NULL; 623 } 624 625 char *start_dir = esm_get_base_dir(base_path); 626 if (!start_dir) return NULL; 627 628 char *package_dir = esm_find_node_module_dir(start_dir, package_name); 629 free(start_dir); 630 if (!package_dir) return NULL; 631 632 char *resolved = esm_resolve_package_entrypoint(package_dir, subpath, base_path, prefer_require); 633 free(package_dir); 634 return resolved; 635} 636 637static char *esm_resolve_node_module(const char *specifier, const char *base_path) { 638 return esm_resolve_node_module_cond(specifier, base_path, false); 639} 640 641static char *esm_resolve_relative_path(const char *specifier, const char *base_path) { 642 char *base_copy = strdup(base_path); 643 if (!base_copy) return NULL; 644 char *dir = dirname(base_copy); 645 char *result = NULL; 646 647 const char *spec = specifier; 648 if (strncmp(specifier, "./", 2) == 0) spec = specifier + 2; 649 650 bool has_ext = esm_has_extension(spec); 651 if ((result = esm_try_resolve(dir, spec, ""))) goto cleanup; 652 653 if (has_ext) { 654 result = esm_try_resolve_relative_typescript_source_fallback(dir, spec, base_path); 655 goto cleanup; 656 } 657 658 char *base_ext = esm_get_extension(base_path); 659 if (!base_ext) goto cleanup; 660 661 if ((result = esm_try_resolve_from_extension_list(dir, spec, base_ext, base_ext))) goto cleanup_ext; 662 663 char idx[PATH_MAX]; 664 snprintf(idx, sizeof(idx), "%s/index%s", spec, base_ext); 665 if ((result = esm_try_resolve(dir, idx, ""))) goto cleanup_ext; 666 667 snprintf(idx, sizeof(idx), "%s/index", spec); 668 if ((result = esm_try_resolve_from_extension_list(dir, idx, base_ext, base_ext))) goto cleanup_ext; 669 670 cleanup_ext: { 671 free(base_ext); 672 } 673 674 cleanup: { 675 free(base_copy); 676 return result; 677 } 678} 679 680static char *esm_resolve_path_cond(const char *specifier, const char *base_path, bool prefer_require) { 681 if (!specifier || !specifier[0]) return NULL; 682 683 if (specifier[0] == '/') { 684 return esm_resolve_absolute(specifier); 685 } 686 687 if (esm_is_relative_specifier(specifier)) { 688 return esm_resolve_relative_path(specifier, base_path); 689 } 690 691 if (specifier[0] == '#') { 692 return esm_resolve_package_imports(specifier, base_path, prefer_require); 693 } 694 695 return esm_resolve_node_module_cond(specifier, base_path, prefer_require); 696} 697 698static char *esm_resolve_path(const char *specifier, const char *base_path) { 699 return esm_resolve_path_cond(specifier, base_path, false); 700} 701 702static char *esm_resolve_path_require(const char *specifier, const char *base_path) { 703 return esm_resolve_path_cond(specifier, base_path, true); 704} 705 706static bool esm_has_suffix(const char *path, const char *ext) { 707 size_t len = strlen(path); 708 size_t elen = strlen(ext); 709 return len > elen && strcmp(path + len - elen, ext) == 0; 710} 711 712static inline bool esm_is_json(const char *path) { 713 return esm_has_suffix(path, ".json"); 714} 715 716static inline bool esm_is_text(const char *path) { 717 return 718 esm_has_suffix(path, ".txt") || 719 esm_has_suffix(path, ".md") || 720 esm_has_suffix(path, ".html") || 721 esm_has_suffix(path, ".css"); 722} 723 724static inline bool esm_is_image(const char *path) { 725 return 726 esm_has_suffix(path, ".png") || 727 esm_has_suffix(path, ".jpg") || 728 esm_has_suffix(path, ".jpeg") || 729 esm_has_suffix(path, ".gif") || 730 esm_has_suffix(path, ".svg") || 731 esm_has_suffix(path, ".webp"); 732} 733 734static inline bool esm_is_native(const char *path) { 735 return esm_has_suffix(path, ".node"); 736} 737 738static inline bool esm_is_cjs_extension(const char *path) { 739 return 740 esm_has_suffix(path, ".cjs") || 741 esm_has_suffix(path, ".cts"); 742} 743 744static inline bool esm_is_esm_extension(const char *path) { 745 return 746 esm_has_suffix(path, ".mjs") || 747 esm_has_suffix(path, ".mts"); 748} 749 750static bool esm_path_contains_node_modules(const char *path) { 751 if (!path) return false; 752 if (strstr(path, "/node_modules/")) return true; 753 return strstr(path, "\\node_modules\\") != NULL; 754} 755 756static bool esm_read_package_json_type_module(const char *pkg_json_path, bool *has_type) { 757 if (has_type) *has_type = false; 758 759 yyjson_doc *doc = yyjson_read_file(pkg_json_path, 0, NULL, NULL); 760 if (!doc) return false; 761 762 yyjson_val *root = yyjson_doc_get_root(doc); 763 yyjson_val *type = (root && yyjson_is_obj(root)) ? yyjson_obj_get(root, "type") : NULL; 764 765 if (!type || !yyjson_is_str(type)) { 766 yyjson_doc_free(doc); 767 return false; 768 } 769 770 const char *type_str = yyjson_get_str(type); 771 if (has_type) *has_type = true; 772 773 bool is_module = type_str && strcmp(type_str, "module") == 0; 774 yyjson_doc_free(doc); 775 776 return is_module; 777} 778 779static bool esm_lookup_package_type_module(const char *resolved_path, bool *is_module) { 780 if (is_module) *is_module = false; 781 if (!resolved_path || !resolved_path[0]) return false; 782 783 char path_copy[PATH_MAX]; 784 snprintf(path_copy, sizeof(path_copy), "%s", resolved_path); 785 char *dir = dirname(path_copy); 786 if (!dir || !dir[0]) return false; 787 788 char current[PATH_MAX]; 789 snprintf(current, sizeof(current), "%s", dir); 790 791 while (true) { 792 char pkg_json_path[PATH_MAX]; 793 snprintf(pkg_json_path, sizeof(pkg_json_path), "%s/package.json", current); 794 795 struct stat st; 796 if (stat(pkg_json_path, &st) == 0 && S_ISREG(st.st_mode)) { 797 bool has_type = false; 798 bool pkg_is_module = esm_read_package_json_type_module(pkg_json_path, &has_type); 799 if (is_module) *is_module = has_type && pkg_is_module; 800 return true; 801 } 802 803 if (strcmp(current, "/") == 0) break; 804 char *slash = strrchr(current, '/'); 805 if (!slash) break; 806 if (slash == current) current[1] = '\0'; 807 else *slash = '\0'; 808 } 809 810 return false; 811} 812 813static ant_module_format_t esm_decide_module_format(const char *resolved_path) { 814 if (!resolved_path || !resolved_path[0]) return MODULE_EVAL_FORMAT_ESM; 815 if (esm_is_cjs_extension(resolved_path)) return MODULE_EVAL_FORMAT_CJS; 816 if (esm_is_esm_extension(resolved_path)) return MODULE_EVAL_FORMAT_ESM; 817 818 if (esm_has_suffix(resolved_path, ".js")) { 819 bool pkg_is_module = false; 820 bool has_package_json = esm_lookup_package_type_module(resolved_path, &pkg_is_module); 821 if (!has_package_json) return MODULE_EVAL_FORMAT_CJS; 822 return pkg_is_module ? MODULE_EVAL_FORMAT_ESM : MODULE_EVAL_FORMAT_CJS; 823 } 824 825 return MODULE_EVAL_FORMAT_ESM; 826} 827 828static ant_value_t esm_eval_module_with_format( 829 ant_t *js, 830 const char *resolved_path, 831 const char *js_code, 832 size_t js_len, 833 ant_value_t ns, 834 ant_module_format_t format 835) { 836 if (format == MODULE_EVAL_FORMAT_CJS) { 837 return esm_load_commonjs_module(js, resolved_path, js_code, js_len, ns); 838 } 839 return js_eval_bytecode_module(js, js_code, js_len); 840} 841 842ant_value_t js_esm_eval_module_source( 843 ant_t *js, 844 const char *resolved_path, const char *js_code, 845 size_t js_len, ant_value_t ns 846) { 847 ant_module_format_t format = esm_decide_module_format(resolved_path); 848 ant_module_t eval_ctx; 849 850 ant_value_t prep_res = esm_prepare_eval_ctx( 851 js, resolved_path, ns, format, 852 js->module == NULL, NULL, &eval_ctx 853 ); 854 855 if (is_err(prep_res)) return prep_res; 856 js_module_eval_ctx_push(js, &eval_ctx); 857 858 ant_value_t result = esm_eval_module_with_format( 859 js, resolved_path, js_code, 860 js_len, ns, format 861 ); 862 863 js_module_eval_ctx_pop(js, &eval_ctx); 864 return result; 865} 866 867static esm_module_kind_t esm_classify_module_kind(const char *resolved_path) { 868 if (esm_is_data_url(resolved_path)) return ESM_MODULE_KIND_URL; 869 if (esm_is_url(resolved_path)) return ESM_MODULE_KIND_URL; 870 if (esm_is_json(resolved_path)) return ESM_MODULE_KIND_JSON; 871 if (esm_is_text(resolved_path)) return ESM_MODULE_KIND_TEXT; 872 if (esm_is_image(resolved_path)) return ESM_MODULE_KIND_IMAGE; 873 if (esm_is_native(resolved_path)) return ESM_MODULE_KIND_NATIVE; 874 return ESM_MODULE_KIND_CODE; 875} 876 877static char *esm_canonicalize_path(const char *path) { 878 if (!path) return NULL; 879 880 char *canonical = strdup(path); 881 if (!canonical) return NULL; 882 883 char *src = canonical, *dst = canonical; 884 885 while (*src) { 886 if (*src == '/') { 887 *dst++ = '/'; 888 while (*src == '/') src++; 889 890 if (strncmp(src, "./", 2) == 0) { 891 src += 2; 892 } else if (strncmp(src, "../", 3) == 0) { 893 src += 3; 894 if (dst > canonical + 1) { 895 dst--; 896 while (dst > canonical && *(dst - 1) != '/') dst--; 897 } 898 } 899 } else { 900 *dst++ = *src++; 901 } 902 } 903 904 *dst = '\0'; 905 906 if (strlen(canonical) > 1 && canonical[strlen(canonical) - 1] == '/') { 907 canonical[strlen(canonical) - 1] = '\0'; 908 } 909 910 return canonical; 911} 912 913static esm_module_t *esm_find_module(const char *module_key) { 914 char *cache_key = esm_make_cache_key(module_key); 915 if (!cache_key) return NULL; 916 917 esm_module_t *mod = NULL; 918 HASH_FIND_STR(global_module_cache.modules, cache_key, mod); 919 920 free(cache_key); 921 return mod; 922} 923 924static esm_module_t *esm_create_module( 925 const char *path, 926 const char *resolved_path, 927 const char *module_key, 928 ant_module_format_t format, 929 const uint8_t *embedded_code, 930 size_t embedded_code_len 931) { 932 char *cache_key = esm_make_cache_key(module_key); 933 if (!cache_key) return NULL; 934 935 esm_module_t *existing_mod = NULL; 936 HASH_FIND_STR(global_module_cache.modules, cache_key, existing_mod); 937 if (existing_mod) { 938 free(cache_key); 939 return existing_mod; 940 } 941 942 esm_module_t *mod = (esm_module_t *)malloc(sizeof(esm_module_t)); 943 if (!mod) { 944 free(cache_key); 945 return NULL; 946 } 947 948 *mod = (esm_module_t){ 949 .path = strdup(path), 950 .cache_key = cache_key, 951 .resolved_path = strdup(resolved_path), 952 .namespace_obj = js_mkundef(), 953 .default_export = js_mkundef(), 954 .is_loaded = false, 955 .is_loading = false, 956 .kind = esm_classify_module_kind(resolved_path), 957 .format = format, 958 .url_content = NULL, 959 .url_content_len = 0, 960 .embedded_code = embedded_code, 961 .embedded_code_len = embedded_code_len, 962 .tla_promise = js_mkundef(), 963 .has_tla = false, 964 }; 965 966 if (!mod->path || !mod->resolved_path) { 967 free(mod->path); 968 free(mod->cache_key); 969 free(mod->resolved_path); 970 free(mod); 971 return NULL; 972 } 973 974 HASH_ADD_STR(global_module_cache.modules, cache_key, mod); 975 global_module_cache.count++; 976 977 return mod; 978} 979 980void js_esm_cleanup_module_cache(void) { 981 esm_module_t *current, *tmp; 982 HASH_ITER(hh, global_module_cache.modules, current, tmp) { 983 HASH_DEL(global_module_cache.modules, current); 984 if (current->path) free(current->path); 985 if (current->cache_key) free(current->cache_key); 986 if (current->resolved_path) free(current->resolved_path); 987 if (current->url_content) free(current->url_content); 988 free(current); 989 } 990 global_module_cache.count = 0; 991} 992 993static ant_value_t esm_read_file(ant_t *js, const char *path, const char *kind, esm_file_data_t *out) { 994 FILE *fp = fopen(path, "rb"); 995 if (!fp) return js_mkerr(js, "Cannot open %s: %s", kind, path); 996 997 fseek(fp, 0, SEEK_END); 998 long fsize = ftell(fp); 999 fseek(fp, 0, SEEK_SET); 1000 1001 char *buf = (char *)malloc((size_t)fsize + 1); 1002 if (!buf) { 1003 fclose(fp); 1004 return js_mkerr(js, "OOM loading %s", kind); 1005 } 1006 1007 fread(buf, 1, (size_t)fsize, fp); 1008 fclose(fp); 1009 buf[fsize] = '\0'; 1010 1011 out->data = buf; 1012 out->size = (size_t)fsize; 1013 return js_mkundef(); 1014} 1015 1016static ant_value_t esm_load_json(ant_t *js, const char *path) { 1017 esm_file_data_t file; 1018 ant_value_t err = esm_read_file(js, path, "JSON file", &file); 1019 if (is_err(err)) return err; 1020 1021 ant_value_t json_str = js_mkstr(js, file.data, file.size); 1022 free(file.data); 1023 return json_parse_value(js, json_str); 1024} 1025 1026static ant_value_t esm_load_text(ant_t *js, const char *path) { 1027 esm_file_data_t file; 1028 ant_value_t err = esm_read_file(js, path, "text file", &file); 1029 if (is_err(err)) return err; 1030 1031 ant_value_t result = js_mkstr(js, file.data, file.size); 1032 free(file.data); 1033 return result; 1034} 1035 1036static ant_value_t esm_load_image(ant_t *js, const char *path) { 1037 esm_file_data_t file; 1038 ant_value_t err = esm_read_file(js, path, "image file", &file); 1039 if (is_err(err)) return err; 1040 1041 unsigned char *content = (unsigned char *)file.data; 1042 size_t size = file.size; 1043 1044 ant_value_t obj = js_mkobj(js); 1045 ant_value_t data_arr = js_mkarr(js); 1046 1047 for (size_t i = 0; i < size; i++) { 1048 js_arr_push(js, data_arr, tov((double)content[i])); 1049 } 1050 1051 js_setprop(js, obj, js_mkstr(js, "data", 4), data_arr); 1052 js_setprop(js, obj, js_mkstr(js, "path", 4), js_mkstr(js, path, strlen(path))); 1053 js_setprop(js, obj, js_mkstr(js, "size", 4), tov((double)size)); 1054 1055 free(file.data); 1056 return obj; 1057} 1058 1059static ant_value_t esm_load_module(ant_t *js, esm_module_t *mod) { 1060 if (mod->is_loaded) return mod->namespace_obj; 1061 if (mod->is_loading) return mod->namespace_obj; 1062 1063 mod->is_loading = true; 1064 1065 switch (mod->kind) { 1066 case ESM_MODULE_KIND_JSON: { 1067 ant_value_t json_val = esm_load_json(js, mod->resolved_path); 1068 return esm_complete_value_module(mod, json_val); 1069 } 1070 case ESM_MODULE_KIND_TEXT: { 1071 ant_value_t text_val = esm_load_text(js, mod->resolved_path); 1072 return esm_complete_value_module(mod, text_val); 1073 } 1074 case ESM_MODULE_KIND_IMAGE: { 1075 ant_value_t img_val = esm_load_image(js, mod->resolved_path); 1076 return esm_complete_value_module(mod, img_val); 1077 } 1078 case ESM_MODULE_KIND_NATIVE: { 1079 ant_value_t ns = esm_make_namespace_object(js); 1080 mod->namespace_obj = ns; 1081 1082 ant_value_t module_ctx = js_create_module_context(js, mod->resolved_path, false); 1083 if (is_err(module_ctx)) { 1084 mod->is_loading = false; 1085 return module_ctx; 1086 } 1087 js_set_slot_wb(js, ns, SLOT_MODULE_CTX, module_ctx); 1088 1089 ant_value_t native_exports = napi_load_native_module(js, mod->resolved_path, ns); 1090 if (is_err(native_exports)) { 1091 mod->is_loading = false; 1092 return native_exports; 1093 } 1094 return esm_complete_namespace_module(js, mod, ns); 1095 } 1096 case ESM_MODULE_KIND_CODE: 1097 case ESM_MODULE_KIND_URL: break; 1098 } 1099 1100 char *content = NULL; 1101 size_t size = 0; 1102 1103 if (mod->embedded_code) { 1104 content = (char *)malloc(mod->embedded_code_len + 1); 1105 if (!content) { 1106 mod->is_loading = false; 1107 return js_mkerr(js, "OOM loading bundled module"); 1108 } 1109 memcpy(content, mod->embedded_code, mod->embedded_code_len); 1110 size = mod->embedded_code_len; 1111 } else if (mod->kind == ESM_MODULE_KIND_URL && esm_is_data_url(mod->resolved_path)) { 1112 content = esm_parse_data_url(mod->resolved_path, &size); 1113 if (!content) { 1114 mod->is_loading = false; 1115 return js_mkerr(js, "Cannot parse data URL module"); 1116 } 1117 } else if (mod->kind == ESM_MODULE_KIND_URL) { 1118 if (mod->url_content) { 1119 content = strdup(mod->url_content); 1120 size = mod->url_content_len; 1121 } else { 1122 char *error = NULL; 1123 content = esm_fetch_url(mod->resolved_path, &size, &error); 1124 if (!content) { 1125 mod->is_loading = false; 1126 ant_value_t err = js_mkerr(js, "Cannot fetch module %s: %s", mod->resolved_path, error ? error : "unknown error"); 1127 if (error) free(error); 1128 return err; 1129 } 1130 mod->url_content = strdup(content); 1131 mod->url_content_len = size; 1132 } 1133 } else { 1134 esm_file_data_t file; 1135 ant_value_t err = esm_read_file(js, mod->resolved_path, "module", &file); 1136 if (is_err(err)) { 1137 mod->is_loading = false; 1138 return err; 1139 } 1140 content = file.data; 1141 size = file.size; 1142 } 1143 content[size] = '\0'; 1144 1145 size_t js_len = size; 1146 const char *strip_detail = NULL; 1147 1148 if (!mod->embedded_code) { 1149 int strip_result = strip_typescript_inplace( 1150 &content, size, mod->resolved_path, 1151 mod->format != MODULE_EVAL_FORMAT_CJS, 1152 &js_len, &strip_detail 1153 ); 1154 1155 if (strip_result < 0) { 1156 ant_value_t err = js_mkerr( 1157 js, "TypeScript error: strip failed (%d): %s", 1158 strip_result, strip_detail 1159 ); 1160 free(content); 1161 mod->is_loading = false; 1162 return err; 1163 }} 1164 1165 char *js_code = content; 1166 ant_value_t ns = esm_make_namespace_object(js); 1167 mod->namespace_obj = ns; 1168 1169 const char *prev_filename = js->filename; 1170 ant_module_t eval_ctx; 1171 1172 ant_value_t prep_res = esm_prepare_eval_ctx( 1173 js, mod->resolved_path, 1174 ns, mod->format, 1175 false, prev_filename, 1176 &eval_ctx 1177 ); 1178 1179 if (is_err(prep_res)) { 1180 free(content); 1181 mod->is_loading = false; 1182 return prep_res; 1183 } 1184 1185 js_set_filename(js, mod->resolved_path); 1186 js_module_eval_ctx_push(js, &eval_ctx); 1187 1188 if (mod->format == MODULE_EVAL_FORMAT_UNKNOWN) { 1189 mod->format = esm_decide_module_format(mod->resolved_path); 1190 eval_ctx.format = mod->format; 1191 } 1192 1193 ant_value_t result = esm_eval_module_with_format( 1194 js, mod->resolved_path, js_code, js_len, ns, mod->format 1195 ); 1196 1197 free(content); 1198 if (vtype(result) == T_PROMISE) { 1199 if (esm_dynamic_import_depth > 0) { 1200 mod->has_tla = true; 1201 mod->tla_promise = result; 1202 } else js_run_event_loop(js); } 1203 1204 js_module_eval_ctx_pop(js, &eval_ctx); 1205 js_set_filename(js, prev_filename); 1206 1207 if (is_err(result)) { 1208 mod->is_loading = false; 1209 return result; 1210 } 1211 return esm_complete_namespace_module(js, mod, ns); 1212} 1213 1214static ant_value_t esm_get_or_load( 1215 ant_t *js, 1216 const char *specifier, 1217 const char *resolved_path, 1218 const char *module_key, 1219 ant_module_format_t format, 1220 const uint8_t *embedded_code, 1221 size_t embedded_code_len 1222) { 1223 esm_module_t *mod = esm_find_module(module_key); 1224 if (!mod) { 1225 mod = esm_create_module( 1226 specifier, 1227 resolved_path, 1228 module_key, 1229 format, 1230 embedded_code, 1231 embedded_code_len 1232 ); 1233 if (!mod) return js_mkerr(js, "Cannot create module"); 1234 } 1235 return esm_load_module(js, mod); 1236} 1237 1238static const char *esm_default_base_path(ant_t *js) { 1239 const char *active = js_module_eval_active_filename(js); 1240 return (active && active[0]) ? active : "."; 1241} 1242 1243ant_value_t js_esm_import_sync_cstr_from( 1244 ant_t *js, 1245 const char *specifier, 1246 size_t spec_len, 1247 const char *base_path 1248) { 1249 const ant_builtin_bundle_alias_t *bundle = NULL; 1250 const ant_builtin_bundle_module_t *module = NULL; 1251 1252 char *spec_copy = strndup(specifier, spec_len); 1253 if (!spec_copy) return js_mkerr(js, "oom"); 1254 1255 char *file_url_path = esm_file_url_to_path(js, spec_copy); 1256 if (file_url_path) { 1257 free(spec_copy); 1258 spec_copy = file_url_path; 1259 spec_len = strlen(spec_copy); 1260 } 1261 1262 bundle = esm_lookup_builtin_alias(spec_copy, spec_len); 1263 if (bundle) { 1264 module = esm_lookup_builtin_module(bundle->module_id); 1265 if (!module) { 1266 free(spec_copy); 1267 return js_mkerr(js, "Invalid builtin module id"); 1268 } 1269 1270 ant_value_t ns = esm_get_or_load( 1271 js, spec_copy, 1272 bundle->source_name, 1273 bundle->source_name, 1274 module->format, 1275 module->code, 1276 module->code_len 1277 ); 1278 1279 free(spec_copy); 1280 return ns; 1281 } 1282 1283 bool loaded = false; 1284 ant_value_t lib = js_esm_load_registered_library(js, spec_copy, spec_len, &loaded); 1285 if (loaded) { 1286 free(spec_copy); 1287 return lib; 1288 } 1289 1290 if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1291 char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path); 1292 if (!resolved_path) { 1293 ant_value_t err = js_mkerr(js, "Cannot resolve module: %s", spec_copy); 1294 free(spec_copy); 1295 return err; 1296 } 1297 1298 ant_value_t ns = esm_get_or_load( 1299 js, 1300 spec_copy, 1301 resolved_path, 1302 resolved_path, 1303 MODULE_EVAL_FORMAT_UNKNOWN, 1304 NULL, 1305 0 1306 ); 1307 free(resolved_path); 1308 free(spec_copy); 1309 return ns; 1310} 1311 1312ant_value_t js_esm_import_sync_cstr_from_require( 1313 ant_t *js, 1314 const char *specifier, 1315 size_t spec_len, 1316 const char *base_path 1317) { 1318 const ant_builtin_bundle_alias_t *bundle = NULL; 1319 const ant_builtin_bundle_module_t *module = NULL; 1320 1321 char *spec_copy = strndup(specifier, spec_len); 1322 if (!spec_copy) return js_mkerr(js, "oom"); 1323 1324 char *file_url_path = esm_file_url_to_path(js, spec_copy); 1325 if (file_url_path) { 1326 free(spec_copy); 1327 spec_copy = file_url_path; 1328 spec_len = strlen(spec_copy); 1329 } 1330 1331 bundle = esm_lookup_builtin_alias(spec_copy, spec_len); 1332 if (bundle) { 1333 module = esm_lookup_builtin_module(bundle->module_id); 1334 if (!module) { 1335 free(spec_copy); 1336 return js_mkerr(js, "Invalid builtin module id"); 1337 } 1338 1339 ant_value_t ns = esm_get_or_load( 1340 js, spec_copy, 1341 bundle->source_name, 1342 bundle->source_name, 1343 module->format, 1344 module->code, 1345 module->code_len 1346 ); 1347 1348 free(spec_copy); 1349 return ns; 1350 } 1351 1352 bool loaded = false; 1353 ant_value_t lib = js_esm_load_registered_library(js, spec_copy, spec_len, &loaded); 1354 if (loaded) { 1355 free(spec_copy); 1356 return lib; 1357 } 1358 1359 if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1360 char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path_require); 1361 if (!resolved_path) { 1362 ant_value_t err = js_mkerr(js, "Cannot resolve module: %s", spec_copy); 1363 free(spec_copy); 1364 return err; 1365 } 1366 1367 ant_value_t ns = esm_get_or_load( 1368 js, spec_copy, 1369 resolved_path, 1370 resolved_path, 1371 MODULE_EVAL_FORMAT_UNKNOWN, 1372 NULL, 0 1373 ); 1374 1375 free(resolved_path); 1376 free(spec_copy); 1377 1378 return ns; 1379} 1380 1381ant_value_t js_esm_import_sync_cstr(ant_t *js, const char *specifier, size_t spec_len) { 1382 return js_esm_import_sync_cstr_from(js, specifier, spec_len, NULL); 1383} 1384 1385ant_value_t js_esm_import_sync_from(ant_t *js, ant_value_t specifier, const char *base_path) { 1386 if (vtype(specifier) != T_STR) 1387 return js_mkerr(js, "import() requires a string specifier"); 1388 1389 ant_offset_t spec_len = 0; 1390 ant_offset_t spec_off = vstr(js, specifier, &spec_len); 1391 const char *spec_str = (const char *)(uintptr_t)(spec_off); 1392 1393 return js_esm_import_sync_cstr_from(js, spec_str, (size_t)spec_len, base_path); 1394} 1395 1396ant_value_t js_esm_import_sync_from_require(ant_t *js, ant_value_t specifier, const char *base_path) { 1397 if (vtype(specifier) != T_STR) 1398 return js_mkerr(js, "require() expects a string specifier"); 1399 1400 ant_offset_t spec_len = 0; 1401 ant_offset_t spec_off = vstr(js, specifier, &spec_len); 1402 const char *spec_str = (const char *)(uintptr_t)(spec_off); 1403 1404 return js_esm_import_sync_cstr_from_require(js, spec_str, (size_t)spec_len, base_path); 1405} 1406 1407ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier) { 1408 return js_esm_import_sync_from(js, specifier, NULL); 1409} 1410 1411ant_value_t js_esm_import_dynamic(ant_t *js, ant_value_t specifier, const char *base_path, ant_value_t *out_tla_promise) { 1412 *out_tla_promise = js_mkundef(); 1413 esm_dynamic_import_depth++; 1414 1415 ant_value_t ns = js_esm_import_sync_from(js, specifier, base_path); 1416 esm_dynamic_import_depth--; 1417 if (is_err(ns)) return ns; 1418 1419 esm_module_t *mod = NULL, *tmp = NULL; 1420 HASH_ITER(hh, global_module_cache.modules, mod, tmp) { 1421 if (mod->has_tla && mod->namespace_obj == ns) { 1422 *out_tla_promise = mod->tla_promise; 1423 mod->has_tla = false; 1424 mod->tla_promise = js_mkundef(); 1425 break; 1426 }} 1427 1428 return ns; 1429} 1430 1431ant_value_t js_esm_make_file_url(ant_t *js, const char *path) { 1432 size_t path_len = strlen(path); 1433 size_t raw_len = 7 + path_len; 1434 1435 char *raw = malloc(raw_len + 1); 1436 if (!raw) return js_mkerr(js, "oom"); 1437 1438 snprintf(raw, raw_len + 1, "file://%s", path); 1439 ant_value_t raw_val = js_mkstr(js, raw, raw_len); 1440 free(raw); 1441 1442 return js_encodeURI(js, &raw_val, 1); 1443} 1444 1445void gc_mark_esm(ant_t *js, gc_mark_fn mark) { 1446esm_module_t *mod = NULL, *tmp = NULL; 1447HASH_ITER(hh, global_module_cache.modules, mod, tmp) { 1448 mark(js, mod->namespace_obj); 1449 mark(js, mod->default_export); 1450 if (mod->has_tla) mark(js, mod->tla_promise); 1451}} 1452 1453ant_value_t js_esm_resolve_specifier(ant_t *js, ant_value_t specifier, const char *base_path) { 1454 const ant_builtin_bundle_alias_t *bundle = NULL; 1455 if (vtype(specifier) != T_STR) { 1456 return js_mkerr(js, "import.meta.resolve() requires a string specifier"); 1457 } 1458 1459 ant_offset_t spec_len = 0; 1460 ant_offset_t spec_off = vstr(js, specifier, &spec_len); 1461 const char *spec_str = (const char *)(uintptr_t)(spec_off); 1462 char *spec_copy = strndup(spec_str, (size_t)spec_len); 1463 if (!spec_copy) return js_mkerr(js, "oom"); 1464 1465 bundle = esm_lookup_builtin_alias(spec_copy, (size_t)spec_len); 1466 if (bundle) { 1467 ant_value_t result = js_mkstr(js, bundle->source_name, strlen(bundle->source_name)); 1468 free(spec_copy); 1469 return result; 1470 } 1471 1472 if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1473 char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path); 1474 free(spec_copy); 1475 1476 if (!resolved_path) { 1477 return js_mkerr(js, "Cannot resolve module"); 1478 } 1479 1480 if (esm_is_url(resolved_path)) { 1481 ant_value_t result = js_mkstr(js, resolved_path, strlen(resolved_path)); 1482 free(resolved_path); 1483 return result; 1484 } 1485 1486 ant_value_t result = js_esm_make_file_url(js, resolved_path); 1487 free(resolved_path); 1488 return result; 1489} 1490 1491ant_value_t js_esm_resolve_specifier_require(ant_t *js, ant_value_t specifier, const char *base_path) { 1492 const ant_builtin_bundle_alias_t *bundle = NULL; 1493 if (vtype(specifier) != T_STR) { 1494 return js_mkerr(js, "require.resolve() expects a string specifier"); 1495 } 1496 1497 ant_offset_t spec_len = 0; 1498 ant_offset_t spec_off = vstr(js, specifier, &spec_len); 1499 const char *spec_str = (const char *)(uintptr_t)(spec_off); 1500 char *spec_copy = strndup(spec_str, (size_t)spec_len); 1501 if (!spec_copy) return js_mkerr(js, "oom"); 1502 1503 bundle = esm_lookup_builtin_alias(spec_copy, (size_t)spec_len); 1504 if (bundle) { 1505 ant_value_t result = js_mkstr(js, bundle->source_name, strlen(bundle->source_name)); 1506 free(spec_copy); 1507 return result; 1508 } 1509 1510 if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1511 char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path_require); 1512 free(spec_copy); 1513 1514 if (!resolved_path) { 1515 return js_mkerr(js, "Cannot resolve module"); 1516 } 1517 1518 if (esm_is_url(resolved_path)) { 1519 ant_value_t result = js_mkstr(js, resolved_path, strlen(resolved_path)); 1520 free(resolved_path); 1521 return result; 1522 } 1523 1524 ant_value_t result = js_esm_make_file_url(js, resolved_path); 1525 free(resolved_path); 1526 1527 return result; 1528}