summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Cargo.lock418
-rw-r--r--Cargo.toml1
-rw-r--r--cli/Cargo.toml3
-rw-r--r--cli/args/flags.rs117
-rw-r--r--cli/main.rs3
-rw-r--r--cli/module_loader.rs5
-rw-r--r--cli/tests/testdata/jupyter/integration_test.ipynb620
-rw-r--r--cli/tools/jupyter/install.rs95
-rw-r--r--cli/tools/jupyter/jupyter_msg.rs268
-rw-r--r--cli/tools/jupyter/mod.rs139
-rw-r--r--cli/tools/jupyter/resources/deno-logo-32x32.pngbin0 -> 1029 bytes
-rw-r--r--cli/tools/jupyter/resources/deno-logo-64x64.pngbin0 -> 2066 bytes
-rw-r--r--cli/tools/jupyter/server.rs724
-rw-r--r--cli/tools/mod.rs1
-rw-r--r--cli/tools/repl/mod.rs7
-rw-r--r--cli/tools/repl/session.rs63
-rw-r--r--ext/node/Cargo.toml2
18 files changed, 2364 insertions, 106 deletions
diff --git a/.gitignore b/.gitignore
index 417bed71c..eab627b38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,7 @@ gclient_config.py_entries
# JUnit files produced by deno test --junit
junit.xml
+
+# Jupyter files
+.ipynb_checkpoints/
+Untitled*.ipynb \ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index fbf9ee8f6..329df26b5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -57,7 +57,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"cipher",
"cpufeatures",
]
@@ -87,11 +87,20 @@ dependencies = [
[[package]]
name = "ahash"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
+dependencies = [
+ "const-random",
+]
+
+[[package]]
+name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"once_cell",
"version_check",
]
@@ -302,6 +311,19 @@ dependencies = [
]
[[package]]
+name = "asynchronous-codec"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182"
+dependencies = [
+ "bytes",
+ "futures-sink",
+ "futures-util",
+ "memchr",
+ "pin-project-lite",
+]
+
+[[package]]
name = "auto_impl"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -339,7 +361,7 @@ checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"miniz_oxide",
"object",
@@ -527,6 +549,12 @@ dependencies = [
[[package]]
name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
@@ -646,6 +674,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
+name = "const-random"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
+dependencies = [
+ "const-random-macro",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
+dependencies = [
+ "getrandom 0.2.10",
+ "lazy_static",
+ "proc-macro-hack",
+ "tiny-keccak",
+]
+
+[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -697,29 +747,108 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "crossbeam"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
+dependencies = [
+ "cfg-if 0.1.10",
+ "crossbeam-channel 0.4.4",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils 0.7.2",
]
[[package]]
name = "crossbeam-channel"
-version = "0.5.8"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
dependencies = [
- "cfg-if",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
+ "maybe-uninit",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
+dependencies = [
+ "cfg-if 1.0.0",
+ "crossbeam-utils 0.8.10",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils 0.7.2",
+ "maybe-uninit",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
+dependencies = [
+ "autocfg",
+ "cfg-if 0.1.10",
+ "crossbeam-utils 0.7.2",
+ "lazy_static",
+ "maybe-uninit",
+ "memoffset 0.5.6",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
+dependencies = [
+ "cfg-if 0.1.10",
+ "crossbeam-utils 0.7.2",
+ "maybe-uninit",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.16"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
- "cfg-if",
+ "autocfg",
+ "cfg-if 0.1.10",
+ "lazy_static",
]
[[package]]
+name = "crossbeam-utils"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
+dependencies = [
+ "cfg-if 1.0.0",
+ "once_cell",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
name = "crypto-bigint"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -782,7 +911,7 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"cpufeatures",
"curve25519-dalek-derive",
"fiat-crypto",
@@ -805,11 +934,22 @@ dependencies = [
[[package]]
name = "dashmap"
+version = "3.11.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f260e2fc850179ef410018660006951c1b55b79e8087e87111a2c388994b9b5"
+dependencies = [
+ "ahash 0.3.8",
+ "cfg-if 0.1.10",
+ "num_cpus",
+]
+
+[[package]]
+name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"hashbrown 0.14.0",
"lock_api",
"once_cell",
@@ -835,7 +975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
dependencies = [
"serde",
- "uuid",
+ "uuid 1.4.1",
]
[[package]]
@@ -846,12 +986,14 @@ dependencies = [
"base32",
"base64 0.13.1",
"bincode",
+ "bytes",
"cache_control",
"chrono",
"clap",
"clap_complete",
"clap_complete_fig",
"console_static_text",
+ "data-encoding",
"data-url",
"deno_ast",
"deno_bench_util",
@@ -903,7 +1045,7 @@ dependencies = [
"pin-project",
"pretty_assertions",
"quick-junit",
- "rand",
+ "rand 0.8.5",
"regex",
"ring",
"rustyline",
@@ -919,16 +1061,17 @@ dependencies = [
"text_lines",
"thiserror",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.8",
"tower-lsp",
"trust-dns-client",
"trust-dns-server",
"twox-hash",
"typed-arena",
- "uuid",
+ "uuid 1.4.1",
"walkdir",
"winapi",
"winres",
+ "zeromq",
"zstd",
]
@@ -1012,7 +1155,7 @@ dependencies = [
"async-trait",
"deno_core",
"tokio",
- "uuid",
+ "uuid 1.4.1",
]
[[package]]
@@ -1117,7 +1260,7 @@ dependencies = [
"once_cell",
"p256 0.11.1",
"p384 0.11.2",
- "rand",
+ "rand 0.8.5",
"ring",
"rsa",
"sec1 0.3.0",
@@ -1128,7 +1271,7 @@ dependencies = [
"signature 1.6.4",
"spki 0.6.0",
"tokio",
- "uuid",
+ "uuid 1.4.1",
"x25519-dalek",
]
@@ -1138,7 +1281,7 @@ version = "0.66.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cc42f49e0aa338e438f59b8367c0ca73c789e9321bd6e1ee086d57733826190"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"deno_ast",
"deno_graph",
"futures",
@@ -1180,7 +1323,7 @@ dependencies = [
"reqwest",
"serde",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.8",
]
[[package]]
@@ -1211,7 +1354,7 @@ dependencies = [
"libc",
"log",
"nix 0.26.2",
- "rand",
+ "rand 0.8.5",
"serde",
"tokio",
"winapi",
@@ -1266,14 +1409,14 @@ dependencies = [
"percent-encoding",
"phf",
"pin-project",
- "rand",
+ "rand 0.8.5",
"ring",
"serde",
"slab",
"smallvec",
"thiserror",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.8",
]
[[package]]
@@ -1304,7 +1447,7 @@ dependencies = [
"num-bigint",
"prost",
"prost-build",
- "rand",
+ "rand 0.8.5",
"reqwest",
"rusqlite",
"serde",
@@ -1312,7 +1455,7 @@ dependencies = [
"termcolor",
"tokio",
"url",
- "uuid",
+ "uuid 1.4.1",
]
[[package]]
@@ -1422,7 +1565,7 @@ dependencies = [
"p384 0.13.0",
"path-clean",
"pbkdf2",
- "rand",
+ "rand 0.8.5",
"regex",
"reqwest",
"ring",
@@ -1529,7 +1672,7 @@ dependencies = [
"test_util",
"tokio",
"tokio-metrics",
- "uuid",
+ "uuid 1.4.1",
"which",
"winapi",
"winres",
@@ -1561,7 +1704,7 @@ dependencies = [
"os_pipe",
"path-dedot",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.8",
]
[[package]]
@@ -1624,7 +1767,7 @@ dependencies = [
"futures",
"serde",
"tokio",
- "uuid",
+ "uuid 1.4.1",
"windows-sys",
]
@@ -1993,7 +2136,7 @@ version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
]
[[package]]
@@ -2015,6 +2158,17 @@ dependencies = [
]
[[package]]
+name = "enum-primitive-derive"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e"
+dependencies = [
+ "num-traits",
+ "quote 1.0.33",
+ "syn 1.0.109",
+]
+
+[[package]]
name = "env_logger"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2142,7 +2296,7 @@ dependencies = [
"base64 0.21.4",
"hyper 0.14.27",
"pin-project",
- "rand",
+ "rand 0.8.5",
"sha1",
"simdutf8",
"thiserror",
@@ -2156,7 +2310,7 @@ version = "3.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"rustix",
"windows-sys",
]
@@ -2193,7 +2347,7 @@ version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"redox_syscall 0.3.5",
"windows-sys",
@@ -2420,7 +2574,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@@ -2431,7 +2585,7 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
@@ -2504,7 +2658,7 @@ dependencies = [
"indexmap 1.9.3",
"slab",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.8",
"tracing",
]
@@ -2520,7 +2674,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
dependencies = [
- "ahash",
+ "ahash 0.8.3",
"allocator-api2",
]
@@ -2764,7 +2918,7 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632089ec08bd62e807311104122fb26d5c911ab172e2b9864be154a575979e29"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"indexmap 1.9.3",
"log",
"serde",
@@ -2830,7 +2984,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
]
[[package]]
@@ -3010,7 +3164,7 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"winapi",
]
@@ -3119,6 +3273,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
+name = "maybe-uninit"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
+
+[[package]]
name = "md-5"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3159,6 +3319,15 @@ checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
[[package]]
name = "memoffset"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
@@ -3275,7 +3444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [
"bitflags 1.3.2",
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
]
@@ -3286,9 +3455,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
"bitflags 1.3.2",
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
- "memoffset",
+ "memoffset 0.7.1",
"pin-utils",
"static_assertions",
]
@@ -3310,7 +3479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
dependencies = [
"bitflags 1.3.2",
- "crossbeam-channel",
+ "crossbeam-channel 0.5.5",
"filetime",
"fsevent-sys",
"inotify",
@@ -3339,7 +3508,7 @@ dependencies = [
"autocfg",
"num-integer",
"num-traits",
- "rand",
+ "rand 0.8.5",
"serde",
]
@@ -3355,7 +3524,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
- "rand",
+ "rand 0.8.5",
"serde",
"smallvec",
"zeroize",
@@ -3548,7 +3717,7 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall 0.2.16",
@@ -3562,7 +3731,7 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"redox_syscall 0.3.5",
"smallvec",
@@ -3663,7 +3832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared",
- "rand",
+ "rand 0.8.5",
]
[[package]]
@@ -3782,7 +3951,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"cpufeatures",
"opaque-debug",
"universal-hash",
@@ -3978,7 +4147,7 @@ dependencies = [
"nextest-workspace-hack",
"quick-xml",
"thiserror",
- "uuid",
+ "uuid 1.4.1",
]
[[package]]
@@ -4026,17 +4195,40 @@ dependencies = [
[[package]]
name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha",
+ "rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
@@ -4064,6 +4256,15 @@ dependencies = [
]
[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4148,7 +4349,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tokio-socks",
- "tokio-util",
+ "tokio-util 0.7.8",
"tower-service",
"url",
"wasm-bindgen",
@@ -4357,7 +4558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1cd5ae51d3f7bf65d7969d579d502168ef578f289452bd8ccc91de28fda20e"
dependencies = [
"bitflags 1.3.2",
- "cfg-if",
+ "cfg-if 1.0.0",
"clipboard-win",
"fd-lock",
"libc",
@@ -4484,7 +4685,7 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
dependencies = [
- "rand",
+ "rand 0.8.5",
"secp256k1-sys",
]
@@ -4637,7 +4838,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.7",
]
@@ -4648,7 +4849,7 @@ version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.7",
]
@@ -4659,7 +4860,7 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.7",
]
@@ -4828,7 +5029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
dependencies = [
"cc",
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
"psm",
"winapi",
@@ -4971,7 +5172,7 @@ checksum = "39cb7fcd56655c8ae7dcf2344f0be6cbff4d9c7cb401fe3ec8e56e1de8dfe582"
dependencies = [
"ast_node",
"better_scoped_tls",
- "cfg-if",
+ "cfg-if 1.0.0",
"either",
"from_variant",
"new_debug_unreachable",
@@ -5165,7 +5366,7 @@ version = "0.192.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "880fd2a588ac88a61cd1d21b10203bbabe31d7adacbd22de3bb4d702bf2c42b4"
dependencies = [
- "dashmap",
+ "dashmap 5.5.3",
"indexmap 1.9.3",
"once_cell",
"petgraph",
@@ -5210,7 +5411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675b5c755b0448268830e85e59429095d3423c0ce4a850b209c6f0eeab069f63"
dependencies = [
"base64 0.13.1",
- "dashmap",
+ "dashmap 5.5.3",
"indexmap 1.9.3",
"once_cell",
"serde",
@@ -5410,7 +5611,7 @@ version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"fastrand",
"redox_syscall 0.3.5",
"rustix",
@@ -5549,6 +5750,15 @@ dependencies = [
]
[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5640,6 +5850,21 @@ dependencies = [
[[package]]
name = "tokio-util"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
@@ -5707,7 +5932,7 @@ dependencies = [
"async-trait",
"auto_impl 0.5.0",
"bytes",
- "dashmap",
+ "dashmap 5.5.3",
"futures",
"httparse",
"log",
@@ -5716,7 +5941,7 @@ dependencies = [
"serde",
"serde_json",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.8",
"tower",
"tower-lsp-macros",
]
@@ -5744,7 +5969,7 @@ version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -5786,13 +6011,13 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c408c32e6a9dbb38037cece35740f2cf23c875d8ca134d33631cec83f74d3fe"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"data-encoding",
"futures-channel",
"futures-util",
"lazy_static",
"radix_trie",
- "rand",
+ "rand 0.8.5",
"thiserror",
"time",
"tokio",
@@ -5807,7 +6032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26"
dependencies = [
"async-trait",
- "cfg-if",
+ "cfg-if 1.0.0",
"data-encoding",
"enum-as-inner",
"futures-channel",
@@ -5816,7 +6041,7 @@ dependencies = [
"idna 0.2.3",
"ipnet",
"lazy_static",
- "rand",
+ "rand 0.8.5",
"serde",
"smallvec",
"thiserror",
@@ -5832,7 +6057,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"futures-util",
"ipconfig",
"lazy_static",
@@ -5855,7 +6080,7 @@ checksum = "99022f9befa6daec2a860be68ac28b1f0d9d7ccf441d8c5a695e35a58d88840d"
dependencies = [
"async-trait",
"bytes",
- "cfg-if",
+ "cfg-if 1.0.0",
"enum-as-inner",
"futures-executor",
"futures-util",
@@ -5881,8 +6106,8 @@ version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
- "cfg-if",
- "rand",
+ "cfg-if 1.0.0",
+ "rand 0.8.5",
"static_assertions",
]
@@ -6054,6 +6279,15 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+dependencies = [
+ "getrandom 0.2.10",
+]
+
+[[package]]
+name = "uuid"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
@@ -6151,7 +6385,7 @@ version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"wasm-bindgen-macro",
]
@@ -6176,7 +6410,7 @@ version = "0.4.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
@@ -6389,7 +6623,7 @@ version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"windows-sys",
]
@@ -6467,6 +6701,32 @@ dependencies = [
]
[[package]]
+name = "zeromq"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "667ece59294ccaf617fcf2e5decc9114a06427c1f68990028b9f12d322686bdc"
+dependencies = [
+ "async-trait",
+ "asynchronous-codec",
+ "bytes",
+ "crossbeam",
+ "dashmap 3.11.10",
+ "enum-primitive-derive",
+ "futures",
+ "futures-util",
+ "lazy_static",
+ "log",
+ "num-traits",
+ "parking_lot 0.11.2",
+ "rand 0.7.3",
+ "regex",
+ "thiserror",
+ "tokio",
+ "tokio-util 0.6.10",
+ "uuid 0.8.2",
+]
+
+[[package]]
name = "zstd"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index b601edcc2..e896d4c98 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -85,6 +85,7 @@ cbc = { version = "=0.1.2", features = ["alloc"] }
chrono = { version = "0.4", default-features = false, features = ["std", "serde", "clock"] }
console_static_text = "=0.8.1"
data-url = "=0.3.0"
+data-encoding = "2.3.3"
dlopen = "0.1.8"
encoding_rs = "=0.8.33"
ecb = "=0.1.2"
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 8906488e9..58a45538a 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -65,12 +65,14 @@ async-trait.workspace = true
base32 = "=0.4.0"
base64.workspace = true
bincode = "=1.3.3"
+bytes.workspace = true
cache_control.workspace = true
chrono.workspace = true
clap = { version = "=4.3.3", features = ["string"] }
clap_complete = "=4.3.1"
clap_complete_fig = "=4.3.1"
console_static_text.workspace = true
+data-encoding.workspace = true
data-url.workspace = true
dissimilar = "=1.0.4"
dprint-plugin-json = "=0.17.4"
@@ -120,6 +122,7 @@ twox-hash = "=1.6.3"
typed-arena = "=2.0.1"
uuid = { workspace = true, features = ["serde"] }
walkdir = "=2.3.2"
+zeromq = { version = "=0.3.3", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
zstd.workspace = true
[target.'cfg(windows)'.dependencies]
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index b69f1ce8f..40aa7b8e3 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -159,6 +159,13 @@ pub struct InstallFlags {
}
#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct JupyterFlags {
+ pub install: bool,
+ pub kernel: bool,
+ pub conn_file: Option<PathBuf>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UninstallFlags {
pub name: String,
pub root: Option<PathBuf>,
@@ -276,6 +283,7 @@ pub enum DenoSubcommand {
Init(InitFlags),
Info(InfoFlags),
Install(InstallFlags),
+ Jupyter(JupyterFlags),
Uninstall(UninstallFlags),
Lsp,
Lint(LintFlags),
@@ -678,7 +686,8 @@ impl Flags {
std::env::current_dir().ok()
}
Bundle(_) | Completions(_) | Doc(_) | Fmt(_) | Init(_) | Install(_)
- | Uninstall(_) | Lsp | Lint(_) | Types | Upgrade(_) | Vendor(_) => None,
+ | Uninstall(_) | Jupyter(_) | Lsp | Lint(_) | Types | Upgrade(_)
+ | Vendor(_) => None,
}
}
@@ -818,6 +827,7 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::error::Result<Flags> {
"init" => init_parse(&mut flags, &mut m),
"info" => info_parse(&mut flags, &mut m),
"install" => install_parse(&mut flags, &mut m),
+ "jupyter" => jupyter_parse(&mut flags, &mut m),
"lint" => lint_parse(&mut flags, &mut m),
"lsp" => lsp_parse(&mut flags, &mut m),
"repl" => repl_parse(&mut flags, &mut m),
@@ -919,6 +929,7 @@ fn clap_root() -> Command {
.subcommand(init_subcommand())
.subcommand(info_subcommand())
.subcommand(install_subcommand())
+ .subcommand(jupyter_subcommand())
.subcommand(uninstall_subcommand())
.subcommand(lsp_subcommand())
.subcommand(lint_subcommand())
@@ -1613,6 +1624,33 @@ These must be added to the path manually if required.")
)
}
+fn jupyter_subcommand() -> Command {
+ Command::new("jupyter")
+ .arg(
+ Arg::new("install")
+ .long("install")
+ .help("Installs kernelspec, requires 'jupyter' command to be available.")
+ .conflicts_with("kernel")
+ .action(ArgAction::SetTrue)
+ )
+ .arg(
+ Arg::new("kernel")
+ .long("kernel")
+ .help("Start the kernel")
+ .conflicts_with("install")
+ .requires("conn")
+ .action(ArgAction::SetTrue)
+ )
+ .arg(
+ Arg::new("conn")
+ .long("conn")
+ .help("Path to JSON file describing connection parameters, provided by Jupyter")
+ .value_parser(value_parser!(PathBuf))
+ .value_hint(ValueHint::FilePath)
+ .conflicts_with("install"))
+ .about("Deno kernel for Jupyter notebooks")
+}
+
fn uninstall_subcommand() -> Command {
Command::new("uninstall")
.about("Uninstall a script previously installed with deno install")
@@ -3166,6 +3204,18 @@ fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) {
});
}
+fn jupyter_parse(flags: &mut Flags, matches: &mut ArgMatches) {
+ let conn_file = matches.remove_one::<PathBuf>("conn");
+ let kernel = matches.get_flag("kernel");
+ let install = matches.get_flag("install");
+
+ flags.subcommand = DenoSubcommand::Jupyter(JupyterFlags {
+ install,
+ kernel,
+ conn_file,
+ });
+}
+
fn uninstall_parse(flags: &mut Flags, matches: &mut ArgMatches) {
let root = matches.remove_one::<PathBuf>("root");
@@ -7829,4 +7879,69 @@ mod tests {
}
);
}
+
+ #[test]
+ fn jupyter() {
+ let r = flags_from_vec(svec!["deno", "jupyter", "--unstable"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Jupyter(JupyterFlags {
+ install: false,
+ kernel: false,
+ conn_file: None,
+ }),
+ unstable: true,
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec!["deno", "jupyter", "--unstable", "--install"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Jupyter(JupyterFlags {
+ install: true,
+ kernel: false,
+ conn_file: None,
+ }),
+ unstable: true,
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec![
+ "deno",
+ "jupyter",
+ "--unstable",
+ "--kernel",
+ "--conn",
+ "path/to/conn/file"
+ ]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Jupyter(JupyterFlags {
+ install: false,
+ kernel: true,
+ conn_file: Some(PathBuf::from("path/to/conn/file")),
+ }),
+ unstable: true,
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec![
+ "deno",
+ "jupyter",
+ "--install",
+ "--conn",
+ "path/to/conn/file"
+ ]);
+ r.unwrap_err();
+ let r = flags_from_vec(svec!["deno", "jupyter", "--kernel",]);
+ r.unwrap_err();
+ let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--kernel",]);
+ r.unwrap_err();
+ }
}
diff --git a/cli/main.rs b/cli/main.rs
index df5dd0b26..98a2e5d48 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -134,6 +134,9 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
DenoSubcommand::Install(install_flags) => spawn_subcommand(async {
tools::installer::install_command(flags, install_flags).await
}),
+ DenoSubcommand::Jupyter(jupyter_flags) => spawn_subcommand(async {
+ tools::jupyter::kernel(flags, jupyter_flags).await
+ }),
DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async {
tools::installer::uninstall(uninstall_flags.name, uninstall_flags.root)
}),
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index 67811304b..4a1e0b671 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -330,7 +330,10 @@ impl CliModuleLoaderFactory {
lib_window: options.ts_type_lib_window(),
lib_worker: options.ts_type_lib_worker(),
is_inspecting: options.is_inspecting(),
- is_repl: matches!(options.sub_command(), DenoSubcommand::Repl(_)),
+ is_repl: matches!(
+ options.sub_command(),
+ DenoSubcommand::Repl(_) | DenoSubcommand::Jupyter(_)
+ ),
prepared_module_loader: PreparedModuleLoader {
emitter,
graph_container: graph_container.clone(),
diff --git a/cli/tests/testdata/jupyter/integration_test.ipynb b/cli/tests/testdata/jupyter/integration_test.ipynb
new file mode 100644
index 000000000..ec6b27973
--- /dev/null
+++ b/cli/tests/testdata/jupyter/integration_test.ipynb
@@ -0,0 +1,620 @@
+{
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "182aef1d",
+ "metadata": {},
+ "source": [
+ "# Integration Tests for Deno Jupyter\n",
+ "This notebook contains a number of tests to ensure that Jupyter is working as expected. You should be able to select \"Kernel -> Restart Kernel and Run All\" in Jupyter's notebook UI to run the tests."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "d7705d88",
+ "metadata": {},
+ "source": [
+ "## Passing Tests"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "669f972e",
+ "metadata": {
+ "heading_collapsed": true
+ },
+ "source": [
+ "### Simple Tests"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "e7e8a512",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### This test should print \"hi\".\n",
+ "If this doesn't work, everything else will probably fail :)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "a5d38758",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {},
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "hi\n"
+ ]
+ }
+ ],
+ "source": [
+ "console.log(\"hi\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "bc5ce8e3",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### Top-level await"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "f7fa885a",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {},
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "x is \u001b[33m42\u001b[39m\n"
+ ]
+ }
+ ],
+ "source": [
+ "let x = await Promise.resolve(42);\n",
+ "console.log(\"x is\", x);"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "c21455ae",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### TypeScript transpiling\n",
+ "Credit to [typescriptlang.org](https://www.typescriptlang.org/docs/handbook/interfaces.html) for this code"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "08a17340",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{ color: \u001b[32m\"red\"\u001b[39m, area: \u001b[33m10000\u001b[39m }"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "interface SquareConfig {\n",
+ " color?: string;\n",
+ " width?: number;\n",
+ "}\n",
+ " \n",
+ "function createSquare(config: SquareConfig): { color: string; area: number } {\n",
+ " return {\n",
+ " color: config.color || \"red\",\n",
+ " area: config.width ? config.width * config.width : 20,\n",
+ " };\n",
+ "}\n",
+ " \n",
+ "createSquare({ colour: \"red\", width: 100 });"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "eaa0ebc0",
+ "metadata": {
+ "heading_collapsed": true
+ },
+ "source": [
+ "### Return Values"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "52876276",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### undefined should not return a value"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "bbf2c09b",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {},
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "undefined"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "e175c803",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### null should return \"null\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "d9801d80",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[1mnull\u001b[22m"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "null"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "a2a716dc",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### boolean should return the boolean"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "cfaac330",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[33mtrue\u001b[39m"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "true"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "8d9f1aba",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### number should return the number"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "ec3be2da",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[33m42\u001b[39m"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "42"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "60965915",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### string should return the string"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "997cf2d7",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[32m\"this is a test of the emergency broadcast system\"\u001b[39m"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\"this is a test of the emergency broadcast system\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "fe38dc27",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### bigint should return the bigint in literal format"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "44b63807",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[33m31337n\u001b[39m"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "31337n"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "843ccb6c",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### symbol should return a string describing the symbol"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "e10c0d31",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\u001b[32mSymbol(foo)\u001b[39m"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Symbol(\"foo\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "171b817f",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### object should describe the object inspection"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "81c99233",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{ foo: \u001b[32m\"bar\"\u001b[39m }"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{foo: \"bar\"}"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "6caeb583",
+ "metadata": {
+ "hidden": true
+ },
+ "source": [
+ "#### resolve returned promise"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "43c1581b",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Promise { \u001b[32m\"it worked!\"\u001b[39m }"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Promise.resolve(\"it worked!\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "9a34b725",
+ "metadata": {
+ "hidden": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Promise {\n",
+ " \u001b[36m<rejected>\u001b[39m Error: it failed!\n",
+ " at <anonymous>:2:16\n",
+ "}"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Promise.reject(new Error(\"it failed!\"));"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "b5c7b819",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "ename": "Error: this is a test\n at foo (<anonymous>:3:9)\n at <anonymous>:4:3",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "(function foo() {\n",
+ " throw new Error(\"this is a test\")\n",
+ "})()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "72d01fdd",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Promise {\n",
+ " \u001b[36m<rejected>\u001b[39m TypeError: Expected string at position 0\n",
+ " at Object.readFile (ext:deno_fs/30_fs.js:716:29)\n",
+ " at <anonymous>:2:6\n",
+ "}"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Deno.readFile(1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "28cf59d0-6908-4edc-bb10-c325beeee362",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "console.log(\"Hello from Deno!\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8d5485c3-0da3-43fe-8ef5-a61e672f5e81",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "console.log(\"%c Hello Deno \", \"background-color: #15803d; color: white;\");"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1401d9d5-6994-4c7b-b55a-db3c16a1e2dc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"Cool 🫡\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7afdaa0a-a2a0-4f52-8c7d-b6c5f237aa0d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "console.table([1, 2, 3])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8e93df23-06eb-414b-98d4-51fbebb53d1f",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Deno",
+ "language": "typescript",
+ "name": "deno"
+ },
+ "language_info": {
+ "file_extension": ".ts",
+ "mimetype": "text/x.typescript",
+ "name": "typescript",
+ "nb_converter": "script",
+ "pygments_lexer": "typescript",
+ "version": "5.2.2"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": true,
+ "sideBar": true,
+ "skip_h1_title": false,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": false,
+ "toc_position": {},
+ "toc_section_display": true,
+ "toc_window_display": true
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/cli/tools/jupyter/install.rs b/cli/tools/jupyter/install.rs
new file mode 100644
index 000000000..d1777d92d
--- /dev/null
+++ b/cli/tools/jupyter/install.rs
@@ -0,0 +1,95 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::anyhow::bail;
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use deno_core::serde_json;
+use deno_core::serde_json::json;
+use std::env::current_exe;
+use std::io::Write;
+use std::path::Path;
+use tempfile::TempDir;
+
+const DENO_ICON_32: &[u8] = include_bytes!("./resources/deno-logo-32x32.png");
+const DENO_ICON_64: &[u8] = include_bytes!("./resources/deno-logo-64x64.png");
+
+pub fn status() -> Result<(), AnyError> {
+ let output = std::process::Command::new("jupyter")
+ .args(["kernelspec", "list", "--json"])
+ .output()
+ .context("Failed to get list of installed kernelspecs")?;
+ let json_output: serde_json::Value =
+ serde_json::from_slice(&output.stdout)
+ .context("Failed to parse JSON from kernelspec list")?;
+
+ if let Some(specs) = json_output.get("kernelspecs") {
+ if let Some(specs_obj) = specs.as_object() {
+ if specs_obj.contains_key("deno") {
+ println!("✅ Deno kernel already installed");
+ return Ok(());
+ }
+ }
+ }
+
+ println!("ℹ️ Deno kernel is not yet installed, run `deno jupyter --unstable --install` to set it up");
+ Ok(())
+}
+
+fn install_icon(
+ dir_path: &Path,
+ filename: &str,
+ icon_data: &[u8],
+) -> Result<(), AnyError> {
+ let path = dir_path.join(filename);
+ let mut file = std::fs::File::create(path)?;
+ file.write_all(icon_data)?;
+ Ok(())
+}
+
+pub fn install() -> Result<(), AnyError> {
+ let temp_dir = TempDir::new().unwrap();
+ let kernel_json_path = temp_dir.path().join("kernel.json");
+
+ // TODO(bartlomieju): add remaining fields as per
+ // https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-specs
+ // FIXME(bartlomieju): replace `current_exe` before landing?
+ let json_data = json!({
+ "argv": [current_exe().unwrap().to_string_lossy(), "--unstable", "jupyter", "--kernel", "--conn", "{connection_file}"],
+ "display_name": "Deno",
+ "language": "typescript",
+ });
+
+ let f = std::fs::File::create(kernel_json_path)?;
+ serde_json::to_writer_pretty(f, &json_data)?;
+ install_icon(temp_dir.path(), "icon-32x32.png", DENO_ICON_32)?;
+ install_icon(temp_dir.path(), "icon-64x64.png", DENO_ICON_64)?;
+
+ let child_result = std::process::Command::new("jupyter")
+ .args([
+ "kernelspec",
+ "install",
+ "--user",
+ "--name",
+ "deno",
+ &temp_dir.path().to_string_lossy(),
+ ])
+ .spawn();
+
+ if let Ok(mut child) = child_result {
+ let wait_result = child.wait();
+ match wait_result {
+ Ok(status) => {
+ if !status.success() {
+ bail!("Failed to install kernelspec, try again.");
+ }
+ }
+ Err(err) => {
+ bail!("Failed to install kernelspec: {}", err);
+ }
+ }
+ }
+
+ let _ = std::fs::remove_dir(temp_dir);
+ println!("✅ Deno kernelspec installed successfully.");
+ Ok(())
+}
diff --git a/cli/tools/jupyter/jupyter_msg.rs b/cli/tools/jupyter/jupyter_msg.rs
new file mode 100644
index 000000000..c28dd3b48
--- /dev/null
+++ b/cli/tools/jupyter/jupyter_msg.rs
@@ -0,0 +1,268 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// This file is forked/ported from <https://github.com/evcxr/evcxr>
+// Copyright 2020 The Evcxr Authors. MIT license.
+
+use bytes::Bytes;
+use chrono::Utc;
+use data_encoding::HEXLOWER;
+use deno_core::anyhow::anyhow;
+use deno_core::anyhow::bail;
+use deno_core::error::AnyError;
+use deno_core::serde_json;
+use deno_core::serde_json::json;
+use ring::hmac;
+use std::fmt;
+use uuid::Uuid;
+
+pub(crate) struct Connection<S> {
+ pub(crate) socket: S,
+ /// Will be None if our key was empty (digest authentication disabled).
+ pub(crate) mac: Option<hmac::Key>,
+}
+
+impl<S: zeromq::Socket> Connection<S> {
+ pub(crate) fn new(socket: S, key: &str) -> Self {
+ let mac = if key.is_empty() {
+ None
+ } else {
+ Some(hmac::Key::new(hmac::HMAC_SHA256, key.as_bytes()))
+ };
+ Connection { socket, mac }
+ }
+}
+
+struct RawMessage {
+ zmq_identities: Vec<Bytes>,
+ jparts: Vec<Bytes>,
+}
+
+impl RawMessage {
+ pub(crate) async fn read<S: zeromq::SocketRecv>(
+ connection: &mut Connection<S>,
+ ) -> Result<RawMessage, AnyError> {
+ Self::from_multipart(connection.socket.recv().await?, connection)
+ }
+
+ pub(crate) fn from_multipart<S>(
+ multipart: zeromq::ZmqMessage,
+ connection: &Connection<S>,
+ ) -> Result<RawMessage, AnyError> {
+ let delimiter_index = multipart
+ .iter()
+ .position(|part| &part[..] == DELIMITER)
+ .ok_or_else(|| anyhow!("Missing delimiter"))?;
+ let mut parts = multipart.into_vec();
+ let jparts: Vec<_> = parts.drain(delimiter_index + 2..).collect();
+ let expected_hmac = parts.pop().unwrap();
+ // Remove delimiter, so that what's left is just the identities.
+ parts.pop();
+ let zmq_identities = parts;
+
+ let raw_message = RawMessage {
+ zmq_identities,
+ jparts,
+ };
+
+ if let Some(key) = &connection.mac {
+ let sig = HEXLOWER.decode(&expected_hmac)?;
+ let mut msg = Vec::new();
+ for part in &raw_message.jparts {
+ msg.extend(part);
+ }
+
+ if let Err(err) = hmac::verify(key, msg.as_ref(), sig.as_ref()) {
+ bail!("{}", err);
+ }
+ }
+
+ Ok(raw_message)
+ }
+
+ async fn send<S: zeromq::SocketSend>(
+ self,
+ connection: &mut Connection<S>,
+ ) -> Result<(), AnyError> {
+ let hmac = if let Some(key) = &connection.mac {
+ let ctx = self.digest(key);
+ let tag = ctx.sign();
+ HEXLOWER.encode(tag.as_ref())
+ } else {
+ String::new()
+ };
+ let mut parts: Vec<bytes::Bytes> = Vec::new();
+ for part in &self.zmq_identities {
+ parts.push(part.to_vec().into());
+ }
+ parts.push(DELIMITER.into());
+ parts.push(hmac.as_bytes().to_vec().into());
+ for part in &self.jparts {
+ parts.push(part.to_vec().into());
+ }
+ // ZmqMessage::try_from only fails if parts is empty, which it never
+ // will be here.
+ let message = zeromq::ZmqMessage::try_from(parts).unwrap();
+ connection.socket.send(message).await?;
+ Ok(())
+ }
+
+ fn digest(&self, mac: &hmac::Key) -> hmac::Context {
+ let mut hmac_ctx = hmac::Context::with_key(mac);
+ for part in &self.jparts {
+ hmac_ctx.update(part);
+ }
+ hmac_ctx
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct JupyterMessage {
+ zmq_identities: Vec<Bytes>,
+ header: serde_json::Value,
+ parent_header: serde_json::Value,
+ metadata: serde_json::Value,
+ content: serde_json::Value,
+}
+
+const DELIMITER: &[u8] = b"<IDS|MSG>";
+
+impl JupyterMessage {
+ pub(crate) async fn read<S: zeromq::SocketRecv>(
+ connection: &mut Connection<S>,
+ ) -> Result<JupyterMessage, AnyError> {
+ Self::from_raw_message(RawMessage::read(connection).await?)
+ }
+
+ fn from_raw_message(
+ raw_message: RawMessage,
+ ) -> Result<JupyterMessage, AnyError> {
+ if raw_message.jparts.len() < 4 {
+ bail!("Insufficient message parts {}", raw_message.jparts.len());
+ }
+
+ Ok(JupyterMessage {
+ zmq_identities: raw_message.zmq_identities,
+ header: serde_json::from_slice(&raw_message.jparts[0])?,
+ parent_header: serde_json::from_slice(&raw_message.jparts[1])?,
+ metadata: serde_json::from_slice(&raw_message.jparts[2])?,
+ content: serde_json::from_slice(&raw_message.jparts[3])?,
+ })
+ }
+
+ pub(crate) fn message_type(&self) -> &str {
+ self.header["msg_type"].as_str().unwrap_or("")
+ }
+
+ pub(crate) fn code(&self) -> &str {
+ self.content["code"].as_str().unwrap_or("")
+ }
+
+ pub(crate) fn cursor_pos(&self) -> usize {
+ self.content["cursor_pos"].as_u64().unwrap_or(0) as usize
+ }
+
+ pub(crate) fn comm_id(&self) -> &str {
+ self.content["comm_id"].as_str().unwrap_or("")
+ }
+
+ // Creates a new child message of this message. ZMQ identities are not transferred.
+ pub(crate) fn new_message(&self, msg_type: &str) -> JupyterMessage {
+ let mut header = self.header.clone();
+ header["msg_type"] = serde_json::Value::String(msg_type.to_owned());
+ header["username"] = serde_json::Value::String("kernel".to_owned());
+ header["msg_id"] = serde_json::Value::String(Uuid::new_v4().to_string());
+ header["date"] = serde_json::Value::String(Utc::now().to_rfc3339());
+
+ JupyterMessage {
+ zmq_identities: Vec::new(),
+ header,
+ parent_header: self.header.clone(),
+ metadata: json!({}),
+ content: json!({}),
+ }
+ }
+
+ // Creates a reply to this message. This is a child with the message type determined
+ // automatically by replacing "request" with "reply". ZMQ identities are transferred.
+ pub(crate) fn new_reply(&self) -> JupyterMessage {
+ let mut reply =
+ self.new_message(&self.message_type().replace("_request", "_reply"));
+ reply.zmq_identities = self.zmq_identities.clone();
+ reply
+ }
+
+ #[must_use = "Need to send this message for it to have any effect"]
+ pub(crate) fn comm_close_message(&self) -> JupyterMessage {
+ self.new_message("comm_close").with_content(json!({
+ "comm_id": self.comm_id()
+ }))
+ }
+
+ pub(crate) fn with_content(
+ mut self,
+ content: serde_json::Value,
+ ) -> JupyterMessage {
+ self.content = content;
+ self
+ }
+
+ pub(crate) async fn send<S: zeromq::SocketSend>(
+ &self,
+ connection: &mut Connection<S>,
+ ) -> Result<(), AnyError> {
+ // If performance is a concern, we can probably avoid the clone and to_vec calls with a bit
+ // of refactoring.
+ let raw_message = RawMessage {
+ zmq_identities: self.zmq_identities.clone(),
+ jparts: vec![
+ serde_json::to_string(&self.header)
+ .unwrap()
+ .as_bytes()
+ .to_vec()
+ .into(),
+ serde_json::to_string(&self.parent_header)
+ .unwrap()
+ .as_bytes()
+ .to_vec()
+ .into(),
+ serde_json::to_string(&self.metadata)
+ .unwrap()
+ .as_bytes()
+ .to_vec()
+ .into(),
+ serde_json::to_string(&self.content)
+ .unwrap()
+ .as_bytes()
+ .to_vec()
+ .into(),
+ ],
+ };
+ raw_message.send(connection).await
+ }
+}
+
+impl fmt::Debug for JupyterMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ writeln!(
+ f,
+ "\nHeader: {}",
+ serde_json::to_string_pretty(&self.header).unwrap()
+ )?;
+ writeln!(
+ f,
+ "Parent header: {}",
+ serde_json::to_string_pretty(&self.parent_header).unwrap()
+ )?;
+ writeln!(
+ f,
+ "Metadata: {}",
+ serde_json::to_string_pretty(&self.metadata).unwrap()
+ )?;
+ writeln!(
+ f,
+ "Content: {}\n",
+ serde_json::to_string_pretty(&self.content).unwrap()
+ )?;
+ Ok(())
+ }
+}
diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs
new file mode 100644
index 000000000..b704d58cd
--- /dev/null
+++ b/cli/tools/jupyter/mod.rs
@@ -0,0 +1,139 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use crate::args::Flags;
+use crate::args::JupyterFlags;
+use crate::tools::repl;
+use crate::util::logger;
+use crate::CliFactory;
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use deno_core::futures::channel::mpsc;
+use deno_core::op;
+use deno_core::resolve_url_or_path;
+use deno_core::serde::Deserialize;
+use deno_core::serde_json;
+use deno_core::Op;
+use deno_core::OpState;
+use deno_runtime::permissions::Permissions;
+use deno_runtime::permissions::PermissionsContainer;
+
+mod install;
+mod jupyter_msg;
+mod server;
+
+pub async fn kernel(
+ flags: Flags,
+ jupyter_flags: JupyterFlags,
+) -> Result<(), AnyError> {
+ if !flags.unstable {
+ eprintln!(
+ "Unstable subcommand 'deno jupyter'. The --unstable flag must be provided."
+ );
+ std::process::exit(70);
+ }
+
+ if !jupyter_flags.install && !jupyter_flags.kernel {
+ install::status()?;
+ return Ok(());
+ }
+
+ if jupyter_flags.install {
+ install::install()?;
+ return Ok(());
+ }
+
+ let connection_filepath = jupyter_flags.conn_file.unwrap();
+
+ // This env var might be set by notebook
+ if std::env::var("DEBUG").is_ok() {
+ logger::init(Some(log::Level::Debug));
+ }
+
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
+ let main_module =
+ resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd())
+ .unwrap();
+ // TODO(bartlomieju): should we run with all permissions?
+ let permissions = PermissionsContainer::new(Permissions::allow_all());
+ let npm_resolver = factory.npm_resolver().await?.clone();
+ let resolver = factory.resolver().await?.clone();
+ let worker_factory = factory.create_cli_main_worker_factory().await?;
+ let (stdio_tx, stdio_rx) = mpsc::unbounded();
+
+ let conn_file =
+ std::fs::read_to_string(&connection_filepath).with_context(|| {
+ format!("Couldn't read connection file: {:?}", connection_filepath)
+ })?;
+ let spec: ConnectionSpec =
+ serde_json::from_str(&conn_file).with_context(|| {
+ format!(
+ "Connection file is not a valid JSON: {:?}",
+ connection_filepath
+ )
+ })?;
+
+ let mut worker = worker_factory
+ .create_custom_worker(
+ main_module.clone(),
+ permissions,
+ vec![deno_jupyter::init_ops(stdio_tx)],
+ Default::default(),
+ )
+ .await?;
+ worker.setup_repl().await?;
+ let worker = worker.into_main_worker();
+ let repl_session =
+ repl::ReplSession::initialize(cli_options, npm_resolver, resolver, worker)
+ .await?;
+
+ server::JupyterServer::start(spec, stdio_rx, repl_session).await?;
+
+ Ok(())
+}
+
+deno_core::extension!(deno_jupyter,
+ options = {
+ sender: mpsc::UnboundedSender<server::StdioMsg>,
+ },
+ middleware = |op| match op.name {
+ "op_print" => op_print::DECL,
+ _ => op,
+ },
+ state = |state, options| {
+ state.put(options.sender);
+ },
+);
+
+#[op]
+pub fn op_print(
+ state: &mut OpState,
+ msg: String,
+ is_err: bool,
+) -> Result<(), AnyError> {
+ let sender = state.borrow_mut::<mpsc::UnboundedSender<server::StdioMsg>>();
+
+ if is_err {
+ if let Err(err) = sender.unbounded_send(server::StdioMsg::Stderr(msg)) {
+ eprintln!("Failed to send stderr message: {}", err);
+ }
+ return Ok(());
+ }
+
+ if let Err(err) = sender.unbounded_send(server::StdioMsg::Stdout(msg)) {
+ eprintln!("Failed to send stdout message: {}", err);
+ }
+ Ok(())
+}
+
+#[derive(Debug, Deserialize)]
+pub struct ConnectionSpec {
+ ip: String,
+ transport: String,
+ control_port: u32,
+ shell_port: u32,
+ stdin_port: u32,
+ hb_port: u32,
+ iopub_port: u32,
+ key: String,
+}
diff --git a/cli/tools/jupyter/resources/deno-logo-32x32.png b/cli/tools/jupyter/resources/deno-logo-32x32.png
new file mode 100644
index 000000000..97871a02e
--- /dev/null
+++ b/cli/tools/jupyter/resources/deno-logo-32x32.png
Binary files differ
diff --git a/cli/tools/jupyter/resources/deno-logo-64x64.png b/cli/tools/jupyter/resources/deno-logo-64x64.png
new file mode 100644
index 000000000..1b9444ef6
--- /dev/null
+++ b/cli/tools/jupyter/resources/deno-logo-64x64.png
Binary files differ
diff --git a/cli/tools/jupyter/server.rs b/cli/tools/jupyter/server.rs
new file mode 100644
index 000000000..c15dab6c2
--- /dev/null
+++ b/cli/tools/jupyter/server.rs
@@ -0,0 +1,724 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// This file is forked/ported from <https://github.com/evcxr/evcxr>
+// Copyright 2020 The Evcxr Authors. MIT license.
+
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::rc::Rc;
+use std::sync::Arc;
+
+use crate::tools::repl;
+use crate::tools::repl::cdp;
+use deno_core::error::AnyError;
+use deno_core::futures;
+use deno_core::futures::channel::mpsc;
+use deno_core::futures::StreamExt;
+use deno_core::serde_json;
+use deno_core::serde_json::json;
+use deno_core::CancelFuture;
+use deno_core::CancelHandle;
+use tokio::sync::Mutex;
+use zeromq::SocketRecv;
+use zeromq::SocketSend;
+
+use super::jupyter_msg::Connection;
+use super::jupyter_msg::JupyterMessage;
+use super::ConnectionSpec;
+
+pub enum StdioMsg {
+ Stdout(String),
+ Stderr(String),
+}
+
+pub struct JupyterServer {
+ execution_count: usize,
+ last_execution_request: Rc<RefCell<Option<JupyterMessage>>>,
+ // This is Arc<Mutex<>>, so we don't hold RefCell borrows across await
+ // points.
+ iopub_socket: Arc<Mutex<Connection<zeromq::PubSocket>>>,
+ repl_session: repl::ReplSession,
+}
+
+impl JupyterServer {
+ pub async fn start(
+ spec: ConnectionSpec,
+ mut stdio_rx: mpsc::UnboundedReceiver<StdioMsg>,
+ repl_session: repl::ReplSession,
+ ) -> Result<(), AnyError> {
+ let mut heartbeat =
+ bind_socket::<zeromq::RepSocket>(&spec, spec.hb_port).await?;
+ let shell_socket =
+ bind_socket::<zeromq::RouterSocket>(&spec, spec.shell_port).await?;
+ let control_socket =
+ bind_socket::<zeromq::RouterSocket>(&spec, spec.control_port).await?;
+ let _stdin_socket =
+ bind_socket::<zeromq::RouterSocket>(&spec, spec.stdin_port).await?;
+ let iopub_socket =
+ bind_socket::<zeromq::PubSocket>(&spec, spec.iopub_port).await?;
+ let iopub_socket = Arc::new(Mutex::new(iopub_socket));
+ let last_execution_request = Rc::new(RefCell::new(None));
+
+ let cancel_handle = CancelHandle::new_rc();
+ let cancel_handle2 = CancelHandle::new_rc();
+
+ let mut server = Self {
+ execution_count: 0,
+ iopub_socket: iopub_socket.clone(),
+ last_execution_request: last_execution_request.clone(),
+ repl_session,
+ };
+
+ let handle1 = deno_core::unsync::spawn(async move {
+ if let Err(err) = Self::handle_heartbeat(&mut heartbeat).await {
+ eprintln!("Heartbeat error: {}", err);
+ }
+ });
+
+ let handle2 = deno_core::unsync::spawn(async move {
+ if let Err(err) =
+ Self::handle_control(control_socket, cancel_handle2).await
+ {
+ eprintln!("Control error: {}", err);
+ }
+ });
+
+ let handle3 = deno_core::unsync::spawn(async move {
+ if let Err(err) = server.handle_shell(shell_socket).await {
+ eprintln!("Shell error: {}", err);
+ }
+ });
+
+ let handle4 = deno_core::unsync::spawn(async move {
+ while let Some(stdio_msg) = stdio_rx.next().await {
+ Self::handle_stdio_msg(
+ iopub_socket.clone(),
+ last_execution_request.clone(),
+ stdio_msg,
+ )
+ .await;
+ }
+ });
+
+ let join_fut =
+ futures::future::try_join_all(vec![handle1, handle2, handle3, handle4]);
+
+ if let Ok(result) = join_fut.or_cancel(cancel_handle).await {
+ result?;
+ }
+
+ Ok(())
+ }
+
+ async fn handle_stdio_msg<S: zeromq::SocketSend>(
+ iopub_socket: Arc<Mutex<Connection<S>>>,
+ last_execution_request: Rc<RefCell<Option<JupyterMessage>>>,
+ stdio_msg: StdioMsg,
+ ) {
+ let maybe_exec_result = last_execution_request.borrow().clone();
+ if let Some(exec_request) = maybe_exec_result {
+ let (name, text) = match stdio_msg {
+ StdioMsg::Stdout(text) => ("stdout", text),
+ StdioMsg::Stderr(text) => ("stderr", text),
+ };
+
+ let result = exec_request
+ .new_message("stream")
+ .with_content(json!({
+ "name": name,
+ "text": text
+ }))
+ .send(&mut *iopub_socket.lock().await)
+ .await;
+
+ if let Err(err) = result {
+ eprintln!("Output {} error: {}", name, err);
+ }
+ }
+ }
+
+ async fn handle_heartbeat(
+ connection: &mut Connection<zeromq::RepSocket>,
+ ) -> Result<(), AnyError> {
+ loop {
+ connection.socket.recv().await?;
+ connection
+ .socket
+ .send(zeromq::ZmqMessage::from(b"ping".to_vec()))
+ .await?;
+ }
+ }
+
+ async fn handle_control(
+ mut connection: Connection<zeromq::RouterSocket>,
+ cancel_handle: Rc<CancelHandle>,
+ ) -> Result<(), AnyError> {
+ loop {
+ let msg = JupyterMessage::read(&mut connection).await?;
+ match msg.message_type() {
+ "kernel_info_request" => {
+ msg
+ .new_reply()
+ .with_content(kernel_info())
+ .send(&mut connection)
+ .await?;
+ }
+ "shutdown_request" => {
+ cancel_handle.cancel();
+ }
+ "interrupt_request" => {
+ eprintln!("Interrupt request currently not supported");
+ }
+ _ => {
+ eprintln!(
+ "Unrecognized control message type: {}",
+ msg.message_type()
+ );
+ }
+ }
+ }
+ }
+
+ async fn handle_shell(
+ &mut self,
+ mut connection: Connection<zeromq::RouterSocket>,
+ ) -> Result<(), AnyError> {
+ loop {
+ let msg = JupyterMessage::read(&mut connection).await?;
+ self.handle_shell_message(msg, &mut connection).await?;
+ }
+ }
+
+ async fn handle_shell_message(
+ &mut self,
+ msg: JupyterMessage,
+ connection: &mut Connection<zeromq::RouterSocket>,
+ ) -> Result<(), AnyError> {
+ msg
+ .new_message("status")
+ .with_content(json!({"execution_state": "busy"}))
+ .send(&mut *self.iopub_socket.lock().await)
+ .await?;
+
+ match msg.message_type() {
+ "kernel_info_request" => {
+ msg
+ .new_reply()
+ .with_content(kernel_info())
+ .send(connection)
+ .await?;
+ }
+ "is_complete_request" => {
+ msg
+ .new_reply()
+ .with_content(json!({"status": "complete"}))
+ .send(connection)
+ .await?;
+ }
+ "execute_request" => {
+ self
+ .handle_execution_request(msg.clone(), connection)
+ .await?;
+ }
+ "comm_open" => {
+ msg
+ .comm_close_message()
+ .send(&mut *self.iopub_socket.lock().await)
+ .await?;
+ }
+ "complete_request" => {
+ let user_code = msg.code();
+ let cursor_pos = msg.cursor_pos();
+
+ let lsp_completions = self
+ .repl_session
+ .language_server
+ .completions(user_code, cursor_pos)
+ .await;
+
+ if !lsp_completions.is_empty() {
+ let matches: Vec<String> = lsp_completions
+ .iter()
+ .map(|item| item.new_text.clone())
+ .collect();
+
+ let cursor_start = lsp_completions
+ .first()
+ .map(|item| item.range.start)
+ .unwrap_or(cursor_pos);
+
+ let cursor_end = lsp_completions
+ .last()
+ .map(|item| item.range.end)
+ .unwrap_or(cursor_pos);
+
+ msg
+ .new_reply()
+ .with_content(json!({
+ "status": "ok",
+ "matches": matches,
+ "cursor_start": cursor_start,
+ "cursor_end": cursor_end,
+ "metadata": {},
+ }))
+ .send(connection)
+ .await?;
+ } else {
+ let expr = get_expr_from_line_at_pos(user_code, cursor_pos);
+ // check if the expression is in the form `obj.prop`
+ let (completions, cursor_start) = if let Some(index) = expr.rfind('.')
+ {
+ let sub_expr = &expr[..index];
+ let prop_name = &expr[index + 1..];
+ let candidates =
+ get_expression_property_names(&mut self.repl_session, sub_expr)
+ .await
+ .into_iter()
+ .filter(|n| {
+ !n.starts_with("Symbol(")
+ && n.starts_with(prop_name)
+ && n != &*repl::REPL_INTERNALS_NAME
+ })
+ .collect();
+
+ (candidates, cursor_pos - prop_name.len())
+ } else {
+ // combine results of declarations and globalThis properties
+ let mut candidates = get_expression_property_names(
+ &mut self.repl_session,
+ "globalThis",
+ )
+ .await
+ .into_iter()
+ .chain(get_global_lexical_scope_names(&mut self.repl_session).await)
+ .filter(|n| n.starts_with(expr) && n != &*repl::REPL_INTERNALS_NAME)
+ .collect::<Vec<_>>();
+
+ // sort and remove duplicates
+ candidates.sort();
+ candidates.dedup(); // make sure to sort first
+
+ (candidates, cursor_pos - expr.len())
+ };
+ msg
+ .new_reply()
+ .with_content(json!({
+ "status": "ok",
+ "matches": completions,
+ "cursor_start": cursor_start,
+ "cursor_end": cursor_pos,
+ "metadata": {},
+ }))
+ .send(connection)
+ .await?;
+ }
+ }
+ "comm_msg" | "comm_info_request" | "history_request" => {
+ // We don't handle these messages
+ }
+ _ => {
+ eprintln!("Unrecognized shell message type: {}", msg.message_type());
+ }
+ }
+
+ msg
+ .new_message("status")
+ .with_content(json!({"execution_state": "idle"}))
+ .send(&mut *self.iopub_socket.lock().await)
+ .await?;
+ Ok(())
+ }
+
+ async fn handle_execution_request(
+ &mut self,
+ msg: JupyterMessage,
+ connection: &mut Connection<zeromq::RouterSocket>,
+ ) -> Result<(), AnyError> {
+ self.execution_count += 1;
+ *self.last_execution_request.borrow_mut() = Some(msg.clone());
+
+ msg
+ .new_message("execute_input")
+ .with_content(json!({
+ "execution_count": self.execution_count,
+ "code": msg.code()
+ }))
+ .send(&mut *self.iopub_socket.lock().await)
+ .await?;
+
+ let result = self
+ .repl_session
+ .evaluate_line_with_object_wrapping(msg.code())
+ .await;
+
+ let evaluate_response = match result {
+ Ok(eval_response) => eval_response,
+ Err(err) => {
+ msg
+ .new_message("error")
+ .with_content(json!({
+ "ename": err.to_string(),
+ "evalue": "",
+ "traceback": [],
+ }))
+ .send(&mut *self.iopub_socket.lock().await)
+ .await?;
+ msg
+ .new_reply()
+ .with_content(json!({
+ "status": "error",
+ "execution_count": self.execution_count,
+ }))
+ .send(connection)
+ .await?;
+ return Ok(());
+ }
+ };
+
+ let repl::cdp::EvaluateResponse {
+ result,
+ exception_details,
+ } = evaluate_response.value;
+
+ if exception_details.is_none() {
+ let output =
+ get_jupyter_display_or_eval_value(&mut self.repl_session, &result)
+ .await?;
+ msg
+ .new_message("execute_result")
+ .with_content(json!({
+ "execution_count": self.execution_count,
+ "data": output,
+ "metadata": {},
+ }))
+ .send(&mut *self.iopub_socket.lock().await)
+ .await?;
+ msg
+ .new_reply()
+ .with_content(json!({
+ "status": "ok",
+ "execution_count": self.execution_count,
+ }))
+ .send(connection)
+ .await?;
+ // Let's sleep here for a few ms, so we give a chance to the task that is
+ // handling stdout and stderr streams to receive and flush the content.
+ // Otherwise, executing multiple cells one-by-one might lead to output
+ // from various cells be grouped together in another cell result.
+ tokio::time::sleep(std::time::Duration::from_millis(5)).await;
+ } else {
+ let exception_details = exception_details.unwrap();
+ let name = if let Some(exception) = exception_details.exception {
+ if let Some(description) = exception.description {
+ description
+ } else if let Some(value) = exception.value {
+ value.to_string()
+ } else {
+ "undefined".to_string()
+ }
+ } else {
+ "Unknown exception".to_string()
+ };
+
+ // TODO(bartlomieju): fill all the fields
+ msg
+ .new_message("error")
+ .with_content(json!({
+ "ename": name,
+ "evalue": "",
+ "traceback": [],
+ }))
+ .send(&mut *self.iopub_socket.lock().await)
+ .await?;
+ msg
+ .new_reply()
+ .with_content(json!({
+ "status": "error",
+ "execution_count": self.execution_count,
+ }))
+ .send(connection)
+ .await?;
+ }
+
+ Ok(())
+ }
+}
+
+async fn bind_socket<S: zeromq::Socket>(
+ config: &ConnectionSpec,
+ port: u32,
+) -> Result<Connection<S>, AnyError> {
+ let endpoint = format!("{}://{}:{}", config.transport, config.ip, port);
+ let mut socket = S::new();
+ socket.bind(&endpoint).await?;
+ Ok(Connection::new(socket, &config.key))
+}
+
+fn kernel_info() -> serde_json::Value {
+ json!({
+ "status": "ok",
+ "protocol_version": "5.3",
+ "implementation_version": crate::version::deno(),
+ "implementation": "Deno kernel",
+ "language_info": {
+ "name": "typescript",
+ "version": crate::version::TYPESCRIPT,
+ "mimetype": "text/x.typescript",
+ "file_extension": ".ts",
+ "pygments_lexer": "typescript",
+ "nb_converter": "script"
+ },
+ "help_links": [{
+ "text": "Visit Deno manual",
+ "url": "https://deno.land/manual"
+ }],
+ "banner": "Welcome to Deno kernel",
+ })
+}
+
+async fn get_jupyter_display(
+ session: &mut repl::ReplSession,
+ evaluate_result: &cdp::RemoteObject,
+) -> Result<Option<HashMap<String, serde_json::Value>>, AnyError> {
+ let mut data = HashMap::default();
+ let response = session
+ .call_function_on_args(
+ r#"function (object) {{
+ return object[Symbol.for("Jupyter.display")]();
+ }}"#
+ .to_string(),
+ &[evaluate_result.clone()],
+ )
+ .await?;
+
+ if response.exception_details.is_some() {
+ return Ok(None);
+ }
+
+ let object_id = response.result.object_id.unwrap();
+
+ let get_properties_response_result = session
+ .post_message_with_event_loop(
+ "Runtime.getProperties",
+ Some(cdp::GetPropertiesArgs {
+ object_id,
+ own_properties: Some(true),
+ accessor_properties_only: None,
+ generate_preview: None,
+ non_indexed_properties_only: Some(true),
+ }),
+ )
+ .await;
+
+ let Ok(get_properties_response) = get_properties_response_result else {
+ return Ok(None);
+ };
+
+ let get_properties_response: cdp::GetPropertiesResponse =
+ serde_json::from_value(get_properties_response).unwrap();
+
+ for prop in get_properties_response.result.into_iter() {
+ if let Some(value) = &prop.value {
+ data.insert(
+ prop.name.to_string(),
+ value
+ .value
+ .clone()
+ .unwrap_or_else(|| serde_json::Value::Null),
+ );
+ }
+ }
+
+ if !data.is_empty() {
+ return Ok(Some(data));
+ }
+
+ Ok(None)
+}
+
+async fn get_jupyter_display_or_eval_value(
+ session: &mut repl::ReplSession,
+ evaluate_result: &cdp::RemoteObject,
+) -> Result<HashMap<String, serde_json::Value>, AnyError> {
+ // Printing "undefined" generates a lot of noise, so let's skip
+ // these.
+ if evaluate_result.kind == "undefined" {
+ return Ok(HashMap::default());
+ }
+
+ if let Some(data) = get_jupyter_display(session, evaluate_result).await? {
+ return Ok(data);
+ }
+
+ let response = session
+ .call_function_on_args(
+ format!(
+ r#"function (object) {{
+ try {{
+ return {0}.inspectArgs(["%o", object], {{ colors: !{0}.noColor }});
+ }} catch (err) {{
+ return {0}.inspectArgs(["%o", err]);
+ }}
+ }}"#,
+ *repl::REPL_INTERNALS_NAME
+ ),
+ &[evaluate_result.clone()],
+ )
+ .await?;
+ let mut data = HashMap::default();
+ if let Some(value) = response.result.value {
+ data.insert("text/plain".to_string(), value);
+ }
+
+ Ok(data)
+}
+
+// TODO(bartlomieju): dedup with repl::editor
+fn get_expr_from_line_at_pos(line: &str, cursor_pos: usize) -> &str {
+ let start = line[..cursor_pos].rfind(is_word_boundary).unwrap_or(0);
+ let end = line[cursor_pos..]
+ .rfind(is_word_boundary)
+ .map(|i| cursor_pos + i)
+ .unwrap_or(cursor_pos);
+
+ let word = &line[start..end];
+ let word = word.strip_prefix(is_word_boundary).unwrap_or(word);
+ let word = word.strip_suffix(is_word_boundary).unwrap_or(word);
+
+ word
+}
+
+// TODO(bartlomieju): dedup with repl::editor
+fn is_word_boundary(c: char) -> bool {
+ if matches!(c, '.' | '_' | '$') {
+ false
+ } else {
+ char::is_ascii_whitespace(&c) || char::is_ascii_punctuation(&c)
+ }
+}
+
+// TODO(bartlomieju): dedup with repl::editor
+async fn get_global_lexical_scope_names(
+ session: &mut repl::ReplSession,
+) -> Vec<String> {
+ let evaluate_response = session
+ .post_message_with_event_loop(
+ "Runtime.globalLexicalScopeNames",
+ Some(cdp::GlobalLexicalScopeNamesArgs {
+ execution_context_id: Some(session.context_id),
+ }),
+ )
+ .await
+ .unwrap();
+ let evaluate_response: cdp::GlobalLexicalScopeNamesResponse =
+ serde_json::from_value(evaluate_response).unwrap();
+ evaluate_response.names
+}
+
+// TODO(bartlomieju): dedup with repl::editor
+async fn get_expression_property_names(
+ session: &mut repl::ReplSession,
+ expr: &str,
+) -> Vec<String> {
+ // try to get the properties from the expression
+ if let Some(properties) = get_object_expr_properties(session, expr).await {
+ return properties;
+ }
+
+ // otherwise fall back to the prototype
+ let expr_type = get_expression_type(session, expr).await;
+ let object_expr = match expr_type.as_deref() {
+ // possibilities: https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#type-RemoteObject
+ Some("object") => "Object.prototype",
+ Some("function") => "Function.prototype",
+ Some("string") => "String.prototype",
+ Some("boolean") => "Boolean.prototype",
+ Some("bigint") => "BigInt.prototype",
+ Some("number") => "Number.prototype",
+ _ => return Vec::new(), // undefined, symbol, and unhandled
+ };
+
+ get_object_expr_properties(session, object_expr)
+ .await
+ .unwrap_or_default()
+}
+
+// TODO(bartlomieju): dedup with repl::editor
+async fn get_expression_type(
+ session: &mut repl::ReplSession,
+ expr: &str,
+) -> Option<String> {
+ evaluate_expression(session, expr)
+ .await
+ .map(|res| res.result.kind)
+}
+
+// TODO(bartlomieju): dedup with repl::editor
+async fn get_object_expr_properties(
+ session: &mut repl::ReplSession,
+ object_expr: &str,
+) -> Option<Vec<String>> {
+ let evaluate_result = evaluate_expression(session, object_expr).await?;
+ let object_id = evaluate_result.result.object_id?;
+
+ let get_properties_response = session
+ .post_message_with_event_loop(
+ "Runtime.getProperties",
+ Some(cdp::GetPropertiesArgs {
+ object_id,
+ own_properties: None,
+ accessor_properties_only: None,
+ generate_preview: None,
+ non_indexed_properties_only: Some(true),
+ }),
+ )
+ .await
+ .ok()?;
+ let get_properties_response: cdp::GetPropertiesResponse =
+ serde_json::from_value(get_properties_response).ok()?;
+ Some(
+ get_properties_response
+ .result
+ .into_iter()
+ .map(|prop| prop.name)
+ .collect(),
+ )
+}
+
+// TODO(bartlomieju): dedup with repl::editor
+async fn evaluate_expression(
+ session: &mut repl::ReplSession,
+ expr: &str,
+) -> Option<cdp::EvaluateResponse> {
+ let evaluate_response = session
+ .post_message_with_event_loop(
+ "Runtime.evaluate",
+ Some(cdp::EvaluateArgs {
+ expression: expr.to_string(),
+ object_group: None,
+ include_command_line_api: None,
+ silent: None,
+ context_id: Some(session.context_id),
+ return_by_value: None,
+ generate_preview: None,
+ user_gesture: None,
+ await_promise: None,
+ throw_on_side_effect: Some(true),
+ timeout: Some(200),
+ disable_breaks: None,
+ repl_mode: None,
+ allow_unsafe_eval_blocked_by_csp: None,
+ unique_context_id: None,
+ }),
+ )
+ .await
+ .ok()?;
+ let evaluate_response: cdp::EvaluateResponse =
+ serde_json::from_value(evaluate_response).ok()?;
+
+ if evaluate_response.exception_details.is_some() {
+ None
+ } else {
+ Some(evaluate_response)
+ }
+}
diff --git a/cli/tools/mod.rs b/cli/tools/mod.rs
index c4a8306ab..13a37addd 100644
--- a/cli/tools/mod.rs
+++ b/cli/tools/mod.rs
@@ -10,6 +10,7 @@ pub mod fmt;
pub mod info;
pub mod init;
pub mod installer;
+pub mod jupyter;
pub mod lint;
pub mod repl;
pub mod run;
diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs
index fb0891fa6..a1e741dfd 100644
--- a/cli/tools/repl/mod.rs
+++ b/cli/tools/repl/mod.rs
@@ -13,7 +13,7 @@ use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsContainer;
use rustyline::error::ReadlineError;
-mod cdp;
+pub(crate) mod cdp;
mod channel;
mod editor;
mod session;
@@ -24,8 +24,9 @@ use channel::RustylineSyncMessageHandler;
use channel::RustylineSyncResponse;
use editor::EditorHelper;
use editor::ReplEditor;
-use session::EvaluationOutput;
-use session::ReplSession;
+pub use session::EvaluationOutput;
+pub use session::ReplSession;
+pub use session::REPL_INTERNALS_NAME;
#[allow(clippy::await_holding_refcell_ref)]
async fn read_line_and_poll(
diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs
index d89cc95c3..a1b602b4b 100644
--- a/cli/tools/repl/session.rs
+++ b/cli/tools/repl/session.rs
@@ -116,9 +116,10 @@ pub fn result_to_evaluation_output(
}
}
-struct TsEvaluateResponse {
- ts_code: String,
- value: cdp::EvaluateResponse,
+#[derive(Debug)]
+pub struct TsEvaluateResponse {
+ pub ts_code: String,
+ pub value: cdp::EvaluateResponse,
}
pub struct ReplSession {
@@ -305,7 +306,7 @@ impl ReplSession {
result_to_evaluation_output(result)
}
- async fn evaluate_line_with_object_wrapping(
+ pub async fn evaluate_line_with_object_wrapping(
&mut self,
line: &str,
) -> Result<TsEvaluateResponse, AnyError> {
@@ -395,29 +396,24 @@ impl ReplSession {
Ok(())
}
- pub async fn get_eval_value(
+ pub async fn call_function_on_args(
&mut self,
- evaluate_result: &cdp::RemoteObject,
- ) -> Result<String, AnyError> {
- // TODO(caspervonb) we should investigate using previews here but to keep things
- // consistent with the previous implementation we just get the preview result from
- // Deno.inspectArgs.
+ function_declaration: String,
+ args: &[cdp::RemoteObject],
+ ) -> Result<cdp::CallFunctionOnResponse, AnyError> {
+ let arguments: Option<Vec<cdp::CallArgument>> = if args.is_empty() {
+ None
+ } else {
+ Some(args.iter().map(|a| a.into()).collect())
+ };
+
let inspect_response = self
.post_message_with_event_loop(
"Runtime.callFunctionOn",
Some(cdp::CallFunctionOnArgs {
- function_declaration: format!(
- r#"function (object) {{
- try {{
- return {0}.inspectArgs(["%o", object], {{ colors: !{0}.noColor }});
- }} catch (err) {{
- return {0}.inspectArgs(["%o", err]);
- }}
- }}"#,
- *REPL_INTERNALS_NAME
- ),
+ function_declaration,
object_id: None,
- arguments: Some(vec![evaluate_result.into()]),
+ arguments,
silent: None,
return_by_value: None,
generate_preview: None,
@@ -432,6 +428,31 @@ impl ReplSession {
let response: cdp::CallFunctionOnResponse =
serde_json::from_value(inspect_response)?;
+ Ok(response)
+ }
+
+ pub async fn get_eval_value(
+ &mut self,
+ evaluate_result: &cdp::RemoteObject,
+ ) -> Result<String, AnyError> {
+ // TODO(caspervonb) we should investigate using previews here but to keep things
+ // consistent with the previous implementation we just get the preview result from
+ // Deno.inspectArgs.
+ let response = self
+ .call_function_on_args(
+ format!(
+ r#"function (object) {{
+ try {{
+ return {0}.inspectArgs(["%o", object], {{ colors: !{0}.noColor }});
+ }} catch (err) {{
+ return {0}.inspectArgs(["%o", err]);
+ }}
+ }}"#,
+ *REPL_INTERNALS_NAME
+ ),
+ &[evaluate_result.clone()],
+ )
+ .await?;
let value = response.result.value.unwrap();
let s = value.as_str().unwrap();
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml
index 44b56978e..35d8a2de5 100644
--- a/ext/node/Cargo.toml
+++ b/ext/node/Cargo.toml
@@ -19,7 +19,7 @@ aes.workspace = true
brotli.workspace = true
bytes.workspace = true
cbc.workspace = true
-data-encoding = "2.3.3"
+data-encoding.workspace = true
deno_core.workspace = true
deno_fetch.workspace = true
deno_fs.workspace = true