summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2022-07-18 20:05:26 +0200
committerGitHub <noreply@github.com>2022-07-18 20:05:26 +0200
commit999cbfb52bc42c58467e3592d5fead9c4ee8a2c1 (patch)
tree5f5336420b675e15cfaa44cf205171623a3f4ab1
parent0d73eb3dd91b5b34bb6c38a25a4e6aac411d38f3 (diff)
feat: import.meta.resolve() (#15074)
This commit adds new "import.meta.resolve()" API which allows to resolve specifiers relative to the module the API is called in. This API supports resolving using import maps.
-rw-r--r--cli/dts/lib.deno.ns.d.ts10
-rw-r--r--cli/tests/integration/run_tests.rs2
-rw-r--r--cli/tests/testdata/import_meta.importmap.json11
-rw-r--r--cli/tests/testdata/import_meta.ts31
-rw-r--r--cli/tests/testdata/import_meta.ts.out7
-rw-r--r--core/bindings.rs41
6 files changed, 101 insertions, 1 deletions
diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts
index b13acc238..e5499c93e 100644
--- a/cli/dts/lib.deno.ns.d.ts
+++ b/cli/dts/lib.deno.ns.d.ts
@@ -21,6 +21,16 @@ declare interface ImportMeta {
* ```
*/
main: boolean;
+
+ /** A function that returns resolved specifier as if it would be imported
+ * using `import(specifier)`.
+ *
+ * ```ts
+ * console.log(import.meta.resolve("./foo.js"));
+ * // file:///dev/foo.js
+ * ```
+ */
+ resolve(specifier: string): string;
}
/** Deno supports user timing Level 3 (see: https://w3c.github.io/user-timing)
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index bb46fe1b5..6ed1d5964 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -952,7 +952,7 @@ itest!(if_main {
});
itest!(import_meta {
- args: "run --quiet --reload import_meta.ts",
+ args: "run --quiet --reload --import-map=import_meta.importmap.json import_meta.ts",
output: "import_meta.ts.out",
});
diff --git a/cli/tests/testdata/import_meta.importmap.json b/cli/tests/testdata/import_meta.importmap.json
new file mode 100644
index 000000000..f8c056afd
--- /dev/null
+++ b/cli/tests/testdata/import_meta.importmap.json
@@ -0,0 +1,11 @@
+{
+ "imports": {
+ "bare": "https://example.com/",
+ "https://example.com/rewrite": "https://example.com/rewritten",
+
+ "1": "https://example.com/PASS-1",
+ "null": "https://example.com/PASS-null",
+ "undefined": "https://example.com/PASS-undefined",
+ "[object Object]": "https://example.com/PASS-object"
+ }
+}
diff --git a/cli/tests/testdata/import_meta.ts b/cli/tests/testdata/import_meta.ts
index d111059ea..37af69020 100644
--- a/cli/tests/testdata/import_meta.ts
+++ b/cli/tests/testdata/import_meta.ts
@@ -1,3 +1,34 @@
+import { assertThrows } from "../../../test_util/std/testing/asserts.ts";
+
console.log("import_meta", import.meta.url, import.meta.main);
import "./import_meta2.ts";
+
+console.log("Resolving ./foo.js", import.meta.resolve("./foo.js"));
+console.log("Resolving bare from import map", import.meta.resolve("bare"));
+console.log(
+ "Resolving https://example.com/rewrite from import map",
+ import.meta.resolve("https://example.com/rewrite"),
+);
+console.log(
+ "Resolving without a value from import map",
+ import.meta.resolve(),
+);
+console.log(
+ "Resolving 1 from import map",
+ import.meta.resolve(1),
+);
+console.log(
+ "Resolving null from import map",
+ import.meta.resolve(null),
+);
+console.log(
+ "Resolving object from import map",
+ import.meta.resolve({}),
+);
+assertThrows(() => {
+ import.meta.resolve("too", "many", "arguments");
+}, TypeError);
+assertThrows(() => {
+ import.meta.resolve("://malformed/url?asdf");
+}, TypeError);
diff --git a/cli/tests/testdata/import_meta.ts.out b/cli/tests/testdata/import_meta.ts.out
index f38aa98ea..a431f61df 100644
--- a/cli/tests/testdata/import_meta.ts.out
+++ b/cli/tests/testdata/import_meta.ts.out
@@ -1,2 +1,9 @@
import_meta2 [WILDCARD]import_meta2.ts false
import_meta [WILDCARD]import_meta.ts true
+Resolving ./foo.js file:///[WILDCARD]/foo.js
+Resolving bare from import map https://example.com/
+Resolving https://example.com/rewrite from import map https://example.com/rewritten
+Resolving without a value from import map https://example.com/PASS-undefined
+Resolving 1 from import map https://example.com/PASS-1
+Resolving null from import map https://example.com/PASS-null
+Resolving object from import map https://example.com/PASS-object
diff --git a/core/bindings.rs b/core/bindings.rs
index a88e54af7..f9fd1f402 100644
--- a/core/bindings.rs
+++ b/core/bindings.rs
@@ -271,6 +271,47 @@ pub extern "C" fn host_initialize_import_meta_object_callback(
let main_key = v8::String::new(scope, "main").unwrap();
let main_val = v8::Boolean::new(scope, info.main);
meta.create_data_property(scope, main_key.into(), main_val.into());
+
+ let builder =
+ v8::FunctionBuilder::new(import_meta_resolve).data(url_val.into());
+ let val = v8::FunctionBuilder::<v8::Function>::build(builder, scope).unwrap();
+ let resolve_key = v8::String::new(scope, "resolve").unwrap();
+ meta.set(scope, resolve_key.into(), val.into());
+}
+
+fn import_meta_resolve(
+ scope: &mut v8::HandleScope,
+ args: v8::FunctionCallbackArguments,
+ mut rv: v8::ReturnValue,
+) {
+ if args.length() > 1 {
+ return throw_type_error(scope, "Invalid arguments");
+ }
+
+ let maybe_arg_str = args.get(0).to_string(scope);
+ if maybe_arg_str.is_none() {
+ return throw_type_error(scope, "Invalid arguments");
+ }
+ let specifier = maybe_arg_str.unwrap();
+ let referrer = {
+ let url_prop = args.data().unwrap();
+ url_prop.to_rust_string_lossy(scope)
+ };
+ let module_map_rc = JsRuntime::module_map(scope);
+ let loader = {
+ let module_map = module_map_rc.borrow();
+ module_map.loader.clone()
+ };
+ match loader.resolve(&specifier.to_rust_string_lossy(scope), &referrer, false)
+ {
+ Ok(resolved) => {
+ let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap();
+ rv.set(resolved_val);
+ }
+ Err(err) => {
+ throw_type_error(scope, &err.to_string());
+ }
+ };
}
pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) {