diff --git a/include/ant.h b/include/ant.h index 1af877d..9a1094a 100644 --- a/include/ant.h +++ b/include/ant.h @@ -73,4 +73,6 @@ void js_prop_iter_end(js_prop_iter_t *iter); jsval_t js_mkpromise(struct js *js); void js_resolve_promise(struct js *js, jsval_t promise, jsval_t value); void js_reject_promise(struct js *js, jsval_t promise, jsval_t value); -void js_run_event_loop(struct js *js); \ No newline at end of file + +void js_run_event_loop(struct js *js); +void js_setup_import_meta(struct js *js, const char *filename); \ No newline at end of file diff --git a/meson.build b/meson.build index 8cb2cd3..f69235f 100644 --- a/meson.build +++ b/meson.build @@ -42,7 +42,7 @@ endif build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() version_conf = configuration_data() -version_conf.set('ANT_VERSION', '0.0.6.10') +version_conf.set('ANT_VERSION', '0.0.6.11') version_conf.set('ANT_GIT_HASH', git_hash) version_conf.set('ANT_BUILD_DATE', build_date) diff --git a/src/ant.c b/src/ant.c index e9734db..baf5b9e 100644 --- a/src/ant.c +++ b/src/ant.c @@ -241,6 +241,7 @@ static jsval_t builtin_Error(struct js *js, jsval_t *args, int nargs); static jsval_t js_import_stmt(struct js *js); static jsval_t js_export_stmt(struct js *js); static jsval_t builtin_import(struct js *js, jsval_t *args, int nargs); +static jsval_t builtin_import_meta_resolve(struct js *js, jsval_t *args, int nargs); static jsval_t builtin_string_indexOf(struct js *js, jsval_t *args, int nargs); static jsval_t builtin_string_substring(struct js *js, jsval_t *args, int nargs); static jsval_t builtin_string_split(struct js *js, jsval_t *args, int nargs); @@ -6956,6 +6957,81 @@ static jsval_t builtin_import(struct js *js, jsval_t *args, int nargs) { return builtin_Promise_resolve(js, promise_args, 1); } +static jsval_t builtin_import_meta_resolve(struct js *js, jsval_t *args, int nargs) { + if (nargs < 1 || vtype(args[0]) != T_STR) { + return js_mkerr(js, "import.meta.resolve() requires a string specifier"); + } + + jsoff_t spec_len; + jsoff_t spec_off = vstr(js, args[0], &spec_len); + const char *specifier = (char *)&js->mem[spec_off]; + + const char *base_path = js->filename ? js->filename : "."; + char *resolved_path = esm_resolve_path(specifier, base_path); + if (!resolved_path) { + return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); + } + + size_t url_len = strlen(resolved_path) + 8; + char *url = malloc(url_len); + if (!url) { + free(resolved_path); + return js_mkerr(js, "oom"); + } + + snprintf(url, url_len, "file://%s", resolved_path); + free(resolved_path); + + jsval_t result = js_mkstr(js, url, strlen(url)); + free(url); + + return result; +} + +void js_setup_import_meta(struct js *js, const char *filename) { + if (!filename) return; + + jsval_t import_meta = mkobj(js, 0); + if (is_err(import_meta)) return; + + size_t url_len = strlen(filename) + 8; + char *url = malloc(url_len); + if (url) { + snprintf(url, url_len, "file://%s", filename); + jsval_t url_val = js_mkstr(js, url, strlen(url)); + if (!is_err(url_val)) setprop(js, import_meta, js_mkstr(js, "url", 3), url_val); + free(url); + } + + jsval_t filename_val = js_mkstr(js, filename, strlen(filename)); + if (!is_err(filename_val)) setprop(js, import_meta, js_mkstr(js, "filename", 8), filename_val); + + char *filename_copy = strdup(filename); + if (filename_copy) { + char *dir = dirname(filename_copy); + if (dir) { + jsval_t dirname_val = js_mkstr(js, dir, strlen(dir)); + if (!is_err(dirname_val)) setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); + } + free(filename_copy); + } + + setprop(js, import_meta, js_mkstr(js, "main", 4), js_mktrue()); + jsval_t resolve_fn = js_mkfun(builtin_import_meta_resolve); + setprop(js, import_meta, js_mkstr(js, "resolve", 7), resolve_fn); + + jsval_t glob = js_glob(js); + jsoff_t import_off = lkp(js, glob, "import", 6); + + if (import_off != 0) { + jsval_t import_fn = resolveprop(js, mkval(T_PROP, import_off)); + if (vtype(import_fn) == T_FUNC) { + jsval_t import_obj = mkval(T_OBJ, vdata(import_fn)); + setprop(js, import_obj, js_mkstr(js, "meta", 4), import_meta); + } + } +} + static jsval_t js_import_stmt(struct js *js) { js->consumed = 1; @@ -7337,7 +7413,11 @@ struct js *js_create(void *buf, size_t len) { setprop(js, p_ctor_obj, js_mkstr(js, "prototype", 9), p_proto); setprop(js, glob, js_mkstr(js, "Promise", 7), mkval(T_FUNC, vdata(p_ctor_obj))); - setprop(js, glob, js_mkstr(js, "import", 6), js_mkfun(builtin_import)); + + jsval_t import_obj = mkobj(js, 0); + setprop(js, import_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_import)); + setprop(js, glob, js_mkstr(js, "import", 6), mkval(T_FUNC, vdata(import_obj))); + setprop(js, glob, js_mkstr(js, "__esm_module_scope", 18), js_mkundef()); js->owns_mem = false; diff --git a/src/main.c b/src/main.c index c63f8b2..acf7759 100644 --- a/src/main.c +++ b/src/main.c @@ -45,7 +45,9 @@ static int execute_module(struct js *js, const char *filename) { size_t len = fread(buffer, 1, file_size, fp); fclose(fp); buffer[len] = '\0'; + js_set_filename(js, filename); + js_setup_import_meta(js, filename); jsval_t result = js_eval(js, buffer, len); free(buffer); diff --git a/tests/test_import_meta.cjs b/tests/test_import_meta.cjs new file mode 100644 index 0000000..4cd28a5 --- /dev/null +++ b/tests/test_import_meta.cjs @@ -0,0 +1,31 @@ +// Test: import.meta support +// Tests import.meta.url, import.meta.dirname, and import.meta.resolve() + +console.log("=== Testing import.meta ===\n"); + +// Test 1: import.meta.url +console.log("Test 1: import.meta.url"); +console.log("import.meta.url:", import.meta.url); +console.log("Type:", typeof import.meta.url); +console.log("Starts with 'file://':", import.meta.url.startsWith("file://")); +console.log("✓ import.meta.url is accessible\n"); + +// Test 2: import.meta.dirname +console.log("Test 2: import.meta.dirname"); +console.log("import.meta.dirname:", import.meta.dirname); +console.log("Type:", typeof import.meta.dirname); +console.log("✓ import.meta.dirname is accessible\n"); + +// Test 3: import.meta.resolve() +console.log("Test 3: import.meta.resolve()"); +try { + const resolved = import.meta.resolve("./example.js"); + console.log("Resolved './example.js':", resolved); + console.log("Type:", typeof resolved); + console.log("Starts with 'file://':", resolved.startsWith("file://")); + console.log("✓ import.meta.resolve() works\n"); +} catch (e) { + console.log("✗ import.meta.resolve() failed:", e, "\n"); +} + +console.log("=== All import.meta tests complete ==="); diff --git a/tests/test_import_meta_full.cjs b/tests/test_import_meta_full.cjs new file mode 100644 index 0000000..4d58307 --- /dev/null +++ b/tests/test_import_meta_full.cjs @@ -0,0 +1,26 @@ +// Comprehensive import.meta test + +console.log("=== Testing all import.meta properties ===\n"); + +console.log("1. import.meta.url:", import.meta.url); +console.log("2. import.meta.filename:", import.meta.filename); +console.log("3. import.meta.dirname:", import.meta.dirname); +console.log("4. import.meta.main:", import.meta.main); +console.log("5. typeof import.meta.resolve:", typeof import.meta.resolve); + +console.log("\n=== Testing import.meta.resolve() ==="); +const resolved = import.meta.resolve("./example.js"); +console.log("Resolved './example.js':", resolved); + +console.log("\n=== Testing that import() still works ==="); +async function testImport() { + try { + const mod = await import('./export-test.js'); + console.log("Dynamic import successful!"); + mod.hello('from import.meta test'); + } catch (e) { + console.log("Dynamic import failed:", e); + } +} + +void testImport();