diff --git a/Cargo.lock b/Cargo.lock index 0d5c46953f3967..81ab85e438bccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1661,6 +1661,7 @@ dependencies = [ "http-body-util", "import_map", "indexmap 2.9.0", + "json5", "jsonc-parser", "jupyter-protocol", "keyring", @@ -1744,9 +1745,9 @@ dependencies = [ [[package]] name = "deno_ast" -version = "0.50.3" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6fca2bd99b9f4ac51e548a34b74f8e397d94694a6ced184315922156f70ed0" +checksum = "c72e0409b3dbd60a5bf296cbc273a8e36bb3a3aab6abac389f1891e187d5ce14" dependencies = [ "base64 0.22.1", "capacity_builder", @@ -1837,9 +1838,9 @@ dependencies = [ [[package]] name = "deno_cache_dir" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ab8c76d18a8a7511825bfb93e8b35e79b3dd8404a48c570c536db2a3c88855" +checksum = "dd048472e451169f443461ab1a6362186af32c6a8b2204de219a7834fc231a28" dependencies = [ "async-trait", "base32", @@ -2011,8 +2012,6 @@ dependencies = [ [[package]] name = "deno_doc" version = "0.186.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92de7919430e4bd66edace9ca4ca1dba7599ddf627bfa35a8c919b99ed5f54cf" dependencies = [ "anyhow", "cfg-if", @@ -2164,8 +2163,6 @@ dependencies = [ [[package]] name = "deno_graph" version = "0.103.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293db685464cf8a18785e32d6fdcfffb78a864465d8e9017bfb005ccacc560a8" dependencies = [ "async-trait", "boxed_error", @@ -2335,9 +2332,9 @@ dependencies = [ [[package]] name = "deno_lint" -version = "0.80.0" +version = "0.81.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816be0fe885ac88b200d73d99f8c9557278ff416acf8abc1661b46837f09a303" +checksum = "aa4da1c0d215a8d89f28b9c8778795683c975e76255d7f00049447d6f04fe93d" dependencies = [ "anyhow", "deno_ast", @@ -2374,9 +2371,9 @@ dependencies = [ [[package]] name = "deno_media_type" -version = "0.2.9" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ec0dada9dc5ac4733b4175d36f6a150b7dd68fab46db35cb1ef00dd7366acb" +checksum = "757b57d3e748bb5fc98cb010f33b0ed03f6aa3a7c70166cbdd529c1809ca3118" dependencies = [ "data-url", "encoding_rs", @@ -2852,6 +2849,8 @@ dependencies = [ "hyper 1.6.0", "hyper-util", "indexmap 2.9.0", + "json5", + "jsonc-parser", "libc", "log", "nix 0.27.1", @@ -2863,6 +2862,7 @@ dependencies = [ "rustyline", "same-file", "serde", + "serde_json", "sys_traits", "test_server", "thiserror 2.0.12", @@ -3639,9 +3639,9 @@ dependencies = [ [[package]] name = "dprint-plugin-typescript" -version = "0.95.11" +version = "0.95.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74aa5df06eb156f7df91bf17a0ca65fd349dfa22463c3eab0a019bab20df12df" +checksum = "ed62468ffd2bc0a759497022c7e20ec08f15d74057ad17c0bfa5b3ca657e8c8d" dependencies = [ "anyhow", "capacity_builder", @@ -3931,8 +3931,6 @@ dependencies = [ [[package]] name = "eszip" version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde1007099ac5d0184579513aa0d5484ad25d9638a21c7c26203548aebe40d9b" dependencies = [ "anyhow", "async-trait", @@ -5552,6 +5550,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "jsonc-parser" version = "0.27.0" diff --git a/Cargo.toml b/Cargo.toml index a7a419e9ef9d9b..36b0e983415477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,11 @@ members = [ ] exclude = ["tests/util/std/hash/_wasm"] +[patch.crates-io] +deno_graph = { path = "../deno_graph" } +eszip = { path = "../eszip" } +deno_doc = { path = "../deno_doc" } + [workspace.package] authors = ["the Deno authors"] edition = "2024" @@ -60,16 +65,16 @@ license = "MIT" repository = "https://github.com/denoland/deno" [workspace.dependencies] -deno_ast = { version = "=0.50.3", features = ["transpiling"] } +deno_ast = { version = "=0.51.0", features = ["transpiling"] } deno_core = { version = "0.367.0" } -deno_cache_dir = "=0.25.0" +deno_cache_dir = "=0.26.1" deno_doc = "=0.186.0" deno_error = "=0.7.0" deno_graph = { version = "=0.103.1", default-features = false } -deno_lint = "=0.80.0" +deno_lint = "=0.81.0" deno_lockfile = "=0.32.2" -deno_media_type = { version = "=0.2.9", features = ["module_specifier"] } +deno_media_type = { version = "=0.3.2", features = ["module_specifier"] } deno_native_certs = "0.3.0" deno_npm = "=0.42.0" deno_path_util = "=0.6.4" @@ -196,6 +201,7 @@ indexmap = { version = "2", features = ["serde"] } ipnet = "2.3" ipnetwork = "0.20.0" itertools = "0.14" +json5 = "0.4.1" jsonc-parser = { version = "0.27.0", features = ["serde"] } jupyter_runtime = "=0.19.0" lazy-regex = "3" @@ -308,7 +314,7 @@ dprint-core = "=0.67.4" dprint-plugin-json = "=0.21.0" dprint-plugin-jupyter = "=0.2.1" dprint-plugin-markdown = "=0.20.0" -dprint-plugin-typescript = "=0.95.11" +dprint-plugin-typescript = "=0.95.12" env_logger = "=0.11.6" fancy-regex = "=0.14.0" libsui = "0.10.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 69fa23ac802e99..30b54d02b3e316 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -133,6 +133,7 @@ http-body.workspace = true http-body-util.workspace = true import_map.workspace = true indexmap.workspace = true +json5.workspace = true jsonc-parser = { workspace = true, features = ["cst", "serde"] } jupyter-protocol = "0.8.0" jupyter_runtime = { package = "runtimelib", version = "=0.28.0", default-features = false, features = ["tokio-runtime", "aws-lc-rs"] } diff --git a/cli/cache/module_info.rs b/cli/cache/module_info.rs index e13c595c19cc2b..7279f8f78b3f1d 100644 --- a/cli/cache/module_info.rs +++ b/cli/cache/module_info.rs @@ -305,12 +305,14 @@ fn serialize_media_type(media_type: MediaType) -> i64 { Dcts => 10, Tsx => 11, Json => 12, - Wasm => 13, - Css => 14, - Html => 15, - SourceMap => 16, - Sql => 17, - Unknown => 18, + Jsonc => 13, + Json5 => 14, + Wasm => 15, + Css => 16, + Html => 17, + SourceMap => 18, + Sql => 19, + Unknown => 20, } } diff --git a/cli/lib/loader.rs b/cli/lib/loader.rs index bdd111ae734c56..bfc3c30a487bad 100644 --- a/cli/lib/loader.rs +++ b/cli/lib/loader.rs @@ -16,13 +16,12 @@ pub fn module_type_from_media_and_requested_type( match requested_module_type { RequestedModuleType::Text => ModuleType::Text, RequestedModuleType::Bytes => ModuleType::Bytes, - RequestedModuleType::None - | RequestedModuleType::Other(_) - | RequestedModuleType::Json => match media_type { + RequestedModuleType::None | RequestedModuleType::Json => match media_type { MediaType::Json => ModuleType::Json, MediaType::Wasm => ModuleType::Wasm, _ => ModuleType::JavaScript, }, + RequestedModuleType::Other(t) => ModuleType::Other(t.clone()), } } @@ -67,6 +66,12 @@ pub fn as_deno_resolver_requested_module_type( RequestedModuleType::Bytes => { deno_resolver::loader::RequestedModuleType::Bytes } + RequestedModuleType::Other(Cow::Borrowed("jsonc")) => { + deno_resolver::loader::RequestedModuleType::Jsonc + } + RequestedModuleType::Other(Cow::Borrowed("json5")) => { + deno_resolver::loader::RequestedModuleType::Json5 + } RequestedModuleType::Other(text) => { deno_resolver::loader::RequestedModuleType::Other(text) } diff --git a/cli/lib/standalone/binary.rs b/cli/lib/standalone/binary.rs index 81bfed31099f32..ddbfaaeb399527 100644 --- a/cli/lib/standalone/binary.rs +++ b/cli/lib/standalone/binary.rs @@ -243,12 +243,14 @@ fn serialize_media_type(media_type: MediaType) -> u8 { MediaType::Dcts => 9, MediaType::Tsx => 10, MediaType::Json => 11, - MediaType::Wasm => 12, - MediaType::Css => 13, - MediaType::Html => 14, - MediaType::SourceMap => 15, - MediaType::Sql => 16, - MediaType::Unknown => 17, + MediaType::Jsonc => 12, + MediaType::Json5 => 13, + MediaType::Wasm => 14, + MediaType::Css => 15, + MediaType::Html => 16, + MediaType::SourceMap => 17, + MediaType::Sql => 18, + MediaType::Unknown => 19, } } diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index e832938a0fca02..ea8e8c45c237ce 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1053,6 +1053,8 @@ impl Config { | MediaType::Dcts | MediaType::Tsx => Some(&workspace_settings.typescript), MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Wasm | MediaType::Css | MediaType::Html diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index bafb9f1a665942..5033b42c20d053 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -935,7 +935,7 @@ fn diagnose_resolution( if module.media_type == MediaType::Json { match maybe_assert_type { // The module has the correct assertion type, no diagnostic - Some("json" | "text" | "bytes") => (), + Some("json" | "jsonc" | "json5" | "text" | "bytes") => (), // The dynamic import statement is missing an attribute type, which // we might not be able to statically detect, therefore we will // not provide a potentially incorrect diagnostic. diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index d662fe701915ec..b0571ddf4364f2 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -145,6 +145,10 @@ impl OpenDocument { self.language_id.is_diagnosable() } + pub fn is_diagnosable_or_typed(&self) -> bool { + self.language_id.is_diagnosable_or_typed() + } + pub fn is_file_like(&self) -> bool { uri_is_file_like(&self.uri) } @@ -1714,7 +1718,8 @@ pub enum LanguageId { TypeScript, Tsx, Json, - JsonC, + Jsonc, + Json5, Markdown, Html, Css, @@ -1739,7 +1744,8 @@ impl LanguageId { LanguageId::TypeScript => Some("ts"), LanguageId::Tsx => Some("tsx"), LanguageId::Json => Some("json"), - LanguageId::JsonC => Some("jsonc"), + LanguageId::Jsonc => Some("jsonc"), + LanguageId::Json5 => Some("json5"), LanguageId::Markdown => Some("md"), LanguageId::Html => Some("html"), LanguageId::Css => Some("css"), @@ -1759,14 +1765,16 @@ impl LanguageId { pub fn as_content_type(&self) -> Option<&'static str> { match self { - LanguageId::JavaScript => Some("application/javascript"), - LanguageId::Jsx => Some("text/jsx"), - LanguageId::TypeScript => Some("application/typescript"), - LanguageId::Tsx => Some("text/tsx"), - LanguageId::Json | LanguageId::JsonC => Some("application/json"), + LanguageId::JavaScript => MediaType::JavaScript.as_content_type(), + LanguageId::Jsx => MediaType::Jsx.as_content_type(), + LanguageId::TypeScript => MediaType::TypeScript.as_content_type(), + LanguageId::Tsx => MediaType::Tsx.as_content_type(), + LanguageId::Json => MediaType::Json.as_content_type(), + LanguageId::Jsonc => MediaType::Jsonc.as_content_type(), + LanguageId::Json5 => MediaType::Json5.as_content_type(), LanguageId::Markdown => Some("text/markdown"), - LanguageId::Html => Some("text/html"), - LanguageId::Css => Some("text/css"), + LanguageId::Html => MediaType::Html.as_content_type(), + LanguageId::Css => MediaType::Css.as_content_type(), LanguageId::Scss => None, LanguageId::Sass => None, LanguageId::Less => None, @@ -1787,6 +1795,19 @@ impl LanguageId { Self::JavaScript | Self::Jsx | Self::TypeScript | Self::Tsx ) } + + fn is_diagnosable_or_typed(&self) -> bool { + matches!( + self, + Self::JavaScript + | Self::Jsx + | Self::TypeScript + | Self::Tsx + | Self::Json + | Self::Jsonc + | Self::Json5 + ) + } } impl FromStr for LanguageId { @@ -1799,7 +1820,8 @@ impl FromStr for LanguageId { "typescript" => Ok(Self::TypeScript), "typescriptreact" | "tsx" => Ok(Self::Tsx), "json" => Ok(Self::Json), - "jsonc" => Ok(Self::JsonC), + "jsonc" => Ok(Self::Jsonc), + "json5" => Ok(Self::Json5), "markdown" => Ok(Self::Markdown), "html" => Ok(Self::Html), "css" => Ok(Self::Css), diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 967ac96138059b..9f8b999a683627 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1128,17 +1128,15 @@ impl Inner { | MediaType::Dmts | MediaType::Dcts | MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Tsx => {} MediaType::Wasm | MediaType::SourceMap | MediaType::Css | MediaType::Html | MediaType::Sql - | MediaType::Unknown => { - if path.extension().and_then(|s| s.to_str()) != Some("jsonc") { - continue; - } - } + | MediaType::Unknown => continue, } dir_files.insert(path); } @@ -1335,7 +1333,7 @@ impl Inner { params.text_document.text.into(), None, ); - if document.is_diagnosable() { + if document.is_diagnosable_or_typed() { self.check_semantic_tokens_capabilities(); self.refresh_dep_info(); self.project_changed( @@ -1380,7 +1378,7 @@ impl Inner { } } if let Some(document) = document - && document.is_diagnosable() + && document.is_diagnosable_or_typed() { self.refresh_dep_info(); self.project_changed( @@ -1504,7 +1502,7 @@ impl Inner { return; } }; - if document.is_diagnosable() { + if document.is_diagnosable_or_typed() { self.refresh_dep_info(); self.project_changed( vec![(Document::Open(document), ChangeKind::Closed)], @@ -1524,7 +1522,7 @@ impl Inner { ); let diagnosable_documents = documents .into_iter() - .filter(|d| d.is_diagnosable()) + .filter(|d| d.is_diagnosable_or_typed()) .collect::>(); if !diagnosable_documents.is_empty() { self.check_semantic_tokens_capabilities(); @@ -1552,7 +1550,7 @@ impl Inner { ); let diagnosable_documents = documents .into_iter() - .filter(|(d, _)| d.is_diagnosable()) + .filter(|(d, _)| d.is_diagnosable_or_typed()) .collect::>(); if !diagnosable_documents.is_empty() { self.refresh_dep_info(); @@ -1601,7 +1599,7 @@ impl Inner { .close_notebook_document(¶ms.notebook_document.uri); let diagnosable_documents = documents .into_iter() - .filter(|d| d.is_diagnosable()) + .filter(|d| d.is_diagnosable_or_typed()) .collect::>(); if !diagnosable_documents.is_empty() { self.refresh_dep_info(); diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index a32291637698c3..2455fbb620c5d2 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -4620,7 +4620,8 @@ impl TscSpecifierMap { } // If the module's media type doesn't correspond to tsc's path-inferred // media type, force it to be the same by appending an extension. - if MediaType::from_path(Path::new(&specifier)) != media_type { + let ts_extension = media_type.as_ts_extension(); + if !specifier.as_str().ends_with(ts_extension) { specifier += media_type.as_ts_extension(); } if specifier != original.as_str() { diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 8a586e33816ebc..ebfcfecd6e7aa9 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -1236,7 +1236,7 @@ impl ModuleLoader let graph = self.0.graph_container.graph(); let code = match graph.get(&resolve_url(file_name).ok()?) { Some(deno_graph::Module::Js(module)) => &module.source.text, - Some(deno_graph::Module::Json(module)) => &module.source.text, + Some(deno_graph::Module::Independent(module)) => &module.source.text, _ => return None, }; // Do NOT use .lines(): it skips the terminating empty line. diff --git a/cli/rt/binary.rs b/cli/rt/binary.rs index aee8fd160a028c..0471b969b38d7c 100644 --- a/cli/rt/binary.rs +++ b/cli/rt/binary.rs @@ -446,6 +446,14 @@ impl<'a> DenoCompileModuleData<'a> { ModuleType::Json, into_string_unsafe(self.is_valid_utf8, data), ), + MediaType::Jsonc => ( + ModuleType::Other(Cow::Borrowed("jsonc")), + into_string_unsafe(self.is_valid_utf8, data), + ), + MediaType::Json5 => ( + ModuleType::Other(Cow::Borrowed("json5")), + into_string_unsafe(self.is_valid_utf8, data), + ), MediaType::Wasm => { (ModuleType::Wasm, DenoCompileModuleSource::Bytes(data)) } diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 842ba89c75a11b..89fcfa01f98fa2 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -481,7 +481,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { } (Some(original_bytes), m.media_type) } - deno_graph::Module::Json(m) => { + deno_graph::Module::Independent(m) => { let original_bytes = match m.source.try_get_original_bytes() { Some(bytes) => bytes, None => { diff --git a/cli/tools/bundle/mod.rs b/cli/tools/bundle/mod.rs index c2bbd2d04bbd9b..78f9f16dcd29c9 100644 --- a/cli/tools/bundle/mod.rs +++ b/cli/tools/bundle/mod.rs @@ -776,6 +776,8 @@ fn requested_type_from_map( let type_ = map.get("type").map(|s| s.as_str()); match type_ { Some("json") => RequestedModuleType::Json, + Some("jsonc") => RequestedModuleType::Jsonc, + Some("json5") => RequestedModuleType::Json5, Some("bytes") => RequestedModuleType::Bytes, Some("text") => RequestedModuleType::Text, Some(other) => RequestedModuleType::Other(other), @@ -1109,6 +1111,12 @@ pub enum BundleLoadError { #[error("Emit error")] Emit(#[from] deno_ast::EmitError), #[class(generic)] + #[error("{}", if let Self::Jsonc(Some(err)) = &self { Cow::Owned(format!("Couldn't parse jsonc file: {err}")) } else { Cow::Borrowed("Empty jsonc file") })] + Jsonc(#[from] Option), + #[class(generic)] + #[error("{}", if let Self::Json5(err) = &self { format!("Couldn't parse json5 file: {err}") } else { unreachable!() })] + Json5(#[from] json5::Error), + #[class(generic)] #[error("Prepare module load error")] PrepareModuleLoad(#[from] crate::module_loader::PrepareModuleLoadError), @@ -1396,6 +1404,8 @@ impl DenoPluginHandler { } RequestedModuleType::None | RequestedModuleType::Json + | RequestedModuleType::Jsonc + | RequestedModuleType::Json5 | RequestedModuleType::Other(_) => { if media_type.is_emittable() { let str = String::from_utf8_lossy(&file.source); @@ -1476,6 +1486,25 @@ impl DenoPluginHandler { Some(RequestedModuleType::Json) => { return Ok((source.to_vec(), esbuild_client::BuiltinLoader::Json)); } + Some(RequestedModuleType::Jsonc) => { + let jsonc_str = String::from_utf8_lossy(source); + let json_str = + jsonc_parser::parse_to_serde_value(&jsonc_str, &Default::default()) + .map_err(|err| BundleLoadError::Jsonc(Some(err)))? + .ok_or_else(|| BundleLoadError::Jsonc(None))? + .to_string(); + return Ok(( + json_str.into_bytes(), + esbuild_client::BuiltinLoader::Json, + )); + } + Some(RequestedModuleType::Json5) => { + let json5_str = String::from_utf8_lossy(source); + json5::from_str::(&json5_str) + .map_err(BundleLoadError::Json5)?; + let js_str = format!("export default ({json5_str});"); + return Ok((js_str.into_bytes(), esbuild_client::BuiltinLoader::Js)); + } Some(RequestedModuleType::Other(_) | RequestedModuleType::None) | None => {} } @@ -1599,7 +1628,7 @@ impl DenoPluginHandler { js_module.media_type, media_type_to_loader(js_module.media_type), ), - deno_graph::Module::Json(json_module) => ( + deno_graph::Module::Independent(json_module) => ( json_module.specifier.clone(), deno_ast::MediaType::Json, esbuild_client::BuiltinLoader::Json, @@ -1662,6 +1691,8 @@ fn media_type_to_loader( Sql => esbuild_client::BuiltinLoader::Text, Wasm => esbuild_client::BuiltinLoader::Binary, Unknown => esbuild_client::BuiltinLoader::Binary, + // TODO(This PR): Investigate. + Jsonc | Json5 => esbuild_client::BuiltinLoader::Json, // _ => esbuild_client::BuiltinLoader::External, } } diff --git a/cli/tools/clean.rs b/cli/tools/clean.rs index 94ad05b0fb3eb7..c8929765698afe 100644 --- a/cli/tools/clean.rs +++ b/cli/tools/clean.rs @@ -240,7 +240,7 @@ async fn clean_except( deno_graph::Module::Js(js_module) => { keep.insert(&js_module.specifier); } - deno_graph::Module::Json(json_module) => { + deno_graph::Module::Independent(json_module) => { keep.insert(&json_module.specifier); } deno_graph::Module::Wasm(wasm_module) => { diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 79ae3a003fbc74..94e2098adfbe9c 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -366,6 +366,8 @@ fn get_module_roots_and_include_paths( | MediaType::Dcts | MediaType::Tsx | MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Wasm => true, MediaType::Css | MediaType::Html diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index d88c9bd69fd554..73f884c0c01af3 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -555,6 +555,8 @@ pub fn cover_files( | MediaType::Wasm | MediaType::Cjs | MediaType::Mjs + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Json => None, MediaType::Dts | MediaType::Dmts | MediaType::Dcts => Some(String::new()), MediaType::TypeScript diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 6ee6624c73d85a..15653416f6888d 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -503,7 +503,7 @@ impl<'a> GraphDisplayContext<'a> { Ok(Some(root)) => { let maybe_cache_info = match root { Module::Js(module) => module.maybe_cache_info.as_ref(), - Module::Json(module) => module.maybe_cache_info.as_ref(), + Module::Independent(module) => module.maybe_cache_info.as_ref(), Module::Wasm(module) => module.maybe_cache_info.as_ref(), Module::Node(_) | Module::Npm(_) | Module::External(_) => None, }; @@ -526,7 +526,7 @@ impl<'a> GraphDisplayContext<'a> { .map(|m| { let size = match m { Module::Js(module) => module.size(), - Module::Json(module) => module.size(), + Module::Independent(module) => module.size(), Module::Wasm(module) => module.size(), Module::Node(_) | Module::Npm(_) | Module::External(_) => 0, }; @@ -635,7 +635,7 @@ impl<'a> GraphDisplayContext<'a> { } Specifier(_) => match module { Module::Js(module) => Some(module.size() as u64), - Module::Json(module) => Some(module.size() as u64), + Module::Independent(module) => Some(module.size() as u64), Module::Wasm(module) => Some(module.size() as u64), Module::Node(_) | Module::Npm(_) | Module::External(_) => None, }, @@ -667,7 +667,7 @@ impl<'a> GraphDisplayContext<'a> { tree_node.children.extend(self.build_dep_info(dep)); } } - Module::Json(_) + Module::Independent(_) | Module::Npm(_) | Module::Node(_) | Module::External(_) => {} diff --git a/cli/tools/publish/mod.rs b/cli/tools/publish/mod.rs index 529756a3945d4b..8e5ed03cfe7b5a 100644 --- a/cli/tools/publish/mod.rs +++ b/cli/tools/publish/mod.rs @@ -1094,7 +1094,7 @@ fn collect_excluded_module_diagnostics( .modules() .filter_map(|m| match m { deno_graph::Module::Js(_) - | deno_graph::Module::Json(_) + | deno_graph::Module::Independent(_) | deno_graph::Module::Wasm(_) => Some(m.specifier()), deno_graph::Module::Npm(_) | deno_graph::Module::Node(_) diff --git a/cli/tools/publish/module_content.rs b/cli/tools/publish/module_content.rs index 5355cf9ec68e8f..73d997065f5531 100644 --- a/cli/tools/publish/module_content.rs +++ b/cli/tools/publish/module_content.rs @@ -92,6 +92,8 @@ impl ModuleContentProvider { | MediaType::Html | MediaType::Sql | MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Wasm | MediaType::Css => { // not unfurlable data diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 2ab2a44641889b..aad927c7bd2df2 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -513,6 +513,8 @@ pub fn as_ts_script_kind(media_type: MediaType) -> i32 { MediaType::Dcts => 3, MediaType::Tsx => 4, MediaType::Json => 6, + MediaType::Jsonc => 6, + MediaType::Json5 => 6, MediaType::SourceMap | MediaType::Css | MediaType::Html @@ -657,8 +659,14 @@ fn resolve_graph_specifier_types( Some(Module::Js(module)) => { Ok(Some((module.specifier.clone(), module.media_type))) } - Some(Module::Json(module)) => { - Ok(Some((module.specifier.clone(), module.media_type))) + Some(Module::Independent(module)) => { + let media_type = match module.media_type { + MediaType::Json | MediaType::Jsonc | MediaType::Json5 => { + MediaType::Json + } + _ => module.media_type, + }; + Ok(Some((module.specifier.clone(), media_type))) } Some(Module::Wasm(module)) => { Ok(Some((module.specifier.clone(), MediaType::Dmts))) @@ -951,7 +959,7 @@ pub fn resolve_specifier_for_tsc( let resolved_dep = referrer_module .and_then(|m| match m { Module::Js(m) => m.dependencies_prefer_fast_check().get(&specifier), - Module::Json(_) => None, + Module::Independent(_) => None, Module::Wasm(m) => m.dependencies.get(&specifier), Module::Npm(_) | Module::Node(_) | Module::External(_) => None, }) @@ -1144,9 +1152,18 @@ pub fn load_for_tsc( .unwrap_or(T::from_arc_str(module.source.text.clone())), ) } - Module::Json(module) => { - media_type = MediaType::Json; - Some(T::from_arc_str(module.source.text.clone())) + Module::Independent(module) => { + match module.media_type { + MediaType::Jsonc | MediaType::Json5 => { + media_type = MediaType::Json; + Some(T::from_arc_str(module.source.text.clone())) + } + // Expecting `MediaType::Json`. + _ => { + media_type = module.media_type; + Some(T::from_arc_str(module.source.text.clone())) + } + } } Module::Wasm(module) => { media_type = MediaType::Dts; diff --git a/cli/type_checker.rs b/cli/type_checker.rs index 81906d803417ef..b784842c2454f6 100644 --- a/cli/type_checker.rs +++ b/cli/type_checker.rs @@ -783,7 +783,7 @@ impl<'a> GraphWalker<'a> { Module::Wasm(module) => { maybe_module_dependencies = Some(&module.dependencies); } - Module::Json(_) | Module::Npm(_) => {} + Module::Independent(_) | Module::Npm(_) => {} Module::External(module) => { // NPM files for `"nodeModulesDir": "manual"`. let media_type = MediaType::from_specifier(&module.specifier); @@ -881,6 +881,8 @@ impl<'a> GraphWalker<'a> { } } MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Wasm | MediaType::Css | MediaType::Html @@ -913,7 +915,7 @@ impl<'a> GraphWalker<'a> { // which is hashed below None } - Module::Json(module) => { + Module::Independent(module) => { if let Some(hasher) = &mut self.maybe_hasher { hasher.write_str(module.specifier.as_str()); hasher.write_str(&module.source.text); @@ -997,6 +999,8 @@ fn has_ts_check(media_type: MediaType, file_text: &str) -> bool { | MediaType::Dmts | MediaType::Tsx | MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Wasm | MediaType::Css | MediaType::Html diff --git a/libs/resolver/cjs/mod.rs b/libs/resolver/cjs/mod.rs index aa6198957a3c5f..07676513513b03 100644 --- a/libs/resolver/cjs/mod.rs +++ b/libs/resolver/cjs/mod.rs @@ -199,7 +199,7 @@ impl self.check_based_on_pkg_json(specifier).unwrap_or(ResolutionMode::Import) } MediaType::Wasm | - MediaType::Json => ResolutionMode::Import, + MediaType::Json | MediaType::Jsonc | MediaType::Json5 => ResolutionMode::Import, MediaType::JavaScript | MediaType::Jsx | MediaType::TypeScript @@ -246,7 +246,7 @@ impl } } MediaType::Wasm | - MediaType::Json => Some(ResolutionMode::Import), + MediaType::Json | MediaType::Jsonc | MediaType::Json5 => Some(ResolutionMode::Import), MediaType::JavaScript | MediaType::Jsx | MediaType::TypeScript diff --git a/libs/resolver/emit.rs b/libs/resolver/emit.rs index 320dbdb5f1bf4a..66e4e2896e1f71 100644 --- a/libs/resolver/emit.rs +++ b/libs/resolver/emit.rs @@ -357,6 +357,8 @@ impl | MediaType::Dmts | MediaType::Dcts | MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Wasm | MediaType::Css | MediaType::Html diff --git a/libs/resolver/graph.rs b/libs/resolver/graph.rs index abe9eb04c46142..0c77f24e52b8eb 100644 --- a/libs/resolver/graph.rs +++ b/libs/resolver/graph.rs @@ -304,7 +304,7 @@ impl< } Some(Module::Node(module)) => module.specifier.clone(), Some(Module::Js(module)) => module.specifier.clone(), - Some(Module::Json(module)) => module.specifier.clone(), + Some(Module::Independent(module)) => module.specifier.clone(), Some(Module::Wasm(module)) => module.specifier.clone(), Some(Module::External(module)) => { node_resolver::resolve_specifier_into_node_modules( diff --git a/libs/resolver/loader/mod.rs b/libs/resolver/loader/mod.rs index 3689245cd00723..d181796a0a0761 100644 --- a/libs/resolver/loader/mod.rs +++ b/libs/resolver/loader/mod.rs @@ -27,6 +27,8 @@ pub enum AllowJsonImports { pub enum RequestedModuleType<'a> { None, Json, + Jsonc, + Json5, Text, Bytes, Other(&'a str), diff --git a/libs/resolver/loader/module_loader.rs b/libs/resolver/loader/module_loader.rs index d34e69a8c652c7..7d729a64643d51 100644 --- a/libs/resolver/loader/module_loader.rs +++ b/libs/resolver/loader/module_loader.rs @@ -4,8 +4,8 @@ use std::borrow::Cow; use boxed_error::Boxed; use deno_ast::ModuleKind; +use deno_graph::IndependentModule; use deno_graph::JsModule; -use deno_graph::JsonModule; use deno_graph::ModuleGraph; use deno_graph::WasmModule; use deno_media_type::MediaType; @@ -409,7 +409,7 @@ impl PreparedModuleLoader { })?; match maybe_module { - Some(deno_graph::Module::Json(JsonModule { + Some(deno_graph::Module::Independent(IndependentModule { source, media_type, specifier, @@ -474,7 +474,9 @@ impl PreparedModuleLoader { MediaType::JavaScript | MediaType::Unknown | MediaType::Mjs - | MediaType::Json => source.text.clone(), + | MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 => source.text.clone(), MediaType::Dts | MediaType::Dcts | MediaType::Dmts => { Default::default() } diff --git a/libs/resolver/loader/npm.rs b/libs/resolver/loader/npm.rs index 765499050dff26..95df3d5c8d8b45 100644 --- a/libs/resolver/loader/npm.rs +++ b/libs/resolver/loader/npm.rs @@ -196,6 +196,8 @@ impl< } RequestedModuleType::None | RequestedModuleType::Json + | RequestedModuleType::Jsonc + | RequestedModuleType::Json5 | RequestedModuleType::Other(_) => { if media_type.is_emittable() { return Err(NpmModuleLoadError::StrippingTypesNodeModules( diff --git a/libs/resolver/workspace.rs b/libs/resolver/workspace.rs index fb09342fb0f2e0..a6f9016c2d08ab 100644 --- a/libs/resolver/workspace.rs +++ b/libs/resolver/workspace.rs @@ -543,6 +543,8 @@ impl SloppyImportsResolver { | MediaType::Dcts | MediaType::Tsx | MediaType::Json + | MediaType::Jsonc + | MediaType::Json5 | MediaType::Wasm | MediaType::Css | MediaType::Html diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 4862a585142130..605a4e812b3f69 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -81,6 +81,8 @@ http-body-util.workspace = true hyper.workspace = true hyper-util.workspace = true indexmap.workspace = true +json5.workspace = true +jsonc-parser.workspace = true libc.workspace = true log.workspace = true notify.workspace = true @@ -89,6 +91,7 @@ regex.workspace = true rustyline = { workspace = true, features = ["custom-bindings"] } same-file.workspace = true serde.workspace = true +serde_json.workspace = true sys_traits.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 2284cabacfae7b..1737feb937da26 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -72,6 +72,7 @@ use crate::worker::FormatJsErrorFn; use crate::worker::MEMORY_TRIM_HANDLER_ENABLED; #[cfg(target_os = "linux")] use crate::worker::SIGUSR2_RX; +use crate::worker::create_custom_module_evaluation_cb; use crate::worker::create_op_metrics; use crate::worker::create_validate_import_attributes_callback; @@ -659,7 +660,7 @@ impl WebWorker { v8_platform: None, is_main: false, wait_for_inspector_disconnect_callback: None, - custom_module_evaluation_cb: None, + custom_module_evaluation_cb: Some(create_custom_module_evaluation_cb()), eval_context_code_cache_cbs: None, }); diff --git a/runtime/worker.rs b/runtime/worker.rs index c477cc615c4a3c..939fd1fb8be1da 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -103,7 +103,7 @@ pub fn create_validate_import_attributes_callback( let valid_attribute = |kind: &str| { enable_raw_imports.load(Ordering::Relaxed) && matches!(kind, "bytes" | "text") - || matches!(kind, "json") + || matches!(kind, "json" | "jsonc" | "json5") }; for (key, value) in attributes { let msg = if key != "type" { @@ -129,6 +129,107 @@ pub fn create_validate_import_attributes_callback( ) } +pub const BOM_CHAR: &[u8] = &[0xef, 0xbb, 0xbf]; + +/// Strips the byte order mark from the provided text if it exists. +fn strip_bom(source_code: &[u8]) -> &[u8] { + if source_code.starts_with(BOM_CHAR) { + &source_code[BOM_CHAR.len()..] + } else { + source_code + } +} + +// TODO(nayeemrmn): Export this from deno_core. +type CustomModuleEvaluationCb = Box< + dyn Fn( + &mut v8::PinScope, + Cow<'_, str>, + &deno_core::FastString, + deno_core::ModuleSourceCode, + ) -> Result< + deno_core::CustomModuleEvaluationKind, + deno_error::JsErrorBox, + >, +>; + +pub fn create_custom_module_evaluation_cb() -> CustomModuleEvaluationCb { + Box::new( + |scope, module_type, module_name, code| match module_type.as_ref() { + "jsonc" => { + let json_str = jsonc_parser::parse_to_serde_value( + deno_core::ModuleSource::get_string_source(code).as_str(), + &Default::default(), + ) + .map_err(|err| { + deno_error::JsErrorBox::type_error(format!( + "Couldn't parse jsonc file \"{module_name}\": {err}" + )) + })? + .ok_or_else(|| { + deno_error::JsErrorBox::type_error(format!( + "Empty jsonc file: {module_name}" + )) + })? + .to_string(); + let json_str = v8::String::new_from_utf8( + scope, + strip_bom(json_str.as_bytes()), + v8::NewStringType::Normal, + ) + .unwrap(); + v8::tc_scope!(let tc_scope, scope); + let parsed_json = v8::json::parse(tc_scope, json_str).ok_or_else(|| { + assert!(tc_scope.has_caught()); + deno_error::JsErrorBox::type_error(format!( + "Internal error: A jsonc file was successfully converted to json, but the output could not be parsed by v8: {module_name}", + )) + })?; + let parsed_json = v8::Global::new(tc_scope, parsed_json); + Ok(deno_core::CustomModuleEvaluationKind::Synthetic( + parsed_json, + )) + } + "json5" => { + let json5_str = deno_core::ModuleSource::get_string_source(code); + json5::from_str::(json5_str.as_str()).map_err( + |err| { + deno_error::JsErrorBox::type_error(format!( + "Couldn't parse json5 file \"{module_name}\": {err}" + )) + }, + )?; + let expr_str = format!("({json5_str})"); + let expr_str = v8::String::new_from_utf8( + scope, + strip_bom(expr_str.as_bytes()), + v8::NewStringType::Normal, + ) + .unwrap(); + v8::tc_scope!(let tc_scope, scope); + let script = + v8::Script::compile(tc_scope, expr_str, None).ok_or_else(|| { + assert!(tc_scope.has_caught()); + deno_error::JsErrorBox::type_error(format!( + "Internal error: A json5 file was validated, but the output could not be parsed by v8: {module_name}", + )) + })?; + let result = script.run(tc_scope).ok_or_else(|| { + assert!(tc_scope.has_caught()); + deno_error::JsErrorBox::type_error(format!( + "Internal error: A json5 file was validated, but the output could not be evaluated by v8: {module_name}", + )) + })?; + let result = v8::Global::new(tc_scope, result); + Ok(deno_core::CustomModuleEvaluationKind::Synthetic(result)) + } + module_type => Err(deno_error::JsErrorBox::type_error(format!( + "\"{module_type}\" is not a valid module type." + ))), + }, + ) +} + pub fn make_wait_for_inspector_disconnect_callback() -> Box { let has_notified_of_inspector_disconnect = AtomicBool::new(false); Box::new(move || { @@ -1147,7 +1248,7 @@ fn common_runtime(opts: CommonRuntimeOptions) -> JsRuntime { .then(create_permissions_stack_trace_callback), extension_code_cache: None, v8_platform: None, - custom_module_evaluation_cb: None, + custom_module_evaluation_cb: Some(create_custom_module_evaluation_cb()), eval_context_code_cache_cbs: None, }); diff --git a/tests/specs/bundle/json5/__test__.jsonc b/tests/specs/bundle/json5/__test__.jsonc new file mode 100644 index 00000000000000..5ae383d51c1dd3 --- /dev/null +++ b/tests/specs/bundle/json5/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "steps": [{ + "args": "bundle -o=./out.js main.ts", + "output": "[WILDCARD]" + }, { + "args": "run ./out.js", + "output": "main.out" + }] +} diff --git a/tests/specs/bundle/json5/foo.json5 b/tests/specs/bundle/json5/foo.json5 new file mode 100644 index 00000000000000..d086805884988e --- /dev/null +++ b/tests/specs/bundle/json5/foo.json5 @@ -0,0 +1,14 @@ +// Adapted from https://json5.org/. +{ + // comments + unquoted: 'and you can quote me on that', + singleQuotes: 'I can use "double quotes" here', + lineBreaks: "Look, Mom! \ +No \\n's!", + hexadecimal: 0xdecaf, + leadingDecimalPoint: .8675309, andTrailing: 8675309., + positiveSign: +1, + trailingComma: 'in objects', andIn: ['arrays',], + "backwardsCompatible": "with JSON", + specialNumbers: [NaN, Infinity, -Infinity], +} diff --git a/tests/specs/bundle/json5/main.out b/tests/specs/bundle/json5/main.out new file mode 100644 index 00000000000000..9e55c9dd4fa6d5 --- /dev/null +++ b/tests/specs/bundle/json5/main.out @@ -0,0 +1,13 @@ +{ + unquoted: "and you can quote me on that", + singleQuotes: 'I can use "double quotes" here', + lineBreaks: "Look, Mom! No \\n's!", + hexadecimal: 912559, + leadingDecimalPoint: 0.8675309, + andTrailing: 8675309, + positiveSign: 1, + trailingComma: "in objects", + andIn: [ "arrays" ], + backwardsCompatible: "with JSON", + specialNumbers: [ NaN, Infinity, -Infinity ] +} diff --git a/tests/specs/bundle/json5/main.ts b/tests/specs/bundle/json5/main.ts new file mode 100644 index 00000000000000..75f33de8946f85 --- /dev/null +++ b/tests/specs/bundle/json5/main.ts @@ -0,0 +1,2 @@ +import foo from "./foo.json5" with { type: "json5" }; +console.log(foo); diff --git a/tests/specs/bundle/jsonc/__test__.jsonc b/tests/specs/bundle/jsonc/__test__.jsonc new file mode 100644 index 00000000000000..5ae383d51c1dd3 --- /dev/null +++ b/tests/specs/bundle/jsonc/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "steps": [{ + "args": "bundle -o=./out.js main.ts", + "output": "[WILDCARD]" + }, { + "args": "run ./out.js", + "output": "main.out" + }] +} diff --git a/tests/specs/bundle/jsonc/foo.jsonc b/tests/specs/bundle/jsonc/foo.jsonc new file mode 100644 index 00000000000000..dcb53d9312636f --- /dev/null +++ b/tests/specs/bundle/jsonc/foo.jsonc @@ -0,0 +1,4 @@ +{ + // comments + "key": "value", +} diff --git a/tests/specs/bundle/jsonc/main.out b/tests/specs/bundle/jsonc/main.out new file mode 100644 index 00000000000000..709a292905d057 --- /dev/null +++ b/tests/specs/bundle/jsonc/main.out @@ -0,0 +1 @@ +{ key: "value" } diff --git a/tests/specs/bundle/jsonc/main.ts b/tests/specs/bundle/jsonc/main.ts new file mode 100644 index 00000000000000..e047d6e02bd605 --- /dev/null +++ b/tests/specs/bundle/jsonc/main.ts @@ -0,0 +1,2 @@ +import foo from "./foo.jsonc" with { type: "jsonc" }; +console.log(foo); diff --git a/tests/specs/check/json5/__test__.jsonc b/tests/specs/check/json5/__test__.jsonc new file mode 100644 index 00000000000000..546b3fb770e759 --- /dev/null +++ b/tests/specs/check/json5/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "check --quiet main.ts", + "output": "main.out", + "exitCode": 1 +} diff --git a/tests/specs/check/json5/foo.json5 b/tests/specs/check/json5/foo.json5 new file mode 100644 index 00000000000000..465e55a2cf369a --- /dev/null +++ b/tests/specs/check/json5/foo.json5 @@ -0,0 +1,3 @@ +{ + value: NaN, +} diff --git a/tests/specs/check/json5/main.out b/tests/specs/check/json5/main.out new file mode 100644 index 00000000000000..124657f96664db --- /dev/null +++ b/tests/specs/check/json5/main.out @@ -0,0 +1,6 @@ +TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. +const _value: string = foo.value; + ~~~~~~ + at [WILDLINE]/main.ts:3:7 + +error: Type checking failed. diff --git a/tests/specs/check/json5/main.ts b/tests/specs/check/json5/main.ts new file mode 100644 index 00000000000000..09146867fd0637 --- /dev/null +++ b/tests/specs/check/json5/main.ts @@ -0,0 +1,3 @@ +import foo from "./foo.json5" with { type: "json5" }; + +const _value: string = foo.value; diff --git a/tests/specs/check/jsonc/__test__.jsonc b/tests/specs/check/jsonc/__test__.jsonc new file mode 100644 index 00000000000000..546b3fb770e759 --- /dev/null +++ b/tests/specs/check/jsonc/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "check --quiet main.ts", + "output": "main.out", + "exitCode": 1 +} diff --git a/tests/specs/check/jsonc/foo.jsonc b/tests/specs/check/jsonc/foo.jsonc new file mode 100644 index 00000000000000..faf42feb61c86d --- /dev/null +++ b/tests/specs/check/jsonc/foo.jsonc @@ -0,0 +1,4 @@ +{ + // comments + "value": 1, +} diff --git a/tests/specs/check/jsonc/main.out b/tests/specs/check/jsonc/main.out new file mode 100644 index 00000000000000..124657f96664db --- /dev/null +++ b/tests/specs/check/jsonc/main.out @@ -0,0 +1,6 @@ +TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. +const _value: string = foo.value; + ~~~~~~ + at [WILDLINE]/main.ts:3:7 + +error: Type checking failed. diff --git a/tests/specs/check/jsonc/main.ts b/tests/specs/check/jsonc/main.ts new file mode 100644 index 00000000000000..e3eb8d915212c2 --- /dev/null +++ b/tests/specs/check/jsonc/main.ts @@ -0,0 +1,3 @@ +import foo from "./foo.jsonc" with { type: "jsonc" }; + +const _value: string = foo.value; diff --git a/tests/specs/run/json5/__test__.jsonc b/tests/specs/run/json5/__test__.jsonc new file mode 100644 index 00000000000000..7444877103b8ed --- /dev/null +++ b/tests/specs/run/json5/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "run --quiet main.ts", + "output": "main.out" +} diff --git a/tests/specs/run/json5/foo.json5 b/tests/specs/run/json5/foo.json5 new file mode 100644 index 00000000000000..d086805884988e --- /dev/null +++ b/tests/specs/run/json5/foo.json5 @@ -0,0 +1,14 @@ +// Adapted from https://json5.org/. +{ + // comments + unquoted: 'and you can quote me on that', + singleQuotes: 'I can use "double quotes" here', + lineBreaks: "Look, Mom! \ +No \\n's!", + hexadecimal: 0xdecaf, + leadingDecimalPoint: .8675309, andTrailing: 8675309., + positiveSign: +1, + trailingComma: 'in objects', andIn: ['arrays',], + "backwardsCompatible": "with JSON", + specialNumbers: [NaN, Infinity, -Infinity], +} diff --git a/tests/specs/run/json5/main.out b/tests/specs/run/json5/main.out new file mode 100644 index 00000000000000..9e55c9dd4fa6d5 --- /dev/null +++ b/tests/specs/run/json5/main.out @@ -0,0 +1,13 @@ +{ + unquoted: "and you can quote me on that", + singleQuotes: 'I can use "double quotes" here', + lineBreaks: "Look, Mom! No \\n's!", + hexadecimal: 912559, + leadingDecimalPoint: 0.8675309, + andTrailing: 8675309, + positiveSign: 1, + trailingComma: "in objects", + andIn: [ "arrays" ], + backwardsCompatible: "with JSON", + specialNumbers: [ NaN, Infinity, -Infinity ] +} diff --git a/tests/specs/run/json5/main.ts b/tests/specs/run/json5/main.ts new file mode 100644 index 00000000000000..75f33de8946f85 --- /dev/null +++ b/tests/specs/run/json5/main.ts @@ -0,0 +1,2 @@ +import foo from "./foo.json5" with { type: "json5" }; +console.log(foo); diff --git a/tests/specs/run/jsonc/__test__.jsonc b/tests/specs/run/jsonc/__test__.jsonc new file mode 100644 index 00000000000000..7444877103b8ed --- /dev/null +++ b/tests/specs/run/jsonc/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "run --quiet main.ts", + "output": "main.out" +} diff --git a/tests/specs/run/jsonc/foo.jsonc b/tests/specs/run/jsonc/foo.jsonc new file mode 100644 index 00000000000000..dcb53d9312636f --- /dev/null +++ b/tests/specs/run/jsonc/foo.jsonc @@ -0,0 +1,4 @@ +{ + // comments + "key": "value", +} diff --git a/tests/specs/run/jsonc/main.out b/tests/specs/run/jsonc/main.out new file mode 100644 index 00000000000000..709a292905d057 --- /dev/null +++ b/tests/specs/run/jsonc/main.out @@ -0,0 +1 @@ +{ key: "value" } diff --git a/tests/specs/run/jsonc/main.ts b/tests/specs/run/jsonc/main.ts new file mode 100644 index 00000000000000..e047d6e02bd605 --- /dev/null +++ b/tests/specs/run/jsonc/main.ts @@ -0,0 +1,2 @@ +import foo from "./foo.jsonc" with { type: "jsonc" }; +console.log(foo);