summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2021-12-15 19:22:36 +0100
committerGitHub <noreply@github.com>2021-12-15 19:22:36 +0100
commita1f0796fccfafee19b2fe06155efe746da2e9654 (patch)
tree01942b1bc202352418c88dbf8a1c447e72f6f976
parentec7d90666f68ae0bf6036a6915296622adc9b65c (diff)
feat: Add support for import assertions and JSON modules (#12866)
This commit adds proper support for import assertions and JSON modules. Implementation of "core/modules.rs" was changed to account for multiple possible module types, instead of always assuming that the code is an "ES module". In effect "ModuleMap" now has knowledge about each modules' type (stored via "ModuleType" enum). Module loading pipeline now stores information about expected module type for each request and validates that expected type matches discovered module type based on file's "MediaType". Relevant tests were added to "core/modules.rs" and integration tests, additionally multiple WPT tests were enabled. There are still some rough edges in the implementation and not all WPT were enabled, due to: a) unclear BOM handling in source code by "FileFetcher" b) design limitation of Deno's "FileFetcher" that doesn't download the same module multiple times in a single run Co-authored-by: Kitson Kelly <me@kitsonkelly.com>
-rw-r--r--Cargo.lock4
-rw-r--r--cli/config_file.rs1
-rw-r--r--cli/emit.rs24
-rw-r--r--cli/lsp/language_server.rs1
-rw-r--r--cli/proc_state.rs17
-rw-r--r--cli/standalone.rs1
-rw-r--r--cli/tests/integration/run_tests.rs33
-rw-r--r--cli/tests/testdata/import_assertions/data.json6
-rw-r--r--cli/tests/testdata/import_assertions/dynamic_error.out5
-rw-r--r--cli/tests/testdata/import_assertions/dynamic_error.ts3
-rw-r--r--cli/tests/testdata/import_assertions/dynamic_import.out2
-rw-r--r--cli/tests/testdata/import_assertions/dynamic_import.ts3
-rw-r--r--cli/tests/testdata/import_assertions/static_error.out5
-rw-r--r--cli/tests/testdata/import_assertions/static_error.ts3
-rw-r--r--cli/tests/testdata/import_assertions/static_export.out2
-rw-r--r--cli/tests/testdata/import_assertions/static_export.ts3
-rw-r--r--cli/tests/testdata/import_assertions/static_import.out2
-rw-r--r--cli/tests/testdata/import_assertions/static_import.ts3
-rw-r--r--cli/tests/testdata/import_assertions/static_reexport.ts1
-rw-r--r--cli/tests/testdata/import_assertions/type_check.out5
-rw-r--r--cli/tests/testdata/import_assertions/type_check.ts3
-rw-r--r--cli/tsc.rs20
-rw-r--r--core/bindings.rs58
-rw-r--r--core/error.rs6
-rw-r--r--core/lib.rs1
-rw-r--r--core/modules.rs868
-rw-r--r--core/runtime.rs27
-rw-r--r--tools/wpt/expectation.json37
28 files changed, 916 insertions, 228 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a368cf392..f60a4a24e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -836,9 +836,9 @@ dependencies = [
[[package]]
name = "deno_graph"
-version = "0.14.0"
+version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b72e7615cd00e7c9b93d804fad6c2a25c9c40a647bf3b5cf857f91aa4c107792"
+checksum = "8f3280596f5b5825b0363142b72fe2786163905c61dfeb18bd5db1c390a94093"
dependencies = [
"anyhow",
"cfg-if 1.0.0",
diff --git a/cli/config_file.rs b/cli/config_file.rs
index 363a215eb..5cd76eaca 100644
--- a/cli/config_file.rs
+++ b/cli/config_file.rs
@@ -104,6 +104,7 @@ pub const IGNORED_COMPILER_OPTIONS: &[&str] = &[
"paths",
"preserveConstEnums",
"reactNamespace",
+ "resolveJsonModule",
"rootDir",
"rootDirs",
"skipLibCheck",
diff --git a/cli/emit.rs b/cli/emit.rs
index c9a90abb7..3aa2c4794 100644
--- a/cli/emit.rs
+++ b/cli/emit.rs
@@ -153,6 +153,7 @@ pub(crate) fn get_ts_config(
"isolatedModules": true,
"lib": lib,
"module": "esnext",
+ "resolveJsonModule": true,
"strict": true,
"target": "esnext",
"tsBuildInfoFile": "deno:///.tsbuildinfo",
@@ -186,6 +187,7 @@ pub(crate) fn get_ts_config(
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
+ "resolveJsonModule": true,
})),
ConfigType::RuntimeEmit { tsc_emit } => {
let mut ts_config = TsConfig::new(json!({
@@ -403,14 +405,15 @@ pub(crate) fn check_and_maybe_emit(
log::debug!("module missing, skipping emit for {}", specifier);
continue;
};
- // Sometimes if `tsc` sees a CommonJS file it will _helpfully_ output it
- // to ESM, which we don't really want to do unless someone has enabled
- // check_js.
- if !check_js
- && matches!(
- media_type,
- MediaType::JavaScript | MediaType::Cjs | MediaType::Mjs
- )
+ // Sometimes if `tsc` sees a CommonJS file or a JSON module, it will
+ // _helpfully_ output it, which we don't really want to do unless
+ // someone has enabled check_js.
+ if matches!(media_type, MediaType::Json)
+ || (!check_js
+ && matches!(
+ media_type,
+ MediaType::JavaScript | MediaType::Cjs | MediaType::Mjs
+ ))
{
log::debug!("skipping emit for {}", specifier);
continue;
@@ -429,7 +432,10 @@ pub(crate) fn check_and_maybe_emit(
MediaType::Dts | MediaType::Dcts | MediaType::Dmts => {
cache.set(CacheType::Declaration, &specifier, emit.data)?;
}
- _ => unreachable!(),
+ _ => unreachable!(
+ "unexpected media_type {} {}",
+ emit.media_type, specifier
+ ),
}
}
}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index aa465cf48..e9e446918 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -569,6 +569,7 @@ impl Inner {
"lib": ["deno.ns", "deno.window"],
"module": "esnext",
"noEmit": true,
+ "resolveJsonModule": true,
"strict": true,
"target": "esnext",
"useDefineForClassFields": true,
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index 1a0f61e71..d82f8d017 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -31,6 +31,7 @@ use deno_core::url::Url;
use deno_core::CompiledWasmModuleStore;
use deno_core::ModuleSource;
use deno_core::ModuleSpecifier;
+use deno_core::ModuleType;
use deno_core::SharedArrayBufferStore;
use deno_graph::create_graph;
use deno_graph::Dependency;
@@ -68,6 +69,7 @@ pub struct ProcState(Arc<Inner>);
enum ModuleEntry {
Module {
code: String,
+ media_type: MediaType,
dependencies: BTreeMap<String, Dependency>,
},
Error(ModuleGraphError),
@@ -584,6 +586,7 @@ impl ProcState {
| MediaType::Unknown
| MediaType::Cjs
| MediaType::Mjs
+ | MediaType::Json
) {
module.maybe_source().unwrap_or("").to_string()
// The emit may also be missing when a declaration file is in the
@@ -602,7 +605,11 @@ impl ProcState {
module.maybe_dependencies().cloned().unwrap_or_default();
graph_data.modules.insert(
specifier.clone(),
- ModuleEntry::Module { code, dependencies },
+ ModuleEntry::Module {
+ code,
+ dependencies,
+ media_type: *media_type,
+ },
);
if let Some(dependencies) = module.maybe_dependencies() {
for dep in dependencies.values() {
@@ -724,10 +731,16 @@ impl ProcState {
_ => &specifier,
};
match graph_data.modules.get(found_specifier) {
- Some(ModuleEntry::Module { code, .. }) => Ok(ModuleSource {
+ Some(ModuleEntry::Module {
+ code, media_type, ..
+ }) => Ok(ModuleSource {
code: code.clone(),
module_url_specified: specifier.to_string(),
module_url_found: found_specifier.to_string(),
+ module_type: match media_type {
+ MediaType::Json => ModuleType::Json,
+ _ => ModuleType::JavaScript,
+ },
}),
_ => Err(anyhow!(
"Loading unprepared module: {}",
diff --git a/cli/standalone.rs b/cli/standalone.rs
index 47b0e2aa8..d7fbb3106 100644
--- a/cli/standalone.rs
+++ b/cli/standalone.rs
@@ -164,6 +164,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
Ok(deno_core::ModuleSource {
code,
+ module_type: deno_core::ModuleType::JavaScript,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
})
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index b6929b58a..c04e4731c 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -2407,3 +2407,36 @@ fn issue12807() {
.unwrap();
assert!(status.success());
}
+
+itest!(import_assertions_static_import {
+ args: "run --allow-read import_assertions/static_import.ts",
+ output: "import_assertions/static_import.out",
+});
+
+itest!(import_assertions_static_export {
+ args: "run --allow-read import_assertions/static_export.ts",
+ output: "import_assertions/static_export.out",
+});
+
+itest!(import_assertions_static_error {
+ args: "run --allow-read import_assertions/static_error.ts",
+ output: "import_assertions/static_error.out",
+ exit_code: 1,
+});
+
+itest!(import_assertions_dynamic_import {
+ args: "run --allow-read import_assertions/dynamic_import.ts",
+ output: "import_assertions/dynamic_import.out",
+});
+
+itest!(import_assertions_dynamic_error {
+ args: "run --allow-read import_assertions/dynamic_error.ts",
+ output: "import_assertions/dynamic_error.out",
+ exit_code: 1,
+});
+
+itest!(import_assertions_type_check {
+ args: "run --allow-read import_assertions/type_check.ts",
+ output: "import_assertions/type_check.out",
+ exit_code: 1,
+});
diff --git a/cli/tests/testdata/import_assertions/data.json b/cli/tests/testdata/import_assertions/data.json
new file mode 100644
index 000000000..37b3ee1e0
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/data.json
@@ -0,0 +1,6 @@
+{
+ "a": "b",
+ "c": {
+ "d": 10
+ }
+}
diff --git a/cli/tests/testdata/import_assertions/dynamic_error.out b/cli/tests/testdata/import_assertions/dynamic_error.out
new file mode 100644
index 000000000..1ad40889b
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/dynamic_error.out
@@ -0,0 +1,5 @@
+[WILDCARD]
+error: Uncaught (in promise) TypeError: Expected a "JavaScript" module but loaded a "JSON" module.
+const data = await import("./data.json");
+ ^
+ at async [WILDCARD]dynamic_error.ts:1:14
diff --git a/cli/tests/testdata/import_assertions/dynamic_error.ts b/cli/tests/testdata/import_assertions/dynamic_error.ts
new file mode 100644
index 000000000..2d9c6757f
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/dynamic_error.ts
@@ -0,0 +1,3 @@
+const data = await import("./data.json");
+
+console.log(data);
diff --git a/cli/tests/testdata/import_assertions/dynamic_import.out b/cli/tests/testdata/import_assertions/dynamic_import.out
new file mode 100644
index 000000000..3280e0f53
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/dynamic_import.out
@@ -0,0 +1,2 @@
+[WILDCARD]
+Module { default: { a: "b", c: { d: 10 } } }
diff --git a/cli/tests/testdata/import_assertions/dynamic_import.ts b/cli/tests/testdata/import_assertions/dynamic_import.ts
new file mode 100644
index 000000000..d6983852c
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/dynamic_import.ts
@@ -0,0 +1,3 @@
+const data = await import("./data.json", { assert: { type: "json" } });
+
+console.log(data);
diff --git a/cli/tests/testdata/import_assertions/static_error.out b/cli/tests/testdata/import_assertions/static_error.out
new file mode 100644
index 000000000..8524079de
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/static_error.out
@@ -0,0 +1,5 @@
+[WILDCARD]
+error: An unsupported media type was attempted to be imported as a module.
+ Specifier: [WILDCARD]data.json
+ MediaType: Json
+ at [WILDCARD]static_error.ts:1:18
diff --git a/cli/tests/testdata/import_assertions/static_error.ts b/cli/tests/testdata/import_assertions/static_error.ts
new file mode 100644
index 000000000..0bc3a93f8
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/static_error.ts
@@ -0,0 +1,3 @@
+import data from "./data.json";
+
+console.log(data);
diff --git a/cli/tests/testdata/import_assertions/static_export.out b/cli/tests/testdata/import_assertions/static_export.out
new file mode 100644
index 000000000..42fbc066c
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/static_export.out
@@ -0,0 +1,2 @@
+[WILDCARD]
+{ a: "b", c: { d: 10 } }
diff --git a/cli/tests/testdata/import_assertions/static_export.ts b/cli/tests/testdata/import_assertions/static_export.ts
new file mode 100644
index 000000000..ac3ee694f
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/static_export.ts
@@ -0,0 +1,3 @@
+import data from "./static_reexport.ts";
+
+console.log(data);
diff --git a/cli/tests/testdata/import_assertions/static_import.out b/cli/tests/testdata/import_assertions/static_import.out
new file mode 100644
index 000000000..42fbc066c
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/static_import.out
@@ -0,0 +1,2 @@
+[WILDCARD]
+{ a: "b", c: { d: 10 } }
diff --git a/cli/tests/testdata/import_assertions/static_import.ts b/cli/tests/testdata/import_assertions/static_import.ts
new file mode 100644
index 000000000..180ab75f2
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/static_import.ts
@@ -0,0 +1,3 @@
+import data from "./data.json" assert { type: "json" };
+
+console.log(data);
diff --git a/cli/tests/testdata/import_assertions/static_reexport.ts b/cli/tests/testdata/import_assertions/static_reexport.ts
new file mode 100644
index 000000000..81af428be
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/static_reexport.ts
@@ -0,0 +1 @@
+export { default } from "./data.json" assert { type: "json" };
diff --git a/cli/tests/testdata/import_assertions/type_check.out b/cli/tests/testdata/import_assertions/type_check.out
new file mode 100644
index 000000000..8e1387456
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/type_check.out
@@ -0,0 +1,5 @@
+[WILDCARD]
+error: TS2339 [ERROR]: Property 'foo' does not exist on type '{ a: string; c: { d: number; }; }'.
+console.log(data.foo);
+ ~~~
+ at [WILDCARD]type_check.ts:3:18
diff --git a/cli/tests/testdata/import_assertions/type_check.ts b/cli/tests/testdata/import_assertions/type_check.ts
new file mode 100644
index 000000000..19adb3eae
--- /dev/null
+++ b/cli/tests/testdata/import_assertions/type_check.ts
@@ -0,0 +1,3 @@
+import data from "./data.json" assert { type: "json" };
+
+console.log(data.foo);
diff --git a/cli/tsc.rs b/cli/tsc.rs
index bc18efee0..f76fb18fb 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -352,6 +352,24 @@ struct LoadArgs {
specifier: String,
}
+fn as_ts_script_kind(media_type: &MediaType) -> i32 {
+ match media_type {
+ MediaType::JavaScript => 1,
+ MediaType::Jsx => 2,
+ MediaType::Mjs => 1,
+ MediaType::Cjs => 1,
+ MediaType::TypeScript => 3,
+ MediaType::Mts => 3,
+ MediaType::Cts => 3,
+ MediaType::Dts => 3,
+ MediaType::Dmts => 3,
+ MediaType::Dcts => 3,
+ MediaType::Tsx => 4,
+ MediaType::Json => 6,
+ _ => 0,
+ }
+}
+
fn op_load(state: &mut State, args: Value) -> Result<Value, AnyError> {
let v: LoadArgs = serde_json::from_value(args)
.context("Invalid request from JavaScript for \"op_load\".")?;
@@ -395,7 +413,7 @@ fn op_load(state: &mut State, args: Value) -> Result<Value, AnyError> {
};
Ok(
- json!({ "data": data, "hash": hash, "scriptKind": media_type.as_ts_script_kind() }),
+ json!({ "data": data, "hash": hash, "scriptKind": as_ts_script_kind(&media_type) }),
)
}
diff --git a/core/bindings.rs b/core/bindings.rs
index 06791b248..5f1806e4b 100644
--- a/core/bindings.rs
+++ b/core/bindings.rs
@@ -1,6 +1,10 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::error::is_instance_of_error;
+use crate::modules::get_module_type_from_assertions;
+use crate::modules::parse_import_assertions;
+use crate::modules::validate_import_assertions;
+use crate::modules::ImportAssertionsKind;
use crate::modules::ModuleMap;
use crate::resolve_url_or_path;
use crate::JsRuntime;
@@ -243,7 +247,7 @@ pub extern "C" fn host_import_module_dynamically_callback(
context: v8::Local<v8::Context>,
referrer: v8::Local<v8::ScriptOrModule>,
specifier: v8::Local<v8::String>,
- _import_assertions: v8::Local<v8::FixedArray>,
+ import_assertions: v8::Local<v8::FixedArray>,
) -> *mut v8::Promise {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
@@ -267,6 +271,22 @@ pub extern "C" fn host_import_module_dynamically_callback(
let resolver = v8::PromiseResolver::new(scope).unwrap();
let promise = resolver.get_promise(scope);
+ let assertions = parse_import_assertions(
+ scope,
+ import_assertions,
+ ImportAssertionsKind::DynamicImport,
+ );
+
+ {
+ let tc_scope = &mut v8::TryCatch::new(scope);
+ validate_import_assertions(tc_scope, &assertions);
+ if tc_scope.has_caught() {
+ let e = tc_scope.exception().unwrap();
+ resolver.reject(tc_scope, e);
+ }
+ }
+ let module_type = get_module_type_from_assertions(&assertions);
+
let resolver_handle = v8::Global::new(scope, resolver);
{
let state_rc = JsRuntime::state(scope);
@@ -280,6 +300,7 @@ pub extern "C" fn host_import_module_dynamically_callback(
module_map_rc,
&specifier_str,
&referrer_name_str,
+ module_type,
resolver_handle,
);
state_rc.borrow_mut().notify_new_dynamic_import();
@@ -294,13 +315,29 @@ pub extern "C" fn host_import_module_dynamically_callback(
_rv: v8::ReturnValue| {
let arg = args.get(0);
if is_instance_of_error(scope, arg) {
+ let e: crate::error::NativeJsError =
+ serde_v8::from_v8(scope, arg).unwrap();
+ let name = e.name.unwrap_or_else(|| "Error".to_string());
let message = v8::Exception::create_message(scope, arg);
if message.get_stack_trace(scope).unwrap().get_frame_count() == 0 {
let arg: v8::Local<v8::Object> = arg.try_into().unwrap();
let message_key = v8::String::new(scope, "message").unwrap();
let message = arg.get(scope, message_key.into()).unwrap();
- let exception =
- v8::Exception::type_error(scope, message.try_into().unwrap());
+ let exception = match name.as_str() {
+ "RangeError" => {
+ v8::Exception::range_error(scope, message.try_into().unwrap())
+ }
+ "TypeError" => {
+ v8::Exception::type_error(scope, message.try_into().unwrap())
+ }
+ "SyntaxError" => {
+ v8::Exception::syntax_error(scope, message.try_into().unwrap())
+ }
+ "ReferenceError" => {
+ v8::Exception::reference_error(scope, message.try_into().unwrap())
+ }
+ _ => v8::Exception::error(scope, message.try_into().unwrap()),
+ };
let code_key = v8::String::new(scope, "code").unwrap();
let code_value =
v8::String::new(scope, "ERR_MODULE_NOT_FOUND").unwrap();
@@ -1311,7 +1348,7 @@ fn create_host_object(
pub fn module_resolve_callback<'s>(
context: v8::Local<'s, v8::Context>,
specifier: v8::Local<'s, v8::String>,
- _import_assertions: v8::Local<'s, v8::FixedArray>,
+ import_assertions: v8::Local<'s, v8::FixedArray>,
referrer: v8::Local<'s, v8::Module>,
) -> Option<v8::Local<'s, v8::Module>> {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
@@ -1328,8 +1365,17 @@ pub fn module_resolve_callback<'s>(
let specifier_str = specifier.to_rust_string_lossy(scope);
- let maybe_module =
- module_map.resolve_callback(scope, &specifier_str, &referrer_name);
+ let assertions = parse_import_assertions(
+ scope,
+ import_assertions,
+ ImportAssertionsKind::StaticImport,
+ );
+ let maybe_module = module_map.resolve_callback(
+ scope,
+ &specifier_str,
+ &referrer_name,
+ assertions,
+ );
if let Some(module) = maybe_module {
return Some(module);
}
diff --git a/core/error.rs b/core/error.rs
index e8c0305d4..332bc5c51 100644
--- a/core/error.rs
+++ b/core/error.rs
@@ -158,9 +158,9 @@ fn get_property<'a>(
}
#[derive(serde::Deserialize)]
-struct NativeJsError {
- name: Option<String>,
- message: Option<String>,
+pub(crate) struct NativeJsError {
+ pub name: Option<String>,
+ pub message: Option<String>,
// Warning! .stack is special so handled by itself
// stack: Option<String>,
}
diff --git a/core/lib.rs b/core/lib.rs
index f47db7f2e..0bb9f5d1a 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -61,6 +61,7 @@ pub use crate::modules::ModuleLoadId;
pub use crate::modules::ModuleLoader;
pub use crate::modules::ModuleSource;
pub use crate::modules::ModuleSourceFuture;
+pub use crate::modules::ModuleType;
pub use crate::modules::NoopModuleLoader;
pub use crate::runtime::CompiledWasmModuleStore;
pub use crate::runtime::SharedArrayBufferStore;
diff --git a/core/modules.rs b/core/modules.rs
index 067ee48e3..8c713587e 100644
--- a/core/modules.rs
+++ b/core/modules.rs
@@ -1,8 +1,11 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::bindings;
+use crate::error::attach_handle_to_error;
use crate::error::generic_error;
use crate::module_specifier::ModuleSpecifier;
+use crate::resolve_import;
+use crate::resolve_url;
use crate::runtime::exception_to_err_result;
use crate::OpState;
use anyhow::Error;
@@ -19,18 +22,159 @@ use std::collections::VecDeque;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
-use std::sync::atomic::AtomicI32;
-use std::sync::atomic::Ordering;
use std::task::Context;
use std::task::Poll;
-lazy_static::lazy_static! {
- pub static ref NEXT_LOAD_ID: AtomicI32 = AtomicI32::new(0);
-}
-
pub type ModuleId = i32;
pub type ModuleLoadId = i32;
+pub const BOM_CHAR: char = '\u{FEFF}';
+
+/// Strips the byte order mark from the provided text if it exists.
+pub fn strip_bom(text: &str) -> &str {
+ if text.starts_with(BOM_CHAR) {
+ &text[BOM_CHAR.len_utf8()..]
+ } else {
+ text
+ }
+}
+
+const SUPPORTED_TYPE_ASSERTIONS: &[&str] = &["json"];
+
+/// Throws V8 exception if assertions are invalid
+pub(crate) fn validate_import_assertions(
+ scope: &mut v8::HandleScope,
+ assertions: &HashMap<String, String>,
+) {
+ for (key, value) in assertions {
+ if key == "type" && !SUPPORTED_TYPE_ASSERTIONS.contains(&value.as_str()) {
+ let message = v8::String::new(
+ scope,
+ &format!("\"{}\" is not a valid module type.", value),
+ )
+ .unwrap();
+ let exception = v8::Exception::type_error(scope, message);
+ scope.throw_exception(exception);
+ return;
+ }
+ }
+}
+
+#[derive(Debug)]
+pub(crate) enum ImportAssertionsKind {
+ StaticImport,
+ DynamicImport,
+}
+
+pub(crate) fn parse_import_assertions(
+ scope: &mut v8::HandleScope,
+ import_assertions: v8::Local<v8::FixedArray>,
+ kind: ImportAssertionsKind,
+) -> HashMap<String, String> {
+ let mut assertions: HashMap<String, String> = HashMap::default();
+
+ let assertions_per_line = match kind {
+ // For static imports, assertions are triples of (keyword, value and source offset)
+ // Also used in `module_resolve_callback`.
+ ImportAssertionsKind::StaticImport => 3,
+ // For dynamic imports, assertions are tuples of (keyword, value)
+ ImportAssertionsKind::DynamicImport => 2,
+ };
+ assert_eq!(import_assertions.length() % assertions_per_line, 0);
+ let no_of_assertions = import_assertions.length() / assertions_per_line;
+
+ for i in 0..no_of_assertions {
+ let assert_key = import_assertions
+ .get(scope, assertions_per_line * i)
+ .unwrap();
+ let assert_key_val = v8::Local::<v8::Value>::try_from(assert_key).unwrap();
+ let assert_value = import_assertions
+ .get(scope, (assertions_per_line * i) + 1)
+ .unwrap();
+ let assert_value_val =
+ v8::Local::<v8::Value>::try_from(assert_value).unwrap();
+ assertions.insert(
+ assert_key_val.to_rust_string_lossy(scope),
+ assert_value_val.to_rust_string_lossy(scope),
+ );
+ }
+
+ assertions
+}
+
+pub(crate) fn get_module_type_from_assertions(
+ assertions: &HashMap<String, String>,
+) -> ModuleType {
+ assertions
+ .get("type")
+ .map(|ty| {
+ if ty == "json" {
+ ModuleType::Json
+ } else {
+ ModuleType::JavaScript
+ }
+ })
+ .unwrap_or(ModuleType::JavaScript)
+}
+
+// Clippy thinks the return value doesn't need to be an Option, it's unaware
+// of the mapping that MapFnFrom<F> does for ResolveModuleCallback.
+#[allow(clippy::unnecessary_wraps)]
+fn json_module_evaluation_steps<'a>(
+ context: v8::Local<'a, v8::Context>,
+ module: v8::Local<v8::Module>,
+) -> Option<v8::Local<'a, v8::Value>> {
+ let scope = &mut unsafe { v8::CallbackScope::new(context) };
+ let tc_scope = &mut v8::TryCatch::new(scope);
+ let module_map = tc_scope
+ .get_slot::<Rc<RefCell<ModuleMap>>>()
+ .unwrap()
+ .clone();
+
+ let handle = v8::Global::<v8::Module>::new(tc_scope, module);
+ let value_handle = module_map
+ .borrow_mut()
+ .json_value_store
+ .remove(&handle)
+ .unwrap();
+ let value_local = v8::Local::new(tc_scope, value_handle);
+
+ let name = v8::String::new(tc_scope, "default").unwrap();
+ // This should never fail
+ assert!(
+ module.set_synthetic_module_export(tc_scope, name, value_local)
+ == Some(true)
+ );
+ assert!(!tc_scope.has_caught());
+
+ // Since TLA is active we need to return a promise.
+ let resolver = v8::PromiseResolver::new(tc_scope).unwrap();
+ let undefined = v8::undefined(tc_scope);
+ resolver.resolve(tc_scope, undefined.into());
+ Some(resolver.get_promise(tc_scope).into())
+}
+
+/// A type of module to be executed.
+///
+/// For non-`JavaScript` modules, this value doesn't tell
+/// how to interpret the module; it is only used to validate
+/// the module against an import assertion (if one is present
+/// in the import statement).
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum ModuleType {
+ JavaScript,
+ Json,
+}
+
+impl std::fmt::Display for ModuleType {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ Self::JavaScript => write!(f, "JavaScript"),
+ Self::Json => write!(f, "JSON"),
+ }
+ }
+}
+
/// EsModule source code that will be loaded into V8.
///
/// Users can implement `Into<ModuleInfo>` for different file types that
@@ -49,6 +193,7 @@ pub type ModuleLoadId = i32;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ModuleSource {
pub code: String,
+ pub module_type: ModuleType,
pub module_url_specified: String,
pub module_url_found: String,
}
@@ -57,6 +202,9 @@ pub type PrepareLoadFuture =
dyn Future<Output = (ModuleLoadId, Result<RecursiveModuleLoad, Error>)>;
pub type ModuleSourceFuture = dyn Future<Output = Result<ModuleSource, Error>>;
+type ModuleLoadFuture =
+ dyn Future<Output = Result<(ModuleRequest, ModuleSource), Error>>;
+
pub trait ModuleLoader {
/// Returns an absolute URL.
/// When implementing an spec-complaint VM, this should be exactly the
@@ -142,7 +290,7 @@ impl ModuleLoader for FsModuleLoader {
referrer: &str,
_is_main: bool,
) -> Result<ModuleSpecifier, Error> {
- Ok(crate::resolve_import(specifier, referrer)?)
+ Ok(resolve_import(specifier, referrer)?)
}
fn load(
@@ -159,9 +307,21 @@ impl ModuleLoader for FsModuleLoader {
module_specifier
))
})?;
+ let module_type = if let Some(extension) = path.extension() {
+ let ext = extension.to_string_lossy().to_lowercase();
+ if ext == "json" {
+ ModuleType::Json
+ } else {
+ ModuleType::JavaScript
+ }
+ } else {
+ ModuleType::JavaScript
+ };
+
let code = std::fs::read_to_string(path)?;
let module = ModuleSource {
code,
+ module_type,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
};
@@ -178,8 +338,9 @@ enum LoadInit {
Main(String),
/// Module specifier for side module.
Side(String),
- /// Dynamic import specifier with referrer.
- DynamicImport(String, String),
+ /// Dynamic import specifier with referrer and expected
+ /// module type (which is determined by import assertion).
+ DynamicImport(String, String, ModuleType),
}
#[derive(Debug, Eq, PartialEq)]
@@ -197,14 +358,15 @@ pub struct RecursiveModuleLoad {
// be randomized
pub id: ModuleLoadId,
pub root_module_id: Option<ModuleId>,
+ pub root_module_type: Option<ModuleType>,
pub state: LoadState,
pub module_map_rc: Rc<RefCell<ModuleMap>>,
// These two fields are copied from `module_map_rc`, but they are cloned ahead
// of time to avoid already-borrowed errors.
pub op_state: Rc<RefCell<OpState>>,
pub loader: Rc<dyn ModuleLoader>,
- pub pending: FuturesUnordered<Pin<Box<ModuleSourceFuture>>>,
- pub visited: HashSet<ModuleSpecifier>,
+ pub pending: FuturesUnordered<Pin<Box<ModuleLoadFuture>>>,
+ pub visited: HashSet<ModuleRequest>,
}
impl RecursiveModuleLoad {
@@ -220,10 +382,14 @@ impl RecursiveModuleLoad {
pub fn dynamic_import(
specifier: &str,
referrer: &str,
+ module_type: ModuleType,
module_map_rc: Rc<RefCell<ModuleMap>>,
) -> Self {
- let init =
- LoadInit::DynamicImport(specifier.to_string(), referrer.to_string());
+ let init = LoadInit::DynamicImport(
+ specifier.to_string(),
+ referrer.to_string(),
+ module_type,
+ );
Self::new(init, module_map_rc)
}
@@ -232,11 +398,22 @@ impl RecursiveModuleLoad {
}
fn new(init: LoadInit, module_map_rc: Rc<RefCell<ModuleMap>>) -> Self {
+ let id = {
+ let mut module_map = module_map_rc.borrow_mut();
+ let id = module_map.next_load_id;
+ module_map.next_load_id += 1;
+ id
+ };
let op_state = module_map_rc.borrow().op_state.clone();
let loader = module_map_rc.borrow().loader.clone();
+ let expected_module_type = match init {
+ LoadInit::DynamicImport(_, _, module_type) => module_type,
+ _ => ModuleType::JavaScript,
+ };
let mut load = Self {
- id: NEXT_LOAD_ID.fetch_add(1, Ordering::SeqCst),
+ id,
root_module_id: None,
+ root_module_type: None,
init,
state: LoadState::Init,
module_map_rc: module_map_rc.clone(),
@@ -247,10 +424,12 @@ impl RecursiveModuleLoad {
};
// Ignore the error here, let it be hit in `Stream::poll_next()`.
if let Ok(root_specifier) = load.resolve_root() {
- if let Some(module_id) =
- module_map_rc.borrow().get_id(root_specifier.as_str())
+ if let Some(module_id) = module_map_rc
+ .borrow()
+ .get_id(root_specifier.as_str(), expected_module_type)
{
load.root_module_id = Some(module_id);
+ load.root_module_type = Some(expected_module_type);
}
}
load
@@ -264,7 +443,7 @@ impl RecursiveModuleLoad {
LoadInit::Side(ref specifier) => {
self.loader.resolve(specifier, ".", false)
}
- LoadInit::DynamicImport(ref specifier, ref referrer) => {
+ LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
self.loader.resolve(specifier, referrer, false)
}
}
@@ -281,7 +460,7 @@ impl RecursiveModuleLoad {
let spec = self.loader.resolve(specifier, ".", false)?;
(spec, None)
}
- LoadInit::DynamicImport(ref specifier, ref referrer) => {
+ LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
let spec = self.loader.resolve(specifier, referrer, false)?;
(spec, Some(referrer.to_string()))
}
@@ -308,20 +487,29 @@ impl RecursiveModuleLoad {
pub fn register_and_recurse(
&mut self,
scope: &mut v8::HandleScope,
+ module_request: &ModuleRequest,
module_source: &ModuleSource,
) -> Result<(), Error> {
+ if module_request.expected_module_type != module_source.module_type {
+ return Err(generic_error(format!(
+ "Expected a \"{}\" module but loaded a \"{}\" module.",
+ module_request.expected_module_type, module_source.module_type,
+ )));
+ }
+
// Register the module in the module map unless it's already there. If the
// specified URL and the "true" URL are different, register the alias.
if module_source.module_url_specified != module_source.module_url_found {
self.module_map_rc.borrow_mut().alias(
&module_source.module_url_specified,
+ module_source.module_type,
&module_source.module_url_found,
);
}
let maybe_module_id = self
.module_map_rc
.borrow()
- .get_id(&module_source.module_url_found);
+ .get_id(&module_source.module_url_found, module_source.module_type);
let module_id = match maybe_module_id {
Some(id) => {
debug!(
@@ -330,12 +518,21 @@ impl RecursiveModuleLoad {
);
id
}
- None => self.module_map_rc.borrow_mut().new_module(
- scope,
- self.is_currently_loading_main_module(),
- &module_source.module_url_found,
- &module_source.code,
- )?,
+ None => match module_source.module_type {
+ ModuleType::JavaScript => {
+ self.module_map_rc.borrow_mut().new_es_module(
+ scope,
+ self.is_currently_loading_main_module(),
+ &module_source.module_url_found,
+ &module_source.code,
+ )?
+ }
+ ModuleType::Json => self.module_map_rc.borrow_mut().new_json_module(
+ scope,
+ &module_source.module_url_found,
+ &module_source.code,
+ )?,
+ },
};
// Recurse the module's imports. There are two cases for each import:
@@ -346,33 +543,43 @@ impl RecursiveModuleLoad {
// recursed synchronously here.
// This robustly ensures that the whole graph is in the module map before
// `LoadState::Done` is set.
- let specifier =
- crate::resolve_url(&module_source.module_url_found).unwrap();
let mut already_registered = VecDeque::new();
- already_registered.push_back((module_id, specifier.clone()));
- self.visited.insert(specifier);
- while let Some((module_id, referrer)) = already_registered.pop_front() {
+ already_registered.push_back((module_id, module_request.clone()));
+ self.visited.insert(module_request.clone());
+ while let Some((module_id, module_request)) = already_registered.pop_front()
+ {
+ let referrer = module_request.specifier.clone();
let imports = self
.module_map_rc
.borrow()
- .get_children(module_id)
+ .get_requested_modules(module_id)
.unwrap()
.clone();
- for specifier in imports {
- if !self.visited.contains(&specifier) {
- if let Some(module_id) =
- self.module_map_rc.borrow().get_id(specifier.as_str())
- {
- already_registered.push_back((module_id, specifier.clone()));
+ for module_request in imports {
+ if !self.visited.contains(&module_request) {
+ if let Some(module_id) = self.module_map_rc.borrow().get_id(
+ module_request.specifier.as_str(),
+ module_request.expected_module_type,
+ ) {
+ already_registered.push_back((module_id, module_request.clone()));
} else {
- let fut = self.loader.load(
- &specifier,
- Some(referrer.clone()),
- self.is_dynamic_import(),
- );
+ let referrer = referrer.clone();
+ let request = module_request.clone();
+ let loader = self.loader.clone();
+ let is_dynamic_import = self.is_dynamic_import();
+ let fut = async move {
+ let load_result = loader
+ .load(
+ &request.specifier,
+ Some(referrer.clone()),
+ is_dynamic_import,
+ )
+ .await;
+ load_result.map(|s| (request, s))
+ };
self.pending.push(fut.boxed_local());
}
- self.visited.insert(specifier);
+ self.visited.insert(module_request);
}
}
}
@@ -380,6 +587,7 @@ impl RecursiveModuleLoad {
// Update `self.state` however applicable.
if self.state == LoadState::LoadingRoot {
self.root_module_id = Some(module_id);
+ self.root_module_type = Some(module_source.module_type);
self.state = LoadState::LoadingImports;
}
if self.pending.is_empty() {
@@ -391,7 +599,7 @@ impl RecursiveModuleLoad {
}
impl Stream for RecursiveModuleLoad {
- type Item = Result<ModuleSource, Error>;
+ type Item = Result<(ModuleRequest, ModuleSource), Error>;
fn poll_next(
self: Pin<&mut Self>,
@@ -407,31 +615,51 @@ impl Stream for RecursiveModuleLoad {
Err(error) => return Poll::Ready(Some(Err(error))),
};
let load_fut = if let Some(_module_id) = inner.root_module_id {
+ // FIXME(bartlomieju): this is very bad
// The root module is already in the module map.
// TODO(nayeemrmn): In this case we would ideally skip to
// `LoadState::LoadingImports` and synchronously recurse the imports
// like the bottom of `RecursiveModuleLoad::register_and_recurse()`.
// But the module map cannot be borrowed here. Instead fake a load
// event so it gets passed to that function and recursed eventually.
- futures::future::ok(ModuleSource {
+ let module_type = inner.root_module_type.unwrap();
+ let module_request = ModuleRequest {
+ specifier: module_specifier.clone(),
+ expected_module_type: module_type,
+ };
+ let module_source = ModuleSource {
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
// The code will be discarded, since this module is already in the
// module map.
code: Default::default(),
- })
- .boxed()
+ module_type,
+ };
+ futures::future::ok((module_request, module_source)).boxed()
} else {
let maybe_referrer = match inner.init {
- LoadInit::DynamicImport(_, ref referrer) => {
- crate::resolve_url(referrer).ok()
+ LoadInit::DynamicImport(_, ref referrer, _) => {
+ resolve_url(referrer).ok()
}
_ => None,
};
- inner
- .loader
- .load(&module_specifier, maybe_referrer, inner.is_dynamic_import())
- .boxed_local()
+ let expected_module_type = match inner.init {
+ LoadInit::DynamicImport(_, _, module_type) => module_type,
+ _ => ModuleType::JavaScript,
+ };
+ let module_request = ModuleRequest {
+ specifier: module_specifier.clone(),
+ expected_module_type,
+ };
+ let loader = inner.loader.clone();
+ let is_dynamic_import = inner.is_dynamic_import();
+ async move {
+ let result = loader
+ .load(&module_specifier, maybe_referrer, is_dynamic_import)
+ .await;
+ result.map(|s| (module_request, s))
+ }
+ .boxed_local()
};
inner.pending.push(load_fut);
inner.state = LoadState::LoadingRoot;
@@ -449,12 +677,22 @@ impl Stream for RecursiveModuleLoad {
}
}
+/// Describes what is the expected type of module, usually
+/// it's `ModuleType::JavaScript`, but if there were import assertions
+/// it might be `ModuleType::Json`.
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+pub struct ModuleRequest {
+ pub specifier: ModuleSpecifier,
+ pub expected_module_type: ModuleType,
+}
+
pub struct ModuleInfo {
pub id: ModuleId,
// Used in "bindings.rs" for "import.meta.main" property value.
pub main: bool,
pub name: String,
- pub import_specifiers: Vec<ModuleSpecifier>,
+ pub requests: Vec<ModuleRequest>,
+ pub module_type: ModuleType,
}
/// A symbolic module entity.
@@ -473,8 +711,9 @@ pub struct ModuleMap {
ids_by_handle: HashMap<v8::Global<v8::Module>, ModuleId>,
handles_by_id: HashMap<ModuleId, v8::Global<v8::Module>>,
info: HashMap<ModuleId, ModuleInfo>,
- by_name: HashMap<String, SymbolicModule>,
+ by_name: HashMap<(String, ModuleType), SymbolicModule>,
next_module_id: ModuleId,
+ next_load_id: ModuleLoadId,
// Handling of futures for loading module sources
pub loader: Rc<dyn ModuleLoader>,
@@ -485,6 +724,10 @@ pub struct ModuleMap {
FuturesUnordered<Pin<Box<PrepareLoadFuture>>>,
pub(crate) pending_dynamic_imports:
FuturesUnordered<StreamFuture<RecursiveModuleLoad>>,
+
+ // This store is used temporarly, to forward parsed JSON
+ // value from `new_json_module` to `json_module_evaluation_steps`
+ json_value_store: HashMap<v8::Global<v8::Module>, v8::Global<v8::Value>>,
}
impl ModuleMap {
@@ -498,20 +741,27 @@ impl ModuleMap {
info: HashMap::new(),
by_name: HashMap::new(),
next_module_id: 1,
+ next_load_id: 1,
loader,
op_state,
dynamic_import_map: HashMap::new(),
preparing_dynamic_imports: FuturesUnordered::new(),
pending_dynamic_imports: FuturesUnordered::new(),
+ json_value_store: HashMap::new(),
}
}
/// Get module id, following all aliases in case of module specifier
/// that had been redirected.
- pub fn get_id(&self, name: &str) -> Option<ModuleId> {
+ pub fn get_id(
+ &self,
+ name: &str,
+ module_type: ModuleType,
+ ) -> Option<ModuleId> {
let mut mod_name = name;
loop {
- let symbolic_module = self.by_name.get(mod_name)?;
+ let symbolic_module =
+ self.by_name.get(&(mod_name.to_string(), module_type))?;
match symbolic_module {
SymbolicModule::Alias(target) => {
mod_name = target;
@@ -521,8 +771,64 @@ impl ModuleMap {
}
}
+ fn new_json_module(
+ &mut self,
+ scope: &mut v8::HandleScope,
+ name: &str,
+ source: &str,
+ ) -> Result<ModuleId, Error> {
+ let name_str = v8::String::new(scope, name).unwrap();
+ let source_str = v8::String::new(scope, strip_bom(source)).unwrap();
+
+ let tc_scope = &mut v8::TryCatch::new(scope);
+
+ let parsed_json = match v8::json::parse(tc_scope, source_str) {
+ Some(parsed_json) => parsed_json,
+ None => {
+ assert!(tc_scope.has_caught());
+ let exception = tc_scope.exception().unwrap();
+ let err = exception_to_err_result(tc_scope, exception, false)
+ .map_err(|err| attach_handle_to_error(tc_scope, err, exception));
+ return err;
+ }
+ };
+
+ let export_names = [v8::String::new(tc_scope, "default").unwrap()];
+ let module = v8::Module::create_synthetic_module(
+ tc_scope,
+ name_str,
+ &export_names,
+ json_module_evaluation_steps,
+ );
+
+ let handle = v8::Global::<v8::Module>::new(tc_scope, module);
+ let value_handle = v8::Global::<v8::Value>::new(tc_scope, parsed_json);
+ self.json_value_store.insert(handle.clone(), value_handle);
+
+ let id = self.next_module_id;
+ self.next_module_id += 1;
+ self.by_name.insert(
+ (name.to_string(), ModuleType::Json),
+ SymbolicModule::Mod(id),
+ );
+ self.handles_by_id.insert(id, handle.clone());
+ self.ids_by_handle.insert(handle, id);
+ self.info.insert(
+ id,
+ ModuleInfo {
+ id,
+ main: false,
+ name: name.to_string(),
+ requests: vec![],
+ module_type: ModuleType::Json,
+ },
+ );
+
+ Ok(id)
+ }
+
// Create and compile an ES module.
- pub(crate) fn new_module(
+ pub(crate) fn new_es_module(
&mut self,
scope: &mut v8::HandleScope,
main: bool,
@@ -547,7 +853,7 @@ impl ModuleMap {
let module = maybe_module.unwrap();
- let mut import_specifiers: Vec<ModuleSpecifier> = vec![];
+ let mut requests: Vec<ModuleRequest> = vec![];
let module_requests = module.get_module_requests();
for i in 0..module_requests.length() {
let module_request = v8::Local::<v8::ModuleRequest>::try_from(
@@ -557,9 +863,31 @@ impl ModuleMap {
let import_specifier = module_request
.get_specifier()
.to_rust_string_lossy(tc_scope);
+
+ let import_assertions = module_request.get_import_assertions();
+
+ let assertions = parse_import_assertions(
+ tc_scope,
+ import_assertions,
+ ImportAssertionsKind::StaticImport,
+ );
+
+ // FIXME(bartomieju): there are no stack frames if exception
+ // is thrown here
+ validate_import_assertions(tc_scope, &assertions);
+ if tc_scope.has_caught() {
+ let e = tc_scope.exception().unwrap();
+ return exception_to_err_result(tc_scope, e, false);
+ }
+
let module_specifier =
self.loader.resolve(&import_specifier, name, false)?;
- import_specifiers.push(module_specifier);
+ let expected_module_type = get_module_type_from_assertions(&assertions);
+ let request = ModuleRequest {
+ specifier: module_specifier,
+ expected_module_type,
+ };
+ requests.push(request);
}
if main {
@@ -576,9 +904,10 @@ impl ModuleMap {
let handle = v8::Global::<v8::Module>::new(tc_scope, module);
let id = self.next_module_id;
self.next_module_id += 1;
- self
- .by_name
- .insert(name.to_string(), SymbolicModule::Mod(id));
+ self.by_name.insert(
+ (name.to_string(), ModuleType::JavaScript),
+ SymbolicModule::Mod(id),
+ );
self.handles_by_id.insert(id, handle.clone());
self.ids_by_handle.insert(handle, id);
self.info.insert(
@@ -587,30 +916,44 @@ impl ModuleMap {
id,
main,
name: name.to_string(),
- import_specifiers,
+ requests,
+ module_type: ModuleType::JavaScript,
},
);
Ok(id)
}
- pub fn get_children(&self, id: ModuleId) -> Option<&Vec<ModuleSpecifier>> {
- self.info.get(&id).map(|i| &i.import_specifiers)
+ pub fn get_requested_modules(
+ &self,
+ id: ModuleId,
+ ) -> Option<&Vec<ModuleRequest>> {
+ self.info.get(&id).map(|i| &i.requests)
}
- pub fn is_registered(&self, specifier: &ModuleSpecifier) -> bool {
- self.get_id(specifier.as_str()).is_some()
+ pub fn is_registered(
+ &self,
+ specifier: &ModuleSpecifier,
+ module_type: ModuleType,
+ ) -> bool {
+ if let Some(id) = self.get_id(specifier.as_str(), module_type) {
+ let info = self.get_info_by_id(&id).unwrap();
+ return info.module_type == module_type;
+ }
+
+ false
}
- pub fn alias(&mut self, name: &str, target: &str) {
- self
- .by_name
- .insert(name.to_string(), SymbolicModule::Alias(target.to_string()));
+ pub fn alias(&mut self, name: &str, module_type: ModuleType, target: &str) {
+ self.by_name.insert(
+ (name.to_string(), module_type),
+ SymbolicModule::Alias(target.to_string()),
+ );
}
#[cfg(test)]
- pub fn is_alias(&self, name: &str) -> bool {
- let cond = self.by_name.get(name);
+ pub fn is_alias(&self, name: &str, module_type: ModuleType) -> bool {
+ let cond = self.by_name.get(&(name.to_string(), module_type));
matches!(cond, Some(SymbolicModule::Alias(_)))
}
@@ -656,11 +999,13 @@ impl ModuleMap {
module_map_rc: Rc<RefCell<ModuleMap>>,
specifier: &str,
referrer: &str,
+ module_type: ModuleType,
resolver_handle: v8::Global<v8::PromiseResolver>,
) {
let load = RecursiveModuleLoad::dynamic_import(
specifier,
referrer,
+ module_type,
module_map_rc.clone(),
);
module_map_rc
@@ -673,7 +1018,10 @@ impl ModuleMap {
.resolve(specifier, referrer, false);
let fut = match resolve_result {
Ok(module_specifier) => {
- if module_map_rc.borrow().is_registered(&module_specifier) {
+ if module_map_rc
+ .borrow()
+ .is_registered(&module_specifier, module_type)
+ {
async move { (load.id, Ok(load)) }.boxed_local()
} else {
async move { (load.id, load.prepare().await.map(|()| load)) }
@@ -699,13 +1047,16 @@ impl ModuleMap {
scope: &mut v8::HandleScope<'s>,
specifier: &str,
referrer: &str,
+ import_assertions: HashMap<String, String>,
) -> Option<v8::Local<'s, v8::Module>> {
let resolved_specifier = self
.loader
.resolve(specifier, referrer, false)
.expect("Module should have been already resolved");
- if let Some(id) = self.get_id(resolved_specifier.as_str()) {
+ let module_type = get_module_type_from_assertions(&import_assertions);
+
+ if let Some(id) = self.get_id(resolved_specifier.as_str(), module_type) {
if let Some(handle) = self.get_handle(id) {
return Some(v8::Local::new(scope, handle));
}
@@ -818,6 +1169,7 @@ mod tests {
match mock_source_code(&inner.url) {
Some(src) => Poll::Ready(Ok(ModuleSource {
code: src.0.to_owned(),
+ module_type: ModuleType::JavaScript,
module_url_specified: inner.url.clone(),
module_url_found: src.1.to_owned(),
})),
@@ -841,7 +1193,7 @@ mod tests {
eprintln!(">> RESOLVING, S: {}, R: {}", specifier, referrer);
- let output_specifier = match crate::resolve_import(specifier, referrer) {
+ let output_specifier = match resolve_import(specifier, referrer) {
Ok(specifier) => specifier,
Err(..) => return Err(MockError::ResolveErr.into()),
};
@@ -905,7 +1257,7 @@ mod tests {
module_loader: Some(loader),
..Default::default()
});
- let spec = crate::resolve_url("file:///a.js").unwrap();
+ let spec = resolve_url("file:///a.js").unwrap();
let a_id_fut = runtime.load_main_module(&spec, None);
let a_id = futures::executor::block_on(a_id_fut).expect("Failed to load");
@@ -925,26 +1277,47 @@ mod tests {
let module_map_rc = JsRuntime::module_map(runtime.v8_isolate());
let modules = module_map_rc.borrow();
- assert_eq!(modules.get_id("file:///a.js"), Some(a_id));
- let b_id = modules.get_id("file:///b.js").unwrap();
- let c_id = modules.get_id("file:///c.js").unwrap();
- let d_id = modules.get_id("file:///d.js").unwrap();
assert_eq!(
- modules.get_children(a_id),
+ modules.get_id("file:///a.js", ModuleType::JavaScript),
+ Some(a_id)
+ );
+ let b_id = modules
+ .get_id("file:///b.js", ModuleType::JavaScript)
+ .unwrap();
+ let c_id = modules
+ .get_id("file:///c.js", ModuleType::JavaScript)
+ .unwrap();
+ let d_id = modules
+ .get_id("file:///d.js", ModuleType::JavaScript)
+ .unwrap();
+ assert_eq!(
+ modules.get_requested_modules(a_id),
Some(&vec![
- crate::resolve_url("file:///b.js").unwrap(),
- crate::resolve_url("file:///c.js").unwrap()
+ ModuleRequest {
+ specifier: resolve_url("file:///b.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ },
+ ModuleRequest {
+ specifier: resolve_url("file:///c.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ },
])
);
assert_eq!(
- modules.get_children(b_id),
- Some(&vec![crate::resolve_url("file:///c.js").unwrap()])
+ modules.get_requested_modules(b_id),
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///c.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ },])
);
assert_eq!(
- modules.get_children(c_id),
- Some(&vec![crate::resolve_url("file:///d.js").unwrap()])
+ modules.get_requested_modules(c_id),
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///d.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ },])
);
- assert_eq!(modules.get_children(d_id), Some(&vec![]));
+ assert_eq!(modules.get_requested_modules(d_id), Some(&vec![]));
}
const CIRCULAR1_SRC: &str = r#"
@@ -980,7 +1353,7 @@ mod tests {
self.count.fetch_add(1, Ordering::Relaxed);
assert_eq!(specifier, "./b.js");
assert_eq!(referrer, "file:///a.js");
- let s = crate::resolve_import(specifier, referrer).unwrap();
+ let s = resolve_import(specifier, referrer).unwrap();
Ok(s)
}
@@ -1037,7 +1410,7 @@ mod tests {
let mut module_map = module_map_rc.borrow_mut();
let specifier_a = "file:///a.js".to_string();
let mod_a = module_map
- .new_module(
+ .new_es_module(
scope,
true,
&specifier_a,
@@ -1051,21 +1424,24 @@ mod tests {
.unwrap();
assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
- let imports = module_map.get_children(mod_a);
+ let imports = module_map.get_requested_modules(mod_a);
assert_eq!(
imports,
- Some(&vec![crate::resolve_url("file:///b.js").unwrap()])
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///b.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ },])
);
let mod_b = module_map
- .new_module(
+ .new_es_module(
scope,
false,
"file:///b.js",
"export function b() { return 'b' }",
)
.unwrap();
- let imports = module_map.get_children(mod_b).unwrap();
+ let imports = module_map.get_requested_modules(mod_b).unwrap();
assert_eq!(imports.len(), 0);
(mod_a, mod_b)
};
@@ -1082,6 +1458,109 @@ mod tests {
}
#[test]
+ fn test_json_module() {
+ #[derive(Default)]
+ struct ModsLoader {
+ pub count: Arc<AtomicUsize>,
+ }
+
+ impl ModuleLoader for ModsLoader {
+ fn resolve(
+ &self,
+ specifier: &str,
+ referrer: &str,
+ _is_main: bool,
+ ) -> Result<ModuleSpecifier, Error> {
+ self.count.fetch_add(1, Ordering::Relaxed);
+ assert_eq!(specifier, "./b.json");
+ assert_eq!(referrer, "file:///a.js");
+ let s = resolve_import(specifier, referrer).unwrap();
+ Ok(s)
+ }
+
+ fn load(
+ &self,
+ _module_specifier: &ModuleSpecifier,
+ _maybe_referrer: Option<ModuleSpecifier>,
+ _is_dyn_import: bool,
+ ) -> Pin<Box<ModuleSourceFuture>> {
+ unreachable!()
+ }
+ }
+
+ let loader = Rc::new(ModsLoader::default());
+
+ let resolve_count = loader.count.clone();
+
+ let mut runtime = JsRuntime::new(RuntimeOptions {
+ module_loader: Some(loader),
+ ..Default::default()
+ });
+
+ runtime
+ .execute_script(
+ "setup.js",
+ r#"
+ function assert(cond) {
+ if (!cond) {
+ throw Error("assert");
+ }
+ }
+ "#,
+ )
+ .unwrap();
+
+ let module_map_rc = JsRuntime::module_map(runtime.v8_isolate());
+
+ let (mod_a, mod_b) = {
+ let scope = &mut runtime.handle_scope();
+ let mut module_map = module_map_rc.borrow_mut();
+ let specifier_a = "file:///a.js".to_string();
+ let mod_a = module_map
+ .new_es_module(
+ scope,
+ true,
+ &specifier_a,
+ r#"
+ import jsonData from './b.json' assert {type: "json"};
+ assert(jsonData.a == "b");
+ assert(jsonData.c.d == 10);
+ "#,
+ )
+ .unwrap();
+
+ let imports = module_map.get_requested_modules(mod_a);
+ assert_eq!(
+ imports,
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///b.json").unwrap(),
+ expected_module_type: ModuleType::Json,
+ },])
+ );
+
+ let mod_b = module_map
+ .new_json_module(
+ scope,
+ "file:///b.json",
+ "{\"a\": \"b\", \"c\": {\"d\": 10}}",
+ )
+ .unwrap();
+ let imports = module_map.get_requested_modules(mod_b).unwrap();
+ assert_eq!(imports.len(), 0);
+ (mod_a, mod_b)
+ };
+
+ runtime.instantiate_module(mod_b).unwrap();
+ assert_eq!(resolve_count.load(Ordering::SeqCst), 1);
+
+ runtime.instantiate_module(mod_a).unwrap();
+
+ let receiver = runtime.mod_evaluate(mod_a);
+ futures::executor::block_on(runtime.run_event_loop(false)).unwrap();
+ futures::executor::block_on(receiver).unwrap().unwrap();
+ }
+
+ #[test]
fn dyn_import_err() {
#[derive(Clone, Default)]
struct DynImportErrLoader {
@@ -1098,7 +1577,7 @@ mod tests {
self.count.fetch_add(1, Ordering::Relaxed);
assert_eq!(specifier, "/foo.js");
assert_eq!(referrer, "file:///dyn_import2.js");
- let s = crate::resolve_import(specifier, referrer).unwrap();
+ let s = resolve_import(specifier, referrer).unwrap();
Ok(s)
}
@@ -1159,7 +1638,7 @@ mod tests {
assert!(c < 7);
assert_eq!(specifier, "./b.js");
assert_eq!(referrer, "file:///dyn_import3.js");
- let s = crate::resolve_import(specifier, referrer).unwrap();
+ let s = resolve_import(specifier, referrer).unwrap();
Ok(s)
}
@@ -1174,6 +1653,7 @@ mod tests {
module_url_specified: specifier.to_string(),
module_url_found: specifier.to_string(),
code: "export function b() { return 'b' }".to_owned(),
+ module_type: ModuleType::JavaScript,
};
async move { Ok(info) }.boxed()
}
@@ -1292,7 +1772,7 @@ mod tests {
_is_main: bool,
) -> Result<ModuleSpecifier, Error> {
self.resolve_count.fetch_add(1, Ordering::Relaxed);
- let s = crate::resolve_import(specifier, referrer).unwrap();
+ let s = resolve_import(specifier, referrer).unwrap();
Ok(s)
}
@@ -1320,6 +1800,7 @@ mod tests {
module_url_specified: specifier.to_string(),
module_url_found: specifier.to_string(),
code: code.to_owned(),
+ module_type: ModuleType::JavaScript,
};
async move { Ok(info) }.boxed()
}
@@ -1357,7 +1838,7 @@ mod tests {
});
let fut = async move {
- let spec = crate::resolve_url("file:///circular1.js").unwrap();
+ let spec = resolve_url("file:///circular1.js").unwrap();
let result = runtime.load_main_module(&spec, None).await;
assert!(result.is_ok());
let circular1_id = result.unwrap();
@@ -1377,26 +1858,47 @@ mod tests {
let module_map_rc = JsRuntime::module_map(runtime.v8_isolate());
let modules = module_map_rc.borrow();
- assert_eq!(modules.get_id("file:///circular1.js"), Some(circular1_id));
- let circular2_id = modules.get_id("file:///circular2.js").unwrap();
+ assert_eq!(
+ modules.get_id("file:///circular1.js", ModuleType::JavaScript),
+ Some(circular1_id)
+ );
+ let circular2_id = modules
+ .get_id("file:///circular2.js", ModuleType::JavaScript)
+ .unwrap();
assert_eq!(
- modules.get_children(circular1_id),
- Some(&vec![crate::resolve_url("file:///circular2.js").unwrap()])
+ modules.get_requested_modules(circular1_id),
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///circular2.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ }])
);
assert_eq!(
- modules.get_children(circular2_id),
- Some(&vec![crate::resolve_url("file:///circular3.js").unwrap()])
+ modules.get_requested_modules(circular2_id),
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///circular3.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ }])
);
- assert!(modules.get_id("file:///circular3.js").is_some());
- let circular3_id = modules.get_id("file:///circular3.js").unwrap();
+ assert!(modules
+ .get_id("file:///circular3.js", ModuleType::JavaScript)
+ .is_some());
+ let circular3_id = modules
+ .get_id("file:///circular3.js", ModuleType::JavaScript)
+ .unwrap();
assert_eq!(
- modules.get_children(circular3_id),
+ modules.get_requested_modules(circular3_id),
Some(&vec![
- crate::resolve_url("file:///circular1.js").unwrap(),
- crate::resolve_url("file:///circular2.js").unwrap()
+ ModuleRequest {
+ specifier: resolve_url("file:///circular1.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ },
+ ModuleRequest {
+ specifier: resolve_url("file:///circular2.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ }
])
);
}
@@ -1428,43 +1930,62 @@ mod tests {
..Default::default()
});
- let fut = async move {
- let spec = crate::resolve_url("file:///redirect1.js").unwrap();
- let result = runtime.load_main_module(&spec, None).await;
- println!(">> result {:?}", result);
- assert!(result.is_ok());
- let redirect1_id = result.unwrap();
- let _ = runtime.mod_evaluate(redirect1_id);
- runtime.run_event_loop(false).await.unwrap();
- let l = loads.lock();
- assert_eq!(
- l.to_vec(),
- vec![
- "file:///redirect1.js",
- "file:///redirect2.js",
- "file:///dir/redirect3.js"
- ]
- );
+ let fut =
+ async move {
+ let spec = resolve_url("file:///redirect1.js").unwrap();
+ let result = runtime.load_main_module(&spec, None).await;
+ println!(">> result {:?}", result);
+ assert!(result.is_ok());
+ let redirect1_id = result.unwrap();
+ let _ = runtime.mod_evaluate(redirect1_id);
+ runtime.run_event_loop(false).await.unwrap();
+ let l = loads.lock();
+ assert_eq!(
+ l.to_vec(),
+ vec![
+ "file:///redirect1.js",
+ "file:///redirect2.js",
+ "file:///dir/redirect3.js"
+ ]
+ );
- let module_map_rc = JsRuntime::module_map(runtime.v8_isolate());
- let modules = module_map_rc.borrow();
+ let module_map_rc = JsRuntime::module_map(runtime.v8_isolate());
+ let modules = module_map_rc.borrow();
- assert_eq!(modules.get_id("file:///redirect1.js"), Some(redirect1_id));
+ assert_eq!(
+ modules.get_id("file:///redirect1.js", ModuleType::JavaScript),
+ Some(redirect1_id)
+ );
- let redirect2_id = modules.get_id("file:///dir/redirect2.js").unwrap();
- assert!(modules.is_alias("file:///redirect2.js"));
- assert!(!modules.is_alias("file:///dir/redirect2.js"));
- assert_eq!(modules.get_id("file:///redirect2.js"), Some(redirect2_id));
+ let redirect2_id = modules
+ .get_id("file:///dir/redirect2.js", ModuleType::JavaScript)
+ .unwrap();
+ assert!(
+ modules.is_alias("file:///redirect2.js", ModuleType::JavaScript)
+ );
+ assert!(
+ !modules.is_alias("file:///dir/redirect2.js", ModuleType::JavaScript)
+ );
+ assert_eq!(
+ modules.get_id("file:///redirect2.js", ModuleType::JavaScript),
+ Some(redirect2_id)
+ );
- let redirect3_id = modules.get_id("file:///redirect3.js").unwrap();
- assert!(modules.is_alias("file:///dir/redirect3.js"));
- assert!(!modules.is_alias("file:///redirect3.js"));
- assert_eq!(
- modules.get_id("file:///dir/redirect3.js"),
- Some(redirect3_id)
- );
- }
- .boxed_local();
+ let redirect3_id = modules
+ .get_id("file:///redirect3.js", ModuleType::JavaScript)
+ .unwrap();
+ assert!(
+ modules.is_alias("file:///dir/redirect3.js", ModuleType::JavaScript)
+ );
+ assert!(
+ !modules.is_alias("file:///redirect3.js", ModuleType::JavaScript)
+ );
+ assert_eq!(
+ modules.get_id("file:///dir/redirect3.js", ModuleType::JavaScript),
+ Some(redirect3_id)
+ );
+ }
+ .boxed_local();
futures::executor::block_on(fut);
}
@@ -1494,7 +2015,7 @@ mod tests {
module_loader: Some(loader),
..Default::default()
});
- let spec = crate::resolve_url("file:///main.js").unwrap();
+ let spec = resolve_url("file:///main.js").unwrap();
let mut recursive_load =
runtime.load_main_module(&spec, None).boxed_local();
@@ -1543,7 +2064,7 @@ mod tests {
module_loader: Some(loader),
..Default::default()
});
- let spec = crate::resolve_url("file:///bad_import.js").unwrap();
+ let spec = resolve_url("file:///bad_import.js").unwrap();
let mut load_fut = runtime.load_main_module(&spec, None).boxed_local();
let result = load_fut.poll_unpin(cx);
if let Poll::Ready(Err(err)) = result {
@@ -1577,7 +2098,7 @@ mod tests {
// In default resolution code should be empty.
// Instead we explicitly pass in our own code.
// The behavior should be very similar to /a.js.
- let spec = crate::resolve_url("file:///main_with_code.js").unwrap();
+ let spec = resolve_url("file:///main_with_code.js").unwrap();
let main_id_fut = runtime
.load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned()))
.boxed_local();
@@ -1596,35 +2117,56 @@ mod tests {
let module_map_rc = JsRuntime::module_map(runtime.v8_isolate());
let modules = module_map_rc.borrow();
- assert_eq!(modules.get_id("file:///main_with_code.js"), Some(main_id));
- let b_id = modules.get_id("file:///b.js").unwrap();
- let c_id = modules.get_id("file:///c.js").unwrap();
- let d_id = modules.get_id("file:///d.js").unwrap();
+ assert_eq!(
+ modules.get_id("file:///main_with_code.js", ModuleType::JavaScript),
+ Some(main_id)
+ );
+ let b_id = modules
+ .get_id("file:///b.js", ModuleType::JavaScript)
+ .unwrap();
+ let c_id = modules
+ .get_id("file:///c.js", ModuleType::JavaScript)
+ .unwrap();
+ let d_id = modules
+ .get_id("file:///d.js", ModuleType::JavaScript)
+ .unwrap();
assert_eq!(
- modules.get_children(main_id),
+ modules.get_requested_modules(main_id),
Some(&vec![
- crate::resolve_url("file:///b.js").unwrap(),
- crate::resolve_url("file:///c.js").unwrap()
+ ModuleRequest {
+ specifier: resolve_url("file:///b.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ },
+ ModuleRequest {
+ specifier: resolve_url("file:///c.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ }
])
);
assert_eq!(
- modules.get_children(b_id),
- Some(&vec![crate::resolve_url("file:///c.js").unwrap()])
+ modules.get_requested_modules(b_id),
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///c.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ }])
);
assert_eq!(
- modules.get_children(c_id),
- Some(&vec![crate::resolve_url("file:///d.js").unwrap()])
+ modules.get_requested_modules(c_id),
+ Some(&vec![ModuleRequest {
+ specifier: resolve_url("file:///d.js").unwrap(),
+ expected_module_type: ModuleType::JavaScript,
+ }])
);
- assert_eq!(modules.get_children(d_id), Some(&vec![]));
+ assert_eq!(modules.get_requested_modules(d_id), Some(&vec![]));
}
#[test]
fn main_and_side_module() {
struct ModsLoader {}
- let main_specifier = crate::resolve_url("file:///main_module.js").unwrap();
- let side_specifier = crate::resolve_url("file:///side_module.js").unwrap();
+ let main_specifier = resolve_url("file:///main_module.js").unwrap();
+ let side_specifier = resolve_url("file:///side_module.js").unwrap();
impl ModuleLoader for ModsLoader {
fn resolve(
@@ -1633,7 +2175,7 @@ mod tests {
referrer: &str,
_is_main: bool,
) -> Result<ModuleSpecifier, Error> {
- let s = crate::resolve_import(specifier, referrer).unwrap();
+ let s = resolve_import(specifier, referrer).unwrap();
Ok(s)
}
@@ -1648,11 +2190,13 @@ mod tests {
module_url_specified: "file:///main_module.js".to_string(),
module_url_found: "file:///main_module.js".to_string(),
code: "if (!import.meta.main) throw Error();".to_owned(),
+ module_type: ModuleType::JavaScript,
}),
"file:///side_module.js" => Ok(ModuleSource {
module_url_specified: "file:///side_module.js".to_string(),
module_url_found: "file:///side_module.js".to_string(),
code: "if (import.meta.main) throw Error();".to_owned(),
+ module_type: ModuleType::JavaScript,
}),
_ => unreachable!(),
};
diff --git a/core/runtime.rs b/core/runtime.rs
index 15bb103ba..643a4198d 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -1259,12 +1259,15 @@ impl JsRuntime {
if let Some(load_stream_result) = maybe_result {
match load_stream_result {
- Ok(info) => {
+ Ok((request, info)) => {
// A module (not necessarily the one dynamically imported) has been
// fetched. Create and register it, and if successful, poll for the
// next recursive-load event related to this dynamic import.
- let register_result =
- load.register_and_recurse(&mut self.handle_scope(), &info);
+ let register_result = load.register_and_recurse(
+ &mut self.handle_scope(),
+ &request,
+ &info,
+ );
match register_result {
Ok(()) => {
@@ -1417,7 +1420,7 @@ impl JsRuntime {
) -> Result<ModuleId, Error> {
let module_map_rc = Self::module_map(self.v8_isolate());
if let Some(code) = code {
- module_map_rc.borrow_mut().new_module(
+ module_map_rc.borrow_mut().new_es_module(
&mut self.handle_scope(),
// main module
true,
@@ -1429,10 +1432,10 @@ impl JsRuntime {
let mut load =
ModuleMap::load_main(module_map_rc.clone(), specifier.as_str()).await?;
- while let Some(info_result) = load.next().await {
- let info = info_result?;
+ while let Some(load_result) = load.next().await {
+ let (request, info) = load_result?;
let scope = &mut self.handle_scope();
- load.register_and_recurse(scope, &info)?;
+ load.register_and_recurse(scope, &request, &info)?;
}
let root_id = load.root_module_id.expect("Root module should be loaded");
@@ -1454,7 +1457,7 @@ impl JsRuntime {
) -> Result<ModuleId, Error> {
let module_map_rc = Self::module_map(self.v8_isolate());
if let Some(code) = code {
- module_map_rc.borrow_mut().new_module(
+ module_map_rc.borrow_mut().new_es_module(
&mut self.handle_scope(),
// not main module
false,
@@ -1466,10 +1469,10 @@ impl JsRuntime {
let mut load =
ModuleMap::load_side(module_map_rc.clone(), specifier.as_str()).await?;
- while let Some(info_result) = load.next().await {
- let info = info_result?;
+ while let Some(load_result) = load.next().await {
+ let (request, info) = load_result?;
let scope = &mut self.handle_scope();
- load.register_and_recurse(scope, &info)?;
+ load.register_and_recurse(scope, &request, &info)?;
}
let root_id = load.root_module_id.expect("Root module should be loaded");
@@ -1630,6 +1633,7 @@ pub mod tests {
use crate::error::custom_error;
use crate::modules::ModuleSource;
use crate::modules::ModuleSourceFuture;
+ use crate::modules::ModuleType;
use crate::op_async;
use crate::op_sync;
use crate::ZeroCopyBuf;
@@ -2642,6 +2646,7 @@ assertEquals(1, notify_return_value);
code: "console.log('hello world');".to_string(),
module_url_specified: "file:///main.js".to_string(),
module_url_found: "file:///main.js".to_string(),
+ module_type: ModuleType::JavaScript,
})
}
.boxed_local()
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index 0d9c0a5e2..dc5bb12a7 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -6352,52 +6352,25 @@
"semantics": {
"scripting-1": {
"the-script-element": {
- "import-assertions": {
- "dynamic-import-with-assertion-argument.any.html": [
- "Dynamic import with an unsupported type assertion should fail"
- ],
- "dynamic-import-with-assertion-argument.any.worker.html": [
- "Dynamic import with an unsupported type assertion should fail"
- ]
- },
+ "import-assertions": true,
"json-module": {
"charset-bom.any.html": [
- "UTF-8 BOM should be stripped when decoding JSON module script",
"UTF-16BE BOM should result in parse error in JSON module script",
"UTF-16LE BOM should result in parse error in JSON module script"
],
"charset-bom.any.worker.html": [
- "UTF-8 BOM should be stripped when decoding JSON module script",
"UTF-16BE BOM should result in parse error in JSON module script",
"UTF-16LE BOM should result in parse error in JSON module script"
],
"invalid-content-type.any.html": true,
"invalid-content-type.any.worker.html": true,
- "non-object.any.html": [
- "Non-object: null",
- "Non-object: true",
- "Non-object: false",
- "Non-object: string",
- "Non-object: array"
- ],
- "non-object.any.worker.html": [
- "Non-object: null",
- "Non-object: true",
- "Non-object: false",
- "Non-object: string",
- "Non-object: array"
- ],
+ "non-object.any.html": true,
+ "non-object.any.worker.html": true,
"repeated-imports.any.html": [
- "Importing a specifier that previously failed due to an incorrect type assertion can succeed if the correct assertion is later given",
- "Importing a specifier that previously succeeded with the correct type assertion should fail if the incorrect assertion is later given",
- "Two modules of different type with the same specifier can load if the server changes its responses",
- "If an import previously succeeded for a given specifier/type assertion pair, future uses of that pair should yield the same result"
+ "Two modules of different type with the same specifier can load if the server changes its responses"
],
"repeated-imports.any.worker.html": [
- "Importing a specifier that previously failed due to an incorrect type assertion can succeed if the correct assertion is later given",
- "Importing a specifier that previously succeeded with the correct type assertion should fail if the incorrect assertion is later given",
- "Two modules of different type with the same specifier can load if the server changes its responses",
- "If an import previously succeeded for a given specifier/type assertion pair, future uses of that pair should yield the same result"
+ "Two modules of different type with the same specifier can load if the server changes its responses"
]
},
"microtasks": {