diff --git a/quickjs.c b/quickjs.c index 3ba7918ec..ee1f411d6 100644 --- a/quickjs.c +++ b/quickjs.c @@ -31009,7 +31009,17 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) } if (res != JS_RESOLVE_RES_FOUND) { if (res != JS_RESOLVE_RES_AMBIGUOUS) { - js_resolve_export_throw_error(ctx, res, m, en->export_name); + /* for a re-export the binding is missing from the target + module, not from m; report the target and the looked-up name */ + JSExportEntry *me = find_export_entry(ctx, m, en->export_name); + if (me && me->export_type == JS_EXPORT_TYPE_INDIRECT && + me->local_name != JS_ATOM__star_) { + JSModuleDef *m1 = + m->req_module_entries[me->u.req_module_idx].module; + js_resolve_export_throw_error(ctx, res, m1, me->local_name); + } else { + js_resolve_export_throw_error(ctx, res, m, en->export_name); + } goto fail; } en->export_type = EXPORTED_NAME_AMBIGUOUS; @@ -31314,7 +31324,10 @@ static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, m1 = m->req_module_entries[me->u.req_module_idx].module; ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name); if (ret != JS_RESOLVE_RES_FOUND) { - js_resolve_export_throw_error(ctx, ret, m, me->export_name); + /* the binding is missing from the re-exported module m1, not + from the re-exporting module m; report m1 and the name being + looked up there, matching the direct-import diagnostic */ + js_resolve_export_throw_error(ctx, ret, m1, me->local_name); goto fail; } } diff --git a/tests.conf b/tests.conf index 8cbf6f8f4..192656e0b 100644 --- a/tests.conf +++ b/tests.conf @@ -13,3 +13,6 @@ tests/microbench.js tests/test_worker_module.js tests/fixture_string_exports.js tests/fixture_throwing_module.js +tests/fixture_reexport_source.js +tests/fixture_reexport_missing.js +tests/fixture_reexport_direct.js diff --git a/tests/fixture_reexport_direct.js b/tests/fixture_reexport_direct.js new file mode 100644 index 000000000..39e383248 --- /dev/null +++ b/tests/fixture_reexport_direct.js @@ -0,0 +1 @@ +import { doesNotExist } from "./fixture_reexport_source.js" diff --git a/tests/fixture_reexport_missing.js b/tests/fixture_reexport_missing.js new file mode 100644 index 000000000..72d1b9608 --- /dev/null +++ b/tests/fixture_reexport_missing.js @@ -0,0 +1 @@ +export { doesNotExist } from "./fixture_reexport_source.js" diff --git a/tests/fixture_reexport_source.js b/tests/fixture_reexport_source.js new file mode 100644 index 000000000..9d7905a39 --- /dev/null +++ b/tests/fixture_reexport_source.js @@ -0,0 +1 @@ +export const real = 1 diff --git a/tests/reexport-error-module.js b/tests/reexport-error-module.js new file mode 100644 index 000000000..778a3074a --- /dev/null +++ b/tests/reexport-error-module.js @@ -0,0 +1,14 @@ +import { assert } from "./assert.js"; + +/* A re-export of a missing binding must blame the *source* module and the + name looked up there -- exactly like a direct import of the same missing + binding does. Comparing the two messages avoids depending on the module + path surviving intact in the error: long absolute paths get truncated in + the fixed-size atom buffer, so a substring check on the filename is + unreliable in CI. */ +const reexportErr = await import("./fixture_reexport_missing.js").then(() => null, e => e); +const directErr = await import("./fixture_reexport_direct.js").then(() => null, e => e); + +assert(reexportErr instanceof SyntaxError); +assert(directErr instanceof SyntaxError); +assert(reexportErr.message, directErr.message);