From c15b7b8b59e4d2472ee45f8cee5469a266fb1601 Mon Sep 17 00:00:00 2001 From: jopemachine Date: Mon, 9 Nov 2020 23:17:15 +0900 Subject: [PATCH 1/3] Add totalmem, freemem using Deno.systemMemoryInfo --- std/node/os.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/node/os.ts b/std/node/os.ts index 773cca8bdcbe8..14bf20abd2ad2 100644 --- a/std/node/os.ts +++ b/std/node/os.ts @@ -123,9 +123,9 @@ export function endianness(): "BE" | "LE" { return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; } -/** Not yet implemented */ +/** Return free memory amount */ export function freemem(): number { - notImplemented(SEE_GITHUB_ISSUE); + return Deno.systemMemoryInfo().free; } /** Not yet implemented */ @@ -185,9 +185,9 @@ export function tmpdir(): string | null { notImplemented(SEE_GITHUB_ISSUE); } -/** Not yet implemented */ +/** Return total physical memory amount */ export function totalmem(): number { - notImplemented(SEE_GITHUB_ISSUE); + return Deno.systemMemoryInfo().total; } /** Not yet implemented */ From ab6e7e88f2d2fe98ef07c2b5116a6a07cd4ae4a5 Mon Sep 17 00:00:00 2001 From: jopemachine Date: Mon, 9 Nov 2020 23:17:27 +0900 Subject: [PATCH 2/3] Update os_test.ts --- std/node/os_test.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/std/node/os_test.ts b/std/node/os_test.ts index e4231aa3cda5c..8879f8345a109 100644 --- a/std/node/os_test.ts +++ b/std/node/os_test.ts @@ -200,6 +200,20 @@ Deno.test({ }, }); +Deno.test({ + name: "Total memory amount should be greater than 0", + fn() { + assert(os.totalmem() > 0); + }, +}); + +Deno.test({ + name: "Free memory amount should be greater than 0", + fn() { + assert(os.freemem() > 0); + }, +}); + Deno.test({ name: "APIs not yet implemented", fn() { @@ -210,13 +224,6 @@ Deno.test({ Error, "Not implemented", ); - assertThrows( - () => { - os.freemem(); - }, - Error, - "Not implemented", - ); assertThrows( () => { os.getPriority(); @@ -238,13 +245,6 @@ Deno.test({ Error, "Not implemented", ); - assertThrows( - () => { - os.totalmem(); - }, - Error, - "Not implemented", - ); assertThrows( () => { os.type(); From cf5501f4402e1d211ed299b7212f244ab47df99e Mon Sep 17 00:00:00 2001 From: jopemachine Date: Tue, 10 Nov 2020 09:41:01 +0900 Subject: [PATCH 3/3] Resolve conflict --- .dprintrc.json | 1 + .gitmodules | 1 + Cargo.lock | 7 + cli/Cargo.toml | 1 + cli/bench/http.rs | 2 +- cli/diagnostics.rs | 8 +- cli/diff.rs | 12 +- cli/dts/lib.deno.unstable.d.ts | 14 + cli/info.rs | 2 +- cli/inspector.rs | 2 +- cli/installer.rs | 107 +++- cli/lint.rs | 4 +- cli/main.rs | 2 +- cli/media_type.rs | 39 +- cli/module_graph.rs | 84 +++ cli/permissions.rs | 2 +- cli/repl.rs | 26 +- cli/tests/compiler_api_test.ts | 21 + cli/tests/integration_tests.rs | 2 +- cli/tests/module_graph/file_tests-a.mjs | 3 + cli/tests/module_graph/file_tests-b.ts | 1 + .../module_graph/file_tests-dynamicimport.ts | 5 + cli/tsc.rs | 59 +- op_crates/web/02_abort_signal.js | 27 +- op_crates/web/abort_controller_test.js | 18 + std/fs/eol.ts | 9 +- std/fs/move.ts | 8 +- std/node/_buffer.ts | 599 +++++++++++++++++ std/node/_crypto.ts | 3 + std/node/_crypto/pbkdf2.ts | 2 +- std/node/_crypto/randomBytes.ts | 2 +- std/node/_crypto/types.ts | 2 +- std/node/_fs.ts | 68 ++ std/node/_os.ts | 224 +++++++ std/node/_querystring.ts | 156 +++++ std/node/_string_decoder.ts | 299 +++++++++ std/node/_timers.ts | 14 + std/node/_url.ts | 140 ++++ std/node/_util.ts | 134 ++++ std/node/assert.ts | 53 +- std/node/assert_test.ts | 9 +- std/node/buffer.ts | 603 +----------------- std/node/buffer_test.ts | 2 +- std/node/crypto.ts | 8 +- std/node/events.ts | 316 ++++----- std/node/fs.ts | 70 +- std/node/os.ts | 228 +------ std/node/path.ts | 2 + std/node/querystring.ts | 158 +---- std/node/string_decoder.ts | 303 +-------- std/node/string_decoder_test.ts | 2 +- std/node/timers.ts | 16 +- std/node/url.ts | 144 +---- std/node/util.ts | 136 +--- tools/format.js | 3 +- 55 files changed, 2257 insertions(+), 1906 deletions(-) create mode 100644 cli/tests/module_graph/file_tests-a.mjs create mode 100644 cli/tests/module_graph/file_tests-b.ts create mode 100644 cli/tests/module_graph/file_tests-dynamicimport.ts create mode 100644 std/node/_buffer.ts create mode 100644 std/node/_crypto.ts create mode 100644 std/node/_fs.ts create mode 100644 std/node/_os.ts create mode 100644 std/node/_querystring.ts create mode 100644 std/node/_string_decoder.ts create mode 100644 std/node/_timers.ts create mode 100644 std/node/_url.ts create mode 100644 std/node/_util.ts diff --git a/.dprintrc.json b/.dprintrc.json index 726b2a4ad5c1a..4ec0ea675809a 100644 --- a/.dprintrc.json +++ b/.dprintrc.json @@ -13,6 +13,7 @@ "includes": ["**/*.{ts,tsx,js,jsx,json,md}"], "excludes": [ ".cargo_home", + ".git", "cli/dts/lib.d.ts", "cli/dts/lib.dom*", "cli/dts/lib.es*", diff --git a/.gitmodules b/.gitmodules index fc18f291ec5dd..cd52dfda310fe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "deno_third_party"] path = third_party url = https://github.com/denoland/deno_third_party.git + shallow = true [submodule "std/wasi/testdata"] path = std/wasi/testdata url = https://github.com/khronosproject/wasi-test-suite.git diff --git a/Cargo.lock b/Cargo.lock index 62ace88e288c8..8829627550024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,6 +444,7 @@ dependencies = [ "rustyline-derive", "semver-parser 0.9.0", "serde", + "shell-escape", "sourcemap", "swc_bundler", "swc_common", @@ -2147,6 +2148,12 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + [[package]] name = "signal-hook-registry" version = "1.2.1" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6e02e7d9d2cf9..8459139bf0dac 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -61,6 +61,7 @@ ring = "0.16.15" rustyline = { version = "6.3.0", default-features = false } rustyline-derive = "0.3.1" serde = { version = "1.0.116", features = ["derive"] } +shell-escape = "0.1.5" sys-info = "0.7.0" sourcemap = "6.0.1" swc_bundler = "=0.16.1" diff --git a/cli/bench/http.rs b/cli/bench/http.rs index e8cfd2b041807..0d26507210344 100644 --- a/cli/bench/http.rs +++ b/cli/bench/http.rs @@ -299,5 +299,5 @@ fn node_tcp() -> Result { fn hyper_http(exe: &str) -> Result { let port = get_port(); println!("http_benchmark testing RUST hyper"); - run(&[exe, &format!("{}", port)], port, None, None) + run(&[exe, &port.to_string()], port, None, None) } diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs index ba21e5aa95ac0..af2a63ad9bd9b 100644 --- a/cli/diagnostics.rs +++ b/cli/diagnostics.rs @@ -546,7 +546,7 @@ mod tests { } ]); let diagnostics: Diagnostics = serde_json::from_value(value).unwrap(); - let actual = format!("{}", diagnostics); + let actual = diagnostics.to_string(); assert_eq!( strip_ansi_codes(&actual), "TS5023 [ERROR]: Unknown compiler option \'invalid\'." @@ -573,7 +573,7 @@ mod tests { } ]); let diagnostics: Diagnostics = serde_json::from_value(value).unwrap(); - let actual = format!("{}", diagnostics); + let actual = diagnostics.to_string(); assert_eq!(strip_ansi_codes(&actual), "TS2584 [ERROR]: Cannot find name \'console\'. Do you need to change your target library? Try changing the `lib` compiler option to include \'dom\'.\nconsole.log(\"a\");\n~~~~~~~\n at test.ts:1:1"); } @@ -614,7 +614,7 @@ mod tests { } ]); let diagnostics: Diagnostics = serde_json::from_value(value).unwrap(); - let actual = format!("{}", diagnostics); + let actual = diagnostics.to_string(); assert_eq!(strip_ansi_codes(&actual), "TS2552 [ERROR]: Cannot find name \'foo_Bar\'. Did you mean \'foo_bar\'?\nfoo_Bar();\n~~~~~~~\n at test.ts:8:1\n\n \'foo_bar\' is declared here.\n function foo_bar() {\n ~~~~~~~\n at test.ts:4:10"); } @@ -655,7 +655,7 @@ mod tests { } ]; let diagnostics: Diagnostic = serde_json::from_value(value).unwrap(); - let actual = format!("{}", diagnostics); + let actual = diagnostics.to_string(); assert_eq!(strip_ansi_codes(&actual), "TS2551 [ERROR]: Property \'ppid\' does not exist on type \'typeof Deno\'. \'Deno.ppid\' is an unstable API. Did you forget to run with the \'--unstable\' flag, or did you mean \'pid\'?\nconsole.log(Deno.ppid);\n ~~~~\n at file:///cli/tests/unstable_ts2551.ts:1:18\n\n \'pid\' is declared here.\n export const pid: number;\n ~~~\n at asset:///lib.deno.ns.d.ts:90:16"); } } diff --git a/cli/diff.rs b/cli/diff.rs index 7510ebfa9b8f0..c8a86736d293d 100644 --- a/cli/diff.rs +++ b/cli/diff.rs @@ -6,27 +6,27 @@ use std::fmt; use std::fmt::Write; fn fmt_add() -> String { - format!("{}", colors::green_bold("+")) + colors::green_bold("+").to_string() } fn fmt_add_text(x: &str) -> String { - format!("{}", colors::green(x)) + colors::green(x).to_string() } fn fmt_add_text_highlight(x: &str) -> String { - format!("{}", colors::black_on_green(x)) + colors::black_on_green(x).to_string() } fn fmt_rem() -> String { - format!("{}", colors::red_bold("-")) + colors::red_bold("-").to_string() } fn fmt_rem_text(x: &str) -> String { - format!("{}", colors::red(x)) + colors::red(x).to_string() } fn fmt_rem_text_highlight(x: &str) -> String { - format!("{}", colors::white_on_red(x)) + colors::white_on_red(x).to_string() } fn write_line_diff( diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 6eb39c21c4a20..044e1b4e24299 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -318,6 +318,20 @@ declare namespace Deno { /** Import emit helpers (e.g. `__extends`, `__rest`, etc..) from * [tslib](https://www.npmjs.com/package/tslib). */ importHelpers?: boolean; + /** This flag controls how `import` works, there are 3 different options: + * + * - `remove`: The default behavior of dropping import statements which only + * reference types. + * - `preserve`: Preserves all `import` statements whose values or types are + * never used. This can cause imports/side-effects to be preserved. + * - `error`: This preserves all imports (the same as the preserve option), + * but will error when a value import is only used as a type. This might + * be useful if you want to ensure no values are being accidentally + * imported, but still make side-effect imports explicit. + * + * This flag works because you can use `import type` to explicitly create an + * `import` statement which should never be emitted into JavaScript. */ + importsNotUsedAsValues?: "remove" | "preserve" | "error"; /** Emit a single file with source maps instead of having a separate file. * Defaults to `false`. */ inlineSourceMap?: boolean; diff --git a/cli/info.rs b/cli/info.rs index 9ca29428721de..d2ea4e7e6398e 100644 --- a/cli/info.rs +++ b/cli/info.rs @@ -272,7 +272,7 @@ mod test { #[test] fn test_module_graph_info_display() { let fixture = get_fixture(); - let actual = format!("{}", fixture); + let actual = fixture.to_string(); assert!(actual.contains(" /a/b/c.ts")); assert!(actual.contains(" 99 unique")); assert!(actual.contains("(12.06KB)")); diff --git a/cli/inspector.rs b/cli/inspector.rs index 1a916c7c7a309..dd1122020b871 100644 --- a/cli/inspector.rs +++ b/cli/inspector.rs @@ -934,7 +934,7 @@ impl InspectorSession { let response = response_rx.await.unwrap(); if let Some(error) = response.get("error") { - return Err(generic_error(format!("{}", error))); + return Err(generic_error(error.to_string())); } let result = response.get("result").unwrap().clone(); diff --git a/cli/installer.rs b/cli/installer.rs index 4a177b3eabbed..dab81bc8d80c9 100644 --- a/cli/installer.rs +++ b/cli/installer.rs @@ -65,11 +65,15 @@ fn generate_executable_file( file_path: PathBuf, args: Vec, ) -> Result<(), AnyError> { - let args: Vec = args.iter().map(|c| format!("\"{}\"", c)).collect(); + use shell_escape::escape; + let args: Vec = args + .into_iter() + .map(|c| escape(c.into()).into_owned()) + .collect(); let template = format!( r#"#!/bin/sh # generated by deno install -deno {} "$@" +exec deno {} "$@" "#, args.join(" "), ); @@ -429,8 +433,13 @@ mod tests { // It's annoying when shell scripts don't have NL at the end. assert_eq!(content.chars().last().unwrap(), '\n'); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + if cfg!(windows) { + assert!(content + .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + } else { + assert!(content + .contains(r#"run 'http://localhost:4545/cli/tests/echo_server.ts'"#)); + } if let Some(home) = original_home { env::set_var("HOME", home); } @@ -470,9 +479,15 @@ mod tests { let content = fs::read_to_string(file_path).unwrap(); println!("this is the file path {:?}", content); - assert!(content.contains( - r#""run" "--unstable" "http://localhost:4545/cli/tests/echo_server.ts"# - )); + if cfg!(windows) { + assert!(content.contains( + r#""run" "--unstable" "http://localhost:4545/cli/tests/echo_server.ts""# + )); + } else { + assert!(content.contains( + r#"run --unstable 'http://localhost:4545/cli/tests/echo_server.ts'"# + )); + } } #[test] @@ -498,8 +513,13 @@ mod tests { assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + if cfg!(windows) { + assert!(content + .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + } else { + assert!(content + .contains(r#"run 'http://localhost:4545/cli/tests/echo_server.ts'"#)); + } } #[test] @@ -525,8 +545,13 @@ mod tests { assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/subdir/main.ts""#)); + if cfg!(windows) { + assert!(content + .contains(r#""run" "http://localhost:4545/cli/tests/subdir/main.ts""#)); + } else { + assert!(content + .contains(r#"run 'http://localhost:4545/cli/tests/subdir/main.ts'"#)); + } } #[test] @@ -552,8 +577,13 @@ mod tests { assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + if cfg!(windows) { + assert!(content + .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + } else { + assert!(content + .contains(r#"run 'http://localhost:4545/cli/tests/echo_server.ts'"#)); + } } #[test] @@ -582,8 +612,13 @@ mod tests { assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); - assert!(content - .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + if cfg!(windows) { + assert!(content + .contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); + } else { + assert!(content + .contains(r#"run 'http://localhost:4545/cli/tests/echo_server.ts'"#)); + } if let Some(install_root) = original_install_root { env::set_var("DENO_INSTALL_ROOT", install_root); } @@ -618,8 +653,11 @@ mod tests { assert!(file_path.exists()); let content = fs::read_to_string(file_path).unwrap(); - dbg!(&content); - assert!(content.contains(r#""run" "--allow-read" "--allow-net" "--quiet" "--no-check" "http://localhost:4545/cli/tests/echo_server.ts" "--foobar""#)); + if cfg!(windows) { + assert!(content.contains(r#""run" "--allow-read" "--allow-net" "--quiet" "--no-check" "http://localhost:4545/cli/tests/echo_server.ts" "--foobar""#)); + } else { + assert!(content.contains(r#"run --allow-read --allow-net --quiet --no-check 'http://localhost:4545/cli/tests/echo_server.ts' --foobar"#)); + } } #[test] @@ -737,4 +775,39 @@ mod tests { let content = fs::read_to_string(file_path).unwrap(); assert!(content == "{}"); } + + // TODO: enable on Windows after fixing batch escaping + #[cfg(not(windows))] + #[test] + fn install_shell_escaping() { + let temp_dir = TempDir::new().expect("tempdir fail"); + let bin_dir = temp_dir.path().join("bin"); + std::fs::create_dir(&bin_dir).unwrap(); + + install( + Flags::default(), + "http://localhost:4545/cli/tests/echo_server.ts", + vec!["\"".to_string()], + Some("echo_test".to_string()), + Some(temp_dir.path().to_path_buf()), + false, + ) + .expect("Install failed"); + + let mut file_path = bin_dir.join("echo_test"); + if cfg!(windows) { + file_path = file_path.with_extension("cmd"); + } + + assert!(file_path.exists()); + let content = fs::read_to_string(file_path).unwrap(); + println!("{}", content); + if cfg!(windows) { + // TODO: see comment above this test + } else { + assert!(content.contains( + r#"run 'http://localhost:4545/cli/tests/echo_server.ts' '"'"# + )); + } + } } diff --git a/cli/lint.rs b/cli/lint.rs index e95af58f2eaf6..ff63b693cc331 100644 --- a/cli/lint.rs +++ b/cli/lint.rs @@ -276,9 +276,9 @@ pub fn format_diagnostic( colors::red(&"^".repeat(line_len - range.start.col)) )); } else if range.end.line == i { - lines.push(format!("{}", colors::red(&"^".repeat(range.end.col)))); + lines.push(colors::red(&"^".repeat(range.end.col)).to_string()); } else if line_len != 0 { - lines.push(format!("{}", colors::red(&"^".repeat(line_len)))); + lines.push(colors::red(&"^".repeat(line_len)).to_string()); } } } diff --git a/cli/main.rs b/cli/main.rs index 9404a47b290a5..f35a4c58703b7 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -187,7 +187,7 @@ async fn info_command( if json { write_json_to_stdout(&json!(info))?; } else { - write_to_stdout_ignore_sigpipe(format!("{}", info).as_bytes())?; + write_to_stdout_ignore_sigpipe(info.to_string().as_bytes())?; } Ok(()) } else { diff --git a/cli/media_type.rs b/cli/media_type.rs index fe5d16a5b05b7..8280c1180b3cf 100644 --- a/cli/media_type.rs +++ b/cli/media_type.rs @@ -94,19 +94,16 @@ impl MediaType { }, }, Some(os_str) => match os_str.to_str() { - Some("ts") => match path.file_stem() { - Some(os_str) => match os_str.to_str() { - Some(file_name) => { + Some("ts") => { + if let Some(os_str) = path.file_stem() { + if let Some(file_name) = os_str.to_str() { if file_name.ends_with(".d") { - MediaType::Dts - } else { - MediaType::TypeScript + return MediaType::Dts; } } - None => MediaType::TypeScript, - }, - None => MediaType::TypeScript, - }, + } + MediaType::TypeScript + } Some("tsx") => MediaType::TSX, Some("js") => MediaType::JavaScript, Some("jsx") => MediaType::JSX, @@ -194,7 +191,7 @@ pub fn serialize_media_type(mt: &MediaType, s: S) -> Result where S: Serializer, { - s.serialize_str(&format!("{}", mt)) + s.serialize_str(&mt.to_string()) } #[cfg(test)] @@ -269,15 +266,15 @@ mod tests { #[test] fn test_display() { - assert_eq!(format!("{}", MediaType::JavaScript), "JavaScript"); - assert_eq!(format!("{}", MediaType::JSX), "JSX"); - assert_eq!(format!("{}", MediaType::TypeScript), "TypeScript"); - assert_eq!(format!("{}", MediaType::Dts), "Dts"); - assert_eq!(format!("{}", MediaType::TSX), "TSX"); - assert_eq!(format!("{}", MediaType::Json), "Json"); - assert_eq!(format!("{}", MediaType::Wasm), "Wasm"); - assert_eq!(format!("{}", MediaType::TsBuildInfo), "TsBuildInfo"); - assert_eq!(format!("{}", MediaType::SourceMap), "SourceMap"); - assert_eq!(format!("{}", MediaType::Unknown), "Unknown"); + assert_eq!(MediaType::JavaScript.to_string(), "JavaScript"); + assert_eq!(MediaType::JSX.to_string(), "JSX"); + assert_eq!(MediaType::TypeScript.to_string(), "TypeScript"); + assert_eq!(MediaType::Dts.to_string(), "Dts"); + assert_eq!(MediaType::TSX.to_string(), "TSX"); + assert_eq!(MediaType::Json.to_string(), "Json"); + assert_eq!(MediaType::Wasm.to_string(), "Wasm"); + assert_eq!(MediaType::TsBuildInfo.to_string(), "TsBuildInfo"); + assert_eq!(MediaType::SourceMap.to_string(), "SourceMap"); + assert_eq!(MediaType::Unknown.to_string(), "Unknown"); } } diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 2f1c9a2671d02..0bd51251fbff0 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -952,6 +952,7 @@ impl Graph { let extension = match emitted_file.media_type { MediaType::JavaScript => ".js", MediaType::SourceMap => ".js.map", + MediaType::Dts => ".d.ts", _ => unreachable!(), }; let key = format!("{}{}", specifier, extension); @@ -1958,6 +1959,24 @@ pub mod tests { assert_eq!(h.tsbuildinfo_calls.len(), 1); } + #[tokio::test] + async fn test_graph_check_ignores_dynamic_import_errors() { + let specifier = + ModuleSpecifier::resolve_url_or_path("file:///tests/dynamicimport.ts") + .expect("could not resolve module"); + let (graph, _) = setup(specifier).await; + let result_info = graph + .check(CheckOptions { + debug: false, + emit: false, + lib: TypeLib::DenoWindow, + maybe_config_path: None, + reload: false, + }) + .expect("should have checked"); + assert!(result_info.diagnostics.is_empty()); + } + #[tokio::test] async fn fix_graph_check_emit_diagnostics() { let specifier = @@ -2006,6 +2025,27 @@ pub mod tests { assert_eq!(h.tsbuildinfo_calls.len(), 1); } + #[tokio::test] + async fn fix_graph_check_mjs_root() { + let specifier = ModuleSpecifier::resolve_url_or_path("file:///tests/a.mjs") + .expect("could not resolve module"); + let (graph, handler) = setup(specifier).await; + let result_info = graph + .check(CheckOptions { + debug: false, + emit: true, + lib: TypeLib::DenoWindow, + maybe_config_path: None, + reload: false, + }) + .expect("should have checked"); + assert!(result_info.maybe_ignored_options.is_none()); + assert!(result_info.diagnostics.is_empty()); + let h = handler.borrow(); + assert_eq!(h.cache_calls.len(), 1); + assert_eq!(h.tsbuildinfo_calls.len(), 1); + } + #[tokio::test] async fn fix_graph_check_types_root() { let specifier = ModuleSpecifier::resolve_url_or_path("file:///typesref.js") @@ -2143,6 +2183,50 @@ pub mod tests { assert!(actual.contains("console.log(b);")); } + #[tokio::test] + async fn fix_graph_emit_declaration() { + let specifier = + ModuleSpecifier::resolve_url_or_path("file:///a.ts").unwrap(); + let graph = setup_memory( + specifier, + map!( + "/a.ts" => r#" + import * as b from "./b.ts"; + + console.log(b); + "#, + "/b.ts" => r#" + export const b = "b"; + "# + ), + ) + .await; + let mut user_config = HashMap::::new(); + user_config.insert("declaration".to_string(), json!(true)); + let (emitted_files, result_info) = graph + .emit(EmitOptions { + bundle_type: BundleType::None, + debug: false, + maybe_user_config: Some(user_config), + }) + .expect("should have emitted"); + assert!(result_info.diagnostics.is_empty()); + assert!(result_info.maybe_ignored_options.is_none()); + assert_eq!(emitted_files.len(), 6); + let out_a = emitted_files.get("file:///a.ts.js"); + assert!(out_a.is_some()); + let out_a = out_a.unwrap(); + assert!(out_a.starts_with("import * as b from")); + assert!(emitted_files.contains_key("file:///a.ts.js.map")); + assert!(emitted_files.contains_key("file:///a.ts.d.ts")); + let out_b = emitted_files.get("file:///b.ts.js"); + assert!(out_b.is_some()); + let out_b = out_b.unwrap(); + assert!(out_b.starts_with("export const b = \"b\";")); + assert!(emitted_files.contains_key("file:///b.ts.js.map")); + assert!(emitted_files.contains_key("file:///b.ts.d.ts")); + } + #[tokio::test] async fn test_graph_info() { let specifier = diff --git a/cli/permissions.rs b/cli/permissions.rs index e968b22ff7109..204176ad3b057 100644 --- a/cli/permissions.rs +++ b/cli/permissions.rs @@ -232,7 +232,7 @@ impl Permissions { )); } Ok(self.query_net( - &format!("{}", parsed.host().unwrap()), + &parsed.host().unwrap().to_string(), parsed.port_or_known_default(), )) } diff --git a/cli/repl.rs b/cli/repl.rs index b5f89ec51f86c..be85dc8134717 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -245,31 +245,31 @@ impl Highlighter for LineHighlighter { .regex .replace_all(&line.to_string(), |caps: &Captures<'_>| { if let Some(cap) = caps.name("comment") { - format!("{}", colors::gray(cap.as_str())) + colors::gray(cap.as_str()).to_string() } else if let Some(cap) = caps.name("string") { - format!("{}", colors::green(cap.as_str())) + colors::green(cap.as_str()).to_string() } else if let Some(cap) = caps.name("regexp") { - format!("{}", colors::red(cap.as_str())) + colors::red(cap.as_str()).to_string() } else if let Some(cap) = caps.name("number") { - format!("{}", colors::yellow(cap.as_str())) + colors::yellow(cap.as_str()).to_string() } else if let Some(cap) = caps.name("boolean") { - format!("{}", colors::yellow(cap.as_str())) + colors::yellow(cap.as_str()).to_string() } else if let Some(cap) = caps.name("null") { - format!("{}", colors::yellow(cap.as_str())) + colors::yellow(cap.as_str()).to_string() } else if let Some(cap) = caps.name("undefined") { - format!("{}", colors::gray(cap.as_str())) + colors::gray(cap.as_str()).to_string() } else if let Some(cap) = caps.name("keyword") { - format!("{}", colors::cyan(cap.as_str())) + colors::cyan(cap.as_str()).to_string() } else if let Some(cap) = caps.name("infinity") { - format!("{}", colors::yellow(cap.as_str())) + colors::yellow(cap.as_str()).to_string() } else if let Some(cap) = caps.name("classes") { - format!("{}", colors::green_bold(cap.as_str())) + colors::green_bold(cap.as_str()).to_string() } else if let Some(cap) = caps.name("hexnumber") { - format!("{}", colors::yellow(cap.as_str())) + colors::yellow(cap.as_str()).to_string() } else if let Some(cap) = caps.name("octalnumber") { - format!("{}", colors::yellow(cap.as_str())) + colors::yellow(cap.as_str()).to_string() } else if let Some(cap) = caps.name("binarynumber") { - format!("{}", colors::yellow(cap.as_str())) + colors::yellow(cap.as_str()).to_string() } else { caps[0].to_string() } diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts index a236a3f3fa065..4535ad6edd3f0 100644 --- a/cli/tests/compiler_api_test.ts +++ b/cli/tests/compiler_api_test.ts @@ -199,3 +199,24 @@ Deno.test({ }); }, }); + +Deno.test({ + name: `Deno.compile() - Allows setting of "importsNotUsedAsValues"`, + async fn() { + const [diagnostics] = await Deno.compile("/a.ts", { + "/a.ts": `import { B } from "./b.ts"; + const b: B = { b: "b" }; + `, + "/b.ts": `export interface B { + b: string; + }; + `, + }, { + importsNotUsedAsValues: "error", + }); + assert(diagnostics); + assertEquals(diagnostics.length, 1); + assert(diagnostics[0].messageText); + assert(diagnostics[0].messageText.includes("This import is never used")); + }, +}); diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 16be1090079bc..3618c2e77ce2e 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -3814,7 +3814,7 @@ async fn inspector_does_not_hang() { for i in 0..128u32 { let request_id = i + 10; // Expect the number {i} on stdout. - let s = format!("{}", i); + let s = i.to_string(); assert_eq!(stdout_lines.next().unwrap(), s); // Expect hitting the `debugger` statement. let s = r#"{"method":"Debugger.paused","#; diff --git a/cli/tests/module_graph/file_tests-a.mjs b/cli/tests/module_graph/file_tests-a.mjs new file mode 100644 index 0000000000000..72b3a67bc9870 --- /dev/null +++ b/cli/tests/module_graph/file_tests-a.mjs @@ -0,0 +1,3 @@ +import * as b from "./b.ts"; + +console.log(b); diff --git a/cli/tests/module_graph/file_tests-b.ts b/cli/tests/module_graph/file_tests-b.ts new file mode 100644 index 0000000000000..59d1689930e55 --- /dev/null +++ b/cli/tests/module_graph/file_tests-b.ts @@ -0,0 +1 @@ +export const b = "b"; diff --git a/cli/tests/module_graph/file_tests-dynamicimport.ts b/cli/tests/module_graph/file_tests-dynamicimport.ts new file mode 100644 index 0000000000000..b5c9f080f95e1 --- /dev/null +++ b/cli/tests/module_graph/file_tests-dynamicimport.ts @@ -0,0 +1,5 @@ +try { + await import("bare_module_specifier"); +} catch (err) { + console.log(err); +} diff --git a/cli/tsc.rs b/cli/tsc.rs index 7f90dd7b22709..bcaeaaeb65493 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -22,6 +22,7 @@ use deno_core::Snapshot; use serde::Deserialize; use std::cell::RefCell; use std::collections::HashMap; +use std::path::PathBuf; use std::rc::Rc; /// Provide static assets that are not preloaded in the compiler snapshot. @@ -63,6 +64,41 @@ fn get_maybe_hash( } } +/// tsc only supports `.ts`, `.tsx`, `.d.ts`, `.js`, or `.jsx` as root modules +/// and so we have to detect the apparent media type based on extensions it +/// supports. +fn get_tsc_media_type(specifier: &ModuleSpecifier) -> MediaType { + let url = specifier.as_url(); + let path = if url.scheme() == "file" { + if let Ok(path) = url.to_file_path() { + path + } else { + PathBuf::from(url.path()) + } + } else { + PathBuf::from(url.path()) + }; + match path.extension() { + None => MediaType::Unknown, + Some(os_str) => match os_str.to_str() { + Some("ts") => { + if let Some(os_str) = path.file_stem() { + if let Some(file_name) = os_str.to_str() { + if file_name.ends_with(".d") { + return MediaType::Dts; + } + } + } + MediaType::TypeScript + } + Some("tsx") => MediaType::TSX, + Some("js") => MediaType::JavaScript, + Some("jsx") => MediaType::JSX, + _ => MediaType::Unknown, + }, + } +} + #[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct EmittedFile { pub data: String, @@ -337,7 +373,7 @@ pub fn exec( .root_names .iter() .map(|(s, mt)| { - let ext_media_type = MediaType::from(&s.as_str().to_owned()); + let ext_media_type = get_tsc_media_type(s); if mt != &ext_media_type { let new_specifier = format!("{}{}", s, mt.as_ts_extension()); root_map.insert(new_specifier.clone(), s.clone()); @@ -490,6 +526,27 @@ mod tests { ); } + #[test] + fn test_get_tsc_media_type() { + let fixtures = vec![ + ("file:///a.ts", MediaType::TypeScript), + ("file:///a.tsx", MediaType::TSX), + ("file:///a.d.ts", MediaType::Dts), + ("file:///a.js", MediaType::JavaScript), + ("file:///a.jsx", MediaType::JSX), + ("file:///a.cjs", MediaType::Unknown), + ("file:///a.mjs", MediaType::Unknown), + ("file:///a.json", MediaType::Unknown), + ("file:///a.wasm", MediaType::Unknown), + ("file:///a.js.map", MediaType::Unknown), + ("file:///.tsbuildinfo", MediaType::Unknown), + ]; + for (specifier, media_type) in fixtures { + let specifier = ModuleSpecifier::resolve_url_or_path(specifier).unwrap(); + assert_eq!(get_tsc_media_type(&specifier), media_type); + } + } + #[tokio::test] async fn test_emit() { let mut state = setup(None, None, None).await; diff --git a/op_crates/web/02_abort_signal.js b/op_crates/web/02_abort_signal.js index 53745c52baca7..5e8943161ecb2 100644 --- a/op_crates/web/02_abort_signal.js +++ b/op_crates/web/02_abort_signal.js @@ -64,22 +64,35 @@ } const handlerSymbol = Symbol("eventHandlers"); + + function makeWrappedHandler(handler) { + function wrappedHandler(...args) { + if (typeof wrappedHandler.handler !== "function") { + return; + } + return wrappedHandler.handler.call(this, ...args); + } + wrappedHandler.handler = handler; + return wrappedHandler; + } function defineEventHandler(emitter, name) { // HTML specification section 8.1.5.1 Object.defineProperty(emitter, `on${name}`, { get() { - return this[handlerSymbol]?.get(name); + return this[handlerSymbol]?.get(name)?.handler; }, set(value) { - const oldListener = this[handlerSymbol]?.get(name); - if (oldListener) { - this.removeEventListener(name, oldListener); - } if (!this[handlerSymbol]) { this[handlerSymbol] = new Map(); } - this.addEventListener(name, value); - this[handlerSymbol].set(value); + let handlerWrapper = this[handlerSymbol]?.get(name); + if (handlerWrapper) { + handlerWrapper.handler = value; + } else { + handlerWrapper = makeWrappedHandler(value); + this.addEventListener(name, handlerWrapper); + } + this[handlerSymbol].set(name, handlerWrapper); }, configurable: true, enumerable: true, diff --git a/op_crates/web/abort_controller_test.js b/op_crates/web/abort_controller_test.js index 4d374198604e9..a65bc69ff8171 100644 --- a/op_crates/web/abort_controller_test.js +++ b/op_crates/web/abort_controller_test.js @@ -95,6 +95,23 @@ function abortSignalEventOrder() { assertEquals(arr[1], 2); assertEquals(arr[2], 3); } + +function abortSignalEventOrderComplex() { + const arr = []; + const controller = new AbortController(); + const { signal } = controller; + signal.addEventListener("abort", () => arr.push(1)); + signal.onabort = () => { + throw new Error(); + }; + signal.addEventListener("abort", () => arr.push(3)); + signal.onabort = () => arr.push(2); + controller.abort(); + assertEquals(arr[0], 1); + assertEquals(arr[1], 2); + assertEquals(arr[2], 3); +} + function abortSignalHandlerLocation() { const controller = new AbortController(); const { signal } = controller; @@ -109,6 +126,7 @@ function main() { controllerHasProperToString(); abortSignalIllegalConstructor(); abortSignalEventOrder(); + abortSignalEventOrderComplex(); abortSignalHandlerLocation(); } diff --git a/std/fs/eol.ts b/std/fs/eol.ts index 2f15be269e00e..5ea554c6e4d76 100644 --- a/std/fs/eol.ts +++ b/std/fs/eol.ts @@ -17,12 +17,9 @@ export function detect(content: string): EOL | null { if (!d || d.length === 0) { return null; } - const crlf = d.filter((x: string): boolean => x === EOL.CRLF); - if (crlf.length > 0) { - return EOL.CRLF; - } else { - return EOL.LF; - } + const hasCRLF = d.some((x: string): boolean => x === EOL.CRLF); + + return hasCRLF ? EOL.CRLF : EOL.LF; } /** Format the file to the targeted EOL */ diff --git a/std/fs/move.ts b/std/fs/move.ts index 421ca0771d566..8b5a6caa263b7 100644 --- a/std/fs/move.ts +++ b/std/fs/move.ts @@ -24,14 +24,14 @@ export async function move( if (await exists(dest)) { await Deno.remove(dest, { recursive: true }); } - await Deno.rename(src, dest); } else { if (await exists(dest)) { throw new Error("dest already exists."); } - await Deno.rename(src, dest); } + await Deno.rename(src, dest); + return; } @@ -53,11 +53,11 @@ export function moveSync( if (existsSync(dest)) { Deno.removeSync(dest, { recursive: true }); } - Deno.renameSync(src, dest); } else { if (existsSync(dest)) { throw new Error("dest already exists."); } - Deno.renameSync(src, dest); } + + Deno.renameSync(src, dest); } diff --git a/std/node/_buffer.ts b/std/node/_buffer.ts new file mode 100644 index 0000000000000..8cbd117ca48e6 --- /dev/null +++ b/std/node/_buffer.ts @@ -0,0 +1,599 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import * as hex from "../encoding/hex.ts"; +import * as base64 from "../encoding/base64.ts"; +import { normalizeEncoding, notImplemented } from "./_utils.ts"; + +const notImplementedEncodings = [ + "ascii", + "binary", + "latin1", + "ucs2", + "utf16le", +]; + +function checkEncoding(encoding = "utf8", strict = true): string { + if (typeof encoding !== "string" || (strict && encoding === "")) { + if (!strict) return "utf8"; + throw new TypeError(`Unkown encoding: ${encoding}`); + } + + const normalized = normalizeEncoding(encoding); + + if (normalized === undefined) { + throw new TypeError(`Unkown encoding: ${encoding}`); + } + + if (notImplementedEncodings.includes(encoding)) { + notImplemented(`"${encoding}" encoding`); + } + + return normalized; +} + +interface EncodingOp { + byteLength(string: string): number; +} + +// https://github.com/nodejs/node/blob/56dbe466fdbc598baea3bfce289bf52b97b8b8f7/lib/buffer.js#L598 +const encodingOps: { [key: string]: EncodingOp } = { + utf8: { + byteLength: (string: string): number => + new TextEncoder().encode(string).byteLength, + }, + ucs2: { + byteLength: (string: string): number => string.length * 2, + }, + utf16le: { + byteLength: (string: string): number => string.length * 2, + }, + latin1: { + byteLength: (string: string): number => string.length, + }, + ascii: { + byteLength: (string: string): number => string.length, + }, + base64: { + byteLength: (string: string): number => + base64ByteLength(string, string.length), + }, + hex: { + byteLength: (string: string): number => string.length >>> 1, + }, +}; + +function base64ByteLength(str: string, bytes: number): number { + // Handle padding + if (str.charCodeAt(bytes - 1) === 0x3d) bytes--; + if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3d) bytes--; + + // Base64 ratio: 3/4 + return (bytes * 3) >>> 2; +} + +/** + * See also https://nodejs.org/api/buffer.html + */ +export class Buffer extends Uint8Array { + /** + * Allocates a new Buffer of size bytes. + */ + static alloc( + size: number, + fill?: number | string | Uint8Array | Buffer, + encoding = "utf8", + ): Buffer { + if (typeof size !== "number") { + throw new TypeError( + `The "size" argument must be of type number. Received type ${typeof size}`, + ); + } + + const buf = new Buffer(size); + if (size === 0) return buf; + + let bufFill; + if (typeof fill === "string") { + encoding = checkEncoding(encoding); + if ( + typeof fill === "string" && + fill.length === 1 && + encoding === "utf8" + ) { + buf.fill(fill.charCodeAt(0)); + } else bufFill = Buffer.from(fill, encoding); + } else if (typeof fill === "number") { + buf.fill(fill); + } else if (fill instanceof Uint8Array) { + if (fill.length === 0) { + throw new TypeError( + `The argument "value" is invalid. Received ${fill.constructor.name} []`, + ); + } + + bufFill = fill; + } + + if (bufFill) { + if (bufFill.length > buf.length) { + bufFill = bufFill.subarray(0, buf.length); + } + + let offset = 0; + while (offset < size) { + buf.set(bufFill, offset); + offset += bufFill.length; + if (offset + bufFill.length >= size) break; + } + if (offset !== size) { + buf.set(bufFill.subarray(0, size - offset), offset); + } + } + + return buf; + } + + static allocUnsafe(size: number): Buffer { + return new Buffer(size); + } + + /** + * Returns the byte length of a string when encoded. This is not the same as + * String.prototype.length, which does not account for the encoding that is + * used to convert the string into bytes. + */ + static byteLength( + string: string | Buffer | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, + encoding = "utf8", + ): number { + if (typeof string != "string") return string.byteLength; + + encoding = normalizeEncoding(encoding) || "utf8"; + return encodingOps[encoding].byteLength(string); + } + + /** + * Returns a new Buffer which is the result of concatenating all the Buffer + * instances in the list together. + */ + static concat(list: Buffer[] | Uint8Array[], totalLength?: number): Buffer { + if (totalLength == undefined) { + totalLength = 0; + for (const buf of list) { + totalLength += buf.length; + } + } + + const buffer = Buffer.allocUnsafe(totalLength); + let pos = 0; + for (const item of list) { + let buf: Buffer; + if (!(item instanceof Buffer)) { + buf = Buffer.from(item); + } else { + buf = item; + } + buf.copy(buffer, pos); + pos += buf.length; + } + + return buffer; + } + + /** + * Allocates a new Buffer using an array of bytes in the range 0 – 255. Array + * entries outside that range will be truncated to fit into it. + */ + static from(array: number[]): Buffer; + /** + * This creates a view of the ArrayBuffer without copying the underlying + * memory. For example, when passed a reference to the .buffer property of a + * TypedArray instance, the newly created Buffer will share the same allocated + * memory as the TypedArray. + */ + static from( + arrayBuffer: ArrayBuffer | SharedArrayBuffer, + byteOffset?: number, + length?: number, + ): Buffer; + /** + * Copies the passed buffer data onto a new Buffer instance. + */ + static from(buffer: Buffer | Uint8Array): Buffer; + /** + * Creates a new Buffer containing string. + */ + static from(string: string, encoding?: string): Buffer; + static from( + // deno-lint-ignore no-explicit-any + value: any, + offsetOrEncoding?: number | string, + length?: number, + ): Buffer { + const offset = typeof offsetOrEncoding === "string" + ? undefined + : offsetOrEncoding; + let encoding = typeof offsetOrEncoding === "string" + ? offsetOrEncoding + : undefined; + + if (typeof value == "string") { + encoding = checkEncoding(encoding, false); + if (encoding === "hex") return new Buffer(hex.decodeString(value).buffer); + if (encoding === "base64") return new Buffer(base64.decode(value).buffer); + return new Buffer(new TextEncoder().encode(value).buffer); + } + + // workaround for https://github.com/microsoft/TypeScript/issues/38446 + return new Buffer(value, offset!, length); + } + + /** + * Returns true if obj is a Buffer, false otherwise. + */ + static isBuffer(obj: unknown): obj is Buffer { + return obj instanceof Buffer; + } + + // deno-lint-ignore no-explicit-any + static isEncoding(encoding: any): boolean { + return ( + typeof encoding === "string" && + encoding.length !== 0 && + normalizeEncoding(encoding) !== undefined + ); + } + + /** + * Copies data from a region of buf to a region in target, even if the target + * memory region overlaps with buf. + */ + copy( + targetBuffer: Buffer | Uint8Array, + targetStart = 0, + sourceStart = 0, + sourceEnd = this.length, + ): number { + const sourceBuffer = this + .subarray(sourceStart, sourceEnd) + .subarray(0, Math.max(0, targetBuffer.length - targetStart)); + + if (sourceBuffer.length === 0) return 0; + + targetBuffer.set(sourceBuffer, targetStart); + return sourceBuffer.length; + } + + /* + * Returns true if both buf and otherBuffer have exactly the same bytes, false otherwise. + */ + equals(otherBuffer: Uint8Array | Buffer): boolean { + if (!(otherBuffer instanceof Uint8Array)) { + throw new TypeError( + `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received type ${typeof otherBuffer}`, + ); + } + + if (this === otherBuffer) return true; + if (this.byteLength !== otherBuffer.byteLength) return false; + + for (let i = 0; i < this.length; i++) { + if (this[i] !== otherBuffer[i]) return false; + } + + return true; + } + + readBigInt64BE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigInt64(offset); + } + readBigInt64LE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigInt64(offset, true); + } + + readBigUInt64BE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigUint64(offset); + } + readBigUInt64LE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigUint64(offset, true); + } + + readDoubleBE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat64(offset); + } + readDoubleLE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat64(offset, true); + } + + readFloatBE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat32(offset); + } + readFloatLE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat32(offset, true); + } + + readInt8(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt8( + offset, + ); + } + + readInt16BE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( + offset, + ); + } + readInt16LE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( + offset, + true, + ); + } + + readInt32BE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( + offset, + ); + } + readInt32LE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( + offset, + true, + ); + } + + readUInt8(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getUint8( + offset, + ); + } + + readUInt16BE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint16(offset); + } + readUInt16LE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint16(offset, true); + } + + readUInt32BE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint32(offset); + } + readUInt32LE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint32(offset, true); + } + + /** + * Returns a new Buffer that references the same memory as the original, but + * offset and cropped by the start and end indices. + */ + slice(begin = 0, end = this.length): Buffer { + // workaround for https://github.com/microsoft/TypeScript/issues/38665 + return this.subarray(begin, end) as Buffer; + } + + /** + * Returns a JSON representation of buf. JSON.stringify() implicitly calls + * this function when stringifying a Buffer instance. + */ + toJSON(): Record { + return { type: "Buffer", data: Array.from(this) }; + } + + /** + * Decodes buf to a string according to the specified character encoding in + * encoding. start and end may be passed to decode only a subset of buf. + */ + toString(encoding = "utf8", start = 0, end = this.length): string { + encoding = checkEncoding(encoding); + + const b = this.subarray(start, end); + if (encoding === "hex") return hex.encodeToString(b); + if (encoding === "base64") return base64.encode(b.buffer); + + return new TextDecoder(encoding).decode(b); + } + + /** + * Writes string to buf at offset according to the character encoding in + * encoding. The length parameter is the number of bytes to write. If buf did + * not contain enough space to fit the entire string, only part of string will + * be written. However, partially encoded characters will not be written. + */ + write(string: string, offset = 0, length = this.length): number { + return new TextEncoder().encodeInto( + string, + this.subarray(offset, offset + length), + ).written; + } + + writeBigInt64BE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( + offset, + value, + ); + return offset + 4; + } + writeBigInt64LE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( + offset, + value, + true, + ); + return offset + 4; + } + + writeBigUInt64BE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( + offset, + value, + ); + return offset + 4; + } + writeBigUInt64LE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( + offset, + value, + true, + ); + return offset + 4; + } + + writeDoubleBE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( + offset, + value, + ); + return offset + 8; + } + writeDoubleLE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( + offset, + value, + true, + ); + return offset + 8; + } + + writeFloatBE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( + offset, + value, + ); + return offset + 4; + } + writeFloatLE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( + offset, + value, + true, + ); + return offset + 4; + } + + writeInt8(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt8( + offset, + value, + ); + return offset + 1; + } + + writeInt16BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( + offset, + value, + ); + return offset + 2; + } + writeInt16LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( + offset, + value, + true, + ); + return offset + 2; + } + + writeInt32BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( + offset, + value, + ); + return offset + 4; + } + writeInt32LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt32( + offset, + value, + true, + ); + return offset + 4; + } + + writeUInt8(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint8( + offset, + value, + ); + return offset + 1; + } + + writeUInt16BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( + offset, + value, + ); + return offset + 2; + } + writeUInt16LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( + offset, + value, + true, + ); + return offset + 2; + } + + writeUInt32BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( + offset, + value, + ); + return offset + 4; + } + writeUInt32LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( + offset, + value, + true, + ); + return offset + 4; + } +} diff --git a/std/node/_crypto.ts b/std/node/_crypto.ts new file mode 100644 index 0000000000000..86c2b772fa588 --- /dev/null +++ b/std/node/_crypto.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +export { default as randomBytes } from "./_crypto/randomBytes.ts"; +export { pbkdf2, pbkdf2Sync } from "./_crypto/pbkdf2.ts"; diff --git a/std/node/_crypto/pbkdf2.ts b/std/node/_crypto/pbkdf2.ts index 15b5eec3ac867..80aacdaf2059b 100644 --- a/std/node/_crypto/pbkdf2.ts +++ b/std/node/_crypto/pbkdf2.ts @@ -1,5 +1,5 @@ import { createHash } from "../../hash/mod.ts"; -import Buffer from "../buffer.ts"; +import { Buffer } from "../buffer.ts"; import { MAX_ALLOC } from "./constants.ts"; import { HASH_DATA } from "./types.ts"; diff --git a/std/node/_crypto/randomBytes.ts b/std/node/_crypto/randomBytes.ts index 7b82761515488..28c7e454eeee6 100644 --- a/std/node/_crypto/randomBytes.ts +++ b/std/node/_crypto/randomBytes.ts @@ -1,4 +1,4 @@ -import Buffer from "../buffer.ts"; +import { Buffer } from "../buffer.ts"; export const MAX_RANDOM_VALUES = 65536; export const MAX_SIZE = 4294967295; diff --git a/std/node/_crypto/types.ts b/std/node/_crypto/types.ts index 093fccadc1258..e56d8416c0407 100644 --- a/std/node/_crypto/types.ts +++ b/std/node/_crypto/types.ts @@ -1,3 +1,3 @@ -import Buffer from "../buffer.ts"; +import { Buffer } from "../buffer.ts"; export type HASH_DATA = string | ArrayBufferView | Buffer; diff --git a/std/node/_fs.ts b/std/node/_fs.ts new file mode 100644 index 0000000000000..052394e21073d --- /dev/null +++ b/std/node/_fs.ts @@ -0,0 +1,68 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { access, accessSync } from "./_fs/_fs_access.ts"; +import { appendFile, appendFileSync } from "./_fs/_fs_appendFile.ts"; +import { chmod, chmodSync } from "./_fs/_fs_chmod.ts"; +import { chown, chownSync } from "./_fs/_fs_chown.ts"; +import { close, closeSync } from "./_fs/_fs_close.ts"; +import * as constants from "./_fs/_fs_constants.ts"; +import { readFile, readFileSync } from "./_fs/_fs_readFile.ts"; +import { readlink, readlinkSync } from "./_fs/_fs_readlink.ts"; +import { exists, existsSync } from "./_fs/_fs_exists.ts"; +import { mkdir, mkdirSync } from "./_fs/_fs_mkdir.ts"; +import { copyFile, copyFileSync } from "./_fs/_fs_copy.ts"; +import { writeFile, writeFileSync } from "./_fs/_fs_writeFile.ts"; +import { readdir, readdirSync } from "./_fs/_fs_readdir.ts"; +import { realpath, realpathSync } from "./_fs/_fs_realpath.ts"; +import { rename, renameSync } from "./_fs/_fs_rename.ts"; +import { rmdir, rmdirSync } from "./_fs/_fs_rmdir.ts"; +import { unlink, unlinkSync } from "./_fs/_fs_unlink.ts"; +import { watch } from "./_fs/_fs_watch.ts"; +import { open, openSync } from "./_fs/_fs_open.ts"; +import { stat, statSync } from "./_fs/_fs_stat.ts"; +import { lstat, lstatSync } from "./_fs/_fs_lstat.ts"; + +import * as promises from "./_fs/promises/mod.ts"; + +export { + access, + accessSync, + appendFile, + appendFileSync, + chmod, + chmodSync, + chown, + chownSync, + close, + closeSync, + constants, + copyFile, + copyFileSync, + exists, + existsSync, + lstat, + lstatSync, + mkdir, + mkdirSync, + open, + openSync, + promises, + readdir, + readdirSync, + readFile, + readFileSync, + readlink, + readlinkSync, + realpath, + realpathSync, + rename, + renameSync, + rmdir, + rmdirSync, + stat, + statSync, + unlink, + unlinkSync, + watch, + writeFile, + writeFileSync, +}; diff --git a/std/node/_os.ts b/std/node/_os.ts new file mode 100644 index 0000000000000..14bf20abd2ad2 --- /dev/null +++ b/std/node/_os.ts @@ -0,0 +1,224 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +import { notImplemented } from "./_utils.ts"; +import { validateIntegerRange } from "./_utils.ts"; +import { EOL as fsEOL } from "../fs/eol.ts"; +import process from "./process.ts"; + +const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno/issues/3802"; + +interface CPUTimes { + /** The number of milliseconds the CPU has spent in user mode */ + user: number; + + /** The number of milliseconds the CPU has spent in nice mode */ + nice: number; + + /** The number of milliseconds the CPU has spent in sys mode */ + sys: number; + + /** The number of milliseconds the CPU has spent in idle mode */ + idle: number; + + /** The number of milliseconds the CPU has spent in irq mode */ + irq: number; +} + +interface CPUCoreInfo { + model: string; + + /** in MHz */ + speed: number; + + times: CPUTimes; +} + +interface NetworkAddress { + /** The assigned IPv4 or IPv6 address */ + address: string; + + /** The IPv4 or IPv6 network mask */ + netmask: string; + + family: "IPv4" | "IPv6"; + + /** The MAC address of the network interface */ + mac: string; + + /** true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false */ + internal: boolean; + + /** The numeric IPv6 scope ID (only specified when family is IPv6) */ + scopeid?: number; + + /** The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. */ + cidr: string; +} + +interface NetworkInterfaces { + [key: string]: NetworkAddress[]; +} + +export interface UserInfoOptions { + encoding: string; +} + +interface UserInfo { + username: string; + uid: number; + gid: number; + shell: string; + homedir: string; +} + +arch[Symbol.toPrimitive] = (): string => arch(); +endianness[Symbol.toPrimitive] = (): string => endianness(); +freemem[Symbol.toPrimitive] = (): number => freemem(); +homedir[Symbol.toPrimitive] = (): string | null => homedir(); +hostname[Symbol.toPrimitive] = (): string | null => hostname(); +platform[Symbol.toPrimitive] = (): string => platform(); +release[Symbol.toPrimitive] = (): string => release(); +totalmem[Symbol.toPrimitive] = (): number => totalmem(); +type[Symbol.toPrimitive] = (): string => type(); +uptime[Symbol.toPrimitive] = (): number => uptime(); + +/** Returns the operating system CPU architecture for which the Deno binary was compiled */ +export function arch(): string { + return Deno.build.arch; +} + +/** Not yet implemented */ +export function cpus(): CPUCoreInfo[] { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** + * Returns a string identifying the endianness of the CPU for which the Deno + * binary was compiled. Possible values are 'BE' for big endian and 'LE' for + * little endian. + **/ +export function endianness(): "BE" | "LE" { + // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness + const buffer = new ArrayBuffer(2); + new DataView(buffer).setInt16(0, 256, true /* littleEndian */); + // Int16Array uses the platform's endianness. + return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; +} + +/** Return free memory amount */ +export function freemem(): number { + return Deno.systemMemoryInfo().free; +} + +/** Not yet implemented */ +export function getPriority(pid = 0): number { + validateIntegerRange(pid, "pid"); + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the string path of the current user's home directory. */ +export function homedir(): string | null { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the host name of the operating system as a string. */ +export function hostname(): string { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns an array containing the 1, 5, and 15 minute load averages */ +export function loadavg(): number[] { + if (Deno.build.os === "windows") { + return [0, 0, 0]; + } + return Deno.loadavg(); +} + +/** Not yet implemented */ +export function networkInterfaces(): NetworkInterfaces { + notImplemented(SEE_GITHUB_ISSUE); +} +/** Returns the a string identifying the operating system platform. The value is set at compile time. Possible values are 'darwin', 'linux', and 'win32'. */ +export function platform(): string { + return process.platform; +} + +/** Returns the operating system as a string */ +export function release(): string { + return Deno.osRelease(); +} + +/** Not yet implemented */ +export function setPriority(pid: number, priority?: number): void { + /* The node API has the 'pid' as the first parameter and as optional. + This makes for a problematic implementation in Typescript. */ + if (priority === undefined) { + priority = pid; + pid = 0; + } + validateIntegerRange(pid, "pid"); + validateIntegerRange(priority, "priority", -20, 19); + + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the operating system's default directory for temporary files as a string. */ +export function tmpdir(): string | null { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Return total physical memory amount */ +export function totalmem(): number { + return Deno.systemMemoryInfo().total; +} + +/** Not yet implemented */ +export function type(): string { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Not yet implemented */ +export function uptime(): number { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Not yet implemented */ +export function userInfo( + options: UserInfoOptions = { encoding: "utf-8" }, +): UserInfo { + notImplemented(SEE_GITHUB_ISSUE); +} + +export const constants = { + // UV_UDP_REUSEADDR: 4, //see https://nodejs.org/docs/latest-v12.x/api/os.html#os_libuv_constants + dlopen: { + // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_dlopen_constants + }, + errno: { + // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_error_constants + }, + signals: Deno.Signal, + priority: { + // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_priority_constants + }, +}; + +export const EOL = Deno.build.os == "windows" ? fsEOL.CRLF : fsEOL.LF; diff --git a/std/node/_querystring.ts b/std/node/_querystring.ts new file mode 100644 index 0000000000000..a49f55f5478e1 --- /dev/null +++ b/std/node/_querystring.ts @@ -0,0 +1,156 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +interface ParseOptions { + /** The function to use when decoding percent-encoded characters in the query string. */ + decodeURIComponent?: (string: string) => string; + /** Specifies the maximum number of keys to parse. */ + maxKeys?: number; +} + +export const hexTable = new Array(256); +for (let i = 0; i < 256; ++i) { + hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); +} + +/** + * Parses a URL query string into a collection of key and value pairs. + * @param str The URL query string to parse + * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. + * @param eq The substring used to delimit keys and values in the query string. Default: '='. + * @param options The parse options + */ +export function parse( + str: string, + sep = "&", + eq = "=", + { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {}, +): { [key: string]: string[] | string } { + const entries = str + .split(sep) + .map((entry) => entry.split(eq).map(decodeURIComponent)); + const final: { [key: string]: string[] | string } = {}; + + let i = 0; + while (true) { + if ((Object.keys(final).length === maxKeys && !!maxKeys) || !entries[i]) { + break; + } + + const [key, val] = entries[i]; + if (final[key]) { + if (Array.isArray(final[key])) { + (final[key] as string[]).push(val); + } else { + final[key] = [final[key] as string, val]; + } + } else { + final[key] = val; + } + + i++; + } + + return final; +} + +interface StringifyOptions { + /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ + encodeURIComponent?: (string: string) => string; +} + +export function encodeStr( + str: string, + noEscapeTable: number[], + hexTable: string[], +): string { + const len = str.length; + if (len === 0) return ""; + + let out = ""; + let lastPos = 0; + + for (let i = 0; i < len; i++) { + let c = str.charCodeAt(i); + // ASCII + if (c < 0x80) { + if (noEscapeTable[c] === 1) continue; + if (lastPos < i) out += str.slice(lastPos, i); + lastPos = i + 1; + out += hexTable[c]; + continue; + } + + if (lastPos < i) out += str.slice(lastPos, i); + + // Multi-byte characters ... + if (c < 0x800) { + lastPos = i + 1; + out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)]; + continue; + } + if (c < 0xd800 || c >= 0xe000) { + lastPos = i + 1; + out += hexTable[0xe0 | (c >> 12)] + + hexTable[0x80 | ((c >> 6) & 0x3f)] + + hexTable[0x80 | (c & 0x3f)]; + continue; + } + // Surrogate pair + ++i; + + // This branch should never happen because all URLSearchParams entries + // should already be converted to USVString. But, included for + // completion's sake anyway. + if (i >= len) throw new Deno.errors.InvalidData("invalid URI"); + + const c2 = str.charCodeAt(i) & 0x3ff; + + lastPos = i + 1; + c = 0x10000 + (((c & 0x3ff) << 10) | c2); + out += hexTable[0xf0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3f)] + + hexTable[0x80 | ((c >> 6) & 0x3f)] + + hexTable[0x80 | (c & 0x3f)]; + } + if (lastPos === 0) return str; + if (lastPos < len) return out + str.slice(lastPos); + return out; +} + +/** + * Produces a URL query string from a given obj by iterating through the object's "own properties". + * @param obj The object to serialize into a URL query string. + * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. + * @param eq The substring used to delimit keys and values in the query string. Default: '='. + * @param options The stringify options + */ +export function stringify( + // deno-lint-ignore no-explicit-any + obj: Record, + sep = "&", + eq = "=", + { encodeURIComponent = escape }: StringifyOptions = {}, +): string { + const final = []; + + for (const entry of Object.entries(obj)) { + if (Array.isArray(entry[1])) { + for (const val of entry[1]) { + final.push(encodeURIComponent(entry[0]) + eq + encodeURIComponent(val)); + } + } else if (typeof entry[1] !== "object" && entry[1] !== undefined) { + final.push(entry.map(encodeURIComponent).join(eq)); + } else { + final.push(encodeURIComponent(entry[0]) + eq); + } + } + + return final.join(sep); +} + +/** Alias of querystring.parse() */ +export const decode = parse; +/** Alias of querystring.stringify() */ +export const encode = stringify; +export const unescape = decodeURIComponent; +export const escape = encodeURIComponent; diff --git a/std/node/_string_decoder.ts b/std/node/_string_decoder.ts new file mode 100644 index 0000000000000..ce7c19538ed52 --- /dev/null +++ b/std/node/_string_decoder.ts @@ -0,0 +1,299 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +import { Buffer } from "./buffer.ts"; +import { normalizeEncoding as castEncoding, notImplemented } from "./_utils.ts"; + +enum NotImplemented { + "ascii", + "latin1", + "utf16le", +} + +function normalizeEncoding(enc?: string): string { + const encoding = castEncoding(enc ?? null); + if (encoding && encoding in NotImplemented) notImplemented(encoding); + if (!encoding && typeof enc === "string" && enc.toLowerCase() !== "raw") { + throw new Error(`Unknown encoding: ${enc}`); + } + return String(encoding); +} +/* + * Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a + * continuation byte. If an invalid byte is detected, -2 is returned. + * */ +function utf8CheckByte(byte: number): number { + if (byte <= 0x7f) return 0; + else if (byte >> 5 === 0x06) return 2; + else if (byte >> 4 === 0x0e) return 3; + else if (byte >> 3 === 0x1e) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} + +/* + * Checks at most 3 bytes at the end of a Buffer in order to detect an + * incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) + * needed to complete the UTF-8 character (if applicable) are returned. + * */ +function utf8CheckIncomplete( + self: StringDecoderBase, + buf: Buffer, + i: number, +): number { + let j = buf.length - 1; + if (j < i) return 0; + let nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0; + else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +/* + * Validates as many continuation bytes for a multi-byte UTF-8 character as + * needed or are available. If we see a non-continuation byte where we expect + * one, we "replace" the validated continuation bytes we've seen so far with + * a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding + * behavior. The continuation byte check is included three times in the case + * where all of the continuation bytes for a character exist in the same buffer. + * It is also done this way as a slight performance increase instead of using a + * loop. + * */ +function utf8CheckExtraBytes( + self: StringDecoderBase, + buf: Buffer, +): string | undefined { + if ((buf[0] & 0xc0) !== 0x80) { + self.lastNeed = 0; + return "\ufffd"; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xc0) !== 0x80) { + self.lastNeed = 1; + return "\ufffd"; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xc0) !== 0x80) { + self.lastNeed = 2; + return "\ufffd"; + } + } + } +} + +/* + * Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. + * */ +function utf8FillLastComplete( + this: StringDecoderBase, + buf: Buffer, +): string | undefined { + const p = this.lastTotal - this.lastNeed; + const r = utf8CheckExtraBytes(this, buf); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +/* + * Attempts to complete a partial non-UTF-8 character using bytes from a Buffer + * */ +function utf8FillLastIncomplete( + this: StringDecoderBase, + buf: Buffer, +): string | undefined { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +} + +/* + * Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a + * partial character, the character's bytes are buffered until the required + * number of bytes are available. + * */ +function utf8Text(this: StringDecoderBase, buf: Buffer, i: number): string { + const total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString("utf8", i); + this.lastTotal = total; + const end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString("utf8", i, end); +} + +/* + * For UTF-8, a replacement character is added when ending on a partial + * character. + * */ +function utf8End(this: Utf8Decoder, buf?: Buffer): string { + const r = buf && buf.length ? this.write(buf) : ""; + if (this.lastNeed) return r + "\ufffd"; + return r; +} + +function utf8Write(this: Utf8Decoder | Base64Decoder, buf: Buffer): string { + if (buf.length === 0) return ""; + let r; + let i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ""; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ""; +} + +function base64Text(this: StringDecoderBase, buf: Buffer, i: number): string { + const n = (buf.length - i) % 3; + if (n === 0) return buf.toString("base64", i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString("base64", i, buf.length - n); +} + +function base64End(this: Base64Decoder, buf?: Buffer): string { + const r = buf && buf.length ? this.write(buf) : ""; + if (this.lastNeed) { + return r + this.lastChar.toString("base64", 0, 3 - this.lastNeed); + } + return r; +} + +function simpleWrite(this: StringDecoderBase, buf: Buffer): string { + return buf.toString(this.encoding); +} + +function simpleEnd(this: GenericDecoder, buf?: Buffer): string { + return buf && buf.length ? this.write(buf) : ""; +} + +class StringDecoderBase { + public lastChar: Buffer; + public lastNeed = 0; + public lastTotal = 0; + constructor(public encoding: string, nb: number) { + this.lastChar = Buffer.allocUnsafe(nb); + } +} + +class Base64Decoder extends StringDecoderBase { + public end = base64End; + public fillLast = utf8FillLastIncomplete; + public text = base64Text; + public write = utf8Write; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 3); + } +} + +class GenericDecoder extends StringDecoderBase { + public end = simpleEnd; + public fillLast = undefined; + public text = utf8Text; + public write = simpleWrite; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 4); + } +} + +class Utf8Decoder extends StringDecoderBase { + public end = utf8End; + public fillLast = utf8FillLastComplete; + public text = utf8Text; + public write = utf8Write; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 4); + } +} + +/* + * StringDecoder provides an interface for efficiently splitting a series of + * buffers into a series of JS strings without breaking apart multi-byte + * characters. + * */ +export class StringDecoder { + public encoding: string; + public end: (buf?: Buffer) => string; + public fillLast: ((buf: Buffer) => string | undefined) | undefined; + public lastChar: Buffer; + public lastNeed: number; + public lastTotal: number; + public text: (buf: Buffer, n: number) => string; + public write: (buf: Buffer) => string; + + constructor(encoding?: string) { + let decoder; + switch (encoding) { + case "utf8": + decoder = new Utf8Decoder(encoding); + break; + case "base64": + decoder = new Base64Decoder(encoding); + break; + default: + decoder = new GenericDecoder(encoding); + } + this.encoding = decoder.encoding; + this.end = decoder.end; + this.fillLast = decoder.fillLast; + this.lastChar = decoder.lastChar; + this.lastNeed = decoder.lastNeed; + this.lastTotal = decoder.lastTotal; + this.text = decoder.text; + this.write = decoder.write; + } +} diff --git a/std/node/_timers.ts b/std/node/_timers.ts new file mode 100644 index 0000000000000..872e1f9ae069c --- /dev/null +++ b/std/node/_timers.ts @@ -0,0 +1,14 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +// TODO: implement the 'NodeJS.Timeout' and 'NodeJS.Immediate' versions of the timers. +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1163ead296d84e7a3c80d71e7c81ecbd1a130e9a/types/node/v12/globals.d.ts#L1120-L1131 +export const setTimeout = window.setTimeout; +export const clearTimeout = window.clearTimeout; +export const setInterval = window.setInterval; +export const clearInterval = window.clearInterval; +export const setImmediate = ( + // deno-lint-ignore no-explicit-any + cb: (...args: any[]) => void, + // deno-lint-ignore no-explicit-any + ...args: any[] +): number => window.setTimeout(cb, 0, ...args); +export const clearImmediate = window.clearTimeout; diff --git a/std/node/_url.ts b/std/node/_url.ts new file mode 100644 index 0000000000000..82daa25e93d7b --- /dev/null +++ b/std/node/_url.ts @@ -0,0 +1,140 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +import { + CHAR_BACKWARD_SLASH, + CHAR_FORWARD_SLASH, + CHAR_LOWERCASE_A, + CHAR_LOWERCASE_Z, +} from "../path/_constants.ts"; +import * as path from "./path.ts"; + +const isWindows = Deno.build.os === "windows"; + +const forwardSlashRegEx = /\//g; +const percentRegEx = /%/g; +const backslashRegEx = /\\/g; +const newlineRegEx = /\n/g; +const carriageReturnRegEx = /\r/g; +const tabRegEx = /\t/g; + +const _url = URL; +export { _url as URL }; + +/** + * Get fully resolved platform-specific file path from the given URL string/ object + * @param path The file URL string or URL object to convert to a path + */ +export function fileURLToPath(path: string | URL): string { + if (typeof path === "string") path = new URL(path); + else if (!(path instanceof URL)) { + throw new Deno.errors.InvalidData( + "invalid argument path , must be a string or URL", + ); + } + if (path.protocol !== "file:") { + throw new Deno.errors.InvalidData("invalid url scheme"); + } + return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path); +} + +function getPathFromURLWin(url: URL): string { + const hostname = url.hostname; + let pathname = url.pathname; + for (let n = 0; n < pathname.length; n++) { + if (pathname[n] === "%") { + const third = pathname.codePointAt(n + 2) || 0x20; + if ( + (pathname[n + 1] === "2" && third === 102) || // 2f 2F / + (pathname[n + 1] === "5" && third === 99) + ) { + // 5c 5C \ + throw new Deno.errors.InvalidData( + "must not include encoded \\ or / characters", + ); + } + } + } + + pathname = pathname.replace(forwardSlashRegEx, "\\"); + pathname = decodeURIComponent(pathname); + if (hostname !== "") { + //TODO add support for punycode encodings + return `\\\\${hostname}${pathname}`; + } else { + // Otherwise, it's a local path that requires a drive letter + const letter = pathname.codePointAt(1)! | 0x20; + const sep = pathname[2]; + if ( + letter < CHAR_LOWERCASE_A || + letter > CHAR_LOWERCASE_Z || // a..z A..Z + sep !== ":" + ) { + throw new Deno.errors.InvalidData("file url path must be absolute"); + } + return pathname.slice(1); + } +} + +function getPathFromURLPosix(url: URL): string { + if (url.hostname !== "") { + throw new Deno.errors.InvalidData("invalid file url hostname"); + } + const pathname = url.pathname; + for (let n = 0; n < pathname.length; n++) { + if (pathname[n] === "%") { + const third = pathname.codePointAt(n + 2) || 0x20; + if (pathname[n + 1] === "2" && third === 102) { + throw new Deno.errors.InvalidData( + "must not include encoded / characters", + ); + } + } + } + return decodeURIComponent(pathname); +} + +/** Get fully resolved platform-specific File URL from the given file path */ +export function pathToFileURL(filepath: string): URL { + let resolved = path.resolve(filepath); + // path.resolve strips trailing slashes so we must add them back + const filePathLast = filepath.charCodeAt(filepath.length - 1); + if ( + (filePathLast === CHAR_FORWARD_SLASH || + (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && + resolved[resolved.length - 1] !== path.sep + ) { + resolved += "/"; + } + const outURL = new URL("file://"); + if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25"); + // In posix, "/" is a valid character in paths + if (!isWindows && resolved.includes("\\")) { + resolved = resolved.replace(backslashRegEx, "%5C"); + } + if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A"); + if (resolved.includes("\r")) { + resolved = resolved.replace(carriageReturnRegEx, "%0D"); + } + if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09"); + outURL.pathname = resolved; + return outURL; +} diff --git a/std/node/_util.ts b/std/node/_util.ts new file mode 100644 index 0000000000000..9cf9966701abe --- /dev/null +++ b/std/node/_util.ts @@ -0,0 +1,134 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +export { promisify } from "./_util/_util_promisify.ts"; +export { callbackify } from "./_util/_util_callbackify.ts"; +import { codes, errorMap } from "./_errors.ts"; +import * as types from "./_util/_util_types.ts"; +export { types }; + +const NumberIsSafeInteger = Number.isSafeInteger; +const { + ERR_OUT_OF_RANGE, + ERR_INVALID_ARG_TYPE, +} = codes; + +const DEFAULT_INSPECT_OPTIONS = { + showHidden: false, + depth: 2, + colors: false, + customInspect: true, + showProxy: false, + maxArrayLength: 100, + maxStringLength: Infinity, + breakLength: 80, + compact: 3, + sorted: false, + getters: false, +}; + +inspect.defaultOptions = DEFAULT_INSPECT_OPTIONS; +inspect.custom = Deno.customInspect; + +// TODO(schwarzkopfb): make it in-line with Node's implementation +// Ref: https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_inspect_object_options +// deno-lint-ignore no-explicit-any +export function inspect(object: unknown, ...opts: any): string { + opts = { ...DEFAULT_INSPECT_OPTIONS, ...opts }; + return Deno.inspect(object, { + depth: opts.depth, + iterableLimit: opts.maxArrayLength, + compact: !!opts.compact, + sorted: !!opts.sorted, + showProxy: !!opts.showProxy, + }); +} + +/** @deprecated - use `Array.isArray()` instead. */ +export function isArray(value: unknown): boolean { + return Array.isArray(value); +} + +/** @deprecated - use `typeof value === "boolean" || value instanceof Boolean` instead. */ +export function isBoolean(value: unknown): boolean { + return typeof value === "boolean" || value instanceof Boolean; +} + +/** @deprecated - use `value === null` instead. */ +export function isNull(value: unknown): boolean { + return value === null; +} + +/** @deprecated - use `value === null || value === undefined` instead. */ +export function isNullOrUndefined(value: unknown): boolean { + return value === null || value === undefined; +} + +/** @deprecated - use `typeof value === "number" || value instanceof Number` instead. */ +export function isNumber(value: unknown): boolean { + return typeof value === "number" || value instanceof Number; +} + +/** @deprecated - use `typeof value === "string" || value instanceof String` instead. */ +export function isString(value: unknown): boolean { + return typeof value === "string" || value instanceof String; +} + +/** @deprecated - use `typeof value === "symbol"` instead. */ +export function isSymbol(value: unknown): boolean { + return typeof value === "symbol"; +} + +/** @deprecated - use `value === undefined` instead. */ +export function isUndefined(value: unknown): boolean { + return value === undefined; +} + +/** @deprecated - use `value !== null && typeof value === "object"` instead. */ +export function isObject(value: unknown): boolean { + return value !== null && typeof value === "object"; +} + +/** @deprecated - use `e instanceof Error` instead. */ +export function isError(e: unknown): boolean { + return e instanceof Error; +} + +/** @deprecated - use `typeof value === "function"` instead. */ +export function isFunction(value: unknown): boolean { + return typeof value === "function"; +} + +/** @deprecated - use `value instanceof RegExp` instead. */ +export function isRegExp(value: unknown): boolean { + return value instanceof RegExp; +} + +/** @deprecated - use `value === null || (typeof value !== "object" && typeof value !== "function")` instead. */ +export function isPrimitive(value: unknown): boolean { + return ( + value === null || (typeof value !== "object" && typeof value !== "function") + ); +} + +/** + * Returns a system error name from an error code number. + * @param code error code number + */ +export function getSystemErrorName(code: number): string | undefined { + if (typeof code !== "number") { + throw new ERR_INVALID_ARG_TYPE("err", "number", code); + } + if (code >= 0 || !NumberIsSafeInteger(code)) { + throw new ERR_OUT_OF_RANGE("err", "a negative integer", code); + } + return errorMap.get(code)?.[0]; +} + +import { _TextDecoder, _TextEncoder } from "./_utils.ts"; + +/** The global TextDecoder */ +export type TextDecoder = import("./_utils.ts")._TextDecoder; +export const TextDecoder = _TextDecoder; + +/** The global TextEncoder */ +export type TextEncoder = import("./_utils.ts")._TextEncoder; +export const TextEncoder = _TextEncoder; diff --git a/std/node/assert.ts b/std/node/assert.ts index ad353912e5986..ed402dd7fb512 100644 --- a/std/node/assert.ts +++ b/std/node/assert.ts @@ -1,25 +1,42 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +export { AssertionError } from "./assertion_error.ts"; import { - assertEquals, - assertMatch, - assertNotEquals, - assertNotStrictEquals, - assertStrictEquals, - assertThrows, + assertEquals as deepStrictEqual, + AssertionError, + assertMatch as match, + assertNotEquals as notDeepStrictEqual, + assertNotStrictEquals as notStrictEqual, + assertStrictEquals as strictEqual, + assertThrows as throws, + fail, } from "../testing/asserts.ts"; -export { AssertionError } from "./assertion_error.ts"; +function assert(expr: unknown, msg = ""): asserts expr { + if (!expr) { + throw new AssertionError(msg); + } +} +const ok = assert; +export default assert; -export { - assert, - assert as default, - assert as ok, +Object.assign(assert, { + deepStrictEqual, fail, -} from "../testing/asserts.ts"; + match, + notDeepStrictEqual, + notStrictEqual, + ok, + strictEqual, + throws, +}); -export const deepStrictEqual = assertEquals; -export const notDeepStrictEqual = assertNotEquals; -export const strictEqual = assertStrictEquals; -export const notStrictEqual = assertNotStrictEquals; -export const match = assertMatch; -export const throws = assertThrows; +export { + deepStrictEqual, + fail, + match, + notDeepStrictEqual, + notStrictEqual, + ok, + strictEqual, + throws, +}; diff --git a/std/node/assert_test.ts b/std/node/assert_test.ts index 01a722ed398a7..f90587c940e8c 100644 --- a/std/node/assert_test.ts +++ b/std/node/assert_test.ts @@ -13,7 +13,6 @@ import { import AssertionError from "./assertion_error.ts"; import assert, { - assert as assert_, AssertionError as AssertionError_, deepStrictEqual, fail, @@ -26,13 +25,7 @@ import assert, { } from "./assert.ts"; Deno.test("API should be exposed", () => { - assertStrictEquals( - assert_, - assert, - "`assert()` should be the default export", - ); - assertStrictEquals(assert_, denoAssert, "`assert()` should be exposed"); - assertStrictEquals(assert_, ok, "`assert()` should be an alias of `ok()`"); + assertStrictEquals(assert, ok, "`assert()` should be an alias of `ok()`"); assertStrictEquals( assertEquals, deepStrictEqual, diff --git a/std/node/buffer.ts b/std/node/buffer.ts index 16cf3abc0a031..664c1ed25aef7 100644 --- a/std/node/buffer.ts +++ b/std/node/buffer.ts @@ -1,601 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import * as hex from "../encoding/hex.ts"; -import * as base64 from "../encoding/base64.ts"; -import { normalizeEncoding, notImplemented } from "./_utils.ts"; - -const notImplementedEncodings = [ - "ascii", - "binary", - "latin1", - "ucs2", - "utf16le", -]; - -function checkEncoding(encoding = "utf8", strict = true): string { - if (typeof encoding !== "string" || (strict && encoding === "")) { - if (!strict) return "utf8"; - throw new TypeError(`Unkown encoding: ${encoding}`); - } - - const normalized = normalizeEncoding(encoding); - - if (normalized === undefined) { - throw new TypeError(`Unkown encoding: ${encoding}`); - } - - if (notImplementedEncodings.includes(encoding)) { - notImplemented(`"${encoding}" encoding`); - } - - return normalized; -} - -interface EncodingOp { - byteLength(string: string): number; -} - -// https://github.com/nodejs/node/blob/56dbe466fdbc598baea3bfce289bf52b97b8b8f7/lib/buffer.js#L598 -const encodingOps: { [key: string]: EncodingOp } = { - utf8: { - byteLength: (string: string): number => - new TextEncoder().encode(string).byteLength, - }, - ucs2: { - byteLength: (string: string): number => string.length * 2, - }, - utf16le: { - byteLength: (string: string): number => string.length * 2, - }, - latin1: { - byteLength: (string: string): number => string.length, - }, - ascii: { - byteLength: (string: string): number => string.length, - }, - base64: { - byteLength: (string: string): number => - base64ByteLength(string, string.length), - }, - hex: { - byteLength: (string: string): number => string.length >>> 1, - }, -}; - -function base64ByteLength(str: string, bytes: number): number { - // Handle padding - if (str.charCodeAt(bytes - 1) === 0x3d) bytes--; - if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3d) bytes--; - - // Base64 ratio: 3/4 - return (bytes * 3) >>> 2; -} - -/** - * See also https://nodejs.org/api/buffer.html - */ -export default class Buffer extends Uint8Array { - /** - * Allocates a new Buffer of size bytes. - */ - static alloc( - size: number, - fill?: number | string | Uint8Array | Buffer, - encoding = "utf8", - ): Buffer { - if (typeof size !== "number") { - throw new TypeError( - `The "size" argument must be of type number. Received type ${typeof size}`, - ); - } - - const buf = new Buffer(size); - if (size === 0) return buf; - - let bufFill; - if (typeof fill === "string") { - encoding = checkEncoding(encoding); - if ( - typeof fill === "string" && - fill.length === 1 && - encoding === "utf8" - ) { - buf.fill(fill.charCodeAt(0)); - } else bufFill = Buffer.from(fill, encoding); - } else if (typeof fill === "number") { - buf.fill(fill); - } else if (fill instanceof Uint8Array) { - if (fill.length === 0) { - throw new TypeError( - `The argument "value" is invalid. Received ${fill.constructor.name} []`, - ); - } - - bufFill = fill; - } - - if (bufFill) { - if (bufFill.length > buf.length) { - bufFill = bufFill.subarray(0, buf.length); - } - - let offset = 0; - while (offset < size) { - buf.set(bufFill, offset); - offset += bufFill.length; - if (offset + bufFill.length >= size) break; - } - if (offset !== size) { - buf.set(bufFill.subarray(0, size - offset), offset); - } - } - - return buf; - } - - static allocUnsafe(size: number): Buffer { - return new Buffer(size); - } - - /** - * Returns the byte length of a string when encoded. This is not the same as - * String.prototype.length, which does not account for the encoding that is - * used to convert the string into bytes. - */ - static byteLength( - string: string | Buffer | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - encoding = "utf8", - ): number { - if (typeof string != "string") return string.byteLength; - - encoding = normalizeEncoding(encoding) || "utf8"; - return encodingOps[encoding].byteLength(string); - } - - /** - * Returns a new Buffer which is the result of concatenating all the Buffer - * instances in the list together. - */ - static concat(list: Buffer[] | Uint8Array[], totalLength?: number): Buffer { - if (totalLength == undefined) { - totalLength = 0; - for (const buf of list) { - totalLength += buf.length; - } - } - - const buffer = Buffer.allocUnsafe(totalLength); - let pos = 0; - for (const item of list) { - let buf: Buffer; - if (!(item instanceof Buffer)) { - buf = Buffer.from(item); - } else { - buf = item; - } - buf.copy(buffer, pos); - pos += buf.length; - } - - return buffer; - } - - /** - * Allocates a new Buffer using an array of bytes in the range 0 – 255. Array - * entries outside that range will be truncated to fit into it. - */ - static from(array: number[]): Buffer; - /** - * This creates a view of the ArrayBuffer without copying the underlying - * memory. For example, when passed a reference to the .buffer property of a - * TypedArray instance, the newly created Buffer will share the same allocated - * memory as the TypedArray. - */ - static from( - arrayBuffer: ArrayBuffer | SharedArrayBuffer, - byteOffset?: number, - length?: number, - ): Buffer; - /** - * Copies the passed buffer data onto a new Buffer instance. - */ - static from(buffer: Buffer | Uint8Array): Buffer; - /** - * Creates a new Buffer containing string. - */ - static from(string: string, encoding?: string): Buffer; - static from( - // deno-lint-ignore no-explicit-any - value: any, - offsetOrEncoding?: number | string, - length?: number, - ): Buffer { - const offset = typeof offsetOrEncoding === "string" - ? undefined - : offsetOrEncoding; - let encoding = typeof offsetOrEncoding === "string" - ? offsetOrEncoding - : undefined; - - if (typeof value == "string") { - encoding = checkEncoding(encoding, false); - if (encoding === "hex") return new Buffer(hex.decodeString(value).buffer); - if (encoding === "base64") return new Buffer(base64.decode(value).buffer); - return new Buffer(new TextEncoder().encode(value).buffer); - } - - // workaround for https://github.com/microsoft/TypeScript/issues/38446 - return new Buffer(value, offset!, length); - } - - /** - * Returns true if obj is a Buffer, false otherwise. - */ - static isBuffer(obj: unknown): obj is Buffer { - return obj instanceof Buffer; - } - - // deno-lint-ignore no-explicit-any - static isEncoding(encoding: any): boolean { - return ( - typeof encoding === "string" && - encoding.length !== 0 && - normalizeEncoding(encoding) !== undefined - ); - } - - /** - * Copies data from a region of buf to a region in target, even if the target - * memory region overlaps with buf. - */ - copy( - targetBuffer: Buffer | Uint8Array, - targetStart = 0, - sourceStart = 0, - sourceEnd = this.length, - ): number { - const sourceBuffer = this - .subarray(sourceStart, sourceEnd) - .subarray(0, Math.max(0, targetBuffer.length - targetStart)); - - if (sourceBuffer.length === 0) return 0; - - targetBuffer.set(sourceBuffer, targetStart); - return sourceBuffer.length; - } - - /* - * Returns true if both buf and otherBuffer have exactly the same bytes, false otherwise. - */ - equals(otherBuffer: Uint8Array | Buffer): boolean { - if (!(otherBuffer instanceof Uint8Array)) { - throw new TypeError( - `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received type ${typeof otherBuffer}`, - ); - } - - if (this === otherBuffer) return true; - if (this.byteLength !== otherBuffer.byteLength) return false; - - for (let i = 0; i < this.length; i++) { - if (this[i] !== otherBuffer[i]) return false; - } - - return true; - } - - readBigInt64BE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigInt64(offset); - } - readBigInt64LE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigInt64(offset, true); - } - - readBigUInt64BE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigUint64(offset); - } - readBigUInt64LE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigUint64(offset, true); - } - - readDoubleBE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat64(offset); - } - readDoubleLE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat64(offset, true); - } - - readFloatBE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat32(offset); - } - readFloatLE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat32(offset, true); - } - - readInt8(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt8( - offset, - ); - } - - readInt16BE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( - offset, - ); - } - readInt16LE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( - offset, - true, - ); - } - - readInt32BE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( - offset, - ); - } - readInt32LE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( - offset, - true, - ); - } - - readUInt8(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getUint8( - offset, - ); - } - - readUInt16BE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint16(offset); - } - readUInt16LE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint16(offset, true); - } - - readUInt32BE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint32(offset); - } - readUInt32LE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint32(offset, true); - } - - /** - * Returns a new Buffer that references the same memory as the original, but - * offset and cropped by the start and end indices. - */ - slice(begin = 0, end = this.length): Buffer { - // workaround for https://github.com/microsoft/TypeScript/issues/38665 - return this.subarray(begin, end) as Buffer; - } - - /** - * Returns a JSON representation of buf. JSON.stringify() implicitly calls - * this function when stringifying a Buffer instance. - */ - toJSON(): Record { - return { type: "Buffer", data: Array.from(this) }; - } - - /** - * Decodes buf to a string according to the specified character encoding in - * encoding. start and end may be passed to decode only a subset of buf. - */ - toString(encoding = "utf8", start = 0, end = this.length): string { - encoding = checkEncoding(encoding); - - const b = this.subarray(start, end); - if (encoding === "hex") return hex.encodeToString(b); - if (encoding === "base64") return base64.encode(b.buffer); - - return new TextDecoder(encoding).decode(b); - } - - /** - * Writes string to buf at offset according to the character encoding in - * encoding. The length parameter is the number of bytes to write. If buf did - * not contain enough space to fit the entire string, only part of string will - * be written. However, partially encoded characters will not be written. - */ - write(string: string, offset = 0, length = this.length): number { - return new TextEncoder().encodeInto( - string, - this.subarray(offset, offset + length), - ).written; - } - - writeBigInt64BE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( - offset, - value, - ); - return offset + 4; - } - writeBigInt64LE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( - offset, - value, - true, - ); - return offset + 4; - } - - writeBigUInt64BE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( - offset, - value, - ); - return offset + 4; - } - writeBigUInt64LE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( - offset, - value, - true, - ); - return offset + 4; - } - - writeDoubleBE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( - offset, - value, - ); - return offset + 8; - } - writeDoubleLE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( - offset, - value, - true, - ); - return offset + 8; - } - - writeFloatBE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( - offset, - value, - ); - return offset + 4; - } - writeFloatLE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( - offset, - value, - true, - ); - return offset + 4; - } - - writeInt8(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt8( - offset, - value, - ); - return offset + 1; - } - - writeInt16BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( - offset, - value, - ); - return offset + 2; - } - writeInt16LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( - offset, - value, - true, - ); - return offset + 2; - } - - writeInt32BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - ); - return offset + 4; - } - writeInt32LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt32( - offset, - value, - true, - ); - return offset + 4; - } - - writeUInt8(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint8( - offset, - value, - ); - return offset + 1; - } - - writeUInt16BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( - offset, - value, - ); - return offset + 2; - } - writeUInt16LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( - offset, - value, - true, - ); - return offset + 2; - } - - writeUInt32BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - ); - return offset + 4; - } - writeUInt32LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - true, - ); - return offset + 4; - } -} - -export { Buffer }; +export * from "./_buffer.ts"; +import * as m from "./_buffer.ts"; +export default m; diff --git a/std/node/buffer_test.ts b/std/node/buffer_test.ts index fbd8d3ccaacd0..a360c91ffc1cd 100644 --- a/std/node/buffer_test.ts +++ b/std/node/buffer_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; -import Buffer from "./buffer.ts"; +import { Buffer } from "./buffer.ts"; Deno.test({ name: "Buffer global scope", diff --git a/std/node/crypto.ts b/std/node/crypto.ts index 49bf897d7656b..d27cab67de4cb 100644 --- a/std/node/crypto.ts +++ b/std/node/crypto.ts @@ -1,4 +1,4 @@ -import randomBytes from "./_crypto/randomBytes.ts"; -import { pbkdf2, pbkdf2Sync } from "./_crypto/pbkdf2.ts"; - -export { pbkdf2, pbkdf2Sync, randomBytes }; +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +export * from "./_crypto.ts"; +import * as m from "./_crypto.ts"; +export default m; diff --git a/std/node/events.ts b/std/node/events.ts index 568393f7f64a2..16790907e39a9 100644 --- a/std/node/events.ts +++ b/std/node/events.ts @@ -31,12 +31,36 @@ export interface WrappedFunction extends Function { listener: GenericFunction; } +// deno-lint-ignore no-explicit-any +function createIterResult(value: any, done: boolean): IteratorResult { + return { value, done }; +} + +interface AsyncIterable { + // deno-lint-ignore no-explicit-any + next(): Promise>; + // deno-lint-ignore no-explicit-any + return(): Promise>; + throw(err: Error): void; + // deno-lint-ignore no-explicit-any + [Symbol.asyncIterator](): any; +} + +export let defaultMaxListeners = 10; + /** * See also https://nodejs.org/api/events.html */ export default class EventEmitter { - public static defaultMaxListeners = 10; + public static captureRejectionSymbol = Symbol.for("nodejs.rejection"); public static errorMonitor = Symbol("events.errorMonitor"); + public static get defaultMaxListeners() { + return defaultMaxListeners; + } + public static set defaultMaxListeners(value: number) { + defaultMaxListeners = value; + } + private maxListeners: number | undefined; private _events: Map< string | symbol, @@ -367,180 +391,168 @@ export default class EventEmitter { this.maxListeners = n; return this; } -} -export { EventEmitter }; - -/** - * Creates a Promise that is fulfilled when the EventEmitter emits the given - * event or that is rejected when the EventEmitter emits 'error'. The Promise - * will resolve with an array of all the arguments emitted to the given event. - */ -export function once( - emitter: EventEmitter | EventTarget, - name: string, - // deno-lint-ignore no-explicit-any -): Promise { - return new Promise((resolve, reject) => { - if (emitter instanceof EventTarget) { - // EventTarget does not have `error` event semantics like Node - // EventEmitters, we do not listen to `error` events here. - emitter.addEventListener( - name, - (...args) => { - resolve(args); - }, - { once: true, passive: false, capture: false }, - ); - return; - } else if (emitter instanceof EventEmitter) { - // deno-lint-ignore no-explicit-any - const eventListener = (...args: any[]): void => { - if (errorListener !== undefined) { - emitter.removeListener("error", errorListener); - } - resolve(args); - }; - let errorListener: GenericFunction; - - // Adding an error listener is not optional because - // if an error is thrown on an event emitter we cannot - // guarantee that the actual event we are waiting will - // be fired. The result could be a silent way to create - // memory or file descriptor leaks, which is something - // we should avoid. - if (name !== "error") { + /** + * Creates a Promise that is fulfilled when the EventEmitter emits the given + * event or that is rejected when the EventEmitter emits 'error'. The Promise + * will resolve with an array of all the arguments emitted to the given event. + */ + public static once( + emitter: EventEmitter | EventTarget, + name: string, + // deno-lint-ignore no-explicit-any + ): Promise { + return new Promise((resolve, reject) => { + if (emitter instanceof EventTarget) { + // EventTarget does not have `error` event semantics like Node + // EventEmitters, we do not listen to `error` events here. + emitter.addEventListener( + name, + (...args) => { + resolve(args); + }, + { once: true, passive: false, capture: false }, + ); + return; + } else if (emitter instanceof EventEmitter) { // deno-lint-ignore no-explicit-any - errorListener = (err: any): void => { - emitter.removeListener(name, eventListener); - reject(err); + const eventListener = (...args: any[]): void => { + if (errorListener !== undefined) { + emitter.removeListener("error", errorListener); + } + resolve(args); }; + let errorListener: GenericFunction; + + // Adding an error listener is not optional because + // if an error is thrown on an event emitter we cannot + // guarantee that the actual event we are waiting will + // be fired. The result could be a silent way to create + // memory or file descriptor leaks, which is something + // we should avoid. + if (name !== "error") { + // deno-lint-ignore no-explicit-any + errorListener = (err: any): void => { + emitter.removeListener(name, eventListener); + reject(err); + }; + + emitter.once("error", errorListener); + } - emitter.once("error", errorListener); + emitter.once(name, eventListener); + return; } + }); + } - emitter.once(name, eventListener); - return; - } - }); -} + /** + * Returns an AsyncIterator that iterates eventName events. It will throw if + * the EventEmitter emits 'error'. It removes all listeners when exiting the + * loop. The value returned by each iteration is an array composed of the + * emitted event arguments. + */ + public static on( + emitter: EventEmitter, + event: string | symbol, + ): AsyncIterable { + // deno-lint-ignore no-explicit-any + const unconsumedEventValues: any[] = []; + // deno-lint-ignore no-explicit-any + const unconsumedPromises: any[] = []; + let error: Error | null = null; + let finished = false; -// deno-lint-ignore no-explicit-any -function createIterResult(value: any, done: boolean): IteratorResult { - return { value, done }; -} + const iterator = { + // deno-lint-ignore no-explicit-any + next(): Promise> { + // First, we consume all unread events + // deno-lint-ignore no-explicit-any + const value: any = unconsumedEventValues.shift(); + if (value) { + return Promise.resolve(createIterResult(value, false)); + } -interface AsyncInterable { - // deno-lint-ignore no-explicit-any - next(): Promise>; - // deno-lint-ignore no-explicit-any - return(): Promise>; - throw(err: Error): void; - // deno-lint-ignore no-explicit-any - [Symbol.asyncIterator](): any; -} + // Then we error, if an error happened + // This happens one time if at all, because after 'error' + // we stop listening + if (error) { + const p: Promise = Promise.reject(error); + // Only the first element errors + error = null; + return p; + } -/** - * Returns an AsyncIterator that iterates eventName events. It will throw if - * the EventEmitter emits 'error'. It removes all listeners when exiting the - * loop. The value returned by each iteration is an array composed of the - * emitted event arguments. - */ -export function on( - emitter: EventEmitter, - event: string | symbol, -): AsyncInterable { - // deno-lint-ignore no-explicit-any - const unconsumedEventValues: any[] = []; - // deno-lint-ignore no-explicit-any - const unconsumedPromises: any[] = []; - let error: Error | null = null; - let finished = false; + // If the iterator is finished, resolve to done + if (finished) { + return Promise.resolve(createIterResult(undefined, true)); + } + + // Wait until an event happens + return new Promise(function (resolve, reject) { + unconsumedPromises.push({ resolve, reject }); + }); + }, - const iterator = { - // deno-lint-ignore no-explicit-any - next(): Promise> { - // First, we consume all unread events // deno-lint-ignore no-explicit-any - const value: any = unconsumedEventValues.shift(); - if (value) { - return Promise.resolve(createIterResult(value, false)); - } + return(): Promise> { + emitter.removeListener(event, eventHandler); + emitter.removeListener("error", errorHandler); + finished = true; - // Then we error, if an error happened - // This happens one time if at all, because after 'error' - // we stop listening - if (error) { - const p: Promise = Promise.reject(error); - // Only the first element errors - error = null; - return p; - } + for (const promise of unconsumedPromises) { + promise.resolve(createIterResult(undefined, true)); + } - // If the iterator is finished, resolve to done - if (finished) { return Promise.resolve(createIterResult(undefined, true)); - } - - // Wait until an event happens - return new Promise(function (resolve, reject) { - unconsumedPromises.push({ resolve, reject }); - }); - }, + }, - // deno-lint-ignore no-explicit-any - return(): Promise> { - emitter.removeListener(event, eventHandler); - emitter.removeListener("error", errorHandler); - finished = true; + throw(err: Error): void { + error = err; + emitter.removeListener(event, eventHandler); + emitter.removeListener("error", errorHandler); + }, - for (const promise of unconsumedPromises) { - promise.resolve(createIterResult(undefined, true)); - } + // deno-lint-ignore no-explicit-any + [Symbol.asyncIterator](): any { + return this; + }, + }; - return Promise.resolve(createIterResult(undefined, true)); - }, + emitter.on(event, eventHandler); + emitter.on("error", errorHandler); - throw(err: Error): void { - error = err; - emitter.removeListener(event, eventHandler); - emitter.removeListener("error", errorHandler); - }, + return iterator; // deno-lint-ignore no-explicit-any - [Symbol.asyncIterator](): any { - return this; - }, - }; - - emitter.on(event, eventHandler); - emitter.on("error", errorHandler); - - return iterator; - - // deno-lint-ignore no-explicit-any - function eventHandler(...args: any[]): void { - const promise = unconsumedPromises.shift(); - if (promise) { - promise.resolve(createIterResult(args, false)); - } else { - unconsumedEventValues.push(args); + function eventHandler(...args: any[]): void { + const promise = unconsumedPromises.shift(); + if (promise) { + promise.resolve(createIterResult(args, false)); + } else { + unconsumedEventValues.push(args); + } } - } - // deno-lint-ignore no-explicit-any - function errorHandler(err: any): void { - finished = true; + // deno-lint-ignore no-explicit-any + function errorHandler(err: any): void { + finished = true; - const toError = unconsumedPromises.shift(); - if (toError) { - toError.reject(err); - } else { - // The next time we call next() - error = err; - } + const toError = unconsumedPromises.shift(); + if (toError) { + toError.reject(err); + } else { + // The next time we call next() + error = err; + } - iterator.return(); + iterator.return(); + } } } -export const captureRejectionSymbol = Symbol.for("nodejs.rejection"); + +export { EventEmitter }; +export const once = EventEmitter.once; +export const on = EventEmitter.on; +export const captureRejectionSymbol = EventEmitter.captureRejectionSymbol; +export const errorMonitor = EventEmitter.errorMonitor; diff --git a/std/node/fs.ts b/std/node/fs.ts index 052394e21073d..04adbf417e0d9 100644 --- a/std/node/fs.ts +++ b/std/node/fs.ts @@ -1,68 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { access, accessSync } from "./_fs/_fs_access.ts"; -import { appendFile, appendFileSync } from "./_fs/_fs_appendFile.ts"; -import { chmod, chmodSync } from "./_fs/_fs_chmod.ts"; -import { chown, chownSync } from "./_fs/_fs_chown.ts"; -import { close, closeSync } from "./_fs/_fs_close.ts"; -import * as constants from "./_fs/_fs_constants.ts"; -import { readFile, readFileSync } from "./_fs/_fs_readFile.ts"; -import { readlink, readlinkSync } from "./_fs/_fs_readlink.ts"; -import { exists, existsSync } from "./_fs/_fs_exists.ts"; -import { mkdir, mkdirSync } from "./_fs/_fs_mkdir.ts"; -import { copyFile, copyFileSync } from "./_fs/_fs_copy.ts"; -import { writeFile, writeFileSync } from "./_fs/_fs_writeFile.ts"; -import { readdir, readdirSync } from "./_fs/_fs_readdir.ts"; -import { realpath, realpathSync } from "./_fs/_fs_realpath.ts"; -import { rename, renameSync } from "./_fs/_fs_rename.ts"; -import { rmdir, rmdirSync } from "./_fs/_fs_rmdir.ts"; -import { unlink, unlinkSync } from "./_fs/_fs_unlink.ts"; -import { watch } from "./_fs/_fs_watch.ts"; -import { open, openSync } from "./_fs/_fs_open.ts"; -import { stat, statSync } from "./_fs/_fs_stat.ts"; -import { lstat, lstatSync } from "./_fs/_fs_lstat.ts"; - -import * as promises from "./_fs/promises/mod.ts"; - -export { - access, - accessSync, - appendFile, - appendFileSync, - chmod, - chmodSync, - chown, - chownSync, - close, - closeSync, - constants, - copyFile, - copyFileSync, - exists, - existsSync, - lstat, - lstatSync, - mkdir, - mkdirSync, - open, - openSync, - promises, - readdir, - readdirSync, - readFile, - readFileSync, - readlink, - readlinkSync, - realpath, - realpathSync, - rename, - renameSync, - rmdir, - rmdirSync, - stat, - statSync, - unlink, - unlinkSync, - watch, - writeFile, - writeFileSync, -}; +export * from "./_fs.ts"; +import * as m from "./_fs.ts"; +export default m; diff --git a/std/node/os.ts b/std/node/os.ts index 14bf20abd2ad2..1599fbeb37b49 100644 --- a/std/node/os.ts +++ b/std/node/os.ts @@ -1,224 +1,4 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -import { notImplemented } from "./_utils.ts"; -import { validateIntegerRange } from "./_utils.ts"; -import { EOL as fsEOL } from "../fs/eol.ts"; -import process from "./process.ts"; - -const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno/issues/3802"; - -interface CPUTimes { - /** The number of milliseconds the CPU has spent in user mode */ - user: number; - - /** The number of milliseconds the CPU has spent in nice mode */ - nice: number; - - /** The number of milliseconds the CPU has spent in sys mode */ - sys: number; - - /** The number of milliseconds the CPU has spent in idle mode */ - idle: number; - - /** The number of milliseconds the CPU has spent in irq mode */ - irq: number; -} - -interface CPUCoreInfo { - model: string; - - /** in MHz */ - speed: number; - - times: CPUTimes; -} - -interface NetworkAddress { - /** The assigned IPv4 or IPv6 address */ - address: string; - - /** The IPv4 or IPv6 network mask */ - netmask: string; - - family: "IPv4" | "IPv6"; - - /** The MAC address of the network interface */ - mac: string; - - /** true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false */ - internal: boolean; - - /** The numeric IPv6 scope ID (only specified when family is IPv6) */ - scopeid?: number; - - /** The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. */ - cidr: string; -} - -interface NetworkInterfaces { - [key: string]: NetworkAddress[]; -} - -export interface UserInfoOptions { - encoding: string; -} - -interface UserInfo { - username: string; - uid: number; - gid: number; - shell: string; - homedir: string; -} - -arch[Symbol.toPrimitive] = (): string => arch(); -endianness[Symbol.toPrimitive] = (): string => endianness(); -freemem[Symbol.toPrimitive] = (): number => freemem(); -homedir[Symbol.toPrimitive] = (): string | null => homedir(); -hostname[Symbol.toPrimitive] = (): string | null => hostname(); -platform[Symbol.toPrimitive] = (): string => platform(); -release[Symbol.toPrimitive] = (): string => release(); -totalmem[Symbol.toPrimitive] = (): number => totalmem(); -type[Symbol.toPrimitive] = (): string => type(); -uptime[Symbol.toPrimitive] = (): number => uptime(); - -/** Returns the operating system CPU architecture for which the Deno binary was compiled */ -export function arch(): string { - return Deno.build.arch; -} - -/** Not yet implemented */ -export function cpus(): CPUCoreInfo[] { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** - * Returns a string identifying the endianness of the CPU for which the Deno - * binary was compiled. Possible values are 'BE' for big endian and 'LE' for - * little endian. - **/ -export function endianness(): "BE" | "LE" { - // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness - const buffer = new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true /* littleEndian */); - // Int16Array uses the platform's endianness. - return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; -} - -/** Return free memory amount */ -export function freemem(): number { - return Deno.systemMemoryInfo().free; -} - -/** Not yet implemented */ -export function getPriority(pid = 0): number { - validateIntegerRange(pid, "pid"); - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the string path of the current user's home directory. */ -export function homedir(): string | null { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the host name of the operating system as a string. */ -export function hostname(): string { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns an array containing the 1, 5, and 15 minute load averages */ -export function loadavg(): number[] { - if (Deno.build.os === "windows") { - return [0, 0, 0]; - } - return Deno.loadavg(); -} - -/** Not yet implemented */ -export function networkInterfaces(): NetworkInterfaces { - notImplemented(SEE_GITHUB_ISSUE); -} -/** Returns the a string identifying the operating system platform. The value is set at compile time. Possible values are 'darwin', 'linux', and 'win32'. */ -export function platform(): string { - return process.platform; -} - -/** Returns the operating system as a string */ -export function release(): string { - return Deno.osRelease(); -} - -/** Not yet implemented */ -export function setPriority(pid: number, priority?: number): void { - /* The node API has the 'pid' as the first parameter and as optional. - This makes for a problematic implementation in Typescript. */ - if (priority === undefined) { - priority = pid; - pid = 0; - } - validateIntegerRange(pid, "pid"); - validateIntegerRange(priority, "priority", -20, 19); - - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the operating system's default directory for temporary files as a string. */ -export function tmpdir(): string | null { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Return total physical memory amount */ -export function totalmem(): number { - return Deno.systemMemoryInfo().total; -} - -/** Not yet implemented */ -export function type(): string { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Not yet implemented */ -export function uptime(): number { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Not yet implemented */ -export function userInfo( - options: UserInfoOptions = { encoding: "utf-8" }, -): UserInfo { - notImplemented(SEE_GITHUB_ISSUE); -} - -export const constants = { - // UV_UDP_REUSEADDR: 4, //see https://nodejs.org/docs/latest-v12.x/api/os.html#os_libuv_constants - dlopen: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_dlopen_constants - }, - errno: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_error_constants - }, - signals: Deno.Signal, - priority: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_priority_constants - }, -}; - -export const EOL = Deno.build.os == "windows" ? fsEOL.CRLF : fsEOL.LF; +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +export * from "./_os.ts"; +import * as m from "./_os.ts"; +export default m; diff --git a/std/node/path.ts b/std/node/path.ts index a206eb7924464..9234c9de970d0 100644 --- a/std/node/path.ts +++ b/std/node/path.ts @@ -1,2 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. export * from "../path/mod.ts"; +import * as m from "../path/mod.ts"; +export default m; diff --git a/std/node/querystring.ts b/std/node/querystring.ts index a49f55f5478e1..ff68b2ae87c18 100644 --- a/std/node/querystring.ts +++ b/std/node/querystring.ts @@ -1,156 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -interface ParseOptions { - /** The function to use when decoding percent-encoded characters in the query string. */ - decodeURIComponent?: (string: string) => string; - /** Specifies the maximum number of keys to parse. */ - maxKeys?: number; -} - -export const hexTable = new Array(256); -for (let i = 0; i < 256; ++i) { - hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); -} - -/** - * Parses a URL query string into a collection of key and value pairs. - * @param str The URL query string to parse - * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. - * @param eq The substring used to delimit keys and values in the query string. Default: '='. - * @param options The parse options - */ -export function parse( - str: string, - sep = "&", - eq = "=", - { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {}, -): { [key: string]: string[] | string } { - const entries = str - .split(sep) - .map((entry) => entry.split(eq).map(decodeURIComponent)); - const final: { [key: string]: string[] | string } = {}; - - let i = 0; - while (true) { - if ((Object.keys(final).length === maxKeys && !!maxKeys) || !entries[i]) { - break; - } - - const [key, val] = entries[i]; - if (final[key]) { - if (Array.isArray(final[key])) { - (final[key] as string[]).push(val); - } else { - final[key] = [final[key] as string, val]; - } - } else { - final[key] = val; - } - - i++; - } - - return final; -} - -interface StringifyOptions { - /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ - encodeURIComponent?: (string: string) => string; -} - -export function encodeStr( - str: string, - noEscapeTable: number[], - hexTable: string[], -): string { - const len = str.length; - if (len === 0) return ""; - - let out = ""; - let lastPos = 0; - - for (let i = 0; i < len; i++) { - let c = str.charCodeAt(i); - // ASCII - if (c < 0x80) { - if (noEscapeTable[c] === 1) continue; - if (lastPos < i) out += str.slice(lastPos, i); - lastPos = i + 1; - out += hexTable[c]; - continue; - } - - if (lastPos < i) out += str.slice(lastPos, i); - - // Multi-byte characters ... - if (c < 0x800) { - lastPos = i + 1; - out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)]; - continue; - } - if (c < 0xd800 || c >= 0xe000) { - lastPos = i + 1; - out += hexTable[0xe0 | (c >> 12)] + - hexTable[0x80 | ((c >> 6) & 0x3f)] + - hexTable[0x80 | (c & 0x3f)]; - continue; - } - // Surrogate pair - ++i; - - // This branch should never happen because all URLSearchParams entries - // should already be converted to USVString. But, included for - // completion's sake anyway. - if (i >= len) throw new Deno.errors.InvalidData("invalid URI"); - - const c2 = str.charCodeAt(i) & 0x3ff; - - lastPos = i + 1; - c = 0x10000 + (((c & 0x3ff) << 10) | c2); - out += hexTable[0xf0 | (c >> 18)] + - hexTable[0x80 | ((c >> 12) & 0x3f)] + - hexTable[0x80 | ((c >> 6) & 0x3f)] + - hexTable[0x80 | (c & 0x3f)]; - } - if (lastPos === 0) return str; - if (lastPos < len) return out + str.slice(lastPos); - return out; -} - -/** - * Produces a URL query string from a given obj by iterating through the object's "own properties". - * @param obj The object to serialize into a URL query string. - * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. - * @param eq The substring used to delimit keys and values in the query string. Default: '='. - * @param options The stringify options - */ -export function stringify( - // deno-lint-ignore no-explicit-any - obj: Record, - sep = "&", - eq = "=", - { encodeURIComponent = escape }: StringifyOptions = {}, -): string { - const final = []; - - for (const entry of Object.entries(obj)) { - if (Array.isArray(entry[1])) { - for (const val of entry[1]) { - final.push(encodeURIComponent(entry[0]) + eq + encodeURIComponent(val)); - } - } else if (typeof entry[1] !== "object" && entry[1] !== undefined) { - final.push(entry.map(encodeURIComponent).join(eq)); - } else { - final.push(encodeURIComponent(entry[0]) + eq); - } - } - - return final.join(sep); -} - -/** Alias of querystring.parse() */ -export const decode = parse; -/** Alias of querystring.stringify() */ -export const encode = stringify; -export const unescape = decodeURIComponent; -export const escape = encodeURIComponent; +export * from "./_querystring.ts"; +import * as m from "./_querystring.ts"; +export default m; diff --git a/std/node/string_decoder.ts b/std/node/string_decoder.ts index ce7c19538ed52..cc6fb5186af53 100644 --- a/std/node/string_decoder.ts +++ b/std/node/string_decoder.ts @@ -1,299 +1,4 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -import { Buffer } from "./buffer.ts"; -import { normalizeEncoding as castEncoding, notImplemented } from "./_utils.ts"; - -enum NotImplemented { - "ascii", - "latin1", - "utf16le", -} - -function normalizeEncoding(enc?: string): string { - const encoding = castEncoding(enc ?? null); - if (encoding && encoding in NotImplemented) notImplemented(encoding); - if (!encoding && typeof enc === "string" && enc.toLowerCase() !== "raw") { - throw new Error(`Unknown encoding: ${enc}`); - } - return String(encoding); -} -/* - * Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a - * continuation byte. If an invalid byte is detected, -2 is returned. - * */ -function utf8CheckByte(byte: number): number { - if (byte <= 0x7f) return 0; - else if (byte >> 5 === 0x06) return 2; - else if (byte >> 4 === 0x0e) return 3; - else if (byte >> 3 === 0x1e) return 4; - return byte >> 6 === 0x02 ? -1 : -2; -} - -/* - * Checks at most 3 bytes at the end of a Buffer in order to detect an - * incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) - * needed to complete the UTF-8 character (if applicable) are returned. - * */ -function utf8CheckIncomplete( - self: StringDecoderBase, - buf: Buffer, - i: number, -): number { - let j = buf.length - 1; - if (j < i) return 0; - let nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 1; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) nb = 0; - else self.lastNeed = nb - 3; - } - return nb; - } - return 0; -} - -/* - * Validates as many continuation bytes for a multi-byte UTF-8 character as - * needed or are available. If we see a non-continuation byte where we expect - * one, we "replace" the validated continuation bytes we've seen so far with - * a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding - * behavior. The continuation byte check is included three times in the case - * where all of the continuation bytes for a character exist in the same buffer. - * It is also done this way as a slight performance increase instead of using a - * loop. - * */ -function utf8CheckExtraBytes( - self: StringDecoderBase, - buf: Buffer, -): string | undefined { - if ((buf[0] & 0xc0) !== 0x80) { - self.lastNeed = 0; - return "\ufffd"; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xc0) !== 0x80) { - self.lastNeed = 1; - return "\ufffd"; - } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xc0) !== 0x80) { - self.lastNeed = 2; - return "\ufffd"; - } - } - } -} - -/* - * Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. - * */ -function utf8FillLastComplete( - this: StringDecoderBase, - buf: Buffer, -): string | undefined { - const p = this.lastTotal - this.lastNeed; - const r = utf8CheckExtraBytes(this, buf); - if (r !== undefined) return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} - -/* - * Attempts to complete a partial non-UTF-8 character using bytes from a Buffer - * */ -function utf8FillLastIncomplete( - this: StringDecoderBase, - buf: Buffer, -): string | undefined { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; -} - -/* - * Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a - * partial character, the character's bytes are buffered until the required - * number of bytes are available. - * */ -function utf8Text(this: StringDecoderBase, buf: Buffer, i: number): string { - const total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) return buf.toString("utf8", i); - this.lastTotal = total; - const end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString("utf8", i, end); -} - -/* - * For UTF-8, a replacement character is added when ending on a partial - * character. - * */ -function utf8End(this: Utf8Decoder, buf?: Buffer): string { - const r = buf && buf.length ? this.write(buf) : ""; - if (this.lastNeed) return r + "\ufffd"; - return r; -} - -function utf8Write(this: Utf8Decoder | Base64Decoder, buf: Buffer): string { - if (buf.length === 0) return ""; - let r; - let i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) return ""; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; - } - if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); - return r || ""; -} - -function base64Text(this: StringDecoderBase, buf: Buffer, i: number): string { - const n = (buf.length - i) % 3; - if (n === 0) return buf.toString("base64", i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString("base64", i, buf.length - n); -} - -function base64End(this: Base64Decoder, buf?: Buffer): string { - const r = buf && buf.length ? this.write(buf) : ""; - if (this.lastNeed) { - return r + this.lastChar.toString("base64", 0, 3 - this.lastNeed); - } - return r; -} - -function simpleWrite(this: StringDecoderBase, buf: Buffer): string { - return buf.toString(this.encoding); -} - -function simpleEnd(this: GenericDecoder, buf?: Buffer): string { - return buf && buf.length ? this.write(buf) : ""; -} - -class StringDecoderBase { - public lastChar: Buffer; - public lastNeed = 0; - public lastTotal = 0; - constructor(public encoding: string, nb: number) { - this.lastChar = Buffer.allocUnsafe(nb); - } -} - -class Base64Decoder extends StringDecoderBase { - public end = base64End; - public fillLast = utf8FillLastIncomplete; - public text = base64Text; - public write = utf8Write; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 3); - } -} - -class GenericDecoder extends StringDecoderBase { - public end = simpleEnd; - public fillLast = undefined; - public text = utf8Text; - public write = simpleWrite; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 4); - } -} - -class Utf8Decoder extends StringDecoderBase { - public end = utf8End; - public fillLast = utf8FillLastComplete; - public text = utf8Text; - public write = utf8Write; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 4); - } -} - -/* - * StringDecoder provides an interface for efficiently splitting a series of - * buffers into a series of JS strings without breaking apart multi-byte - * characters. - * */ -export class StringDecoder { - public encoding: string; - public end: (buf?: Buffer) => string; - public fillLast: ((buf: Buffer) => string | undefined) | undefined; - public lastChar: Buffer; - public lastNeed: number; - public lastTotal: number; - public text: (buf: Buffer, n: number) => string; - public write: (buf: Buffer) => string; - - constructor(encoding?: string) { - let decoder; - switch (encoding) { - case "utf8": - decoder = new Utf8Decoder(encoding); - break; - case "base64": - decoder = new Base64Decoder(encoding); - break; - default: - decoder = new GenericDecoder(encoding); - } - this.encoding = decoder.encoding; - this.end = decoder.end; - this.fillLast = decoder.fillLast; - this.lastChar = decoder.lastChar; - this.lastNeed = decoder.lastNeed; - this.lastTotal = decoder.lastTotal; - this.text = decoder.text; - this.write = decoder.write; - } -} +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +export * from "./_string_decoder.ts"; +import * as m from "./_string_decoder.ts"; +export default m; diff --git a/std/node/string_decoder_test.ts b/std/node/string_decoder_test.ts index 7c36ea3832910..c18f5ce56b430 100644 --- a/std/node/string_decoder_test.ts +++ b/std/node/string_decoder_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { assertEquals } from "../testing/asserts.ts"; -import Buffer from "./buffer.ts"; +import { Buffer } from "./buffer.ts"; import { StringDecoder } from "./string_decoder.ts"; Deno.test({ diff --git a/std/node/timers.ts b/std/node/timers.ts index 872e1f9ae069c..095f6c5dc4054 100644 --- a/std/node/timers.ts +++ b/std/node/timers.ts @@ -1,14 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -// TODO: implement the 'NodeJS.Timeout' and 'NodeJS.Immediate' versions of the timers. -// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1163ead296d84e7a3c80d71e7c81ecbd1a130e9a/types/node/v12/globals.d.ts#L1120-L1131 -export const setTimeout = window.setTimeout; -export const clearTimeout = window.clearTimeout; -export const setInterval = window.setInterval; -export const clearInterval = window.clearInterval; -export const setImmediate = ( - // deno-lint-ignore no-explicit-any - cb: (...args: any[]) => void, - // deno-lint-ignore no-explicit-any - ...args: any[] -): number => window.setTimeout(cb, 0, ...args); -export const clearImmediate = window.clearTimeout; +export * from "./_timers.ts"; +import * as m from "./_timers.ts"; +export default m; diff --git a/std/node/url.ts b/std/node/url.ts index 82daa25e93d7b..2aa2702aadcab 100644 --- a/std/node/url.ts +++ b/std/node/url.ts @@ -1,140 +1,4 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -import { - CHAR_BACKWARD_SLASH, - CHAR_FORWARD_SLASH, - CHAR_LOWERCASE_A, - CHAR_LOWERCASE_Z, -} from "../path/_constants.ts"; -import * as path from "./path.ts"; - -const isWindows = Deno.build.os === "windows"; - -const forwardSlashRegEx = /\//g; -const percentRegEx = /%/g; -const backslashRegEx = /\\/g; -const newlineRegEx = /\n/g; -const carriageReturnRegEx = /\r/g; -const tabRegEx = /\t/g; - -const _url = URL; -export { _url as URL }; - -/** - * Get fully resolved platform-specific file path from the given URL string/ object - * @param path The file URL string or URL object to convert to a path - */ -export function fileURLToPath(path: string | URL): string { - if (typeof path === "string") path = new URL(path); - else if (!(path instanceof URL)) { - throw new Deno.errors.InvalidData( - "invalid argument path , must be a string or URL", - ); - } - if (path.protocol !== "file:") { - throw new Deno.errors.InvalidData("invalid url scheme"); - } - return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path); -} - -function getPathFromURLWin(url: URL): string { - const hostname = url.hostname; - let pathname = url.pathname; - for (let n = 0; n < pathname.length; n++) { - if (pathname[n] === "%") { - const third = pathname.codePointAt(n + 2) || 0x20; - if ( - (pathname[n + 1] === "2" && third === 102) || // 2f 2F / - (pathname[n + 1] === "5" && third === 99) - ) { - // 5c 5C \ - throw new Deno.errors.InvalidData( - "must not include encoded \\ or / characters", - ); - } - } - } - - pathname = pathname.replace(forwardSlashRegEx, "\\"); - pathname = decodeURIComponent(pathname); - if (hostname !== "") { - //TODO add support for punycode encodings - return `\\\\${hostname}${pathname}`; - } else { - // Otherwise, it's a local path that requires a drive letter - const letter = pathname.codePointAt(1)! | 0x20; - const sep = pathname[2]; - if ( - letter < CHAR_LOWERCASE_A || - letter > CHAR_LOWERCASE_Z || // a..z A..Z - sep !== ":" - ) { - throw new Deno.errors.InvalidData("file url path must be absolute"); - } - return pathname.slice(1); - } -} - -function getPathFromURLPosix(url: URL): string { - if (url.hostname !== "") { - throw new Deno.errors.InvalidData("invalid file url hostname"); - } - const pathname = url.pathname; - for (let n = 0; n < pathname.length; n++) { - if (pathname[n] === "%") { - const third = pathname.codePointAt(n + 2) || 0x20; - if (pathname[n + 1] === "2" && third === 102) { - throw new Deno.errors.InvalidData( - "must not include encoded / characters", - ); - } - } - } - return decodeURIComponent(pathname); -} - -/** Get fully resolved platform-specific File URL from the given file path */ -export function pathToFileURL(filepath: string): URL { - let resolved = path.resolve(filepath); - // path.resolve strips trailing slashes so we must add them back - const filePathLast = filepath.charCodeAt(filepath.length - 1); - if ( - (filePathLast === CHAR_FORWARD_SLASH || - (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && - resolved[resolved.length - 1] !== path.sep - ) { - resolved += "/"; - } - const outURL = new URL("file://"); - if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25"); - // In posix, "/" is a valid character in paths - if (!isWindows && resolved.includes("\\")) { - resolved = resolved.replace(backslashRegEx, "%5C"); - } - if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A"); - if (resolved.includes("\r")) { - resolved = resolved.replace(carriageReturnRegEx, "%0D"); - } - if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09"); - outURL.pathname = resolved; - return outURL; -} +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +export * from "./_url.ts"; +import * as m from "./_url.ts"; +export default m; diff --git a/std/node/util.ts b/std/node/util.ts index 9cf9966701abe..b9239ec5d2984 100644 --- a/std/node/util.ts +++ b/std/node/util.ts @@ -1,134 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export { promisify } from "./_util/_util_promisify.ts"; -export { callbackify } from "./_util/_util_callbackify.ts"; -import { codes, errorMap } from "./_errors.ts"; -import * as types from "./_util/_util_types.ts"; -export { types }; - -const NumberIsSafeInteger = Number.isSafeInteger; -const { - ERR_OUT_OF_RANGE, - ERR_INVALID_ARG_TYPE, -} = codes; - -const DEFAULT_INSPECT_OPTIONS = { - showHidden: false, - depth: 2, - colors: false, - customInspect: true, - showProxy: false, - maxArrayLength: 100, - maxStringLength: Infinity, - breakLength: 80, - compact: 3, - sorted: false, - getters: false, -}; - -inspect.defaultOptions = DEFAULT_INSPECT_OPTIONS; -inspect.custom = Deno.customInspect; - -// TODO(schwarzkopfb): make it in-line with Node's implementation -// Ref: https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_inspect_object_options -// deno-lint-ignore no-explicit-any -export function inspect(object: unknown, ...opts: any): string { - opts = { ...DEFAULT_INSPECT_OPTIONS, ...opts }; - return Deno.inspect(object, { - depth: opts.depth, - iterableLimit: opts.maxArrayLength, - compact: !!opts.compact, - sorted: !!opts.sorted, - showProxy: !!opts.showProxy, - }); -} - -/** @deprecated - use `Array.isArray()` instead. */ -export function isArray(value: unknown): boolean { - return Array.isArray(value); -} - -/** @deprecated - use `typeof value === "boolean" || value instanceof Boolean` instead. */ -export function isBoolean(value: unknown): boolean { - return typeof value === "boolean" || value instanceof Boolean; -} - -/** @deprecated - use `value === null` instead. */ -export function isNull(value: unknown): boolean { - return value === null; -} - -/** @deprecated - use `value === null || value === undefined` instead. */ -export function isNullOrUndefined(value: unknown): boolean { - return value === null || value === undefined; -} - -/** @deprecated - use `typeof value === "number" || value instanceof Number` instead. */ -export function isNumber(value: unknown): boolean { - return typeof value === "number" || value instanceof Number; -} - -/** @deprecated - use `typeof value === "string" || value instanceof String` instead. */ -export function isString(value: unknown): boolean { - return typeof value === "string" || value instanceof String; -} - -/** @deprecated - use `typeof value === "symbol"` instead. */ -export function isSymbol(value: unknown): boolean { - return typeof value === "symbol"; -} - -/** @deprecated - use `value === undefined` instead. */ -export function isUndefined(value: unknown): boolean { - return value === undefined; -} - -/** @deprecated - use `value !== null && typeof value === "object"` instead. */ -export function isObject(value: unknown): boolean { - return value !== null && typeof value === "object"; -} - -/** @deprecated - use `e instanceof Error` instead. */ -export function isError(e: unknown): boolean { - return e instanceof Error; -} - -/** @deprecated - use `typeof value === "function"` instead. */ -export function isFunction(value: unknown): boolean { - return typeof value === "function"; -} - -/** @deprecated - use `value instanceof RegExp` instead. */ -export function isRegExp(value: unknown): boolean { - return value instanceof RegExp; -} - -/** @deprecated - use `value === null || (typeof value !== "object" && typeof value !== "function")` instead. */ -export function isPrimitive(value: unknown): boolean { - return ( - value === null || (typeof value !== "object" && typeof value !== "function") - ); -} - -/** - * Returns a system error name from an error code number. - * @param code error code number - */ -export function getSystemErrorName(code: number): string | undefined { - if (typeof code !== "number") { - throw new ERR_INVALID_ARG_TYPE("err", "number", code); - } - if (code >= 0 || !NumberIsSafeInteger(code)) { - throw new ERR_OUT_OF_RANGE("err", "a negative integer", code); - } - return errorMap.get(code)?.[0]; -} - -import { _TextDecoder, _TextEncoder } from "./_utils.ts"; - -/** The global TextDecoder */ -export type TextDecoder = import("./_utils.ts")._TextDecoder; -export const TextDecoder = _TextDecoder; - -/** The global TextEncoder */ -export type TextEncoder = import("./_utils.ts")._TextEncoder; -export const TextEncoder = _TextEncoder; +export * from "./_util.ts"; +import * as m from "./_util.ts"; +export default m; diff --git a/tools/format.js b/tools/format.js index 9786e6fe37af0..1d891f361b27f 100755 --- a/tools/format.js +++ b/tools/format.js @@ -3,10 +3,11 @@ import { getPrebuiltToolPath, getSources, join, ROOT_PATH } from "./util.js"; async function dprint() { + const configFile = join(ROOT_PATH, ".dprintrc.json"); const execPath = getPrebuiltToolPath("dprint"); console.log("dprint"); const p = Deno.run({ - cmd: [execPath, "fmt"], + cmd: [execPath, "fmt", "--config=" + configFile], }); const { success } = await p.status(); if (!success) {