From b33e8d5d427934544fa6e0de44df8793aae63c9d Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Sun, 18 Oct 2020 22:19:47 +0800 Subject: [PATCH 01/16] refactor(cli/repl): extract is_closing to a function (#8015) This extracts is closing into a function so that it can easily be used as the condition for the loop. --- cli/repl.rs | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 3540b422b2997..2e41b056546d6 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -217,6 +217,31 @@ async fn inject_prelude( Ok(()) } +pub async fn is_closing( + worker: &mut MainWorker, + session: &mut InspectorSession, + context_id: u64, +) -> Result { + let closed = post_message_and_poll( + worker, + session, + "Runtime.evaluate", + Some(json!({ + "expression": "(globalThis.closed)", + "contextId": context_id, + })), + ) + .await? + .get("result") + .unwrap() + .get("value") + .unwrap() + .as_bool() + .unwrap(); + + Ok(closed) +} + pub async fn run( program_state: &ProgramState, mut worker: MainWorker, @@ -267,7 +292,7 @@ pub async fn run( inject_prelude(&mut worker, &mut session, context_id).await?; - loop { + while !is_closing(&mut worker, &mut session, context_id).await? { let line = read_line_and_poll(&mut *worker, editor.clone()).await; match line { Ok(line) => { @@ -315,27 +340,6 @@ pub async fn run( evaluate_response }; - let is_closing = post_message_and_poll( - &mut *worker, - &mut session, - "Runtime.evaluate", - Some(json!({ - "expression": "(globalThis.closed)", - "contextId": context_id, - })), - ) - .await? - .get("result") - .unwrap() - .get("value") - .unwrap() - .as_bool() - .unwrap(); - - if is_closing { - break; - } - let evaluate_result = evaluate_response.get("result").unwrap(); let evaluate_exception_details = evaluate_response.get("exceptionDetails"); From 8f9da368f78b0dcd55e701b30daea8b45102a491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sun, 18 Oct 2020 16:26:05 +0200 Subject: [PATCH 02/16] refactor(lint): show hint for lint errors (#8016) This commit adds formatting of optional "hint" that can be present in lint diagnostic. --- cli/lint.rs | 25 +++++++++++++++++++------ cli/tests/lint/expected_quiet.out | 2 ++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/cli/lint.rs b/cli/lint.rs index 882ec44145b30..a2a1252c025b8 100644 --- a/cli/lint.rs +++ b/cli/lint.rs @@ -222,6 +222,7 @@ impl LintReporter for PrettyLintReporter { &pretty_message, &source_lines, d.range.clone(), + d.hint.as_ref(), &fmt_errors::format_location(&JsStackFrame::from_location( Some(d.filename.clone()), Some(d.range.start.line as i64), @@ -256,6 +257,7 @@ pub fn format_diagnostic( message_line: &str, source_lines: &[&str], range: deno_lint::diagnostic::Range, + maybe_hint: Option<&String>, formatted_location: &str, ) -> String { let mut lines = vec![]; @@ -284,12 +286,23 @@ pub fn format_diagnostic( } } - format!( - "{}\n{}\n at {}", - message_line, - lines.join("\n"), - formatted_location - ) + if let Some(hint) = maybe_hint { + format!( + "{}\n{}\n at {}\n\n {} {}", + message_line, + lines.join("\n"), + formatted_location, + colors::gray("hint:"), + hint, + ) + } else { + format!( + "{}\n{}\n at {}", + message_line, + lines.join("\n"), + formatted_location + ) + } } #[derive(Serialize)] diff --git a/cli/tests/lint/expected_quiet.out b/cli/tests/lint/expected_quiet.out index 45a3083445218..21fd92be3ecd1 100644 --- a/cli/tests/lint/expected_quiet.out +++ b/cli/tests/lint/expected_quiet.out @@ -3,6 +3,8 @@ ^^^^^^^^^^^^^^^^^^^ at [WILDCARD]file1.js:1:0 + hint: [WILDCARD] + (no-empty) Empty block statement while (false) {} ^^ From 3e51610bbb5d5d63863b9989690ca1e8127fdf88 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sun, 18 Oct 2020 12:12:17 -0400 Subject: [PATCH 03/16] Remove github actions cache (#8020) Running into issues with cache when trying to upgrade V8. Based on the analysis in https://github.com/denoland/deno/pull/7903#issuecomment-706252380 we know the cache is not providing much benefit. --- .github/workflows/ci.yml | 54 ---------------------------------------- 1 file changed, 54 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55f8a7c37143d..a9c528e0c0558 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,60 +94,6 @@ jobs: rustc --version cargo --version - - name: Configure cargo data directory - # After this point, all cargo registry and crate data is stored in - # $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files - # that are needed during the build process. Additionally, this works - # around a bug in the 'cache' action that causes directories outside of - # the workspace dir to be saved/restored incorrectly. - run: echo "::set-env name=CARGO_HOME::$(pwd)/.cargo_home" - - - name: Cache - uses: actions/cache@v2 - with: - # Note: crates from the denoland/deno git repo always get rebuilt, - # and their outputs ('deno', 'libdeno.rlib' etc.) are quite big, - # so we cache only those subdirectories of target/{debug|release} that - # contain the build output for crates that come from the registry. - path: |- - .cargo_home - target/*/.* - target/*/build - target/*/deps - target/*/gn_out - key: deno-${{ matrix.os }}-${{ matrix.kind }}-${{ hashFiles('Cargo.lock') }} - restore-keys: | - deno-${{ matrix.os }}-${{ matrix.kind }}- - - # It seems that the 'target' directory does not always get restored - # from cache correctly on MacOS. In the build log we see the following: - # - # Fresh serde_derive v1.0.115 - # - # But a little while after that Cargo aborts because 'serde_derive' is - # now nowhere to be found. We're not the only ones experiencing this, - # see https://github.com/actions-rs/cargo/issues/111. - # - # error[E0463]: can't find crate for `serde_derive` - # ##[error] --> /Users/runner/.cargo/registry/src/github.com- - # | 1ecc6299db9ec823/serde-1.0.115/src/lib.rs:285:1 - # | - # 285 | extern crate serde_derive; - # | ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate - - name: Work around MacOS + Cargo + Github Actions cache bug - if: runner.os == 'macOS' - run: | - cargo clean --locked --release \ - -p ast_node \ - -p is-macro \ - -p serde_derive \ - -p swc_ecma_codegen \ - -p swc_ecma_codegen_macros \ - -p swc_ecma_parser \ - -p swc_ecma_parser_macros \ - -p swc_visit \ - -p swc_visit_macros - - name: lint.py if: matrix.kind == 'lint' run: python ./tools/lint.py From 065db9df19fa9f1f598fc3a5c7fd978b484428de Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Mon, 19 Oct 2020 00:16:26 +0800 Subject: [PATCH 04/16] test(std/io): use a real tempdir (#8019) This replaces a case of a temp file in the working tree with a tempfile in a real temporary directory avoiding pollution of the working directory. --- std/io/ioutil_test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/std/io/ioutil_test.ts b/std/io/ioutil_test.ts index 69f76f6913849..9b17ce66dae3d 100644 --- a/std/io/ioutil_test.ts +++ b/std/io/ioutil_test.ts @@ -9,8 +9,6 @@ import { } from "./ioutil.ts"; import { StringReader } from "./readers.ts"; import { BufReader } from "./bufio.ts"; -import { tempFile } from "./util.ts"; -import * as path from "../path/mod.ts"; class BinaryReader implements Deno.Reader { index = 0; @@ -87,7 +85,10 @@ Deno.test("testCopyN2", async function (): Promise { }); Deno.test("copyNWriteAllData", async function (): Promise { - const { filepath, file } = await tempFile(path.resolve("io")); + const tmpDir = await Deno.makeTempDir(); + const filepath = `${tmpDir}/data`; + const file = await Deno.open(filepath, { create: true, write: true }); + const size = 16 * 1024 + 1; const data = "a".repeat(32 * 1024); const r = new StringReader(data); From 59888ff0b27f75feb3ea88cdea943bf9fbd345fd Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sun, 18 Oct 2020 13:07:11 -0400 Subject: [PATCH 05/16] upgrade rusty_v8 (#8017) --- Cargo.lock | 4 ++-- core/Cargo.toml | 2 +- core/bindings.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1281c34b6c54a..8e5fb4db35e5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1919,9 +1919,9 @@ dependencies = [ [[package]] name = "rusty_v8" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c11463988ec37b3f8cb84e4c2fe8f63058c46e07348b6d1d27b114a2b981304" +checksum = "692dddfb8ae9915b19774a05cadc71363c98bad4fafc13636579da5a7a110016" dependencies = [ "bitflags", "cargo_gn", diff --git a/core/Cargo.toml b/core/Cargo.toml index c114a64d0b0c4..a7e820548b07d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,7 +19,7 @@ indexmap = "1.6.0" lazy_static = "1.4.0" libc = "0.2.77" log = "0.4.11" -rusty_v8 = "0.11.0" +rusty_v8 = "0.12.0" serde_json = { version = "1.0", features = ["preserve_order"] } serde = { version = "1.0", features = ["derive"] } smallvec = "1.4.2" diff --git a/core/bindings.rs b/core/bindings.rs index 782c98bd12e53..071d6e015f6e4 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -297,7 +297,7 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) { match message.get_event() { v8::PromiseRejectEvent::PromiseRejectWithNoHandler => { - let error = message.get_value(); + let error = message.get_value().unwrap(); let error_global = v8::Global::new(scope, error); state .pending_promise_exceptions From fb2cae9687569fc891c13ab4a2862b9e66902a75 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Mon, 19 Oct 2020 06:35:09 -0400 Subject: [PATCH 06/16] deno_core 0.64.0 (#8025) --- Cargo.lock | 2 +- cli/Cargo.toml | 4 ++-- core/Cargo.toml | 2 +- op_crates/fetch/Cargo.toml | 2 +- op_crates/web/Cargo.toml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e5fb4db35e5f..39ddd5cf3d644 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -450,7 +450,7 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.63.0" +version = "0.64.0" dependencies = [ "anyhow", "futures", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index efe815dc553f8..28ed44a8ddd8f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -20,7 +20,7 @@ harness = false path = "./bench/main.rs" [build-dependencies] -deno_core = { path = "../core", version = "0.63.0" } +deno_core = { path = "../core", version = "0.64.0" } deno_web = { path = "../op_crates/web", version = "0.15.0" } deno_fetch = { path = "../op_crates/fetch", version = "0.7.0" } @@ -29,7 +29,7 @@ winres = "0.1.11" winapi = "0.3.9" [dependencies] -deno_core = { path = "../core", version = "0.63.0" } +deno_core = { path = "../core", version = "0.64.0" } deno_doc = "0.1.12" deno_lint = "0.2.4" deno_web = { path = "../op_crates/web", version = "0.15.0" } diff --git a/core/Cargo.toml b/core/Cargo.toml index a7e820548b07d..997306b245dde 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,7 +1,7 @@ # Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. [package] name = "deno_core" -version = "0.63.0" +version = "0.64.0" edition = "2018" description = "A secure JavaScript/TypeScript runtime built with V8, Rust, and Tokio" authors = ["the Deno authors"] diff --git a/op_crates/fetch/Cargo.toml b/op_crates/fetch/Cargo.toml index 79197e52832a8..5e6c725b0f985 100644 --- a/op_crates/fetch/Cargo.toml +++ b/op_crates/fetch/Cargo.toml @@ -14,6 +14,6 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.63.0", path = "../../core" } +deno_core = { version = "0.64.0", path = "../../core" } reqwest = { version = "0.10.8", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } serde = { version = "1.0.116", features = ["derive"] } diff --git a/op_crates/web/Cargo.toml b/op_crates/web/Cargo.toml index 7d0c3de6c76f2..51686efbb6c47 100644 --- a/op_crates/web/Cargo.toml +++ b/op_crates/web/Cargo.toml @@ -14,7 +14,7 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.63.0", path = "../../core" } +deno_core = { version = "0.64.0", path = "../../core" } idna = "0.2.0" serde = { version = "1.0.116", features = ["derive"] } From f91c1155f0cccef9a49efdcc489be3f258000957 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Mon, 19 Oct 2020 19:33:15 +0800 Subject: [PATCH 07/16] docs(tools): add repl section (#8011) This adds a section on the repl with the keybindings that we support out of the box. --- docs/tools.md | 1 + docs/tools/repl.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 docs/tools/repl.md diff --git a/docs/tools.md b/docs/tools.md index e64c9d840b779..5945ee0159665 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -7,5 +7,6 @@ and TypeScript: - [dependency inspector (`deno info`)](./tools/dependency_inspector.md) - [documentation generator (`deno doc`)](./tools/documentation_generator.md) - [formatter (`deno fmt`)](./tools/formatter.md) +- [repl (`deno repl`)](./tools/repl.md) - [test runner (`deno test`)](./testing.md) - [linter (`deno lint`)](./tools/linter.md) diff --git a/docs/tools/repl.md b/docs/tools/repl.md new file mode 100644 index 0000000000000..5b684b386d217 --- /dev/null +++ b/docs/tools/repl.md @@ -0,0 +1,45 @@ +# Read-eval-print-loop + +`deno repl` starts an read-eval-print-loop, which lets you interactively build +up program state in the global context. + +## Keyboard shortcuts + +| Keystroke | Action | +| --------------------- | ------------------------------------------------------------------------------------------------ | +| Ctrl-A, Home | Move cursor to the beginning of line | +| Ctrl-B, Left | Move cursor one character left | +| Ctrl-C | Interrupt and cancel the current edit | +| Ctrl-D | If if line _is_ empty, signal end of line | +| Ctrl-D, Del | If line is _not_ empty, delete character under cursor | +| Ctrl-E, End | Move cursor to end of line | +| Ctrl-F, Right | Move cursor one character right | +| Ctrl-H, Backspace | Delete character before cursor | +| Ctrl-I, Tab | Next completion | +| Ctrl-J, Ctrl-M, Enter | Finish the line entry | +| Ctrl-K | Delete from cursor to end of line | +| Ctrl-L | Clear screen | +| Ctrl-N, Down | Next match from history | +| Ctrl-P, Up | Previous match from history | +| Ctrl-R | Reverse Search history (Ctrl-S forward, Ctrl-G cancel) | +| Ctrl-T | Transpose previous character with current character | +| Ctrl-U | Delete from start of line to cursor | +| Ctrl-V | Insert any special character without performing its associated action (#65) | +| Ctrl-W | Delete word leading up to cursor (using white space as a word boundary) | +| Ctrl-X Ctrl-U | Undo | +| Ctrl-Y | Paste from Yank buffer | +| Ctrl-Y | Paste from Yank buffer (Meta-Y to paste next yank instead) | +| Ctrl-Z | Suspend (Unix only) | +| Ctrl-_ | Undo | +| Meta-0, 1, ..., - | Specify the digit to the argument. `–` starts a negative argument. | +| Meta-< | Move to first entry in history | +| Meta-> | Move to last entry in history | +| Meta-B, Alt-Left | Move cursor to previous word | +| Meta-Backspace | Kill from the start of the current word, or, if between words, to the start of the previous word | +| Meta-C | Capitalize the current word | +| Meta-D | Delete forwards one word | +| Meta-F, Alt-Right | Move cursor to next word | +| Meta-L | Lower-case the next word | +| Meta-T | Transpose words | +| Meta-U | Upper-case the next word | +| Meta-Y | See Ctrl-Y | From d3dea24560f7434bd1020f0d9dc4c787f79479da Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Mon, 19 Oct 2020 13:33:51 +0200 Subject: [PATCH 08/16] fix(std/tar): fix constant condition (#8010) --- std/archive/tar.ts | 9 +++++---- std/archive/tar_test.ts | 10 ++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/std/archive/tar.ts b/std/archive/tar.ts index a169d5b4ba77f..0685ad8374f3b 100644 --- a/std/archive/tar.ts +++ b/std/archive/tar.ts @@ -485,7 +485,10 @@ class TarEntry implements Reader { entryBytesLeft, ); - if (entryBytesLeft <= 0) return null; + if (entryBytesLeft <= 0) { + this.#consumed = true; + return null; + } const block = new Uint8Array(bufSize); const n = await readBlock(this.#reader, block); @@ -493,9 +496,7 @@ class TarEntry implements Reader { this.#read += n || 0; if (n === null || bytesLeft <= 0) { - // FIXME(bartlomieju): this condition makes no sense - // deno-lint-ignore no-constant-condition - if (null) this.#consumed = true; + if (n === null) this.#consumed = true; return null; } diff --git a/std/archive/tar_test.ts b/std/archive/tar_test.ts index ff1ada4d52a0b..cec98face993b 100644 --- a/std/archive/tar_test.ts +++ b/std/archive/tar_test.ts @@ -113,7 +113,9 @@ Deno.test("appendFileWithLongNameToTarArchive", async function (): Promise< const untar = new Untar(tar.getReader()); const result = await untar.extract(); assert(result !== null); + assert(!result.consumed); const untarText = new TextDecoder("utf-8").decode(await Deno.readAll(result)); + assert(result.consumed); // tests assertEquals(result.fileName, fileName); @@ -137,6 +139,7 @@ Deno.test("untarAsyncIterator", async function (): Promise { // read data from a tar archive const untar = new Untar(tar.getReader()); + let lastEntry; for await (const entry of untar) { const expected = entries.shift(); assert(expected); @@ -145,11 +148,14 @@ Deno.test("untarAsyncIterator", async function (): Promise { if (expected.filePath) { content = await Deno.readFile(expected.filePath); } - assertEquals(content, await Deno.readAll(entry)); assertEquals(expected.name, entry.fileName); - } + if (lastEntry) assert(lastEntry.consumed); + lastEntry = entry; + } + assert(lastEntry); + assert(lastEntry.consumed); assertEquals(entries.length, 0); }); From f5c23f8058ed7ce85846c6b1e623329a09fa1a76 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Mon, 19 Oct 2020 19:41:25 +0800 Subject: [PATCH 09/16] fix(cli/repl): write all results to stdout (#7893) This writes all evaluaton results to stdout regardless if the result is an error or not. This matches the behavior of other read-eval-print-loops like Node. --- cli/repl.rs | 17 +++++++--------- cli/tests/integration_tests.rs | 36 +++++++++++++++++----------------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 2e41b056546d6..be887e941059e 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -391,16 +391,13 @@ pub async fn run( let inspect_result = inspect_response.get("result").unwrap(); - match evaluate_exception_details { - Some(_) => eprintln!( - "Uncaught {}", - inspect_result.get("value").unwrap().as_str().unwrap() - ), - None => println!( - "{}", - inspect_result.get("value").unwrap().as_str().unwrap() - ), - } + let value = inspect_result.get("value").unwrap().as_str().unwrap(); + let output = match evaluate_exception_details { + Some(_) => format!("Uncaught {}", value), + None => value.to_string(), + }; + + println!("{}", output); editor.lock().unwrap().add_history_entry(line.as_str()); } diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index d375ba1a143aa..5bf1a5a852eaa 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1232,7 +1232,7 @@ fn repl_test_eof() { #[test] fn repl_test_strict() { - let (_, err) = util::run_and_collect_output( + let (out, err) = util::run_and_collect_output( true, "repl", Some(vec![ @@ -1243,13 +1243,12 @@ fn repl_test_strict() { None, false, ); - assert!(err.contains( + assert!(out.contains( "Uncaught TypeError: Cannot add property c, object is not extensible" )); + assert!(err.is_empty()); } -const REPL_MSG: &str = "exit using ctrl+d or close()\n"; - #[test] fn repl_test_close_command() { let (out, err) = util::run_and_collect_output( @@ -1312,8 +1311,8 @@ fn repl_test_eval_unterminated() { None, false, ); - assert!(out.ends_with(REPL_MSG)); - assert!(err.contains("Unexpected end of input")); + assert!(out.contains("Unexpected end of input")); + assert!(err.is_empty()); } #[test] @@ -1325,8 +1324,8 @@ fn repl_test_reference_error() { None, false, ); - assert!(out.ends_with(REPL_MSG)); - assert!(err.contains("not_a_variable is not defined")); + assert!(out.contains("not_a_variable is not defined")); + assert!(err.is_empty()); } #[test] @@ -1338,8 +1337,8 @@ fn repl_test_syntax_error() { None, false, ); - assert!(out.ends_with(REPL_MSG)); - assert!(err.contains("Unexpected identifier")); + assert!(out.contains("Unexpected identifier")); + assert!(err.is_empty()); } #[test] @@ -1351,8 +1350,8 @@ fn repl_test_type_error() { None, false, ); - assert!(out.ends_with(REPL_MSG)); - assert!(err.contains("console is not a function")); + assert!(out.contains("console is not a function")); + assert!(err.is_empty()); } #[test] @@ -1425,8 +1424,8 @@ fn repl_test_save_last_thrown() { Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]), false, ); - assert!(out.ends_with("1\n")); - assert_eq!(err, "Uncaught 1\n"); + assert!(out.ends_with("Uncaught 1\n1\n")); + assert!(err.is_empty()); } #[test] @@ -1453,10 +1452,11 @@ fn repl_test_assign_underscore_error() { Some(vec![("NO_COLOR".to_owned(), "1".to_owned())]), false, ); - assert!( - out.ends_with("Last thrown error is no longer saved to _error.\n1\n1\n") - ); - assert_eq!(err, "Uncaught 2\n"); + println!("{}", out); + assert!(out.ends_with( + "Last thrown error is no longer saved to _error.\n1\nUncaught 2\n1\n" + )); + assert!(err.is_empty()); } #[test] From 8eb44537ecb35aebf2aa95018ab8806ab899e0f7 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Mon, 19 Oct 2020 20:06:21 +0800 Subject: [PATCH 10/16] fix(cli/repl): keyboard interrupt should continue (#7960) This changes the behavior of keyboard interrupts (ctrl+c) to continue, clearing the current line instead of exiting. Exit can still be done with ctrl+d or by calling close(). --- cli/repl.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index be887e941059e..1bb5df0960759 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -402,7 +402,8 @@ pub async fn run( editor.lock().unwrap().add_history_entry(line.as_str()); } Err(ReadlineError::Interrupted) => { - break; + println!("exit using ctrl+d or close()"); + continue; } Err(ReadlineError::Eof) => { break; From 342b151b5d9d6c97926588dd118b59032f3b0e40 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Mon, 19 Oct 2020 21:35:48 +0900 Subject: [PATCH 11/16] docs(std/datetime): document toIMF, isLeap, difference, and constants (#7931) --- std/datetime/README.md | 101 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/std/datetime/README.md b/std/datetime/README.md index 253da498c48d6..97f2b7c2493bd 100644 --- a/std/datetime/README.md +++ b/std/datetime/README.md @@ -32,6 +32,8 @@ are supported: - `'foo'` - quoted literal. - `./-` - unquoted literal. +## Methods + ### parse Takes an input `string` and a `formatString` to parse to a `date`. @@ -85,3 +87,102 @@ import { weekOfYear } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; weekOfYear(new Date("2020-12-28T03:24:00")); // Returns 53 ``` + +### toIMF + +Formats the given date to IMF date time format. (Reference: +https://tools.ietf.org/html/rfc7231#section-7.1.1.1 ) + +```js +import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT" +``` + +### isLeap + +Returns true if the given date or year (in number) is a leap year. Returns false +otherwise. + +```js +import { isLeap } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +isLeap(new Date("1970-01-01")); // => returns false +isLeap(new Date("1972-01-01")); // => returns true +isLeap(new Date("2000-01-01")); // => returns true +isLeap(new Date("2100-01-01")); // => returns false +isLeap(1972); // => returns true +``` + +### difference + +Returns the difference of the 2 given dates in the given units. If the units are +omitted, it returns the difference in the all available units. + +Available units: "milliseconds", "seconds", "minutes", "hours", "days", "weeks", +"months", "quarters", "years" + +```js +import { difference } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +const date0 = new Date("2018-05-14"); +const date1 = new Date("2020-05-13"); + +difference(date0, date1, { units: ["days", "months", "years"] }); +// => returns { days: 730, months: 23, years: 1 } + +difference(date0, date1); +// => returns { +// milliseconds: 63072000000, +// seconds: 63072000, +// minutes: 1051200, +// hours: 17520, +// days: 730, +// weeks: 104, +// months: 23, +// quarters: 5, +// years: 1 +// } +``` + +## Constants + +### SECOND + +``` +import { SECOND } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +console.log(SECOND); // => 1000 +``` + +### MINUTE + +``` +import { MINUTE } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +console.log(MINUTE); // => 60000 (60 * 1000) +``` + +### HOUR + +``` +import { HOUR } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +console.log(HOUR); // => 3600000 (60 * 60 * 1000) +``` + +### DAY + +``` +import { DAY } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +console.log(DAY); // => 86400000 (24 * 60 * 60 * 1000) +``` + +### WEEK + +``` +import { WEEK } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; + +console.log(WEEK); // => 604800000 (7 * 24 * 60 * 60 * 1000) +``` From 19b918d112c786b1db17fe2d83be79f1114ba240 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 19 Oct 2020 13:36:53 +0100 Subject: [PATCH 12/16] feat(std/path): Add toFileUrl() (#7971) --- std/path/mod.ts | 1 + std/path/posix.ts | 13 ++++++++++ std/path/to_file_url_test.ts | 49 ++++++++++++++++++++++++++++++++++++ std/path/win32.ts | 31 +++++++++++++++++++---- 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 std/path/to_file_url_test.ts diff --git a/std/path/mod.ts b/std/path/mod.ts index 0b4156e69981e..58c2c456104fa 100644 --- a/std/path/mod.ts +++ b/std/path/mod.ts @@ -24,6 +24,7 @@ export const { relative, resolve, sep, + toFileUrl, toNamespacedPath, } = path; diff --git a/std/path/posix.ts b/std/path/posix.ts index 68ffb06c9ac84..d2845f3da5f0a 100644 --- a/std/path/posix.ts +++ b/std/path/posix.ts @@ -440,3 +440,16 @@ export function fromFileUrl(url: string | URL): string { url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), ); } + +/** Converts a path string to a file URL. + * + * toFileUrl("/home/foo"); // new URL("file:///home/foo") + */ +export function toFileUrl(path: string): URL { + if (!isAbsolute(path)) { + throw new TypeError("Must be an absolute path."); + } + const url = new URL("file:///"); + url.pathname = path.replace(/%/g, "%25").replace(/\\/g, "%5C"); + return url; +} diff --git a/std/path/to_file_url_test.ts b/std/path/to_file_url_test.ts new file mode 100644 index 0000000000000..6d0ca8d809f78 --- /dev/null +++ b/std/path/to_file_url_test.ts @@ -0,0 +1,49 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { posix, win32 } from "./mod.ts"; +import { assertEquals, assertThrows } from "../testing/asserts.ts"; + +Deno.test("[path] toFileUrl", function () { + assertEquals(posix.toFileUrl("/home/foo").href, "file:///home/foo"); + assertEquals(posix.toFileUrl("/home/ ").href, "file:///home/%20"); + assertEquals(posix.toFileUrl("/home/%20").href, "file:///home/%2520"); + assertEquals(posix.toFileUrl("/home\\foo").href, "file:///home%5Cfoo"); + assertThrows( + () => posix.toFileUrl("foo").href, + TypeError, + "Must be an absolute path.", + ); + assertThrows( + () => posix.toFileUrl("C:/"), + TypeError, + "Must be an absolute path.", + ); + assertEquals( + posix.toFileUrl("//localhost/home/foo").href, + "file:////localhost/home/foo", + ); + assertEquals(posix.toFileUrl("//localhost/").href, "file:////localhost/"); + assertEquals(posix.toFileUrl("//:/home/foo").href, "file:////:/home/foo"); +}); + +Deno.test("[path] toFileUrl (win32)", function () { + assertEquals(win32.toFileUrl("/home/foo").href, "file:///home/foo"); + assertEquals(win32.toFileUrl("/home/ ").href, "file:///home/%20"); + assertEquals(win32.toFileUrl("/home/%20").href, "file:///home/%2520"); + assertEquals(win32.toFileUrl("/home\\foo").href, "file:///home/foo"); + assertThrows( + () => win32.toFileUrl("foo").href, + TypeError, + "Must be an absolute path.", + ); + assertEquals(win32.toFileUrl("C:/").href, "file:///C:/"); + assertEquals( + win32.toFileUrl("//localhost/home/foo").href, + "file://localhost/home/foo", + ); + assertEquals(win32.toFileUrl("//localhost/").href, "file:////localhost/"); + assertThrows( + () => win32.toFileUrl("//:/home/foo").href, + TypeError, + "Invalid hostname.", + ); +}); diff --git a/std/path/win32.ts b/std/path/win32.ts index 246c18a973f81..1684a2c45b6c4 100644 --- a/std/path/win32.ts +++ b/std/path/win32.ts @@ -917,11 +917,8 @@ export function fromFileUrl(url: string | URL): string { throw new TypeError("Must be a file URL."); } let path = decodeURIComponent( - url.pathname - .replace(/^\/*([A-Za-z]:)(\/|$)/, "$1/") - .replace(/\//g, "\\") - .replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), - ); + url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), + ).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); if (url.hostname != "") { // Note: The `URL` implementation guarantees that the drive letter and // hostname are mutually exclusive. Otherwise it would not have been valid @@ -930,3 +927,27 @@ export function fromFileUrl(url: string | URL): string { } return path; } + +/** Converts a path string to a file URL. + * + * toFileUrl("\\home\\foo"); // new URL("file:///home/foo") + * toFileUrl("C:\\Users\\foo"); // new URL("file:///C:/Users/foo") + * toFileUrl("\\\\localhost\\home\\foo"); // new URL("file://localhost/home/foo") + */ +export function toFileUrl(path: string): URL { + if (!isAbsolute(path)) { + throw new TypeError("Must be an absolute path."); + } + const [, hostname, pathname] = path.match( + /^(?:[/\\]{2}([^/\\]+)(?=[/\\][^/\\]))?(.*)/, + )!; + const url = new URL("file:///"); + url.pathname = pathname.replace(/%/g, "%25"); + if (hostname != null) { + url.hostname = hostname; + if (!url.hostname) { + throw new TypeError("Invalid hostname."); + } + } + return url; +} From e58763737e82db481a05e34ad883b1cd286dd05d Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Mon, 19 Oct 2020 20:43:58 +0800 Subject: [PATCH 13/16] docs(getting_started): fix WebAssembly example (#8028) --- docs/getting_started/webassembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/webassembly.md b/docs/getting_started/webassembly.md index b7ca6ee407e5c..c1f734fb6d1d4 100644 --- a/docs/getting_started/webassembly.md +++ b/docs/getting_started/webassembly.md @@ -16,5 +16,5 @@ const wasmCode = new Uint8Array([ const wasmModule = new WebAssembly.Module(wasmCode); const wasmInstance = new WebAssembly.Instance(wasmModule); const main = wasmInstance.exports.main as CallableFunction -console.log(wasmInstance.exports.main().toString()); +console.log(main().toString()); ``` From e432db70e9b5628990517baa689b74721f6da8e1 Mon Sep 17 00:00:00 2001 From: vwkd <33468089+vwkd@users.noreply.github.com> Date: Mon, 19 Oct 2020 15:06:04 +0200 Subject: [PATCH 14/16] docs: readTextFile / readTextFileSync throw when reading directory (#7999) --- cli/dts/lib.deno.ns.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index 0b9876865e615..d33eb8a0b9c86 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -1226,7 +1226,7 @@ declare namespace Deno { export function rename(oldpath: string, newpath: string): Promise; /** Synchronously reads and returns the entire contents of a file as utf8 - * encoded string. Reading a directory returns an empty string. + * encoded string. Reading a directory throws an error. * * ```ts * const data = Deno.readTextFileSync("hello.txt"); @@ -1237,7 +1237,7 @@ declare namespace Deno { export function readTextFileSync(path: string | URL): string; /** Asynchronously reads and returns the entire contents of a file as utf8 - * encoded string. Reading a directory returns an empty string. + * encoded string. Reading a directory throws an error. * * ```ts * const data = await Deno.readTextFile("hello.txt"); From 35028db5e571b4232dd58293cea7e5a8ec2e3571 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Mon, 19 Oct 2020 22:54:50 +0800 Subject: [PATCH 15/16] fix(cli/repl): unterminated string literal should invalidate (#7896) This adds the grave character to the pair matching so that template string literals trigger multi-line edits. --- cli/repl.rs | 40 ++++++++++++++++++++++++++++++---- cli/tests/integration_tests.rs | 34 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 1bb5df0960759..3d22f8156ea59 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -12,7 +12,6 @@ use regex::Captures; use regex::Regex; use rustyline::error::ReadlineError; use rustyline::highlight::Highlighter; -use rustyline::validate::MatchingBracketValidator; use rustyline::validate::ValidationContext; use rustyline::validate::ValidationResult; use rustyline::validate::Validator; @@ -26,7 +25,6 @@ use std::sync::Mutex; #[derive(Completer, Helper, Hinter)] struct Helper { highlighter: LineHighlighter, - validator: MatchingBracketValidator, } impl Validator for Helper { @@ -34,7 +32,42 @@ impl Validator for Helper { &self, ctx: &mut ValidationContext, ) -> Result { - self.validator.validate(ctx) + let mut stack: Vec = Vec::new(); + for c in ctx.input().chars() { + match c { + '(' | '[' | '{' => stack.push(c), + ')' | ']' | '}' => match (stack.pop(), c) { + (Some('('), ')') | (Some('['), ']') | (Some('{'), '}') => {} + (Some(left), _) => { + return Ok(ValidationResult::Invalid(Some(format!( + "Mismatched pairs: {:?} is not properly closed", + left + )))) + } + (None, c) => { + return Ok(ValidationResult::Invalid(Some(format!( + "Mismatched pairs: {:?} is unpaired", + c + )))) + } + }, + '`' => { + if stack.is_empty() || stack.last().unwrap() != &c { + stack.push(c); + } else { + stack.pop(); + } + } + + _ => {} + } + } + + if !stack.is_empty() { + return Ok(ValidationResult::Incomplete); + } + + Ok(ValidationResult::Valid(None)) } } @@ -274,7 +307,6 @@ pub async fn run( let helper = Helper { highlighter: LineHighlighter::new(), - validator: MatchingBracketValidator::new(), }; let editor = Arc::new(Mutex::new(Editor::new())); diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 5bf1a5a852eaa..fdf2425cd672d 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1127,6 +1127,40 @@ fn run_watch() { drop(t); } +#[cfg(unix)] +#[test] +fn repl_test_pty_multiline() { + use std::io::Read; + use util::pty::fork::*; + + let tests_path = util::tests_path(); + let fork = Fork::from_ptmx().unwrap(); + if let Ok(mut master) = fork.is_parent() { + master.write_all(b"(\n1 + 2\n)\n").unwrap(); + master.write_all(b"{\nfoo: \"foo\"\n}\n").unwrap(); + master.write_all(b"`\nfoo\n`\n").unwrap(); + master.write_all(b"close();\n").unwrap(); + + let mut output = String::new(); + master.read_to_string(&mut output).unwrap(); + + assert!(output.contains('3')); + assert!(output.contains("{ foo: \"foo\" }")); + assert!(output.contains("\"\\nfoo\\n\"")); + + fork.wait().unwrap(); + } else { + util::deno_cmd() + .current_dir(tests_path) + .env("NO_COLOR", "1") + .arg("repl") + .spawn() + .unwrap() + .wait() + .unwrap(); + } +} + #[test] fn repl_test_console_log() { let (out, err) = util::run_and_collect_output( From 08441b855d8cfbe7edd41811c8c719e5fae01f83 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Mon, 19 Oct 2020 17:01:36 +0200 Subject: [PATCH 16/16] fix(op_crates/fetch): Body.body should be stream of Uint8Array (#8030) --- cli/tests/unit/fetch_test.ts | 4 +++- op_crates/fetch/26_fetch.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index 64309c26947db..ab92997116aea 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -133,6 +133,7 @@ unitTest({ perms: { net: true } }, async function fetchAsyncIterator(): Promise< assert(response.body !== null); let total = 0; for await (const chunk of response.body) { + assert(chunk instanceof Uint8Array); total += chunk.length; } @@ -145,12 +146,13 @@ unitTest({ perms: { net: true } }, async function fetchBodyReader(): Promise< const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); const headers = response.headers; assert(response.body !== null); - const reader = await response.body.getReader(); + const reader = response.body.getReader(); let total = 0; while (true) { const { done, value } = await reader.read(); if (done) break; assert(value); + assert(value instanceof Uint8Array); total += value.length; } diff --git a/op_crates/fetch/26_fetch.js b/op_crates/fetch/26_fetch.js index 88744981b52de..887e329f9d5bf 100644 --- a/op_crates/fetch/26_fetch.js +++ b/op_crates/fetch/26_fetch.js @@ -786,7 +786,7 @@ this._stream = new ReadableStream({ start(controller) { - controller.enqueue(buf); + controller.enqueue(new Uint8Array(buf)); controller.close(); }, });