Repository: denoland/deno_core
Pull request #665 (2024-03-21 01:54:30)

Allow relative imports in extension JS/TS code
Before

```ts
// In `ext/node/polyfills/foo.ts`
import * as errors from "ext:deno_node/internal/errors.ts";
```

Now

```ts
// In `ext/node/polyfills/foo.ts`
import * as errors from "./internal/errors.ts";
```

It's pretty ugly because the `url` crate restricts you from directly modifying "bases" or "hosts" of URLs that aren't valid bases, but this avoids having to reimplement a worse version of `Url::join`

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/denoland/deno_core/pull/665?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland) Report
Attention: Patch coverage is `96.66667%` with `1 lines` in your changes are missing coverage. Please review.
> Project coverage is 80.96%. Comparing base [(`e381df2`)](https://app.codecov.io/gh/denoland/deno_core/commit/e381df2dfcaf955d5747c0499c5ddb6974d17977?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland) to head [(`c2ccd91`)](https://app.codecov.io/gh/denoland/deno_core/pull/665?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland).

| [Files](https://app.codecov.io/gh/denoland/deno_core/pull/665?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland) | Patch % | Lines |
|---|---|---|
| [core/modules/loaders.rs](https://app.codecov.io/gh/denoland/deno_core/pull/665?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland#diff-Y29yZS9tb2R1bGVzL2xvYWRlcnMucnM=) | 91.66% | [1 Missing :warning: ](https://app.codecov.io/gh/denoland/deno_core/pull/665?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #665      +/-   ##
==========================================
+ Coverage   80.94%   80.96%   +0.01%     
==========================================
  Files          97       97              
  Lines       24320    24350      +30     
==========================================
+ Hits        19685    19714      +29     
- Misses       4635     4636       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/denoland/deno_core/pull/665?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=denoland).


Patch:
diff --git a/core/modules/loaders.rs b/core/modules/loaders.rs
index 115a49a..4efcc4d 100644
--- a/core/modules/loaders.rs
+++ b/core/modules/loaders.rs
@@ -179,6 +179,21 @@ impl ModuleLoader for ExtModuleLoader {
     referrer: &str,
     _kind: ResolutionKind,
   ) -> Result<ModuleSpecifier, Error> {
+    // If specifier is relative to an extension module, we need to do some special handling
+    if specifier.starts_with("../")
+      || specifier.starts_with("./")
+      || referrer.starts_with("ext:")
+    {
+      // add `/` to the referrer to make it a valid base URL, so we can join the specifier to it
+      return Ok(crate::resolve_url(
+        &crate::resolve_url(referrer.replace("ext:", "ext:/").as_str())?
+          .join(specifier)
+          .map_err(crate::ModuleResolutionError::InvalidBaseUrl)?
+          .as_str()
+          // remove the `/` we added
+          .replace("ext:/", "ext:"),
+      )?);
+    }
     Ok(resolve_import(specifier, referrer)?)
   }
 


Test patch:
diff --git a/core/modules/tests.rs b/core/modules/tests.rs
index 5229cd8..167380b 100644
--- a/core/modules/tests.rs
+++ b/core/modules/tests.rs
@@ -1849,3 +1849,23 @@ fn test_load_with_code_cache() {
     assert_eq!(keys, vec!["file:///a.js", "file:///b.js",]);
   }
 }
+
+#[test]
+fn ext_module_loader_relative() {
+  let loader = ExtModuleLoader::new(vec![]).unwrap();
+  let cases = [
+    (
+      ("../../foo.js", "ext:test/nested/mod/bar.js"),
+      "ext:test/foo.js",
+    ),
+    (("./foo.js", "ext:test/bar.js"), "ext:test/foo.js"),
+    ((".././foo.js", "ext:test/bar.js"), "ext:foo.js"),
+    (("./foo.js", "ext:bar.js"), "ext:foo.js"),
+  ];
+  for ((specifier, referrer), expected) in cases {
+    let result = loader
+      .resolve(specifier, referrer, ResolutionKind::Import)
+      .unwrap();
+    assert_eq!(result.as_str(), expected);
+  }
+}
diff --git a/testing/checkin/runtime/__init.js b/testing/checkin/runtime/__init.js
index 6edc904..7d61c33 100644
--- a/testing/checkin/runtime/__init.js
+++ b/testing/checkin/runtime/__init.js
@@ -1,5 +1,5 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-import "ext:checkin_runtime/__bootstrap.js";
+import "./__bootstrap.js";
 import * as async from "checkin:async";
 import * as testing from "checkin:testing";
 import * as console from "checkin:console";